Skip to content

断句、收尾与逐句时延 ​

流式识别的时延不能只看请求总耗时。用户真正感知的是:最后一个有效语音帧之后,多久看到稳定文字,多久收到最终句子,多久确认会话已经结束。断句和收尾的状态设计,通常比单次模型推理耗时更容易造成尾部延迟和漏字。

先统一时间口径 ​

一条句子的时间线可以写成:

text
真实末音
  -> VAD 判定结束
  -> 静音窗口完成或显式提交
  -> 音频段送入 ASR
  -> Preview 产生
  -> Final 定稿
  -> terminal 发送

至少记录以下时间点:

时间点含义用于发现的问题
audio_end输入中最后一个有效语音帧的时间测量起点是否真实
vad_endVAD 确认语音区间结束尾部静音阈值和模型误判
submit该段音频进入识别队列排队和调度延迟
preview首个可见中间结果首字延迟
final句子完成且不再修改逐句稳定时延
terminal会话生命周期结束停止和 EOF 收尾

推荐同时报告两项指标:audio_end -> final 的逐句时延,以及 stop/EOF -> terminal 的停止时延。两者混在一起,会掩盖“文字已经完成但会话还未结束”的状态问题。

低推理耗时为什么仍有数秒等待 ​

一种实际现象是模型几百毫秒完成,但短停顿未达到确认门槛,音频仍留在未提交区间。直到最大段长触发,处理器才回看早先的停顿并提交。此时瓶颈发生在模型调用之前,继续优化解码无法消除等待。

排查时画出停顿候选、确认时刻、提交时刻和执行时刻。不要将文件尾、VAD 推定尾部和人工末音视为同一个起点。输出时间区间指向文件中间的句子,也不能用整段文件结束时间计算该句时延。

缩短静音阈值、限制旧停顿回溯或组合更早的辅助 VAD,都曾在局部样本减少等待,却在其他样本新增切点、切开词组、改变角色或漏字。可复用的是“先分析未提交区间,再对照切分结果”的方法;任何历史阈值都需要在当前链路重新验收。

预计算结果的所有权 ​

静音确认期可以提前准备 ASR 或角色结果,确认后复用。但已入队任务与会话准备器必须分开管理:队列接受任务时移交结果所有权,后续 prepare 的取消不能让已接受任务失效。EOF 应等待任务的完成路径,而不是把所有准备结果统一清空。

text
prepare 拥有可取消候选
  -> 校验 PCM / 配置 / 角色计划
  -> 队列接受:移交给 immutable job
  -> job 独立完成并发布

队列拒绝:所有权仍留在 prepare,由其释放

验证至少覆盖“已接受后立刻 EOF”“准备期间续说”“队列满”“新准备取消旧准备”和“角色计划变化”。只测普通句子无法触发这些竞争条件。

尾部缓存必须有唯一归属 ​

静音或停止到来时,处理器经常同时持有三类数据:已提交段、正在等待的尾部帧、尚未完成的识别任务。若多个分支都能清理或提交尾部缓存,就会出现:

  • 末尾几个字没有进入任何段。
  • 同一段被提交两次,产生重复句子。
  • Final 已发送,但尾部异步任务又写回旧句子。
  • terminal 提前发送,客户端把后续事件视为无效。

实践中应给缓存定义单一所有者,并为每次提交生成唯一段标识:

text
收音中       -> 追加到 current
VAD 结束     -> current 转为 pending,创建 segment_id
提交识别     -> pending 只读,进入 in_flight
Final        -> in_flight 转为 finalized
terminal     -> 仅在 current/pending/in_flight 全部清空后发送

停止和 EOF 走同一条收尾路径,只在触发来源上做区分。这样既能减少重复逻辑,也能保证“正常停止”和“输入结束”使用相同的缓存归属规则。

静音预计算的边界 ​

把静音窗口提前计入调度可以降低等待,但它必须建立在清晰的音频边界上:

  1. 先确认静音帧确实属于当前句子,而不是下一句的开头。
  2. 预计算不能绕过尾帧提交和 in-flight 任务等待。
  3. 任何提前发送的 Preview 都不能被误当成 Final。
  4. 参数变化要绑定到具体采样率、帧长和处理模式,不能只记录一个“静音 1.5 秒”。

如果发现时延下降但漏字增加,优先检查尾帧归属和提交顺序,而不是继续缩短静音阈值。

状态机检查清单 ​

排查停止或尾字问题时,逐项回答:

  • 最后一帧 PCM 是否到达会话处理器?
  • VAD 是否将最后一段标记为可提交?
  • 提交任务是否带有唯一 segment_id?
  • Final 是否只由该段的唯一完成路径发送?
  • Final 后是否仍允许角色或文本覆盖?
  • terminal 是否等待所有 in-flight 任务结束?
  • 客户端是否把 terminal 和正常关闭分别处理?

这些问题比单看一条最终文本更能定位收尾缺陷。

小结 ​

逐句时延的起点是“真实末音”,终点要区分“句子 Final”和“会话 terminal”。把尾部缓存、提交任务和最终事件纳入一套状态机,才能同时降低等待和避免漏字、重复、提前结束。

别急,先让缓存热一下。