Appearance
断句、收尾与逐句时延
流式识别的时延不能只看请求总耗时。用户真正感知的是:最后一个有效语音帧之后,多久看到稳定文字,多久收到最终句子,多久确认会话已经结束。断句和收尾的状态设计,通常比单次模型推理耗时更容易造成尾部延迟和漏字。
先统一时间口径
一条句子的时间线可以写成:
text
真实末音
-> VAD 判定结束
-> 静音窗口完成或显式提交
-> 音频段送入 ASR
-> Preview 产生
-> Final 定稿
-> terminal 发送至少记录以下时间点:
| 时间点 | 含义 | 用于发现的问题 |
|---|---|---|
audio_end | 输入中最后一个有效语音帧的时间 | 测量起点是否真实 |
vad_end | VAD 确认语音区间结束 | 尾部静音阈值和模型误判 |
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 走同一条收尾路径,只在触发来源上做区分。这样既能减少重复逻辑,也能保证“正常停止”和“输入结束”使用相同的缓存归属规则。
静音预计算的边界
把静音窗口提前计入调度可以降低等待,但它必须建立在清晰的音频边界上:
- 先确认静音帧确实属于当前句子,而不是下一句的开头。
- 预计算不能绕过尾帧提交和 in-flight 任务等待。
- 任何提前发送的 Preview 都不能被误当成 Final。
- 参数变化要绑定到具体采样率、帧长和处理模式,不能只记录一个“静音 1.5 秒”。
如果发现时延下降但漏字增加,优先检查尾帧归属和提交顺序,而不是继续缩短静音阈值。
状态机检查清单
排查停止或尾字问题时,逐项回答:
- 最后一帧 PCM 是否到达会话处理器?
- VAD 是否将最后一段标记为可提交?
- 提交任务是否带有唯一
segment_id? - Final 是否只由该段的唯一完成路径发送?
- Final 后是否仍允许角色或文本覆盖?
- terminal 是否等待所有 in-flight 任务结束?
- 客户端是否把 terminal 和正常关闭分别处理?
这些问题比单看一条最终文本更能定位收尾缺陷。
小结
逐句时延的起点是“真实末音”,终点要区分“句子 Final”和“会话 terminal”。把尾部缓存、提交任务和最终事件纳入一套状态机,才能同时降低等待和避免漏字、重复、提前结束。
