
刚开完一场两小时的跨部门会议负责记纪要的同事从会议室出来之后又趴在工位前整理了快一下午。这种事在我们团队发生过很多次会上大家讨论得很热烈散会之后却发现记录不全、待办不清、后期追溯只能翻聊天记录。后来我用腾讯云 AI 语音识别做了个“会议小帮手”把开会现场的语音自动转成文字再加工成结构化纪要整个过程不需要专人跟会记录。这篇文章就把这个项目的需求、架构、开发过程、踩坑实录完整梳理一遍想自己搭一套会议转写工具的同学可以直接照着做。1. 需求与方案做一个能听懂会议的语音转写工具1.1 会议场景的三个现实痛点我复盘了团队过去两个月的会议情况发现痛点其实集中在三个地方。第一信息记录不完整。口头讨论里的结论、临时冒出的风险项、某个人顺口承诺的一句“我下周看一下”这些内容在快速对话中很容易被漏掉。就算有人专门记纪要也很难做到一字不差更别说同步所有人的语气和上下文。第二纪要整理成本高。一场 60 分钟的会议音频可能只有 60 分钟但整理成可用纪要往往需要 2 到 3 倍的时间。会议开得越多这个隐性成本越夸张。尤其是一天连开 5 场会的日子光整理纪要就占掉整个晚上。第三会后追溯困难。大家聊到某个方案细节当时觉得记下来了真到落地复盘时才发现找不到原始表述。想翻聊天记录没有。想回听录音几十段录音散落在不同人的手机里。时间一长会议内容基本等于沉没资产。这三个痛点对应到一个共同解法把会议语音稳定、准确地转成可检索文本再做结构化整理。于是就有了“会议小帮手”这个项目。1.2 技术方案对比自研 ASR、开源模型部署、云 API 接入做语音转写摆在我面前的有三条路自研 ASR、私有化部署开源模型、接入云服务商的语音识别 API。自研 ASR 基本不用考虑。训练一套可用的中文语音识别模型需要大量标注语料、GPU 资源、声学模型与语言模型的调参经验单人短期根本做不出可用效果就算做出来后续维护成本也很高。私有化部署开源模型是很多团队会考虑的方向比如部署 Whisper 等开源方案。优点是数据不出内网、可定制缺点是硬件门槛不低想跑出低延迟、高并发的效果至少要准备一块像样的 GPU另外通用模型在会议室这种长音频、多人混合说话的场景里识别效果往往不如经过大量垂直数据调优的商业 API。我在测试环境用开源模型跑过一段真实会议录音口语词、打断、重叠说话一多文本就变得零碎给纪要整理增加不少负担。云 API 是投入产出比最高的路径。现在的云厂商语音识别服务已经把声学模型、语言模型、热词、说话人分离这些东西打包好了开发者只需要关心业务侧怎么消费结果。我的结论很直接对于中小团队的自用工具优先接云 API把精力省下来做纪要和分发。1.3 为什么选腾讯云语音识别本来我可以多接几家对比但实际项目里我只用了腾讯云语音识别原因有三个。第一中文会议场景的识别效果确实能打。会议里中文口语占绝大多数夹杂少量英文术语、产品名、人名16k 模型下整体字准率实测在 90% 以上如果配合热词表专有名词基本不会再错。第二接口简单出活快。录音文件识别、实时语音识别都是标准的云 APISDK 覆盖了主流语言代码量很小。从拿到密钥到跑通第一次转写我只花了一个下午。第三音频处理相关的附加能力完整。它自带说话人分离、脏词过滤、标点预测、热词表这些对后面做会议纪要结构化非常关键。说话人分离能把“谁说了什么”分出来这是单纯的语音识别结果给不了的。选型这一步没有太多纠结。工具型项目能用现成的高质量服务解决就是最优解。2. 架构设计与关键决策2.1 两条转写通道实时字幕与会后精转写“会议小帮手”不是一开始就做实时字幕的。我设计了两条转写通道分别满足不同场景。一条是离线精转写通道走录音文件识别。会议结束后把录音文件提交到语音识别服务拿到完整转写结果再做纪要结构化和分发。优点是处理时间宽松可以用更长的上下文窗口做纠错和后处理缺点是结果有延迟适合不着急的正式会议。另一条是实时转写通道走流式语音识别。开会过程中音频流边采集边上送服务端持续返回识别文本前置端可以把文本流渲染成字幕或者实时要点。优点是现场就能看到内容适合远程会议、投屏演示、实时字幕需求缺点是对网络稳定性要求高断线重连和结果平滑处理比较复杂。我建议所有第一次做这类系统的人先把离线通道跑通。原因很简单实时识别需要处理“音频流采集、分片发送、断线重连、片段重叠去重”这一整套流程复杂度至少翻倍。第一版先做到“会议结束 5 分钟内出纪要”已经能省掉团队 80% 的整理时间。2.2 数据流与任务状态机整个系统的核心数据流可以分成四段第一段是音频接入。会议现场通过麦克风阵列或录音笔采集音频统一转成服务端要求的编码格式。第二段是转写任务提交。系统把音频文件上传到对象存储拿到 URL 后提交转写任务或者直接流式推送给实时识别接口。第三段是结果回收。录音文件识别走“异步任务 轮询/回调”的方式实时识别走“长连接 增量返回”的方式。第四段是纪要生成。把转写文本按时间戳分段经过后处理生成结构化纪要推送给参会者。异步任务的状态机是录音文件识别里必须想清楚的一环。我维护了一张任务表每个任务有唯一 ID状态包括 pending、running、success、failed。提交转写后先更新为 pending回调或轮询到处理中就改成 running拿到最终结果后改成 success如果接口返回错误码则记录失败原因方便重试或人工介入。这个状态机看起来简单实操中很关键。没有它后面排查任务失败、统计识别耗时都会很痛苦。2.3 先想清楚的四个技术参数接入语音识别前有几个参数需要提前跟会议音频采集方式对齐否则后续会到处踩坑。采样率。腾讯云语音识别常见引擎是 16k也就是说音频采样率最好对齐 16000。如果拿到的是 8k 电话录音或者 44.1k 的音频文件需要先做重采样。我后来在踩坑部分会详细讲这个问题的具体表现。声道数。会议录音如果是单声道就按 1 声道提交如果是双声道立体声需要确认是同一方向的双麦还是不同声源的分轨。大部分会议录音我用单声道处理简单省事识别效果也更稳定。音频格式。录音文件识别支持常见的 wav、mp3、m4a、ogg 等格式。但流式识别对音频格式有更严格要求一般要按 PCM 数据块上送。我建议上传前统一转成 16k、单声道、16bit 的 WAV这是兼容性最好的组合。转写结果格式。接口返回结果可以有文本、带词级别时间戳等不同格式。会议纪要场景我选择带句子时间戳的结果方便后续做定位回溯比如“某句话出现在会议第几分几秒”。3. 开发实战从零接入腾讯云语音识别3.1 准备工作账号、密钥、SDK接入前需要完成三件事注册腾讯云账号、开通语音识别服务、创建 API 密钥。开通服务这一步很简单在控制台找到语音识别产品开通后服务默认可用。API 密钥在“访问管理”里创建会得到一个 SecretId 和 SecretKey。我强烈建议把密钥放在服务端环境变量或配置中心里不要写进前端代码更不要提交到代码仓库。开发语言我选了 Python。SDK 安装直接用 pip 装即可pip install tencentcloud-sdk-python如果只需要语音识别相关模块也可以单独安装 asr 模块。SDK 会把请求签名、超时重试、响应解析这些基础能力封装好实际开发中盯着业务逻辑写就行。另外我把音频上传到对象存储统一管理因为录音文件识别接口支持传 URL比直接传 base64 编码更省事也不容易触发请求体大小限制。会议录音一般几 MB 到几十 MB走对象存储 URL 是最稳的做法。3.2 第一版核心功能录音文件识别离线转写这一块我实现了标准的“提交任务 查询结果”流程。第一步提交录音文件识别任务。核心参数是引擎类型、声道数、结果格式和音频 URL。我的核心代码大致如下from tencentcloud.common import credential from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.asr.v20190614 import asr_client, models cred credential.Credential(YOUR_SECRET_ID, YOUR_SECRET_KEY) http_profile HttpProfile() client_profile ClientProfile() client_profile.httpProfile http_profile client asr_client.AsrClient(cred, , client_profile) req models.CreateRecTaskRequest() req.EngineModelType 16k_zh_en # 中英文混合引擎 req.ChannelNum 1 req.ResTextFormat 1 req.SourceType 1 # 1 表示音频来自 URL req.Url https://your-bucket.example.com/meeting-20240912.wav resp client.CreateRecTask(req) task_id resp.TaskId print(ftask_id: {task_id})第二步轮询任务状态拿到最终结果。这里我用了带退避的轮询策略避免高频请求把接口打爆import time def get_rec_result(task_id): req models.DescribeTaskStatusRequest() req.TaskId task_id for _ in range(30): resp client.DescribeTaskStatus(req) if resp.Status 2: # 状态 2 表示已完成 return resp.Result elif resp.Status 3: # 状态 3 表示失败 raise RuntimeError(frec task failed: {resp.ErrorMsg}) time.sleep(2) raise TimeoutError(rec task timeout)完成状态拿到后接口返回的数据结构大致长这样{ TaskId: 156789123, Status: 2, Result: [ { SentenceText: 今天会议重点确认三件事第一是接口联调时间第二是验收标准第三是风险预案。, StartTime: 12000, EndTime: 32000, SpeakerId: 1 } ] }每一段文本都自带时间戳和说话人编号后面做纪要分段就非常舒服。3.3 进阶版实时语音识别的实现思路第二版我补上了实时语音识别给远程会议提供实时字幕能力。腾讯云的实时语音识别走 WebSocket 长连接基本流程是创建连接、发送请求参数、持续推送音频分片、接收识别结果。实现上我没有从头写 WebSocket 协议而是基于官方 SDK 封装了一层服务。核心模块包括三块音频采集模块。在会议终端上采集麦克风音频转成 16k 单声道 PCM 数据流按每 100 到 200 毫秒一个分片推给识别服务。分片太小网络开销大分片太大首字延迟明显。连接管理模块。负责维护 WebSocket 连接检测心跳和断连状态。我遇到过一次长时间无音频输入导致连接被服务端回收的问题解决办法是定期发送静音数据帧或者应用层心跳保持连接活跃。结果合并模块。实时识别返回的是增量文本片段需要按句子聚合。我维护了一个“当前缓冲区”把识别结果追加进去遇到句号、问号等句子结束标记就把整句提交给纪要生成模块。实时识别真正难的不是接 API而是会议现场的网络抖动和音频抢话。断线重连后音频分片可能重复发送导致同一句话被识别两次。我的解决方案是给每个音频分片带上递增序号重连后从最后确认的序号继续发送服务端或客户端再做去重。如果只是内部团队自用我建议第一版仍以录音文件识别为主实时字幕做成可选增强功能。实时识别很酷但稳定性的投入产出比要自己算清楚。3.4 说话人分离与热词表把准确率再往上拉一截这两项配置帮我省掉了很多后处理功夫。说话人分离开启后识别结果里会带上 SpeakerId。会议里正常发言的几个人能按声纹特征区分开后面生成纪要时就能标注“谁说了这件事”。我最初没开这个能力转写文本就是一大段无主文字根本没法还原讨论的对话结构。开启后纪要的可读性提升了一大截。需要注意的是说话人分离对音频采集质量有要求。如果用的是笔记本自带麦克风距离发言人远近不同分离效果会变差多个人同时说话的重叠段也会被切分到同一个人身上。稳妥做法是会场放一个指向性好的全向麦克风再配合说话人分离参数使用。热词表是另一个效果极其明显的功能。团队的产品名、项目代号、人名、专业术语默认模型很可能识别错。我在控制台维护了一张热词表把团队常用词“光子引擎”“极速达”“透传方案”这些词都加了进去识别准确率从明显错词提升到基本全对。以我们团队实际效果来看热词表覆盖到的词准确率能从 60% 左右直接干到 95% 以上。热词表生效范围是词级别的改写不是整句重写。所以不要指望加了热词就能把所有会议内容都变对它能解决的是专有名词和领域术语的问题通用口语还得靠基础模型兜底。4. 让转写结果变成真正可用的人话纪要4.1 文本后处理去口语、分段、提取待办语音识别输出的是直接口语转写离“可用纪要”还差几步。我写了一个后处理流水线把转写文本变成结构清晰的内容。第一步去口语噪声。会议对话里有大量“嗯”“那个”“就是说”这样的口头禅直接去掉。判断方式很简单维护一个常见口语词表按词频过滤同时用规则处理重复词和冗余表达。第二步按语义分段。识别结果自带时间戳我就按“话题切换 停顿 说话人切换”三个信号做分段。比如同一说话人的长段落里停顿超过 2 秒可能就是一个新话题不同说话人连续发言也可能是同一个话题的讨论过程。第三步提取关键要素。这一步铁律是“提取责任人和时间点”。转写文本里一旦出现“我下周给你”“明天看下接口”“周三对齐”这类表述就是潜在的待办事项。我用正则先捞候选句再结合上下文判断真实含义最后整理成“责任人 事项 截止时间”的表格。第四步生成摘要。常规纪要包含参会时间、议题、结论、待办事项四个模块。我从分段后的文本里挑出每段的中心句加上开头和结尾确认过的结论拼成一份两三百字的会议摘要。4.2 会议纪要的自动生成方案之前纯靠人写纪要质量和效率完全取决于记录人的状态。现在我让系统自动生成初稿再由组织者在上面微调。方案一规则模板生成。适合格式统一的周会、同步会。我预先定义一个固定模板把提取出来的话题、结论、待办、责任人按槽位填进去。优点是稳定、可解释、不需要外部大模型缺点是遇到讨论跳跃的会议规则容易漏提关键内容。方案二大模型摘要。适合长会议、复杂讨论。把完整转写文本交给大模型用一段提示词让它输出结构化纪要。我的提示词大概是这样的你是一个会议纪要整理助手请根据以下转写文本生成结构化会议纪要包含议题、结论、待办事项含责任人和截止时间。保持口语直译不要补充原文没有的信息。实际跑下来效果不错大模型能自动分辨讨论中的结论和背景不过我也加了一条规则兜底所有待办事项必须能回到原文对应的时间戳防止模型编造。两个方案我目前是并用的规则模板处理日常例会大模型输出做深度纪要最后都回到同一套段落结构里。5. 上线后的实战踩坑记录5.1 识别率为什么突然暴跌上线第一周晚上有同事反馈某场会议的识别结果出现大段乱码。我排查后发现问题不在服务端而在前端音频采集。那次会议用的是某位同事的手机录音 App默认输出的是 48k 采样率、AAC 编码、双声道的 m4a 文件。我统一提交给接口时没做转码结果服务端对异常参数的兼容性变差识别效果直线下降。从此我立了一个规矩所有音频进入转写系统前必须先经过统一预处理。强制转成 16k、单声道、16bit、WAV并写了一个转码脚本做批量检查。上次连续跑了三十多场会议识别质量一直很稳定。注意如果发现某一场会议的识别率突然变差先检查音频文件和参数别急着怀疑云服务出问题。绝大多数识别质量事故都出在采集端和格式转换上。5.2 异步任务排队带来的问题录音文件识别是异步任务处理时长和任务排队数量直接相关。我遇到过两次任务高峰提交了上百个识别任务结果个别任务等了将近 30 分钟才出结果。对“会后 5 分钟出纪要”的承诺来说这不可接受。排查之后我做了几个调整。一是给任务按优先级排序重要会议先提交二是错峰提交把批量转写任务分散到非高峰期三是升级了并发配额跟服务商申请了更多并发数。上线一个月后任务平均排队时间稳定在 3 分钟以内。顺带提醒一句一定要把任务状态机和超时时间设计好。如果任务长时间 pending系统要有提醒机制不要让用户看到一个大转圈。5.3 网络抖动与超时重试策略调用云 API 过程中最常遇到的就是请求超时和连接抖动。一开始我用的固定间隔重试效果一般反而在服务端压力大的时候加重了问题。后来我换成指数退避重试重试间隔分别是 1 秒、2 秒、4 秒最多重试 5 次且把所有轮询类请求都改成异步任务回调优先。实测下来偶发超时的恢复速度快了很多。实时识别通道我也加了重连保护。断线后先走指数退避建连服务端恢复后自动从上次确认的音频序号续传保证字幕不出现大段乱序。这套重试机制处理了外部网络的不稳定性是我认为整个系统里最值得写的代码之一。5.4 平时自己不会注意的音频细节后端同学写接口时很容易忽略的细节是会议音频采集设备的选型。用笔记本自带麦克风采集会场一吵就完蛋。我用过几种方案最后稳定下来的是外接全向麦克风阵列放在会议桌中间距离所有发言人相对均衡。还有一个小经验不要在转写前做过多音频降噪。我最初想在本地用降噪库预处理录音结果过度降噪把语音本身削掉了一部分识别率反而下降。云服务端本身已经做了降噪处理本地只需要轻量处理即可。拾音距离、麦克风位置、环境噪声这些因素对识别率的实际影响比参数调优大得多。这个结论是我踩了几次坑之后才彻底确认下来的。6. 从会议小帮手到团队知识沉淀6.1 让每一次会议都变成可检索资产会议小帮手跑顺之后我给它加了一个更大的目标让会议从一次性信息变成团队的长期知识资产。做法很简单把每次会议的转写文本和纪要沉淀到内部文档系统按时间、参会人、话题做索引。想看某个人上周说过什么搜索一下就有结果想找某次决策的原始讨论过程也能定位到具体时间点。更进一步我计划把历史会议文本做向量化接入检索问答能力。团队成员可以直接问“上个月确认的数据中台方案是什么”系统在历史会议记录里检索并给出答案。这个方向对团队的价值比实时字幕大得多因为所有信息都有了归属和入口。6.2 我个人的简洁建议项目做到现在如果让我重新来一遍我会把优先级排成先做离线录音文件识别稳固“会后快速出纪要”这件事再做实时字幕覆盖远程会议场景最后才考虑训练定制模型这类锦上添花的事。另外一点是想清楚“谁用、什么时候用”。我刚开始埋头做实时字幕觉得特别酷结果同事们开会不会盯着字幕看大家真正要的是散会时就能拿到一份可执行的纪要。后来把产品重心切到纪要自动生成上使用率和好评度才真正起来。工具型产品最怕自嗨。多听听使用者的真实反馈比多堆功能有用得多。