3.1 离线转写:传完文件等结果的用法

为什么先做离线,而不是实时

工具刚立项时老板的期望是“开会时大屏实时出字幕”,被我拦下来了。理由很简单:我们每周三次例会,没有哪次是必须当场看到文字的,纪要的核心需求是会后发给大家。而实时转写对网络、对会议室设备、对开发复杂度的要求都高得多——离线转写(录音文件识别)只要把文件传上去,等结果回来就行,先把主流程跑通,实时那一路等业务真的需要再说。

离线转写的调用链路其实非常简单:提交一个音频文件,服务异步识别,完成后返回带时间戳的全文文本。我们用的是腾讯云语音识别 ASR 的录音文件识别能力,配合云函数串流程,主链路没写多少代码,绝大部分时间花在调音频参数和等回调上。

离线转写的完整流程

我把整条链路整理成一张表,当时是照着这张表给老板讲的:

步骤 涉及产品 输入 输出 耗时量级
1. 上传录音 COS 本地或会议室采集的音频 存储桶里的音频对象 秒级到十几秒(视文件大小)
2. 触发转写 云函数 SCF COS 上传完成的事件 提交给 ASR 的转写请求 毫秒级
3. 异步识别 语音识别 ASR 音频对象地址 识别任务记录(含任务 ID) 提交即返回,任务排队等待
4. 等待并查询 云函数 SCF 任务 ID 识别完成的通知 通常为音频时长的零点几倍
5. 取回结果 云函数 SCF 任务 ID 的查询请求 带时间戳的全文文本 秒级
6. 落盘存储 COS 结构化文本 转写结果对象、纪要草稿 秒级

第 4 步的耗时是最常被问到的:一段一小时的会议录音,通常几分钟就能出结果,不用干等到纪要截止那一刻。我们周五例会,录音传完我去倒杯水,回来结果已经躺好了。

提交转写请求时有几个参数值得抠一下。识别语言我们固定选中文普通话,另外开两个开关:一个是要返回句子级时间戳(纪要定位靠它),一个是开启标点恢复(不然拿到的是一串没标点的字,后处理要多写不少正则)。音频格式那栏填的是预处理后的统一格式(2.4 节),格式和采样率必须和实际文件一致,不然识别质量会莫名其妙掉一档。

还有一个小细节值得提:提交任务时的音频地址,我们传的是对象在 COS 里的可访问地址,而不是把文件二进制塞进请求里——后者对一小时的大文件完全不现实,前者让识别服务自己去拉。地址有效期要注意,用带临时鉴权的地址时,过期了任务会提交失败或拉取失败,所以我们一律给足够长的有效期或用稳定地址。这一步看着不起眼,却是首跑失败记录里“任务提交被拒”那行的根源。

哪些会议适合离线

不是所有会都值得实时。反面例子是我们有次头脑风暴会开了实时转写,大家盯着屏幕看字幕,发言节奏完全被打乱。三种典型场景:

场景 特点 为何适合离线
周例会 固定节奏、议题明确、纪要会后发 不需要当场看字,会后几分钟出结果足够
跨部门评审 时长两三小时、发言人数多 长录音离线更稳,不用一直占着长连接
培训分享 内容偏讲授、需要逐字稿存档 离线结果完整度更高,还能反复回查
客户访谈 只想留下文字记录、不便现场投屏 事后整理成纪要,现场不分心

而像全员大会这种需要当场在大屏确认决议的场子,我们后来才补上实时(3.2 节)。

一份录音的完整时间线

抽象表格说完了,给一个具体案例。上周一次 52 分钟的周例会,录音文件 48 兆字节,全链路各环节实际耗时:

环节 实际耗时 备注
客户端直传上传 11 秒 走 COS 直传(2.2 节),内网上行约 4 兆每秒
触发云函数 不到 1 秒 上传完成事件几乎即时
任务排队 约 20 秒 白天提交多,晚间基本不排队
异步识别 3 分 40 秒 约为音频时长的零点几倍
取回并写入结果桶 2 秒 全文 9000 多字,加时间戳约 40 千字节

也就是说,从会议结束到逐字稿躺在存储桶里,总共四分钟出头。我们给纪要的承诺是“会后 15 分钟内收到短信链接”,中间留给纪要提取和人工校对,时间一直很充裕。

超长录音怎么处理

有个坑差点漏掉:单次识别任务对音频时长有上限。我们平时的会一小时封顶,但年初一次全天战略会录了将近四个小时,直接提交被拒了。解法不复杂:上传后按静音段把长录音切成多份,每份单独提交识别任务,全部回来后按时间戳顺序拼接。注意每份返回的是相对该片段开始的时间戳,拼接前要加上各片段在原文件中的起始偏移做一次校正,不然纪要里的时间点会全错。校正之后按时间排序合并,逐字稿几乎无痕。

切片这步有个偷懒技巧:别等转写阶段才切,预处理(2.4 节)做音量归一时就可以顺便按静音切。静音阈值要调,会议室背景噪声不小,阈值设太高会把正常停顿也切了,把一句话切成两半再拼回来,断句会变得很怪。

回调设计:上传→触发→取结果

用 COS 直传(2.2 节)之后,转写串联是我写的第一个完整云函数流程,整体思路是事件驱动而不是定时轮询:

  1. 客户端把录音直传到 COS 的原始音频桶。
  2. COS 的上传完成事件触发云函数,函数拿到对象键和存储桶地址。
  3. 函数用音频对象地址调 ASR 提交识别任务,拿到任务 ID,写进一张待办表(我们存在数据库里,量小的话写文件也行)。
  4. 我用了轮询而不是等服务端回调——回调需要外网可访问的接收地址,内部小工具不想再开一个入口:第二个函数每分钟扫一次待办表,查询任务状态,完成了就把全文文本拉回来,写进转写结果桶,再接着触发后面的纪要提取。

这套结构的好处是每一步都可重跑:转写失败就任务重试,不影响已上传的音频;纪要提取失败也不影响转写结果。重试也做了分层:任务查询失败重试三次就告警,识别任务本身失败则记录原因并通知我,不无限重试——识别失败往往是音频本身有问题,重试一百次也一样。云函数的事件触发和超时重试细节,在 腾讯云实战手记 里有更系统的讲法,我第一次写 SCF 就是照着那本调通的。

首跑失败的排查记录

第一次跑通整条链路那天我记得很清楚,因为返回的是一串乱码似的文本,重复音节特别多。前后排查了两个小时,最后定位到两个问题,都和音频参数有关:

症状 原因 排查动作 修法
文本全是重叠的重复音节 录音是双声道,识别按单声道处理 查文件属性,声道数对不上 预处理统一转单声道
整段识别质量断崖式下跌 采样率是 44.1k,按 16k 处理 对比预处理前后的采样率 上传前强制重采样到 16k
录音被截成两半才成功 分片续传没合并完整文件 比对源文件和上传后文件大小 修分片合并逻辑(2.3 节)
偶发任务提交被拒 音频对象地址带了临时鉴权参数且已过期 单独试该地址访问 用稳定地址或刷新鉴权

排查这类问题的顺序我总结为三条:先看输入参数(声道、采样率、格式),再看链路是否完整(文件有没有传全),最后才怀疑识别服务本身。前两步能解释绝大多数莫名其妙的失败,真正识别服务挂了的次数,我们一年里一只手数得过来。

预处理环节(2.4 节)统一强制转成单声道 16k 之后,我又加了一道硬校验:上传前检查文件的声道数和采样率,不符合就自动转,转不了就告警。这道校验上线后,识别异常的告警基本消失了。

转写结果的字段含义、时间戳怎么用在纪要里,接着看 3.3 节。如果音频归档想留在本地而非云端,可以参考 NAS 家庭服务器实战里的方案。

离线转写常见报错速查

跑了一年多,离线转写遇到的报错我归了一张速查表,新来的同事照着这张表就能处理大部分告警:

报错现象 八成是 处理动作
提交任务被拒 音频时长超单任务上限 走切片流程分段提交(见上文)
提交被拒但时长没超 音频格式参数和实际文件不符 核对声道、采样率、格式三件套
任务长时间不返回 任务排队或内部异常 待办表超过阈值未完成就告警人工看
返回文本为空 音频几乎全是静音或文件损坏 播放原文件确认,重新采集
文本含大量重复音节 声道不对 重新走预处理

这张表的价值不在技术多深,在于新人不依赖我也能值班。小团队工具想活下来,关键是流程不绑在一个人身上。

转写结果交给谁

离线转写的结果落盘之后,工具的活才刚开始:逐字稿要交给大模型做纪要提取(第四篇),时间戳要挂进纪要的每一条决议,低置信度句子要进校对清单。分段交给后面的环节是基于一个设计原则——转写这一步只干转写的事,不做任何业务判断:不删句子、不改措辞、不挑重点。识别引擎给什么,原样存什么,所有润色都在下游。这样做的好处是转写结果可以反复用:这次提取纪要,下次做培训剪辑,再下次做季度归档检索,都不用重跑识别。

延伸阅读