384 与 192:为什么接续一轮的门槛只有开新轮的一半
翻 speech-to-speech 的 VAD 参数列表时,有一对默认值会让人愣一下:
--min_speech_ms默认384--min_speech_continuation_ms默认192
同样是「多长的活跃语音才算数」,为什么存在两个门槛,而且后者正好是前者的一半?help 文本里给的说明是「默认与推荐搭配:192 配 384」。推荐搭配这四个字背后是一套挺讲究的设计,不理解它,你调这两个数就是在瞎撞。
这篇只讲这一件事:这两个门槛分别管什么、为什么可以不对称、以及你什么时候该动它们、什么时候动了也没用。
先说清楚「可重开轮次」是什么
min_speech_continuation_ms 的 help 原文限定得很死:它是接续一个可重开轮次(reopenable turn)所需的活跃语音毫秒数,而这个「可重开轮次」有三个前提——软结束、未提交、还在重开窗口内。
要理解这三个词,得先看 Smart Turn 这一层。README 的「Smart Turn endpointing」一节描述的流程是:
- Silero VAD 先敲定语音段,STT / LLM 的工作可以推测性地先开始;
- 被判为完整的轮次,立刻开始处理,在提交输出前使用
--speculative_reopen_ms(默认 800 毫秒); - 被判为不完整的轮次,先等
--smart_turn_incomplete_delay_ms(默认 600 毫秒)再启动 STT / LLM,其输出继续被--smart_turn_max_wait_ms(默认 2000 毫秒)门控; - 这两段延迟里任何一段内语音恢复了,现有轮次会被重开为一个更新的修订版,累积音频重新发出,上一修订版的工作在到达用户之前就被丢弃。
Smart Turn v3.2 是 pipecat-ai/smart-turn-v3 这个外部 CPU ONNX 模型,不是这个仓库自己训的;--smart_turn 默认是 True,要关得传 --no_smart_turn;判定完整与否的概率阈值是 --smart_turn_threshold,默认 0.5。
所以「可重开」描述的是一个中间态:Silero 说你说完了,流水线已经开始干活了,但输出还没提交给你,这一轮还挂在那儿等着看你是不是只是喘了口气。你在这个窗口里再开口,不算新的一轮,算把这一轮续上。
两个门槛的不对称,本质是风险不对称
help 原文里那句最关键:新轮次与抢话(barge-in)始终要求 min_speech_ms。
把两条路的代价摆在一起,不对称就很好理解了:
| 判定的事 | 门槛 | 判错了会发生什么 |
|---|---|---|
| 接续一个可重开轮次 | min_speech_continuation_ms(192 ms) | 同一轮被延长、生成一个更新的修订版,旧修订版的工作在到达用户前被丢弃 |
| 开一个新轮次 | min_speech_ms(384 ms) | 上下文里凭空多一轮,后续对话都跟着跑偏 |
| 抢话打断助手 | min_speech_ms(384 ms) | 正在播的助手音频被砍掉,整条流水线做取消与清队列 |
第三行的代价具体有多大,Realtime 侧那套 CancelScope 机制写得很清楚:打断成立时,send loop 会调 cancel_scope.cancel(),世代计数 cancel_scope.generation 递增、discarding 置位,两个队列被抽干(output_queue 保留 __RESPONSE_DONE__ 哨兵,text_output_queue 保留用户侧事件),流水线线程在每个流式 token 上检查 cancel_scope.is_stale(gen) 提前中止,协议侧还要按顺序发出终态事件。触发条件是:响应处于 in_response 或 response_pending,并且打断门控放行(SpeechStartedEvent.interrupt_response 置位且会话配置允许,该配置默认为真)。门控关掉时,响应期间的用户语音仍会被转写,但响应继续播放。
对比之下,接续一个还没提交的轮次代价小得多——它本来就还挂在推测状态,本来就准备好了被修订。**门槛做低一半,买的是「用户中途换气不会被误判成说完了」,而付出的代价被限制在一个本来就允许作废的窗口里。**这就是为什么它敢低,也是为什么另外两条路不敢低。
钳制规则:你调的值可能根本没生效
这是最容易踩的一点。--min_speech_continuation_ms 的 help 里写死了两条:
- 被钳制在
[100, min_speech_ms]区间; - 设为 0 则禁用这个拆分,回落到
min_speech_ms。
推论有三条,都值得记住:
第一,你把它设成大于 min_speech_ms 的值不会生效。比如保持 --min_speech_ms 384 却写 --min_speech_continuation_ms 600,上限钳制会把它按 384 处理,结果就是两个门槛变成一样高,等于你手动取消了这套迟滞。
第二,下限是 100,想把接续门槛压到几十毫秒去追求「接得更快」是做不到的。
第三,要整体放宽或收紧,得先动 min_speech_ms,因为它同时是新轮门槛和接续门槛的上限。只调 192 那个数,上限还是 384。
顺带提一句同族的另一个 100 毫秒:--short_segment_merge_ms(默认 0,关闭)开启后会把相邻的、每段都短于 min_speech_ms 的 VAD 段暂存并缝合,但活跃语音短于 100 毫秒的碎片永远不会被暂存。这两个 100 是不同参数下的不同规则,别混。
迟滞之外:轮次还「在不在」由三个窗口决定
门槛管的是「多长的语音算数」,但前提是那一轮还处在可重开状态。管这件事的是另外三个参数:
| 窗口 | 默认 | 管什么 |
|---|---|---|
--speculative_reopen_ms | 800 ms | 软结束轮次保持可重开的基础窗口 |
--unanswered_reopen_ms | 7000 ms | 针对「还没有任何助手输出」的轮次的兜底上限 |
--smart_turn_max_wait_ms | 2000 ms | Smart Turn 判不完整时的宽限期 |
两条约束关系必须原样记住,help 里写得很明确:unanswered_reopen_ms 低于 speculative_reopen_ms 时无效;启用 Smart Turn 时,它会被钳制到 smart_turn_max_wait_ms(README 的说法是让轮次在整个宽限期内都保持可重开)。
这就解释了一类困惑:你把接续门槛调低了,但感觉某些停顿之后说的话仍然被当成了新的一轮。那大概率不是 192 这个门槛的问题,而是那一轮已经不可重开了——窗口过了,或者输出已经提交。这时候该看的是上面三个窗口,不是迟滞参数。
怎么判断该动哪个参数
给一条自查顺序,依据都来自 help 文本的语义:
- **先确认那一轮是否还可重开。**若停顿明显长于窗口,问题在
speculative_reopen_ms/unanswered_reopen_ms/smart_turn_max_wait_ms这一层,跟 192 无关。 - 再看语音段是否被切碎了。
--min_silence_ms默认只有64毫秒,切分粒度本来就细。help 明说--short_segment_merge_ms在min_silence_ms取值很低时有用——碎片问题的正解是缝合,不是把门槛调低。 - **然后才是迟滞这对数。**接续判定过严就在
[100, min_speech_ms]内下调min_speech_continuation_ms;想让新轮与抢话整体更保守,动min_speech_ms,但记住上限会跟着变。 - 确认不是触发层的问题。
--thresh默认0.6,是 VAD 的触发阈值,越高越要求高置信度才判为语音;--speech_pad_ms默认500,管的是触发前保留并前置拼上的音频长度。这两个都在门槛判定之前起作用。
想看某个后端组合下这些参数的实际取值,直接问 CLI:
speech-to-speech serve -h
要看非默认后端组合的参数,把选择器放在 -h 前面:
speech-to-speech serve --stt mlx-audio-whisper -h
以上命令取自官方 README 的用法说明,未逐项实测,以官方文档与 --help 的实际输出为准。另外提醒一句 README 写明的行为:CLI 只为选中的后端构造配置,未激活后端的已知选项仍会被接受,只是带警告地忽略——参数名写错了程序不会停下来报错。
最后一句要划掉的话
这篇提到的所有毫秒值——384、192、800、600、2000、7000、64、500——都是参数默认值,是这条流水线自己引入的、可配置的等待。它们不包含 STT 推理、LLM 首 token、TTS 首包这三段真正吃算力的时间,那三段取决于你的模型和硬件。
所以不要把它们加起来说「端到端延迟大概是多少毫秒」。这套设计的取舍从来不在延迟上:Silero 判完就立刻开工能省时间,但可能白干;Smart Turn 和这对迟滞门槛做的事,是给白干上一个成本可控的闸。这些数字买的不是延迟,是误判成本。
延伸阅读
本文依据 speech-to-speech 官方仓库(github.com/huggingface/speech-to-speech)的 README、
src/speech_to_speech/arguments_classes/ 下的参数定义与 Realtime Engine 架构文档整理,核对日 2026-08-09。
本文内容为仓库源码与文档口径,我们没有安装、部署或调用过该服务,文中毫秒值均为参数默认值而非实测延迟。
参数与默认值随版本变动,请以 speech-to-speech serve -h 的实际输出为准。