asr-bench:流式 ASR 批量评测框架本机跑通
一、背景速览:为什么要自己搭 ASR 评测
厂商官网只给一个"识别准确率 98%"的营销数字,但外呼链路真正卡人的是三件事,官网都不会写:
配套调研结论见调研中心: 开源 ASR 权重模型选型与私有化部署 ↗(含 FireRedASR2 / Qwen3-ASR 与云厂商精度对比、自建盈亏平衡测算)、 ASR 评测工具调研 & 批量测试看板 ↗。
mock 适配器验证整条管线,凭证到手后换一个 --vendors 参数即可。
二、环境准备:装什么、怎么装
2.1 依赖分层
| 依赖 | 用途 | 必需性 |
|---|---|---|
| Python 3.9+ | 框架本体(asyncio 流式推流) | 必需 |
websockets | 火山/阿里的 WebSocket 流式协议 | 接真实厂商时必需 |
amazon-transcribe | AWS Transcribe Streaming 官方 SDK | 可选 |
azure-cognitiveservices-speech | Azure Speech STT 官方 SDK | 可选 |
macOS say + afconvert | 合成带标注的测试集(系统自带) | 生成数据集时用 |
2.2 建虚拟环境并安装
# 进入工具目录 $ cd asr-bench # 建独立 venv,避免污染系统 Python $ python3 -m venv venv $ ./venv/bin/pip install -r requirements.txt # 确认版本 $ ./venv/bin/python --version Python 3.9.6
2.3 生成带标注的测试集
正式评测必须用真实业务录音,但跑通阶段先用系统 TTS 合成——好处是参考文本 100% 准确,CER 计算不会被标注错误污染。
$ python3 scripts/gen_dataset.py # 产出:12 条中文客服话术 wav + manifest.jsonl $ wc -l data/manifest.jsonl 12 data/manifest.jsonl $ ls data/audio | head -3 kf_001_tingting.wav kf_002_tingting.wav kf_003_tingting.wav
manifest 格式极简,替换成自己的录音只需按这个格式写:
{"audio": "audio/kf_001_tingting.wav", "reference": "帮我查询一下我的话费余额。"}三、跑通步骤
-
用 mock 适配器跑通全流程
不需要任何凭证,验证音频加载、按语速推流、打点、CER 计算、聚合、报告输出六个环节。
执行评测$ ./venv/bin/python -m asr_bench run \ --vendors mock \ --manifest data/manifest.jsonl \ --config vendors.example.json \ --repeats 2 -
观察逐条明细
每条样本实时打印首包、尾包、CER 与识别文本,异常样本一眼可见。
[mock] kf_001_tingting.wav ok 首包=251.2ms 尾包=181.1ms CER=0.0 => 帮我查询一下我的话费余额。 [mock] kf_005_tingting.wav ok 首包=251.1ms 尾包=180.9ms CER=0.0 => 我的宽带今天上午突然断网了,你们能派人来修吗? [mock] kf_012_tingting.wav ok 首包=250.3ms 尾包=181.1ms CER=0.0 => 不用了谢谢,再见。 -
读汇总分位数
延迟指标只看均值会被长尾骗,框架直接给 P50/P95/P99。
===== 评测汇总 ===== ◆ mock 样本 24/24 成功率 100.0% 首包(ms) P50=251.1 P95=251.3 P99=251.3 mean=251.1 (n=24) 首字(ms) P50=251.1 P95=251.3 P99=251.3 mean=251.1 (n=24) 尾包(ms) P50=181.1 P95=181.2 P99=181.3 mean=181.0 (n=24) 建连(ms) P50=81.1 P95=81.2 P99=81.2 mean=81.0 (n=24) CER mean=0.00% pooled=0.00% (n=24) 结果目录: asr-bench/results/20260730-080217 -
查落盘产物
raw_results.jsonl每条明细可复查,summary.json供看板消费,report.md可直接贴文档。$ ls results/20260730-080217/ raw_results.jsonl summary.json report.md # 导出 HTML 看板数据 $ python3 scripts/export_site_data.py results/20260730-080217
四、本机验证结果
| 指标 | P50 | P95 | P99 | 均值 | 口径说明 |
|---|---|---|---|---|---|
| 建连 | 81.1 | 81.2 | 81.2 | 81.0 | WebSocket 建连 + 任务确认 |
| 首包 | 251.1 | 251.3 | 251.3 | 251.1 | 首包音频发出 → 首个 partial |
| 首字 | 251.1 | 251.3 | 251.3 | 251.1 | 首个非空识别文本 |
| 尾包 | 181.1 | 181.2 | 181.3 | 181.0 | 末包+结束指令 → final 确认 |
单位 ms,n=24。成功率 100%,无失败样本。
mock 各阶段延迟构成(P50,ms)
mock 的数值来自配置(建连 80ms、首包 250ms、尾包 180ms),实测结果与配置误差均在 1.3ms 内 —— 说明框架的打点与推流节奏控制是准的,这正是本次要验证的东西。
4.1 验证通过项
| 验证环节 | 结论 | 依据 |
|---|---|---|
| 音频加载与时长解析 | PASS | 12 条 wav 全部加载成功,无格式报错 |
| 按真实语速推流 | PASS | 绝对时钟对齐,100ms/包,未出现累积漂移 |
| 打点精度 | PASS | 实测与配置偏差 ≤1.3ms(251.1 vs 250) |
| 分位数计算 | PASS | P50/P95/P99 单调,n 计数正确 |
| 重复轮次聚合 | PASS | repeats=2 → 12×2=24 条,无漏无重 |
| 三种产物落盘 | PASS | jsonl / json / md 全部生成且可解析 |
五、从 mock 升级到真实厂商
框架已内置四家适配器,凭证支持 env:VAR 占位,不用把密钥写进配置文件:
| vendor | 服务 | 需要的环境变量 |
|---|---|---|
volcengine | 火山引擎 豆包大模型流式识别(sauc bigmodel) | VOLC_APP_KEY VOLC_ACCESS_KEY |
aliyun | 阿里云 NLS 实时语音识别 | ALIYUN_NLS_APPKEY ALIYUN_AK_ID ALIYUN_AK_SECRET |
aws | AWS Transcribe Streaming | AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY |
azure | Microsoft Azure Speech STT | AZURE_SPEECH_KEY(region 在 vendors.json 配) |
$ cp vendors.example.json vendors.json $ export VOLC_APP_KEY=xxx VOLC_ACCESS_KEY=xxx $ export AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx AZURE_SPEECH_KEY=xxx # 每条样本至少 3 轮,延迟才有统计意义 $ ./venv/bin/python -m asr_bench run \ --vendors volcengine,aliyun,aws,azure \ --manifest data/manifest.jsonl --config vendors.json --repeats 3
新增一家厂商要写多少代码
继承 asr_bench/adapters/base.py 的 StreamingASRAdapter,实现一个 transcribe(),在 adapters/__init__.py 登记即可。基类已经把最容易写错的按真实语速推流封好了(绝对时钟对齐,避免发送耗时累积导致推流变慢,进而把延迟测虚高)。
六、踩坑记录
_mock_transcript),所以 CER 必然是 0.00%。
这个数字只证明"CER 计算链路通了",完全不代表任何识别精度。想验证 CER 计算是否正确,
得手动在 manifest 里注错字,看 CER 是否按预期变化。
say 出来的是 16kHz 干净朗读音,而外呼真实场景是 8kHz 电话信道 + 背景噪声 + 口音。
两者的 CER 差距可能大于厂商之间的差距。正式评测必须替换真实业务录音,且电话信道样本要单独成组统计。