19 个 VAD 参数逐个讲:默认值背后的意图
打开 speech-to-speech 的 src/speech_to_speech/arguments_classes/vad_arguments.py,会看到 19 个参数排在一起。--help 把每个都解释了一遍,但读完你大概率还是不知道该动哪个——因为这 19 个不是 19 个平级的旋钮,它们分属五件不同的事,每一组内部才有互相牵制的关系。
这篇按组拆。目标不是把 help 翻译一遍,而是让你看完能自己回答一个问题:我遇到的这个现象,该去动哪一组里的哪一个。
先看全表
| 参数 | 默认值 | 管什么 |
|---|---|---|
--thresh | 0.6 | VAD 触发阈值,取值一般在 0–1,越高越要求高置信度才判为语音 |
--sample_rate | 16000 | 音频采样率(Hz) |
--min_silence_ms | 64 | 用于切分语音的最小静音间隔长度 |
--min_speech_ms | 384 | 被视为有效语音的最小语音段长度 |
--min_speech_continuation_ms | 192 | 迟滞阈值:接续一个可重开轮次所需的活跃语音毫秒数 |
--max_speech_ms | inf | 强制切分前的最大连续语音长度,默认无限 |
--speech_pad_ms | 500 | VAD 触发前保留并前置拼到语音段上的音频长度 |
--audio_enhancement | False | 通过降噪、均衡、回声消除等手段改善音质 |
--enable_realtime_transcription | False | 启用语音过程中的渐进式音频释放以支持实时转写 |
--realtime_processing_pause | 0.5 | 渐进音频块的释放间隔(秒) |
--speculative_reopen_ms | 800 | 软结束的 Realtime 轮次保持可重开的时长 |
--unanswered_reopen_ms | 7000 | 对「尚未收到任何助手输出」的软结束推测轮次,重开时长的兜底上限 |
--short_segment_merge_ms | 0 | 大于 0 时,短于 min_speech_ms 的相邻段会被暂存缝合这么久再丢弃 |
--smart_turn | True | 是否用 Smart Turn v3.2 决定助手输出保持推测状态多久 |
--smart_turn_model_path | None | 可选的 Smart Turn v3.x CPU ONNX 模型路径 |
--smart_turn_threshold | 0.5 | 判定「轮次完整」的概率阈值 |
--smart_turn_max_wait_ms | 2000 | 判不完整时的推测重开宽限期 |
--smart_turn_incomplete_delay_ms | 600 | 判不完整后,延迟 STT 与 LLM 处理的时长 |
--smart_turn_cpu_count | 1 | 每次 Smart Turn 推理可用的 CPU 线程数 |
看表先记一件事:除了 --realtime_processing_pause 的单位是秒(默认 0.5),其余带 _ms 后缀的全是毫秒。 这两个单位在同一个文件里并存,是最容易写错配置的地方,写脚本时尤其要留神。
第一组:Silero 怎么把声音切成段
--thresh、--sample_rate、--min_silence_ms、--min_speech_ms、--max_speech_ms、--speech_pad_ms 这六个,决定的是「哪一段音频算一句话」。
--min_silence_ms 默认只有 64——不是 640,这个数字第一次看到时基本都会怀疑自己看错了。help 的说法是「用于切分语音的最小静音间隔长度」,64 毫秒意味着说话中间稍微一顿,段就断了。切得这么碎不是失误,而是把「一句话说完没有」这个判断从 VAD 这一层推到了后面的迟滞与 Smart Turn 那两层去做;VAD 只负责给出细颗粒的候选段。理解了这一点,后面几组参数为什么存在就顺了。
碎片带来的副作用由 --min_speech_ms(默认 384)兜住:短于它的段不算有效语音。两者配合的结果是——碎段会被产出,但达不到 384 毫秒就不被当成一次真正的开口。
--speech_pad_ms 默认 500,管的是段的前面:VAD 触发之前保留这么多音频,并前置拼到检测出的语音段上。help 还补了一句关键语义——一旦检测到语音,音频会持续保留到 VAD 宣布该段结束。这个 500 毫秒的意义在于,人开口的头一个字往往在 VAD 反应过来之前就已经发出去了,不留这段缓冲,转写就会吃掉字头。
--thresh 默认 0.6,--max_speech_ms 默认无限。前者往上调会更要求高置信度才判为语音,方向上更保守;后者只有在你需要强制打断超长连续语音时才用得到。
判断依据:如果你的问题是「一句话被切成好几段」,先看第一组;如果是「说完了系统还在等」,那是第三、第四组的事,动 --thresh 没用。
第二组:碎片缝合 --short_segment_merge_ms
默认 0,也就是关的。开启后,连续的、每段都短于 min_speech_ms 的 VAD 段会被暂存并缝合这么多毫秒,之后才丢弃。help 里明写它「在 min_silence_ms 取值很低时有用」——而默认值恰好就低到 64,所以这个开关本质上是给默认切分策略准备的补救手段。
有一条限制要记住:活跃语音短于 100 毫秒的碎片永远不会被暂存。 也就是说它救的是「几段短促但成形的语音」,不是纯粹的噪声毛刺。指望靠它把环境杂音拼成句子,方向就错了。
第三组:迟滞,384 与 192 为什么要分家
--min_speech_continuation_ms 默认 192,正好是 --min_speech_ms 默认 384 的一半。help 把它称为迟滞(hysteresis)阈值,两者的分工是:
- 开新轮次、抢话(barge-in):始终要求满
--min_speech_ms,也就是 384 毫秒的活跃语音; - 接续一个可重开的轮次(软结束、尚未提交、还在重开窗口内):只要求
--min_speech_continuation_ms,192 毫秒。
判断依据在于两件事的代价不一样:接续已有轮次只是把同一轮延长,判错了代价小;而开一个新轮次、或者打断助手正在说的话,判错的代价大。所以门槛做成不对称的——接续放低一半,新轮与抢话保持高位。help 里也直接给了推荐搭配:192 配 384,这也是默认组合。
两个细节别踩:
- 该参数被钳制在
[100, min_speech_ms]区间。你填一个大于min_speech_ms的值不会生效,它会被压回去——想让接续门槛更高,得先把min_speech_ms抬上去。 - 设成
0是「禁用这个拆分」,回落到统一用min_speech_ms,不是「不设门槛」。
第四组:三个重开窗口,谁钳制谁
这是整份参数表里最容易讲错的地方,因为三个窗口名字都像「等多久」,实际管的对象完全不同。
| 窗口 | 默认 | 管什么 |
|---|---|---|
--speculative_reopen_ms | 800 ms | 软结束轮次保持可重开的基础窗口 |
--unanswered_reopen_ms | 7000 ms | 针对「还没有任何助手输出」的轮次的兜底上限 |
--smart_turn_max_wait_ms | 2000 ms | Smart Turn 判不完整时的宽限期 |
--speculative_reopen_ms(800)是基础:一个软结束的 Realtime 轮次在这段时间内保持可重开,除非有响应把它提交掉。
--unanswered_reopen_ms(7000)是兜底上限,只对「尚未收到任何助手输出」的推测轮次生效。轮次未提交期间,这个窗口内恢复的语音会重开同一轮,而不是开一个新轮。
两条约束必须原样记住,否则你会以为自己改的参数生效了其实没有:
unanswered_reopen_ms低于speculative_reopen_ms时无效;- 启用 Smart Turn 时,它被钳制到
smart_turn_max_wait_ms,让轮次在整个宽限期内都保持可重开。
而 Smart Turn 默认就是开的(--smart_turn 默认 True),所以第二条约束在默认配置下始终在起作用。
第五组:Smart Turn 那五个参数
Smart Turn 是外部模型,不是这个仓库自己训的——--smart_turn_model_path 留空时会从 pipecat-ai/smart-turn-v3 下载最新支持的 v3.2 CPU ONNX 模型。它的职责是在 Silero 敲定一个 Realtime 轮次之后,用当前轮次的内容与韵律来校验「是真说完了,还是只是停顿」。
README 描述的流程是这样的:
- Silero 先敲定语音段,STT / LLM 的工作可以推测性地先开始;
- 判为完整的轮次:立刻开始处理,在提交输出前使用
--speculative_reopen_ms(800 毫秒); - 判为不完整的轮次:先等
--smart_turn_incomplete_delay_ms(600 毫秒)再启动 STT / LLM,其输出继续被--smart_turn_max_wait_ms(2 秒)门控——这 600 毫秒是跑在那 2 秒之内的,不是加在外面; - 上述两段延迟中任意一段内语音恢复:现有轮次被重开为一个更新的修订版,累积的音频重新发出,上一修订版的工作在到达用户之前就被丢弃。
这套设计的核心权衡值得单独说清楚:Silero 一判定说完就马上干活,能省时间,但可能白干(用户只是换了口气)。Smart Turn 的作用是给「白干」上一个成本可控的闸——完整轮次少等,不完整轮次先晾 600 毫秒再开工、最多晾 2 秒。换句话说,这几个毫秒数字买的不是延迟,是「误判成本」。想清楚这一点,你才知道自己该往哪个方向调:你更怕系统抢话,还是更怕它反应慢。
--smart_turn_threshold 默认 0.5,help 的语义是「越高越倾向在含糊停顿处继续等待」。所以方向上:怕被抢话就往上调,怕等太久就往下调。具体调到多少这里不给建议——除了默认值和 help 明写的推荐搭配(192 配 384),任何具体数值都得你按自己的场景定。
--smart_turn_cpu_count 默认 1,是 ONNX Runtime 每次 Smart Turn 推理可用的 CPU 线程数。要关掉整个 Smart Turn,用 --no_smart_turn。
剩下的三个:音质与实时转写
--audio_enhancement 默认 False,help 说它通过降噪、均衡、回声消除等手段改善音质。
--enable_realtime_transcription 默认 False,启用语音过程中的渐进式音频释放以支持实时转写;--realtime_processing_pause 默认 0.5 秒,是渐进块的释放间隔。这两个是 VAD 侧的参数,和模块级的 --enable_live_transcription 是两个不同参数,名字像但所属文件不同,配置时别混用。
一条必须守住的口径
上面所有毫秒值都是参数默认值,是这条流水线自己引入的、可配置的等待。它们不包含 STT 推理、LLM 首 token、TTS 首包这三段真正吃算力的时间——那三段取决于你的模型和硬件。
所以,不要把 800、2000、7000、600 这些数字加起来,说成「端到端首响应就是这么多毫秒」。真实的首响应链路是:用户停止说话 → VAD 判定静音(受 min_silence_ms 影响)→ Smart Turn 判定与其延迟(完整/不完整两条路)→ STT 转写 → LLM 首 token → TTS 首包 → 网络传输。参数只覆盖其中的等待项,推理耗时得你自己在自己的机器上测。
回到最初那个问题:一句话被切碎,看第一组和 --short_segment_merge_ms;系统爱抢话,看迟滞与 --smart_turn_threshold;说完之后迟迟不响应,看第四组那三个窗口和它们的钳制关系;改了没反应,先确认是不是撞上了钳制规则。
延伸阅读
本文依据 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 的实际输出为准。