WER 4.0% 背后:流式转写延迟、成本与稳定性的实战避坑指南

发布时间:2026/10/31 12:53:42
WER 4.0% 背后:流式转写延迟、成本与稳定性的实战避坑指南

做语音评测这几年,我越来越觉得“WER”是个被过度宣传但也容易被误读的指标。前两天有人拿 Gemini 3.5 Transcribe 的流式转写结果问我,说官方指标看着很漂亮,为什么自己现场跑起来感觉没那么神。我干脆自己搭了一套评测流程,用大概 40 小时的会议、电话和噪声音频实测了一轮,综合 WER 落在 4.0% 左右。这篇分享不是把官方文档的参数复述一遍,而是把流式转写接入过程中真正消耗时间的工程取舍讲清楚:延迟、成本、稳定性、后处理、评测口径,一个都不能少。如果你正在做语音转写选型,或者在为实时字幕、客服质检方案纠结,这篇应该能帮你少踩一半的坑。

1. 先搞清楚一件事:WER 4.0% 在真实业务里到底意味着什么

1.1 WER 不是“错误率”,是一套复杂的对齐算法

WER 的底层是编辑距离,算法本身不关心语义。把参考文本和识别结果做最小代价对齐,然后算 (插入数 + 删除数 + 替换数) / 参考词数。比如参考是“今天天气很好”,识别结果是“今天天气真好”,这里就是一个替换错误,WER 约 16.7%。但如果这句话里把“不是”识别成“但是”,从词序列看只是替换了一个字,可语义完全反转,对下游业务来说可能是事故。

所以拿到 4.0% 这个数,先别急着开心或失望,要追问一句:这个数是用什么文本、什么对齐规则、什么归一化方式算出来的?同样的音频,按字算和按词算结果能差出两三个点;数字是转中文读音比对还是直接比对阿拉伯数字,结果也不一样。我在实际测试里踩过这个坑,一开始用字符串直接比对,WER 虚高到 9%,把数字和英文归一化之后才回到 4% 附近。

1.2 我这次测的 4.0% 是在什么条件下得到的

这次实测一共准备了约 40 小时音频,三类场景,全部人工转写参考文本且做了双人复核。中文按“字”级计算,数字和英文先统一转成读音再比对标点不计入错误。综合结果如下:

场景时长WER主要误差类型
安静会议室双人对话20小时2.6%专名、同音字
电话语音(8kHz)10小时3.9%数字、吞字
办公室噪声/背景音乐10小时6.8%插入、字首丢失

综合平均 4.0%。但请注意这个平均数的欺骗性,会议室场景能把整体拉低,噪声场景单独看却接近 7%。如果你只拿一个综合指标向老板汇报,后面大概率会被现场翻车打脸。做生产系统选型时,一定要按业务实际场景拆开看。

1.3 公开基准和真实音频之间隔了十条街

公开基准测试集通常经过人工筛选,噪声、方言、叠话比例都不高,评测时还允许后处理模型帮忙兜底。真实业务音频则是另一回事:电话转接音、线路底噪、多人同时说话、背景广播、甚至有人咳嗽一声都被录进去。Gemini 3.5 Transcribe 在公开基准上的表现和真实场景的表现会有差距,这几乎是所有识别引擎的通病,不是某一家的特例。所以看到“WER 4%”的宣传或评测,第一反应应该是问测试集构成,而不是直接代入自己的业务。

2. 实测流式转写:Gemini 3.5 Transcribe 接入过程的完整复盘

2.1 流式转写链路:音频进、文本出、延迟怎么算

流式转写和批式转写最大的区别是“边说话边出结果”。链路大致是:音频采集分帧(50~100ms)→ VAD/端点检测 → 模型推理 → 输出 partial(部分假设)和 final(最终假设)→ 标点恢复 → 回传客户端。其中 partial 是实时草稿,final 是引擎确认不再修改的文本。

这里有一个很多人第一次接触容易搞混的点:流式延迟不是单一指标,至少得分“首包延迟”和“最终结果延迟”。首包延迟指从开始说话到第一个 partial 出现的时间,Gemini 3.5 Transcribe 在我这轮单路测试里大约是 350ms;最终结果延迟指从这一句话说到 EOU(End of Utterance,即引擎判定一句话结束)到拿到 final 文本的时间,默认静音阈值下大约 800ms。这两个指标一个影响“哇它有反应了”,一个影响“这行字幕到底稳不稳”,优化方向完全不同。

2.2 接入时的三个关键选择:采样率、编码和协议

接入流式接口,第一件事是统一音频格式,否则后面所有问题都是玄学。

第一个选择是采样率。16kHz 单声道 PCM 是识别引擎最稳的标准输入。电话语音常见 8kHz,直接送也能识别,但实测 WER 会差 0.3~0.5 个点,建议先用 FFmpeg 重采样,命令格式大概是:

ffmpeg -i input.wav -ar 16000 -ac 1 -c:a pcm_s16le output_16k.wav

第二个选择是编码。流式接口一般支持 PCM、WAV、FLAC、Opus 等。实时场景我更推荐 PCM 或 Opus。PCM 无压缩,带宽占用高但延迟端到端最可控;Opus 在 128kbps 下音质损失很小,实测 WER 和 PCM 几乎无差异,适合弱网环境。

第三个选择是传输协议。WebSocket 双向流适合长连接场景,调试方便;如果并发和可靠性要求高,gRPC 双向流的断线重连机制更成熟。建议把音频分帧序号带到协议消息里,否则重连后音频拼接顺序很容易错位。

2.3 一个最小可跑的流式转写示例(Python 思路)

下面的代码是演示思路的简化版本,不同 SDK 的类名和参数会有差异,但核心结构一致。重点放在事件回调上,不要照抄。

import os from some_sdk import StreamingTranscriber, AudioSource client = StreamingTranscriber( api_key=os.environ["GEMINI_API_KEY"], sample_rate=16000, encoding="pcm_s16le", vad="auto", hotwords=["产品名A", "客户名B"], partial_results=True, ) pending_text = {} def on_partial(seg_id, text): # 注意:不要在回调里写数据库或更新 UI pending_text[seg_id] = text def on_final(seg_id, text): pending_text.pop(seg_id, None) print(text) source = AudioSource.open("meeting.wav", chunk_ms=100) with client.session() as session: for pcm in source.iter_pcm(): session.push(pcm) session.finish()

这里最想提醒的一点:回调函数里不要做重活。我在第一版demo里直接在 partial 回调里写日志和更新界面,结果并发一上来就把客户端主线程卡死。正确做法是回调里只把结果丢进队列,由单独消费者线程统一处理。

2.4 并发不是开关,是预算

很多人以为流式接口和普通 HTTP 接口一样,多开几个线程就能获得并发能力。实际上流式识别的并发是“持续占用资源”,一路音频从开始到结束都要占着一个模型实例或配额,不是请求发完就释放。

我做了个并发压测:单路 p95 延迟约 950ms,升到 4 路并发后 p95 涨到 1.8 秒,8 路并发直接到 3.2 秒。如果用 4.0% 的单路延迟去倒推生产容量,大概率会翻车。如果业务确实需要几十路并发,必须预留连接池、缓冲队列和重试策略,并且给每路加上超时熔断,防止一路慢音频拖垮整个服务。

3. 深入拆解 WER:误差分类、评测脚本与人工复核

3.1 误差到底掉在哪里:替换、删除、插入各占多少

只看综合 WER 很难定位问题。我这次把错误分成三类统计,结果分布大概是替换 62%、删除 18%、插入 20%。这个占比能说明很多事情:

替换是最大头,中文场景里多为同音字、多音字和专名错误,比如“张翊”识别成“张毅”,“重阳”识别成“崇阳”。删除主要来自句中语气词和字首吞音,比如“嗯”“啊”这种非语言成分被 VAD 剪掉,有时也会把一句话开头的一两个字吞掉。插入则是 VAD 把笑声、清嗓子、关门声识别成了文字,噪声场景尤为严重。

所以如果你的业务场景噪声多,优化重点应该是插入错误;如果是专名多,重点应该是热词功能和上下文偏置;如果是电话语音,重点应该是数字和吞字。盲目追求 WER 数字下降,但不看错误结构,很难做出有效优化。

3.2 评测脚本怎么处理数字、英文和标点

评测脚本的归一化规则直接影响 WER 的可信度。我在这次实测中用的规则如下:统一转小写、全角转半角、数字转中文读音(比如 2024 转成“二零二四”)、英文保留原词、移除所有标点,然后用编辑距离计算。

import re import jiwer def normalize_text(text: str) -> str: text = text.lower().replace(",", ",").replace("。", ".") text = full_width_to_half_width(text) text = digits_to_spoken(text) text = re.sub(r"[^\w\u4e00-\u9fff]", "", text) return text.strip() wer = jiwer.wer(normalize_text(reference), normalize_text(hypothesis))

写评测脚本时还有两个细节要留意。一是流式结果必须等 final 拼接完再打分,不能对 partial 打分,否则句子没说完就开始算错误,WER 会虚高。二是归一化规则一旦定了就不要中途改,否则前后两轮评测数据没法对比。

3.3 人工复核比算法评测更重要

WER 无法反映语义层面的问题。我建议评测时随机抽 10% 的音频,人工听一遍并打三个标签:方向性错误、关键实体错误、可理解度。方向性错误就是前面说的“不是/但是”这类情况,关键实体错误是人名、电话号、金额出错,可理解度是让一个没听过原音频的人能否看懂转写文本。

这轮人工复核帮我发现了一个纯 WER 指标完全看不出来的问题:模型在人名和地名上的错误率虽然占比不高,但一旦错就是整段话的核心信息错。业务侧如果要接关键词提取或工单自动分类,这种错误会直接污染下游。所以评测不能只看算法指标,一定要留人工抽检的预算。

4. 工程取舍:延迟、成本、稳定性和准确率不可能全都要

4.1 延迟:首包延迟和 end-of-turn 延迟是两回事

流式转写体验好不好,很大程度上取决于 EOU 检测的敏感度。EOU 判定一句话是否结束,最常见的方式是静音长度。Gemini 3.5 Transcribe 默认静音判定大概是 800ms,可调范围在 300ms 到 1200ms 之间。

如果为了“响应快”把静音阈值调到 300ms,效果是停顿稍长就会被当成一句话结束,紧接着开始下一句时音频已经丢了前半截,最终结果会出现断词和吞字。如果调到 1200ms,又会让整个交互感觉很迟钝。我实测后的建议是:默认值或者略微下调到 600ms,不要低于这个值。如果业务场景里有很多长句和思考停顿,宁可 UI 端做一点缓冲延迟,也不要为了快而牺牲完整性。

4.2 成本:流式不是批发,是按秒计费的试吃

流式转写适合“实时交互”场景,但成本比批式要高。我把它俩放在一个表里做对比:

维度流式转写批式转写
计费方式按音频时长,同时占用常驻配额按音频分钟,可排队处理
延迟秒级出结果分钟级,取决于队列
适用场景实时字幕、实时质检、会议摘要录音归档、批量审核、离线字幕
错误率可能略高于批式可用更大模型和更多后处理

如果业务是“录完音之后几小时出转写”,完全没必要用流式接口。流式的价值在线索要“快”,不是“便宜”。我见过有人拿流式接口跑批量转写任务,结果成本比批式高一倍,延迟优势又体现不出来,属于典型的选型错误。

4.3 稳定性:断线重连、音频积压与静音检测

生产环境里最大的敌人其实是客户端音频积压。网络波动导致连接断开,SDK 还在继续录音,重连成功后如果把断线期间的旧音频全部塞给服务端,延迟会瞬间爆炸,而且文本拼接也会乱序。

常规做法是:重连成功后只发送当前时间点之后的音频,最多保留 30 秒缓冲,超出的旧音频直接丢弃,并在 UI 上提示“网络不佳,部分内容可能缺失”。另一个细节是 VAD 剪静音时,前后要保留约 300~400ms 的音频保护,直接把静音段一刀切干净,句子首尾的字会被误伤。

4.4 后处理:标点恢复和逆文本正则化容易被低估

WER 只衡量“识别文字对不对”,但业务方往往要求“文本能不能直接用”。这里就涉及标点恢复和逆文本正则化。标点恢复可以做成后续模型,但也可能引入二次错误,尤其在长句和口语化内容里,断句断错位比没标点更难读。如果引擎自带标点恢复,建议优先打开,然后在业务侧做规则修正。

数字格式化也要单独处理。识别引擎通常会把“2024”转成“二零二四”,但业务场景里可能希望保留阿拉伯数字。这类问题在评测阶段容易漏掉,因为评测代码已经做了归一化,但下游系统拿到的原始结果是带中文读音的。建议在接入层的后处理管线里统一做一轮格式转换,优先级高于业务逻辑。

5. 实测踩坑:403、账户资格和 CLI 集成里的真实问题

5.1 遇到 403 先别怪 API,按顺序排查

实测过程中遇到 403 几乎是必然的。一次是账户没权限,一次是 API Key 配错项目,还有一次是请求头少了关键参数。我的排查顺序是固定的:

  1. 确认账户和项目是否在产品支持清单内,有些新模型处于灰度阶段,不是所有账号都有权限。
  2. 确认 API Key 所属项目和当前请求的项目一致,且开通了计费。
  3. 检查配额和速率限制是否超限,很多 403 其实是配额用尽。
  4. 确认使用的区域端点是否支持该服务。
  5. 最后再看网络侧,是否存在企业防火墙或网络策略拦截。

也可以先用 curl 做一个最小请求,排除客户端 SDK 封装带来的干扰:

curl -X POST \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"audio": {"sample_rate_hertz": 16000}}' \ "$ENDPOINT"

这里想特别说一句:碰到 403 时不要第一反应想“绕过”,先确认自己有没有资格。很多模型能力是分阶段放量的,账户暂时没开通就等一等,或者申请内测资格。用不明来源的工具去改请求或伪装身份,既不稳定也不安全。

5.2 “your account is not eligible…” 这类提示的常见含义

这次测试时我见过类似 “your account is not eligible for gemini code assist for individuals at this time” 的提示。这类文案的共同点是:不是网络问题,也不是参数问题,而是账户资格问题。常见原因有模型处于内测候选名单之外、订阅套餐不包含该功能、组织管理员未开通权限等。

面对这类提示,正确做法是检查订阅状态、向支持渠道申请或等待官方逐步开放。个人开发者如果暂时没有资格,可以先找替代方案把流程跑通,等渠道开放后再切换。我自己就是这么做的,先把接口抽象封装好,后面换模型只需要改配置,不影响业务代码。

5.3 macOS 下 CLI 的本地调试体验

我在 macOS 上用命令行工具做了一轮快速验证,发现 CLI 对流式调试确实方便,能直接看到 partial 和 final 的输出顺序,比写一个完整客户端快很多。安装后用官方安装脚本或包管理工具装好,运行前把 API Key 放到环境变量里,而不是写进 shell 历史。

建议先跑一段 10 秒的小音频做 smoke test,确认鉴权、网络、参数都没问题,再上真实长音频。CLI 的日志级别调到 verbose 后能观察到请求的完整往返时间,这对定位“到底是网络慢还是模型推理慢”很有帮助。但 CLI 不适合做并发测试,它的单路模式反映不了真实并发下的延迟变化。

5.4 partial 和 final 混排导致文本重复

流式转写里最容易出现的工程 bug 是文本重复:客户端同时收到同一个 segment 的多个 partial 和一个 final,如果按收到顺序直接拼字符串,就会出现“今天天气很好今天天气很好”这种结果。正确逻辑是以 final 为准,同一 segment_id 的 partial 只是预览,不该写进最终结果。

pending = {} merged = [] for event in events: if event.type == "partial": pending[event.segment_id] = event.text elif event.type == "final": merged.append(event.text) pending.pop(event.segment_id, None)

另外要小心 segment_id 的边界。如果一个 segment 结束时间戳和下一个 segment 开始时间戳重叠,说明中间存在断句歧义,这时候应以文本含义为准,两端都不重不丢在实现上几乎是做不到的。稳妥做法是允许少量重叠,但禁止重复输出。

6. 选型建议:什么时候该用 Gemini 3.5 Transcribe,什么时候别用

6.1 推荐用它的场景

先说适合的场景。第一是实时会议字幕,尤其是中英混说比较多的会议,这类场景对延迟敏感但对绝对准确率宽容度稍高,人可以一边看字幕一边听。第二是客服实时质检,人工坐席通话过程中需要实时提示话术风险点,WER 4% 左右加上关键词命中,基本够用。第三是直播字幕或现场活动的实时字幕,对低延迟要求高,对成本不是第一敏感。

还有一个我实测下来比较惊喜的点:中英混说的识别切换比很多本地模型自然。中文为主、偶尔夹英文单词或整句的音频,Gemini 3.5 Transcribe 的语种切换错误明显少于我本地部署的同类模型,这对国内企业会议场景很重要。

6.2 不推荐的场景

不适合的场景也明确说一下。一是批量长音频转写,录音时长几小时的访谈或课程,用流式接口既贵又不稳定,批式转写明显更合适。二是嵌入式或离线场景,需要本地推理、断网可用,这显然不是云端 API 的强项。三是数据合规非常严格的行业,比如某些金融、医疗场景要求音频不出本地,那再低的 WER 也和你无关。

6.3 怎么用最低成本验证你的场景

如果你还在犹豫,建议按三天的节奏做一个最小验证,别一上来就接全量业务。Day 1:准备至少 30 分钟有代表性的真实音频,覆盖干净、噪声、电话、多人讨论四类,并完成人工参考文本。Day 2:写一个最小 demo,跑通流式接口,统计每个场景的 WER、延迟和成本。Day 3:把错误样本逐条分类,人工听一轮,判断哪类错误是业务不能接受的。

我自己的体会是,WER 4.0% 只是一个平均数的壳子,真正值钱的是你有没有一套让自己信服的评测方法和一个能把误差结构分析出来的工具箱。如果你正在做选型,不妨把这件事当成一次工程评测来做,而不是一次 API 试用,留出至少两周的评估时间。这样上线前就能看清 Gemini 3.5 Transcribe 适不适合你的真实场景,而不是等业务跑起来之后,才开始怀疑评测数字到底是真的还是自欺欺人。