企业外呼系统正在从“预先录好的语音菜单”走向“能够理解对话并即时响应的 AI 坐席”。VibeVoip 的核心思路,是把传统 SIP 通信能力与大模型、语音识别和语音合成组合起来,形成一套面向企业业务场景、可以在本地部署的 AI 智能外呼平台。
这类系统的难点并不只在模型效果。电话接通、媒体流传输、实时转写、意图判断、话术生成、语音播放、任务调度和合规审计,任何一环延迟过高或状态不一致,用户都会听到明显的停顿,业务人员也无法可靠追踪结果。
一条完整的外呼链路
可以把一次 AI 外呼拆成几层:
- 任务层:读取客户名单、设置外呼时间、控制并发量和重试策略。
- 通信层:通过 SIP 发起呼叫,接收接通、忙线、无人接听和挂断等事件。
- 媒体层:处理通话音频,并把语音流传给 ASR 或实时语音模型。
- 智能层:根据对话上下文识别客户意图,选择业务流程,生成下一句回复。
- 执行层:通过 TTS 播放回复,记录通话结果,并在需要时转人工或触发业务 API。
典型流程可以表示为:
外呼任务
-> SIP 呼叫
-> 接通事件
-> 音频流
-> ASR 转写
-> 对话状态机 + 大模型
-> TTS 合成
-> 播放语音
-> 结果记录 / 转人工 / 业务回调
这里的关键不是让大模型自由发挥,而是让大模型处在一个有边界的业务状态机中。例如,催收、回访、预约确认和销售线索筛选都有不同的允许动作。模型负责理解自然语言和生成表达,状态机负责决定当前是否允许进入下一步。
为什么本地部署值得考虑
企业电话数据通常包含姓名、联系方式、订单信息和通话内容。把所有音频与转写文本直接发送到外部服务,可能带来数据合规、网络稳定性和成本控制问题。
本地部署可以让企业根据实际情况选择组件:
- 在内网运行 ASR、LLM 或 TTS,减少敏感数据出域。
- 通过 GPU 或 CPU 资源控制并发和推理成本。
- 根据业务术语、产品知识和禁用话术定制模型提示词。
- 在网络受限环境中保持基本的外呼能力。
但本地部署并不等于没有运维成本。团队仍然需要准备 SIP 中继或电话网关、音频编解码支持、模型推理服务、监控、录音存储和故障转移机制。对于实时电话场景,端到端延迟、首字节时间和断线恢复通常比离线文本生成更重要。
可以这样实践:先做一个可替换的对话核心
下面的示例不依赖具体 SIP 厂商或大模型 SDK,而是演示一个可运行的最小对话核心。它把电话转写文本作为输入,根据简单的业务状态生成下一步话术。实际接入时,可以把 handle_transcript 替换为 ASR 回调,把 reply 替换为 LLM 返回结果,再由 TTS 和媒体层播放出去。
将代码保存为 vibevoip_demo.py 后直接运行:
from dataclasses import dataclass, field
from typing import List
@dataclass
class CallSession:
customer_name: str
state: str = "opening"
history: List[str] = field(default_factory=list)
def handle_transcript(session: CallSession, transcript: str) -> str:
"""最小业务状态机;生产环境可在状态判断后调用 LLM。"""
text = transcript.strip()
session.history.append(f"客户:{text}")
if session.state == "opening":
session.state = "confirming"
reply = f"您好,{session.customer_name}。这里是企业服务回访,请问现在方便占用您一分钟吗?"
elif session.state == "confirming":
if any(word in text for word in ("不方便", "没空", "稍后")):
session.state = "callback"
reply = "好的,我可以为您安排稍后回拨。请问哪个时间段比较方便?"
elif any(word in text for word in ("可以", "方便", "好")):
session.state = "survey"
reply = "感谢配合。想了解一下,您最近使用服务时最关注哪一点?"
else:
reply = "我想确认一下,您现在是否方便进行一分钟的回访?"
elif session.state == "survey":
session.state = "closing"
reply = "了解了,我已经记录您的反馈。感谢您的时间,祝您今天愉快。"
elif session.state == "callback":
session.state = "closing"
reply = "好的,已记录回拨时间。我们会按约定联系您。"
else:
reply = "感谢您的接听,再见。"
session.history.append(f"AI:{reply}")
return reply
if __name__ == "__main__":
session = CallSession(customer_name="李女士")
print("AI:", handle_transcript(session, "接通"))
print("AI:", handle_transcript(session, "现在方便"))
print("AI:", handle_transcript(session, "我最关注处理速度"))
print("状态:", session.state)
运行命令:
python3 vibevoip_demo.py
这个示例刻意把通信层和智能层分开。可以这样改造:
- SIP 服务收到接通事件后,创建一个
CallSession。 - ASR 每产生一段稳定转写文本,就调用
handle_transcript。 - 若当前状态允许调用模型,则把
history、业务规则和知识库检索结果组成提示词。 - 将模型返回文本交给 TTS,再通过 RTP 或电话媒体网关播放。
- 挂断时保存状态、转写文本、模型版本和最终业务结果。
大模型应该被业务规则约束
外呼系统不适合只使用一条“请自然地和客户聊天”的提示词。更可靠的提示词至少应说明角色、目标、禁止事项和输出格式。例如,假设系统已经完成了客户身份确认,可以使用类似的约束:
你是企业回访坐席。
目标:确认客户是否愿意接受产品回访,并收集一个最主要的问题。
规则:
1. 每次只说一到两句话。
2. 不要虚构价格、承诺或政策。
3. 客户明确拒绝时,停止推销并礼貌结束。
4. 客户要求人工服务时,输出 TRANSFER_HUMAN。
5. 只能根据当前业务状态选择下一步动作。
当前状态:survey
客户最近一句话:{{transcript}}
请输出 JSON:{"action":"speak|transfer|hangup", "text":"..."}
生产环境中还需要在模型输出之后做结构化校验。无法解析的输出应进入兜底话术,而不是直接播放给客户。涉及身份、付款、医疗、金融或合同承诺时,应将高风险节点交给人工或固定脚本处理。
一天内可完成的最小版本边界
如果目标是快速验证方案,可以把第一版范围控制在一个业务流程和几个固定结果:
- 一个 SIP 呼叫入口。
- 一条固定的外呼任务队列。
- 一个 ASR、一个 LLM 和一个 TTS 适配器。
- 两到三个业务状态,例如开场确认、信息采集和结束。
- 忙线、未接通、拒绝和转人工四种异常路径。
- 基本的通话日志与结果导出。
不要在第一天同时实现多租户计费、复杂知识库、全量质检和十几种业务流程。电话系统的真实问题往往会出现在音频格式、并发限制、超时和异常重试上。先让一条可观测的链路稳定跑通,再扩展智能能力,验证速度会更快。
落地检查清单
上线前至少确认以下事项:
- SIP 中继、号码和呼叫方向已经验证。
- ASR、LLM、TTS 的超时和降级策略明确。
- 每通电话都有唯一会话 ID,事件可以按时间顺序追踪。
- 模型不会绕过业务状态直接执行敏感操作。
- 明确录音、转写、个人信息和数据保留策略。
- 已覆盖拒绝、静音、打断、方言、网络抖动和人工转接场景。
- 可以统计接通率、平均通话时长、转人工率、模型延迟和任务完成率。
VibeVoip 这类系统的价值,来自通信基础设施与 AI 能力的组合:SIP 负责把电话接起来,媒体层负责让声音流动,模型负责理解和表达,而业务状态机负责让整个过程可控。采用时应从一个明确的企业流程开始,用可替换的适配器连接 SIP、ASR、LLM 和 TTS,再根据真实通话数据逐步扩大自动化范围。