Skip to content

音频回放与质量回归 ​

“这次识别通过了”不是一个足够精确的结论。流式语音服务至少同时包含协议行为、文本质量、角色质量、目标身份质量、时延、并发和部署兼容七个维度。项目实践中把它们拆开验证,才能知道一次修复真正改善了什么。

七个验收维度 ​

维度最小证据常见误判
协议完整性状态码、事件顺序、Final 冻结、terminal、正常关闭有文字就算接口通过
转写质量原始音频、人工参考、逐句文本、漏字和串音标注Preview/Final 存在就算准确
角色质量人工身份标注、角色数、切换点、重叠区表现模型槽位数量等于真实身份
目标身份质量纯目标说话人、纯非目标说话人、交替、重叠、短句和句首句尾样本两个负例通过即可外推
时延真实末音到 VAD、提交、Preview、Final、terminal 的时间线从录音开始计时一个总耗时
并发性能固定配置下的吞吐、队列、资源和错误统计网关并发数等于每个模型的容量
部署兼容目标硬件、驱动、固件、系统、容器运行时和设备映射自有机器通过等于目标环境通过

证据包应该包含什么 ​

一次可复核的回归不需要复制所有原音,但必须能让别人重建结论。推荐按以下结构组织:

text
case/
├── manifest.json       # 用例、参数、环境和源码提交
├── input.sha256        # 原始 PCM 或输入文件摘要
├── expected.json       # 人工参考、角色标注和通过标准
├── events.ndjson       # 原始事件,保留顺序和时间戳
├── metrics.json        # 时延、错误、资源和并发统计
└── conclusion.md       # 通过项、失败项和边界

manifest.json 至少记录四类信息:

json
{
  "case": "交替说话与重叠语音",
  "source_commit": "<commit>",
  "environment": "<hardware-driver-os-runtime>",
  "start": {
    "sample_rate": 16000,
    "channels": 1,
    "processor_mode": "role"
  },
  "acceptance": ["protocol", "text", "role", "latency"]
}

摘要用于绑定证据,不用于取代证据。输入文件、镜像、交付 ZIP 和报告都应能通过 SHA-256 回到当次源码和配置。

回放脚本也会制造误差 ​

逐帧发送后固定 sleep 会累积读取、编码和发送开销,长录音因此越来越慢。应使用单调时钟和样本进度计算绝对截止时间:due = started + samples_sent / sample_rate,每次只等待到 due。赶不上时记录调度偏差,不悄悄补静音或改变输入节奏。

比较两版服务时,固定 PCM、启动参数、发送节奏、入口和超时规则。浏览器音频预处理、重采样、量化和帧拼接变化,都可能让“同一段声音”变成不同字节。前端优化帧长或发送方式后,还要核对首尾样本与最终质量,不能以音频播放时长接近作为输入一致证明。

下面的片段使用绝对时间发送等长 PCM 帧。frame_samples 是每声道样本数,不是字节数;不同格式要按声道和位宽换算。发送本身耗时计入时间线,落后时立即发送并记录偏差,不在每帧后额外追加固定 sleep。

python
import asyncio
import time

async def replay(frames, send_pcm, sample_rate, frame_samples):
    started = time.monotonic()
    sent_samples = 0
    for frame in frames:
        due = started + sent_samples / sample_rate
        await asyncio.sleep(max(0.0, due - time.monotonic()))
        await send_pcm(frame)
        sent_samples += frame_samples(frame)

四层音频与输出对照 ​

漏字首先定位在采集 PCM、接收 PCM、分段 PCM、模型输入哪一层。对同格式同范围的连续音频比较字节数和摘要;对子段核对区间与覆盖关系;模型输入包含参考或 padding 时按合同分解,不能直接和原音整体比较。

bash
wc -c capture.pcm received.pcm
shasum -a 256 capture.pcm received.pcm
cmp capture.pcm received.pcm

接收完整但模型尾帧缺失,查提交与收尾;模型输入完整但原始解码缺字,查上下文、切分和模型质量;原始事件有字而 UI 缺字,查合并与渲染。保存 raw text 与规范化文本,避免重复折叠或字符串替换隐藏模型错误。

文本使用人工参考统计 CER/WER,并单列删除、插入和替换;说话人使用对应时间区间的身份标注,检查换人、同一人返回与重叠。目标模式分别统计目标内容保留和非目标误输出。若采用 DER,还要固定边界容忍、重叠计分和身份映射规则,避免不同口径的数字互相比较。

证明实际执行了目标路径 ​

页面显示模式开启、日志中的布尔字段为 true,都不一定证明目标识别已执行。应核对任务路由和模型实际输入,例如登记参考是否进入预期输入、目标参数是否在最终任务保留、失败恢复是否绕过门控。中间层清空参数后仍回报原始模式,会产生虚假的验收结论。

普通识别、角色分离和目标识别分别留存证据;新增成功样本不能覆盖旧版本的失败记录。报告中写明构建身份和环境,避免把不同版本的短测、长测和并发结果拼成一次“全通过”。

用例矩阵要覆盖变化点 ​

不要只用一条“正常会议录音”。至少覆盖以下组合:

  • 纯目标说话人、纯非目标说话人、目标说话人与非目标说话人交替。
  • 重叠说话、短句、句首弱音、句尾弱音和长停顿。
  • 单人、多人、角色切换密集和跨窗口连续会话。
  • 正常结束、调用方停止、服务端 EOF、网络中断和重启恢复。
  • 冷启动、热态、低并发和接近容量上限的并发。

每增加一种模型、VAD、门控或协议映射,就要判断它影响了哪些矩阵项。没有受影响项的回归记录,不能支撑“整体通过”。

报告结论如何写 ​

报告把结论分成三种状态,避免把证据不足写成失败或通过:

  • 通过:样本、环境和验收条件均满足,且没有观察到违反项。
  • 未通过:出现了明确违反项,附原始事件、音频片段或日志位置。
  • 未验证:缺少目标环境、人工标注或触发条件,不能据此关闭问题。

例如,“协议通过,角色质量未验证”比“本轮通过”更准确;“测试入口未复现”只能说明当前样本和环境未复现,不能替代目标环境上的触发条件回归。

最小回归流程 ​

  1. 固定源码提交、模型摘要、启动参数和输入摘要。
  2. 先跑能直接覆盖问题的小样本,确认修复方向。
  3. 按角色、时延、协议和异常收尾扩展矩阵。
  4. 保留原始事件和失败证据,不用人工整理后的截图替代。
  5. 将每个维度分别判定,再给出范围明确的总评。
  6. 将断言绑定到模型、运行库、配置和实际执行入口。

小结 ​

输入一致、模型路径一致、指标口径一致,才构成有效的前后对照。协议完成、文字准确、角色稳定和目标过滤分别验证,优化哪一层就用对应断言证明。

别急,先让缓存热一下。