我们做过两版 AI 电话。

第一版是传统三段式:ASR → LLM → TTS。链路内部我们自己打点,大约 600ms。按行业里的说法,这个数不算慢。

但用户还是说:你们的机器人反应慢。

第二版换成端到端实时语音模型,音频进、音频出。这次没人说慢了。

然后出现了另一个问题:太快了,快得假。

这篇文章讲两件事。第一,为什么 600ms 还是被说慢。第二,为什么"太快"也会出问题——这一点是我换完架构才想明白的。

一、用户说的"延迟",不是一个数

用户感知的等待,至少由四段组成:

  • 判停:他说完了,系统还要再等一会儿才确认"说完了"
  • 链路:文本进模型,模型出语音
  • 下行:重采样、网络、播放缓冲,才到他耳朵
  • 打断:他插话,AI 还要多久才停

这四段里,只有第二段是模型的速度,其他三段都是自己配的。

我翻了一遍自己的项目代码,同一个系统里就有两套默认值:Qwen 适配器判停 1000ms,OpenAI 适配器 500ms。还有一套按环境分的表:安静 800ms、嘈杂 1000ms、极嘈杂 800ms、回声 1200ms。代码注释里留着调整的原因——安静环境把打断阈值调高,是为了不被清嗓声打断;嘈杂环境把它调低,是因为实测下来,用户插话大多在 0.8 秒左右。

这些数不是拍脑袋定的,是拿录音和日志一点点试出来的。

所以用户说"慢",很多时候不是链路慢,是判停配长了。这个账不分清楚,参数怎么调都调不到点上。

二、三段式为什么是"慢半拍"

三段式是串行的:

等 ASR 出最终文本 → 等 LLM 出首 token → 等 TTS 出首包 → 重采样 → 播放缓冲

单看每一段都不算慢,串起来就是"慢半拍"。

听感上最明显的是开头那一下:我说完了,它愣了半拍才开口。不是不出声,是那半拍让你觉得它在处理,而处理得有点吃力。

更麻烦的是打断。用户插话时,三段式要分别停三处:停 ASR、停 LLM 生成、停 TTS 播放。任何一处没停干净,用户就会听到 AI 又多说半句。

你优化的是其中一段,用户感知的是整条链。

三、端到端改了什么

端到端实时语音模型把"音频进 → 文本 → 文本 → 音频出",压成了"音频进 → 音频出"。

听感上的变化很直接:它不再等你把话"翻译完"才动。 你说到一半它已经在听,你说完它接上,中间那半拍没了。

三个原因:

  1. 没有"等 ASR 出最终文本"这道门,音频流直接进模型
  2. 没有 TTS 首包,少了一次文本转语音
  3. 打断只停一处——模型自带服务端 VAD、转录和打断语义,引擎只做音频搬运和状态机

但要说清楚:它去掉的是串行等待,不是物理时间。 网络还在,推理还在。公开测过的各家产品,首音频从几百毫秒到一秒多都有,厂商之间的差距比架构之间的差距还大。

四、太快,也是一种假

这一段是我没预料到的。

第一版用户嫌慢,我们以为换成端到端就解决了。跑了一阵,反馈变成了另一种:

  • "它好像根本没听我说完。"
  • "我一停,它马上就接上,像在抢话。"
  • "感觉它早就知道我要问什么。"

前两条我还能理解成心理作用。第三条让我意识到问题不在速度。

人说话不是这样。

真人对话的节奏是:听完 → 停一下 → 回应。问题越难,停得越久;会"嗯"、"这个"、"我想想"。这个停顿不是浪费时间,是对方正在处理的证据。

AI 秒回,听感上翻译过来就是:"它没听懂,它只是在背台词。" 或者更糟:"答案早就等在那里,根本没在听我说。"

所以我们现在的优化方向,不是继续把延迟往下压,而是把"思考感"加回去:

  • 简单问题(是/否、确认信息):几乎不停
  • 需要查库、计算的:先出声"我看一下",再去查
  • 复杂问题:停久一点,允许有语气词
  • 节奏要有变化:不能每次都停一样长。等长的停顿比秒回还假,那听起来像机器在数秒

这里要分清两件事:

  • 反应能力:判停、链路、打断,还是要压到最低。这是"听得见、跟得上"。
  • 思考节奏:适当停顿、适当迟疑。这是"像个人"。

延迟不是越低越好,是越像人越好。

五、端到端的代价

架构没有免费的。换成端到端之后,我们付出了四笔成本。

第一,模型不听话,人设难控。 三段式里 LLM 输出的是文本,你可以用 prompt、知识库、工具调用把它管得很死。端到端直接出音频,话术的确定性下降,人设更容易跑偏。外呼场景尤其明显:客户问价格、问地址、问预约,三段式可以查库再答;端到端要先把它接回文本流程,或者接受它"自由发挥"。

第二,成本更高。 端到端按音频 token 计费,比三段式的文本 token 贵。单通电话看不出差别,通话量上来以后,是会明显感觉到的。

第三,可观测性变差。 三段式的 ASR 文本、LLM 输出、TTS 文本都能落库,出问题可以逐段查。端到端只有音频,排查要靠录音、转写和日志。

第四,厂商绑定。 各家的协议、音色、打断语义都不一样。换厂商不是改个 URL,适配层要重写。

六、工程上仍然要做的事

端到端不等于什么都不用管。我们仍然保留了几件事,它们解决的不是"快不快",是"稳不稳":

  • 振铃期就预连接模型,别等接通才开始建连
  • 开场白预渲染:接通瞬间就出声,不用等模型首包
  • 下行缓冲设上限:FreeSWITCH 的播放缓冲是定长的,约两秒,不设上限会出现整块静默被丢弃
  • 同步调用挪出事件循环:一次同步的 ESL 调用就能把整个事件循环卡住,所有线路一起停顿;挪出去之后这个停顿降到可以忽略
  • 打断 flush 五步:代号 +1、清队列、放 flush 标记、清残留 buffer、重置重采样器。少一步,用户就会听到半句残留

架构解决量级,工程解决长尾。

七、怎么判断"是不是真的好了"

别只看平均值,也别只靠自己的耳朵——反复听同一段录音,会形成"听感记忆",越听越觉得没问题。

两件事:

  1. 盲听。 同一段对话,三段式和端到端各剪一段,去掉所有标志,找没参与项目的人听,只问一句:"哪个更像在跟人说话?"
  2. 打四个点。 判停、首响、首声、打断停止,各记一次,看 P50 和 P95。平均值会骗人。

具体阈值我不写,因为不同线路、不同话术差很多,别人给的标准照搬过来没意义。

八、边界

  • 600ms 是我们的链路口径:ASR 最终文本 → TTS 首包,不含判停,也不含下行
  • "基本没延迟"是体感描述,不是指标;端到端的 P95 我们还在补
  • 加回思考时间会牺牲一点"反应快"的印象,这是取舍,不是所有人都需要
  • 公开测过的数据,不同厂商、不同音色、不同网络,差异很大,不要拿去当基准

九、最后

如果你的 AI 电话被说"慢",先别换模型,确认三件事:

  1. 判停是不是还停在 1000ms(或者你根本没配过)
  2. 打断是不是还要停三处
  3. 用户说的"慢",是说完要等,还是插话停不下来

如果反过来,用户说的是"假"、"像机器人"、"太快了",那问题可能不在速度,在节奏。

你遇到的是哪一种?评论区聊聊。

前面还写过两篇:一篇讲底层通话,FreeSWITCH 通话接通但无声音:单向音频、NAT 与 RTP 排查步骤;一篇讲实际效果,我做了个 AI 电话助理:它能根据回答继续追问吗?。这篇补的是中间那段——延迟和听感。

—— 听风开窍|AI 电话助理实践记录

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐