语音接大模型:转写全对,为什么回答还是错的

发布时间:2026/10/28 10:18:03
语音接大模型:转写全对,为什么回答还是错的

版权与内容来源声明
本文为原创整理。文中涉及官方文档、开源仓库、论文与公开报道的内容,均在附表 A 中标注来源;引用官方原文保持原样,不作改写。文中命令、版本号与界面截图以本文成文时的实测/核验结果为准,标注「待验证」的部分请以你本地环境实际输出为判断依据。本文不推荐任何破解、盗版软件,也不推荐任何规避地区限制的访问方式,亦不对任何收益结果作承诺。转载请注明出处。

一、抽检全对,回答全错

先说一个很常见的场景。设备巡检的师傅按住说话,说三五秒,语音先过识别,再送大模型,模型返回一条操作建议或一次数据查询结果。上线之后,业务侧的反馈是「它老答错」。技术侧先查识别,每天抽一批录音,人耳复听,跟转写逐字比对。

比对的结果是:字基本都对。偶尔有标点差异,偶尔少一个语气词。连续看两周,字面错误少到可以忽略。于是结论很快定下来——识别没问题,问题在模型,去调提示词、换模型、加检索。

这个结论通常是错的。错不在判断,在抽检本身看的维度。

抽检看的是「字对不对」,而下游要的是「结构对不对」。这两件事之间隔着一整条链路:识别吐出片段,工程代码把片段拼成一段文本,这段文本才是模型的输入。拼接这一步删掉了大量字段,而删掉的那些字段,恰好是模型判断语义边界、指代对象、数值归属的依据。字一个没错,结构全丢了。

更麻烦的是,抽检样本天然偏向容易核对的样本:人会下意识挑安静环境、单人、短句、口音轻的录音,因为这种样本好比对。而线上出错的,往往是多人、长句、带型号和数字、说到一半改口的那一类。抽样偏了,指标就永远好看。

抽检时看的实际漏掉的东西在下游的表现
每个字是否与复听一致片段之间的边界落在哪一句被拆成两个意图,或两句并成一句
整段读起来是否通顺数字与单位的搭配是否被改数值算错,但句子读起来毫无破绽
单条样本本身是否正确多轮之间说话人是否搞混指代指向了错误的人
有没有明显错字出错位置对应第几秒无法复现,无法回归,无法回听

还有一个组织层面的原因:「转写→模型输入」这一段不属于任何人的职责。做识别的人交文本,做应用的人收文本,中间那层拼接代码往往是某个下午顺手写的,没有测试,没有对照集,也没人评审。结构性的信息就丢在这段没人认领的代码里。

二、识别交出来的是片段,不是一段文字

先看识别实际交出来的东西。以 Whisper 系实现为例,返回值不是一个字符串,而是一串片段,每段至少带这些字段:起始时间、结束时间、文本、对应的 token、平均对数概率、非语音概率。切分依据是声学停顿和窗口长度——很多实现按三十秒滑窗处理,段间还有重叠。

工程里最常见的写法,是一行"\n".join(seg["text"] for seg in segments)。这一行之后,五个字段里只剩文本,另外四个连同片段边界一起消失。消失的东西可以列清楚:

  • 边界:停顿落在哪、一段有多长、段与段是不是同一句话;
  • 说话人:这段是谁说的(如果做了分离的话);
  • 把握程度:哪一段模型自己也拿不准,哪一段是硬猜的;
  • 对齐:哪个字对应第几秒;
  • 覆盖关系:流式场景下哪些是中间结果、哪些是最终结果。

这五类信息里,只有「字」被抽检覆盖了。其余四类,从删除的那一刻起就再也没有人看过。

这里要说清一件事,免得把方向搞反:通用语音识别确实做得足够好,字面错误率低到抽检看不出来——这不是错觉。Radford 等人在Robust Speech Recognition via Large-Scale Weak Supervision(arXiv:2212.04356)的摘要里写的是:在 68 万小时多语言、多任务弱监督数据上训练的模型,在标准基准上零样本迁移即可与先前的全监督结果相比,并且「When compared to humans, the models approach their accuracy and robustness」。注意用词是 approach——接近,不是超过;比较对象是论文选定的基线与测试集,不是你车间里那台设备、你那批型号、你那位带口音的师傅。摘要里没有给出任何可以直接搬到具体业务上的准确率数字,本文也就不引用这类数字。

这句话的真正用处,是解释为什么整条链路会集体误判:通用识别足够好,字面错误足够少,注意力自然转向大模型。但「字对」和「意思对」之间隔着一层结构,而结构在拼接那一行被删掉了——这正是后面五层失真的共同源头。

层次丢掉的结构典型表现下游直接后果
一分句边界长句被切成两段,或两句并成一段意图被拆开或合并,槽位串到别的请求上
二音到形的唯一性四点七变四点一,毫安变毫安时数值、单位、型号进计算或检索,整条答案错
三语句完整性尾词还在路上就发起请求模型按先验补全,答非所问
四说话人归属两个人说的话被拼成一段指代无解,立场混成一个人
五时间对齐只剩整段文本,没有秒数出错无法定位、无法回归、无法复核

三、第一层:分句边界丢失

现象:识别给出的边界是声学的,不是语法的。标点由后处理模型补——补标点的模型看到的是切好的片段,只能尽力把逗号句号塞进去,没有办法还原说话人本来在哪里断句。于是专业长句一到,边界必错。

三种错法最典型:

  • 并列枚举被切断。「一号泵、二号泵、三号泵的轴承温度」中间换一口气,前半段就单独成段,后半段的「的轴承温度」失去修饰对象,变成一个孤立短语。
  • 条件句被切断。「如果压力超过零点六兆帕,就先把旁路打开」,前半句单独送进去,模型读到的是一个陈述句而不是条件句,它会去解释「压力超过零点六」是什么状态,而不是给出「打开旁路」这个动作。
  • 两句被并成一句。说完「三号泵怎么样」紧接着说「上周的记录调出来」,中间停顿短,被合成一段,模型读到的是一个语义混乱的长请求。

为什么下游会答错:意图分类和槽位抽取的输入单位是「句」。边界一错,一个请求被拆成两个不完整的请求,或者两个请求被合成一个语义混乱的请求。前者的表现是「只答了一半」,后者的表现是「答了另一件事」,两种在日志里都长得完全正常。

再往深一层,是指代范围变了。中文里「它」「这个」「那个」的先行语通常在同一句或上一句里,句子边界一错,搜索范围就错。用户说「三号泵振动偏高。它上周才换的轴承」——如果把两句并成一句,模型很可能把「它」理解成轴承而不是泵,后面整段建议就围绕轴承展开。

第三层原因是框架假设:编排框架把一次请求当成一条消息,不会回头质疑「你这句话是不是被切了」,切错了也照单全收。

修法方向:不要拿识别的切分当句子边界,而是用「停顿阈值 + 句末特征 + 最长长度」重新聚合,并且把每段的秒数一起留着。下面这段只用标准库,把片段按 1.2 秒以上的停顿聚成句,输出带时间范围:

segs=[{"start":0.0,"end":2.4,"text":"三号泵的振动值"},{"start":2.4,"end":4.1,"text":"上周是四点七"},{"start":4.1,"end":5.0,"text":"毫米每秒"},{"start":6.8,"end":9.2,"text":"今天复测变成四点一"},]defregroup(segs,pause=1.2):out,buf,t0,prev_end=[],[],None,Noneforsinsegs:ifprev_endisnotNoneands["start"]-prev_end>=pause:out.append({"start":t0,"end":prev_end,"text":"".join(buf)})buf=[]ifnotbuf:t0=s["start"]buf.append(s["text"])prev_end=s["end"]ifbuf:out.append({"start":t0,"end":prev_end,"text":"".join(buf)})returnoutforsinregroup(segs):print(f"[{s['start']:05.2f}-{s['end']:05.2f}]{s['text']}")

输出两行:[00.00-05.00] 三号泵的振动值上周是四点七毫米每秒和[06.80-09.20] 今天复测变成四点一。

第二句没有单位。这就是问题本身——单位在上一段里,聚合规则按停顿切,把「毫米每秒」留在了前一句。所以聚合之后还得补一步「单位与量词向后继承」,或者干脆把相邻两段一起送。很多人做到聚合就停了,结果单位仍然错位,只是错得更隐蔽。

四、第二层:同音与专名,错得很像对的

现象:同音替换不会破坏句子的通顺度,反而常被语言模型「顺」得更通顺。这让它成为人工抽检里最难发现的一类错误,也是下游最致命的一类。

类型说的是转写成下游怎么错
数字近音四点七四点一进趋势判断,结论从上升变下降
单位漏字四千八百毫安时四千八百毫安按容量推续航,差一个电压倍数
型号混读A 型密封圈诶型密封圈库存检索查不到,模型改为泛泛而谈
专名同音淬火退火工艺参数整段给错,且看不出错
数字写法混用十二点五12.5 或十二点五抽取规则漏掉一种写法,字段为空

为什么下游会答错,四个不同的机制:

数值会进二次计算。模型不是在复述这个数,而是拿它算差值、算变化、和阈值比较。输入错一位,输出整段结论反向,而措辞依然自信。「由 4.7 降到 4.1」和「由 4.1 升到 4.7」都通顺,只有一个是对的。

单位决定换算系数。「毫安」和「毫安时」差一个「小时」,模型的常识会替它选一个看起来合理的解释,错误被包装成正确答案。这类最难查,因为数值本身对,错的是量纲。

专名决定检索命中。型号、物料名、工艺名是知识库检索的键,键错一位就召回一堆不相干的文档,模型只能基于错误的上下文作答;回答内部逻辑完全自洽,只有对照原始录音才会发现出发点就错了。

最麻烦的一点:平均对数概率帮不上忙。同音替换在声学上完全合理,模型自己并不觉得不确定,置信度不会掉。想靠「置信度低就重试」兜住这一类错误,基本兜不住。

修法方向:术语热词表前置,在解码阶段做偏置,而不是事后让大模型猜——提示词里写「请纠正错别字」是没有用的,模型手里没有音频,它只能按语言习惯猜,而语言习惯恰恰是错的来源。再加一道关键数字与单位的二次确认。下面这段演示抽取「数字 + 单位」并与期望值比对,能把「毫安 / 毫安时」这种差异直接暴露出来:

importre NUM=re.compile(r"(\d+(?:\.\d+)?)\s*(毫安时|毫安|毫米每秒|毫米|伏|安)")EXPECT=[(4.7,"毫米每秒"),(4800.0,"毫安时")]defnumbers(text):return[(float(v),u)forv,uinNUM.findall(text)]defconfirm(text):got=numbers(text)return{"ok":got==EXPECT,"missing":[eforeinEXPECTifenotingot],"extra":[gforgingotifgnotinEXPECT],}say="振动 4.7 毫米每秒,容量 4800 毫安"print(numbers(say))print(confirm(say))

输出是[(4.7, '毫米每秒'), (4800.0, '毫安')],比对结果为ok: False,missing里是(4800.0, '毫安时')。

这里有个坑要提前说:这段只处理阿拉伯数字写法。真实录音里「四千八百毫安时」是中文数字,不先归一化就整段漏抽。漏抽比抽错更危险,因为抽错会触发比对失败,漏抽只会安静地返回一个空列表,看起来一切正常。

五、第三层:流式半句

现象:为了压延迟,常见做法是语音活动检测一判定「说完了」就立刻发请求,或者每隔几百毫秒把增量文本推给模型。三个隐患:

  • 判「说完」的依据是静音时长,不是语义完整。用户换气、思考、翻页、看仪表,都会触发判停。
  • 中间结果会被最终结果覆盖。如果下游缓存了中间结果生成的内容,就会出现同一轮对话里前后两个回答互相矛盾——前一个基于半句,后一个基于整句。
  • 尾音被切。最后一个字还没说完就被截断,识别成另一个字,且这个错字在中间结果里不会自我纠正。

为什么下游会答错:大模型面对不完整指令时的默认行为是补全,不是追问。补全方向由先验决定:一句话停在「三号泵的振动值跟上周比」,模型很可能自己补上「上升了多少」并给出一个数。它答得流畅、结构完整、格式合法,只是答在了一个用户没问的问题上。这类错误在日志里毫无异常,因为请求和响应都是合法 JSON,没有报错,没有超时。

策略附加延迟完整性适用场景
见静音就发最低差,换气即断单词级命令,开、关、上一首
固定间隔发增量低差,且中间结果会被覆盖实时字幕,不接推理
静默窗口 + 句末特征中,数百毫秒较好一问一答的语音助手
等最终结果再发高最好长指令,含数字与型号

修法方向:设静默窗口,同时给最长等待兜底——超过上限就强制放行,但在请求里标注「本段可能不完整」,让下游走追问分支而不是硬答。下面这段把三个条件写在一起:

ENDINGS=("吗","呢","吧","多少","怎么样","是不是")defready(text,tail_silence_ms,window_ms=600,max_wait_ms=2500,held_ms=0):iftail_silence_ms>=window_ms:returnTrue,"静默窗口达标"iftext.endswith(ENDINGS):returnTrue,"句末特征命中"ifheld_ms>=max_wait_ms:returnTrue,"超时强制放行,标注不完整"returnFalse,"继续等"cases=[("三号泵的振动值是多少",120,0),("三号泵的振动值",700,0),("三号泵的振动值跟上周比",300,2600),]fortext,silence,heldincases:ok,why=ready(text,silence,held_ms=held)print(f"{ok!s:5}{why:18}{text}")

三个用例分别命中「句末特征」「静默窗口」「超时强制放行」。最后一条是关键:宁可让模型知道这段不完整,也不要让它以为这段是完整的。一个显式的「不完整」标记,会把模型的默认行为从补全切换到追问,这一句话的差别,比换模型大。

这份语音链路的对照材料:里面是片段字段清单、热词表模板和一份「半句与整句」的对照样例,和本章的静默窗口判断配套使用。放在资料包里,扫码即可获取:

六、第四层与第五层:说话人丢失、时间戳脱钩

第四层,说话人丢失。

多人场景——会议室、坐席与客户、两个人配合巡检——识别输出的是一段连续文本,没有角色标签。工程上的常规做法是把整段作为一条用户消息送进去。

下游会因此答错,三个具体机制:

指代无解。「他说的那个阈值」里的「他」在纯文本里没有对应实体,模型只能就近挑一个名字,或用「对方」含糊带过。前者可能挑错人,后者让回答失去可执行性。

立场被合并。客户说「我担心这个方案撑不住」,工程师说「撑得住」,两句话拼成一段,模型读到的是自相矛盾的一段陈述,输出往往是折中的废话——既说有风险,又说可行。

指令与复述混淆。「改成零点六」这句话是谁说的,决定了它是一条待执行的指令,还是一条转述。没有说话人标签,这两种解释在文本上完全一样。

修法:先做说话人分离,再按「角色:内容」拼上下文;分离结果不确定时,保留「未知说话人」标签,不要静默合并。

第五层,时间戳与原文脱钩。

拼接时只留文本,起止时间丢掉;有的实现连片段顺序都不留,只剩一个整段字符串。后果有四条,逐条都影响能不能把系统调好:

无法定位。用户投诉「它答错了」,你手里只有整段文本,找不到对应第几秒,也就无法回听。想确认是识别错还是模型错,第一步就卡住。

无法回归。改了热词表、调了聚合规则,没有「同一段音频 + 同一套期望输出」的对照集,改完不知道是变好还是变坏。这类改动经常是修好一个、弄坏两个。

无法归因。错误可能来自识别、聚合、检索或生成。没有秒数,就没法把责任分到具体环节,只能整条链路一起怀疑。

无法切片。长会议要按时间窗口送模型,没有时间戳只能按字数硬切,切在句子中间的概率很高。

这两层的共同点是:它们不影响「字对不对」,所以永远不会出现在字准确率的报表里。但它们决定了这个系统能不能被调试。一个不能定位、不能回归的系统,第一次上线是什么水平,往后大概就一直是这个水平。

七、五条工程判据

把前面五层收成一张表,再给五条可以落地的判据。这张表建议直接贴进项目文档,逐行对,缺哪行补哪行。

失真点现象兜底做法
分句边界长句被切断,条件句变陈述句按停顿与句末特征重新聚合,保留秒数
同音专名四点七变四点一,型号检索不到热词表前置偏置,关键数字回读确认
单位与量词毫安与毫安时混用单位向后继承,单位白名单校验
流式半句尾词未落地就发请求静默窗口,超时强制放行并打标
说话人多人拼成一段,指代无解先分离再拼「角色:内容」
时间戳只剩整段文本片段与秒数落盘,随请求一起传

判据一:保留原始片段与时间戳,不要只留拼接后的整段文本。

改哪里:识别适配层。落地形态是一张片段表,字段至少包含会话标识、片段序号、起始毫秒、结束毫秒、文本、说话人、平均对数概率;整段文本只是这张表的一个视图,随时可以重建。

怎么验收:随便挑一条线上请求,看能否在十秒内答出「这句话在第几秒」。答不出来就是没落地。

判据二:术语热词表前置。

改哪里:解码配置,不是提示词。把型号、物料名、工艺名、单位做成词表,喂给识别的偏置参数。事后让大模型纠错是无效的,它没有音频。

怎么验收:拿一批含专名的历史音频重跑,对比专名命中情况。没有变化,说明词表没真正进解码。

判据三:关键数字与单位二次确认。

改哪里:识别之后、生成之前。可以是一条规则校验,也可以是一次回读:「您说的是四点七毫米每秒,对吗?」成本是一次交互,收益是把一整类静默错误变成显式确认。

怎么验收:构造「毫安 / 毫安时」「四点七 / 四点一」的对照集,看拦截情况。这类对照集几十条就够。

判据四:流式设静默窗口。

改哪里:语音活动检测与请求发送之间。窗口长度按业务调,一问一答的场景通常数百毫秒可接受;同时给最长等待兜底并打标。

怎么验收:统计「超时强制放行」的占比,和这部分请求的下游错误情况。占比高说明窗口设小了;占比低但错误仍多,说明问题不在这一层。

判据五:多人场景先做说话人分离。

改哪里:识别之后、拼上下文之前。分离有错时保留「未知」标签,不要静默合并。

怎么验收:取一段双人对话,逐句检查拼出来的上下文里归属是否正确。错归属的比例,就是这条判据的验收值。

判据改动位置验收方式
保留片段与时间戳识别适配层任一请求能定位到秒
热词表前置解码配置含专名的历史音频重跑对比
数字二次确认识别与生成之间对照集上的拦截情况
静默窗口检测与发送之间超时放行占比及该部分错误情况
说话人分离拼上下文之前双人对话的归属正确情况

这五条有一个共同特点:都不需要换模型。换模型解决的是「字更准」,这五条解决的是「结构不丢」。前者有上限,上限由音频质量决定;后者是你自己就能补的。

顺序上,先做判据一和五,它们不改延迟,只加字段和标签;再做判据二和三,需要重跑对照集;最后做判据四,要压测着调。反过来做,就会一边调延迟一边改准确率,最后说不清是哪个改动起的作用。

语音问答的排查清单:一份按「片段字段 / 热词表 / 数字回读 / 静默窗口 / 说话人标签」五段整理的检查表,可以直接对着自己的链路逐项勾。放在资料包里,扫码即可获取:

附表 A:本文引用事实与出处对照表

文中位置事实或结论出处核对方式
第二章论文标题、作者、编号与提交日期Radford、Kim、Xu、Brockman、McLeavey、Sutskever,Robust Speech Recognition via Large-Scale Weak Supervision,arXiv:2212.04356,2022-12-06 提交打开 arXiv 摘要页逐字核对标题与作者列表
第二章68 万小时多语言多任务弱监督;零样本迁移可与全监督结果相比;与人类相比为「approach their accuracy and robustness」同上,摘要原文逐字核对摘要,引用处保持原措辞,未改写
第二章摘要未给出可直接迁移到具体业务的准确率数字同上,摘要范围内摘要中不存在此类数字,故本文不引用任何识别准确率
第三至六章五层失真的机制描述与场景举例本文基于语音问答链路的工程整理属机制分析,不含实测数据集,不出现准确率类数字
第三、四、五章三段示例代码纯标准库 python,本地运行通过按代码块原样复制即可复跑,输出见各章正文
全文涉及现状的表述截至 2026-10-24 的公开信息逐处标注时间限定,不用「近日」「眼下」替代

附表 B:术语速查表

术语含义出现位置
ASR自动语音识别,把音频转成文本全文
片段识别输出的一段带起止时间的文本单元第二、三章
中间结果与最终结果流式识别的过程中间文本与收尾文本,后者会覆盖前者第五章
语音活动检测判断有没有人说话、说完了没有,常被当成判停依据第五章
静默窗口尾部静音达到一定时长才认为一句说完第五、七章
热词偏置解码时提高指定词的命中倾向,需要词表前置第四、七章
平均对数概率片段的对数概率均值,可粗略看作模型对这段的把握第二、四章
说话人分离判断哪一段是谁说的,输出角色标签第六、七章
时间戳对齐保留词或片段对应的秒数,用于定位与回归第六、七章
二次确认对关键数字与单位做回读或反问,不直接进结论第四、七章

写在最后:这篇用到的资料

写这篇文章时,把语音链路的几个环节又重新拆了一遍,顺手也整理了几份配套的东西:

  • 大模型学习路线图:从零基础到能自己动手做 Agent,按阶段说明每一步该学什么、哪些可以先跳过
  • 《LangChain + LangGraph + MCP 智能体开发实战》视频课:7 个模块,从私有化部署、Embedding+RAG 到 MCP+Agent 全流程
  • AI 大模型知识库(在线可查):Agent Skills 从入门到落地、Claude Skills 完全指南等专题,按目录浏览即可
  • 640 套 AI 大模型行业报告 + 经典 PDF 书籍:看行业落地案例和别人怎么做的时候用得上
  • 大模型零基础到精通教学视频:跟着敲一遍,比只读文档快得多

资料是我自己整理的,放在下面这个码上,扫码即可获取:






如果这篇对你有用,评论区说说你卡在哪一步,我按留言再补后续的拆解。