语音 Agent 最难的不是听懂,而是知道什么时候闭嘴:打断、确认与超时的实战指南

语音 Agent 最像真人的瞬间,不是它说得多聪明,而是你一开口,它就知道该停。

很多语音 Agent 的识别率已经不低,回答也算准确,可用户聊上几轮,还是会产生一种强烈冲动:赶紧挂电话。

问题通常不是“没听懂”,而是“不会轮流说话”。

| 失败版本 | 优化版本 | | Agent:好的,我将为您介绍目前的套餐,第一种是—— | Agent:好的,我将为您介绍目前的套餐,第一种是—— | | 用户:等一下,我想先改地址。 | 用户:等一下,我想先改地址。 | | Agent:——每月包含100GB流量,并且…… | Agent:[立即停止播放] | | 用户:你先停一下! | Agent:可以。您要修改哪个订单的地址? | | Agent:抱歉,没有听清,请再说一遍。 | |

这组对话里,用户的话可能每个字都被正确识别了,但系统仍然失控:TTS继续播放,模型继续生成,麦克风甚至可能把扬声器里的声音再次识别成用户输入。

真正决定语音体验的,是下面四个判断:

1. 什么时候开始回答;

2. 什么时候停止回答;

3. 什么时候必须确认;

4. 等待多久后应该回退。

一句“请自然地与用户交流”,解决不了这些问题。因为 Prompt负责制定行为政策,音频层和状态机才负责执行政策。

一、让Agent真正可打断,而不是假装听见用户

用户在Agent说话时开口,系统不能只把新语音转成文字,还必须完成一条完整链路:

检测用户语音 → 判断是否有效插话 → 停止TTS → 取消生成 → 清空旧音频 → 处理最新意图

少任何一步,都可能出现“嘴上答应停,旧回答却又从缓冲区里冒出来”的情况。

不是所有声音都应该触发打断

实时场景中至少有三类输入:

  • 真实插话:“等一下”“我想改地址”“不是这个订单”;
  • 环境声音:咳嗽、电视声、旁人讲话、碰撞声;
  • 反馈词:“嗯”“对”“好的”“你继续”。

如果检测到一点声音就立即停止,Agent会变得过度敏感;如果一定要等ASR输出完整句子,又会停得太晚。

更稳妥的方法是分层处理:

1. 短促声音出现时,进入“候选打断”状态,必要时降低播放音量;

2. 持续语音达到业务设定阈值后,暂停TTS;

3. 识别到新需求、纠正或停止意图后,取消当前生成;

4. 如果只是“嗯”“对”等反馈词,则恢复或继续播放。

具体时长不能照抄固定答案。电话线路、会议麦克风、车载环境和智能音箱的噪声条件完全不同,必须用真实录音调整。

async function onUserSpeechDetected(event) {

if (event.durationMs < BARGE_IN_THRESHOLD) return;

// 1. 停止用户当前能听见的输出

audioPlayer.stop();

// 2. 取消仍在生成的模型回复

currentGeneration?.abort();

// 3. 清空尚未播放的旧音频

ttsBuffer.clear();

conversationState = "USER_SPEAKING";

}

Prompt则负责规定“停下来之后怎么做”:

打断规则:

1. 用户开始表达新需求时,立即停止当前回答。

2. 不要重复被打断前已经说完的内容。

3. 优先处理用户最新一句话;新旧任务冲突时,以新任务为准。

4. “嗯”“好的”“你继续”等简短反馈默认不是新任务。

5. 无法判断是插话还是回应时,用一句短问题确认。

需要特别注意:Prompt不能直接关闭扬声器,也不能清空TTS缓冲区。模型再“听话”,底层没有停止播放和取消请求能力,Agent依然闭不上嘴。

二、确认不是越多越安全,而是要看出错代价

另一个常见误区,是让Agent复述用户说过的每一句话。

用户说:“帮我查一下明天的天气。”

Agent回答:“您是想查询明天的天气,对吗?”

这种确认没有增加多少安全性,反而多制造了一轮等待。更糟的是,过度确认会让用户随口说出“嗯”“行”,系统却未必知道这究竟是在确认哪个字段。

合理原则是:按照不可逆性和纠错成本确认,而不是逢输入必复述。

| 场景 | 风险等级 | 是否确认 | 推荐方式 | |---|---:|---|---| | 查询天气 | 低 | 通常不需要 | 直接回答 | | 修改称呼 | 低 | 不需要完整确认 | 自然接续 | | 输入地址 | 中 | 确认歧义字段 | “是16栋还是10栋?” | | 预约日期 | 中 | 确认日期与时段 | 局部总结 | | 下单、付款 | 高 | 必须确认 | 总结关键参数后执行 | | 删除数据 | 高 | 必须确认 | 明确说明操作后果 |

判断是否需要确认,也不能只看模型自己的“信心”。还要同时检查:

  • ASR识别置信度是否偏低;
  • 当前结果是否与上下文冲突;
  • 手机号、日期、金额等字段格式是否有效;
  • 操作是否可撤销;
  • 出错后用户需要付出多大纠错成本。

例如,ASR把“十五”识别成“五十”,文本本身可能很通顺,模型未必觉得可疑。但如果上下文中用户刚说过“预算不超过二十元”,系统就应该发现冲突并局部确认。

确认规则:
  • 普通问答和可随时撤销的操作,不复述用户原话。
  • 姓名、手机号、地址、日期、金额存在歧义时,只确认歧义字段。
  • 付款、下单、删除、提交等不可逆操作,执行前总结关键参数并获得明确同意。
  • 用户纠正某个字段后,只更新并确认该字段。
  • 每次确认尽量控制在一句话内。

高效确认应该像真人对话:“门牌号是1608,对吗?”而不是每次都从“请问您刚才是否说……”开始。

三、沉默不是异常,不要用统一超时粗暴催促

很多系统只设置一个“无响应超时”,这是语音交互中最容易埋雷的设计之一。

以下沉默看起来相似,含义却完全不同:

  • 用户还没开始说,可能正在思考;
  • 用户说到一半停顿,可能在回忆门牌号;
  • 用户分段报手机号,尚未结束;
  • Agent提问后,用户去查看订单;
  • 工具调用没有返回,用户其实正在等系统。

如果这些情况共用一个超时,Agent就会频繁抢话:“请问您还在吗?”

更合理的方式,是为不同状态设计渐进式回退:

第一次:“我在,您可以慢慢说。”

第二次:“您是在确认送货地址,还是想修改地址?”

第三次:“如果现在不方便,我们可以稍后继续,或者为您转人工。”

function onListeningTimeout(timeoutCount) {

if (timeoutCount === 1) {

return speak("我在,您可以慢慢说。");

}

if (timeoutCount === 2) {

return speak("您想继续当前操作,还是换一个问题?");

}

return speak("我们可以稍后继续,或者现在转人工。");

}

这不是把某个固定秒数奉为标准,而是改变回退逻辑:第一次不施压,第二次缩小范围,第三次提供出口。

尤其要禁止两种行为:用户刚停顿就抢答,以及每次超时都完整重复原问题。

四、用状态机管理“现在轮到谁说”

下面这张状态机图,可以作为语音Agent编排层的基础框架:

flowchart TD

L[Listening] -->|持续语音达到阈值| U[User Speaking]

L -->|首次等待超时| T[Timeout]

T -->|用户重新开口| U

T -->|连续超时| H[Handoff / Exit]

U -->|短暂停顿| U

U -->|端点确认| K[Thinking]

U -->|噪声或无有效文本| L

K -->|生成简短回复| A[Agent Speaking]

K -->|高风险操作| C[Confirming]

C -->|用户明确同意| K

C -->|用户纠正字段| U

C -->|取消操作| L

A -->|有效插话| I[Interrupted]

A -->|简短反馈词| A

A -->|播放结束| L

I -->|停止TTS、取消生成、清空缓冲区| U

这张图的价值在于,它把“自然对话”从形容词变成了可执行规则。

例如,系统处于 AGENT_SPEAKING 时,检测到“嗯”不一定要转入 INTERRUPTED;检测到“等一下,我要改地址”,则必须完成状态切换,并停止旧任务。

五、一份可以直接改的System Prompt

你是一个实时语音助手。你的首要目标是让对话自然、简短、可打断,

而不是一次性输出完整答案。

【回复节奏】

  • 默认每次只说1~3句话。
  • 先给当前最必要的信息,等待用户回应后再补充。
  • 不要使用长段落、复杂列表或连续解释多个主题。

【用户打断】

  • 用户提出新问题、纠正信息或要求停止时,立即放弃当前回复。
  • 优先处理用户最新意图,不要继续完成旧回答。
  • 被打断后不要从头重复已经说过的内容。
  • “嗯”“对”“好的”“你继续”等简短反馈通常表示继续,不要改变任务。

【复述确认】

  • 低风险信息不确认。
  • 姓名、地址、日期、金额等关键字段存在歧义时,只确认歧义部分。
  • 付款、下单、删除、提交等不可逆操作,执行前必须总结并获得明确同意。
  • 用户纠正一个字段时,只更新该字段。

【沉默与超时】

  • 用户短暂停顿时保持安静,不要立刻抢答。
  • 首次超时只做简短提示。
  • 再次超时时缩小问题范围或提供选项。
  • 连续超时后提供退出、稍后继续或转人工选项。
  • 不要连续重复同一句问题。

【无法判断时】

  • 使用一句简短问题确认。
  • 不要自行补全关键事实。
  • 不要用长篇道歉占用对话时间。

它能约束回复长度、确认方式和打断后的语言行为,但还需要音频层与编排层配合。

六、别靠“听起来不错”,用对抗测试验证

上线前,至少测试以下八个场景:

1. Agent说到一半,用户改变需求;

2. 用户只说“嗯”“对”“你继续”;

3. 用户报地址时出现较长停顿;

4. 背景中出现电视声或他人说话;

5. ASR把“十五”识别成“五十”;

6. 用户纠正一个字段后再次插话;

7. 工具调用长时间无返回;

8. 用户连续沉默,但没有明确退出。

建议记录这些指标:

  • 打断响应延迟:有效插话开始到TTS真正停播的时间;
  • 误打断率:噪声、咳嗽和反馈词导致错误停播的比例;
  • 漏打断率:用户明确插话但Agent没有停止的比例;
  • 平均确认次数:完成一项任务需要确认多少次;
  • 端点误判率:用户没说完,Agent就开始回答的比例;
  • 超时恢复率:提示后用户重新进入对话的比例;
  • 任务完成率与人工转接率。

没有真实项目数据时,不要写“体验提升XX%”。可以先用统一语料做三组对照:

| 测试方案 | 打断延迟 | 误/漏打断 | 确认次数 | 端点误判 | 任务结果 | | 仅优化Prompt | 记录实测值 | 记录实测值 | 记录实测值 | 记录实测值 | 完成/失败 | | Prompt+状态机 | 记录实测值 | 记录实测值 | 记录实测值 | 记录实测值 | 完成/失败 | | Prompt+状态机+音频层 | 记录实测值 | 记录实测值 | 记录实测值 | 记录实测值 | 完成/失败 |

调试时还应保留音频时间轴、AGENT_SPEAKING → INTERRUPTED → USER_SPEAKING 状态日志、取消生成记录和TTS缓冲区清理记录。很多问题只听最终录音很难定位,日志却能直接告诉你:到底是没检测到插话,还是检测到了却没停播。

Prompt是行为政策,不是实时控制器。

结语:好的语音Agent,先学会让出发言权

回到开头的场景,真正有效的优化并不是让Agent说一句更长的道歉,而是让它完成三个动作:

立刻停播、放弃旧回答、处理最新需求。

打断解决“什么时候停”,风险确认解决“什么时候问”,渐进式超时解决“什么时候等”和“什么时候退出”。四层能力缺一不可:

  • 音频层负责听见插话;
  • 编排层负责切换状态;
  • Prompt层负责决定怎么回应;
  • 评估层负责证明方案真的有效。

想验证这套策略,不必一开始就搭建完整电话系统。可以先到 api.884819.xyz 接入适合的模型,用同一组对抗测试分别运行“默认Prompt”和“节奏控制Prompt”,记录回复长度、确认次数、超时回退表现,以及插话后的意图处理结果。

8848AI平台注册只需要用户名和密码,不需要邮箱验证;没有月租和订阅,按量付费,平台内置AI对话,注册后即可使用,Deepseek、千问等国产模型完全免费。

新用户注册即送体验token。

建议先测试三个场景:中途改需求、关键字段纠错、连续沉默。文本策略稳定后,再接入VAD、TTS停播和生成取消能力。

好的语音Agent不是永远知道该说什么,而是知道何时停、何时问,以及何时承认自己没听清。

但Agent学会闭嘴之后,下一道难题才真正出现:用户说“不是15号,是50号”时,旧字段应该保留、覆盖,还是全部作废?

下一篇,我们将拆解语音Agent的短期记忆、字段更新与上下文清理,解决那个更隐蔽的问题——嘴上说改好了,执行时却仍然使用旧地址。

本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。

#语音Agent #AI教程 #Prompt技巧 #智能客服 #大模型应用 #人工智能 #8848AI