候选人正说到"那次迁移最难的部分是,嗯……",AI面试官已经接话:"好的,感谢分享!"
他还没说完,只是在想。Silero VAD 的端点检测把 700毫秒的静音判定成了"这一轮结束",触发大模型,然后在一个句子中间开口盖过了人声。翻日志才发现,这不是一次偶发的坏会话,而是几乎四分之一的回答都遭遇了同样的事。
核心结论先摆出来:固定的 700ms 的 min_silence_duration_ms,在412次回答轮次里掐断了95次,占23%。静音检测本身没毛病,问题出在把"静音"当成了"说完"。人在边想边说时,停顿经常超过700毫秒——在这份数据里,回答中途停顿的第90百分位是2.6秒。
把阈值提到1500ms,掐断率降到7.5%,但每次回复都多出约800毫秒的死寂,用户直接说"卡了"。两级端点检测(600ms先给一个试探性结束,转写文本看起来完整才提交,硬上限2.5秒)把掐断率压到2.2%,回复间隔中位数1.1秒。最便宜也最强的信号是最后一个词:95次掐断里有61次停在"so""and""um""because"上。
Silero VAD 到底决定了什么
Silero VAD 只决定一件事:一段32毫秒的音频里有没有语音。端点检测——也就是"用户说完了"——是你在它之上另建的判断,通常靠一个静音计时器。多数语音管线把这两件事压成一个参数,错误就从这里开始。
最初上线的写法大致是这样:加载模型,用 VADIterator,阈值0.5,采样率16000,min_silence_duration_ms=700,speech_pad_ms=30;每32毫秒喂一个音频块,一旦事件里出现"end",就结束本轮,把转写送进大模型、启动语音合成。
VADIterator 在语音概率持续低于阈值达到设定时长后发出结束事件,内部还带一点迟滞(概率要掉到阈值减0.15以下才算结束),所以它并不神经质。它完全照指令执行,只是指令本身给错了。
700ms 是聊天式语音机器人的常见默认值,因为听起来干脆。"天气怎么样?""晴。"短轮次,不用思考。这个场景恰好相反。
为什么700毫秒会掐掉这么多回答
因为长而开放的回答,内部本来就有长停顿。当一个人解释两年前做过的决定,他会停在句子中间去回忆、去挑一个词、去决定要说得多坦诚。这些停顿是回答的一部分。
发生这件事的系统是 Preterview,一个语音模拟面试平台,用三种面试官风格之一提开放式问题,听口头回答,事后写报告,所以单次回答常常持续60到120秒,中间全是思考。
测量样本是60场练习会话里的412个回答轮次。判定"被掐断"的机械规则是:端点触发后4秒内说话人还在继续讲。这样可以直接跑日志,不用重听每一段回答。
- 412轮里95轮被掐断,占23%
- 回答中途停顿中位数:1.1秒
- 第90百分位:2.6秒
- 明显仍属同一回答的最长停顿:4.3秒
700ms 不是"稍微短了点",它落在中位数停顿之下。只掐掉23%反而让人意外,原因是很多人语速快、用"um"填缝,Silero 正确地把这些算作语音。安静思考的人受伤最重。
为什么不直接把阈值提到1500ms
提高静音阈值修好了掐断,却弄坏了响应性,因为这份额外的等待要在每一轮上支付,包括那些本来就已经说完的轮次。等于用一次偶发的大故障,换一个持续的小故障。
把同样的412轮录音离线重放、阈值设成1500ms:掐断减少三分之二,但回复间隔中位数从1.0秒变成1.8秒,而且这段间隔还要叠加在大模型首字时间和语音合成启动之上。测试者的反馈从"它打断我"变成"它是不是卡死了"。有位测试者对着沉默说了声"hello?",VAD 立刻把它当成新的语音捡了起来。
而且还剩31次掐断。2.6秒的第90百分位意味着没有任何一个固定数字能通吃:短到感觉灵敏的阈值,对慢思考者一定太短。
两级端点检测怎么修
两级端点检测把"用户可能说完了"和"用户说完了"分开。短静音触发试探性结束,此时开始准备回复但不发声;只有第二道检查同意,或者撞上硬性最大静音时长,这一轮才真正提交。
逻辑简化后是这样:试探阈值600ms,最大静音2500ms,尾词表包含"so""and""but""um""uh""because""like""which""the"。判断文本是否完整:取小写、去掉尾部空格后的词,若为空返回否;若最后一个词去掉标点后落在尾词表里,返回否;否则看转写是否以句号、问号、感叹号结尾。静音回调里,达到600ms且尚未推测时,标记推测状态,启动回复但不发声;达到600ms且文本看起来完整,提交回复、放出音频;达到2500ms,无条件提交。语音恢复时,若处于推测状态,丢弃草稿、取消回复。
三件事让它成立:
- 推测式生成把等待藏了起来。大模型在600ms就开跑,等提交检查通过时,最初的 token 已经在了;需要更长的等待时,它和本来就要做的工作重叠。
- 最后一个词携带大量信号。原始95次掐断里有61次停在填充词或连词上。"So……"几乎从来不是回答的结尾。标点检查依赖流式语音转写补上句号,而它在完整句子上确实比在碎片上更可靠。
- 硬上限保证不无限等。有人以"and, yeah"收尾然后沉默,启发式判定为不完整,但到2.5秒系统照样提交。没人会永远等下去。
代价是什么
代价是浪费的大模型调用。每次说话人在试探性结束后重新开口,那份推测回复就被丢掉。重放中18%的试探性结束被取消,折算下来每场会话多出约14%的大模型输入 token。
同样412轮上的最终对比:从95次打断降到9次,换来0.1秒的中位延迟和14%的额外输入 token。打断会毁掉体验,token 账单几乎没动,这个取舍不难做。
这套测量有哪些前提
最大的一个:离线重放捕捉不到人的反应。在700ms的线上会话里,被打断的说话人往往停下来让 AI 讲,所以一些"真实"的掐断根本没以"继续说话"的形式出现。23%这个数字很可能是低估。
其他几点:4秒这条规则是自定的,换个窗口就是另一个计数,选它是因为数据里没有真正的新轮次会在 AI 开始回复后这么快出现;填充词表只适用于英语、且是针对这批说话人调的,话语标记不同的语言需要自己的词表;还剩9次掐断,多数是人在一个听起来完整的句子后停了超过2.5秒,又补了一句"哦,还有……",这个程度可以接受,人类面试官到那儿也会插话。
那 min_silence_duration_ms 该设多少
短指令式语音机器人,500到800ms都行。凡是需要人边想边说的场景,别让单一静音阈值充当端点检测。先测自己的停顿分布:如果你的中位停顿和阈值差不多近,你就在打断人。
700ms 的静音计时器短于开放式表达里的中位思考停顿,而静音本身分不清一个想完的念头和一个没说完的念头。把计时器提到1500ms,是用打断换卡顿。真正修好它的是两级端点检测:Silero VAD 在600ms触发试探性结束,推测式回复悄悄开跑,只有转写看起来完整(没有尾随的"so"或"and"、带结束标点)或2.5秒上限到期时才提交。412轮里掐断从95次降到9次,代价是0.1秒的额外中位延迟。
热门跟贴