资讯动态

基于腾讯云语音识别构建会议纪要助手:从录音到Markdown的完整实践

发布时间:2026/10/20 18:01:02 来源:尧图企业网站定制
前阵子我们团队开完周会我盯着自己那页越写越乱的笔记发呆——需求记了半截负责人记混了散会十分钟后就被同事追问“刚才说的那个接口到底谁对接”。那段时间我一直在想开会一小时整理纪要两小时信息还是丢三落四真正的问题不在于大家不认真而在于人工记录这件事本身就撑不住会议的信息量。后来我花了一个晚上用腾讯云AI语音识别给自己搭了个会议小帮手自动把录音转成文字标出时间戳和发言人再一键生成Markdown格式纪要。这东西做完之后我最大的感受是会议终于不是“开完就没了”它变成了可检索、可回放、可追踪的文本资产。这篇文章就把这个项目的完整过程写出来包括为什么选云端API而不是本地方案、架构怎么设计、核心代码怎么写以及我在真实使用中踩过的坑和调试笔记。适合那些想用语音识别做点工具的人、饱受会议纪要折磨的同事以及刚入门想找个完整云API集成项目练手的开发者。1. 项目背景会议的痛点和“小帮手”的定位开会这件事有个很隐蔽的成本会上说的话是高度上下文相关的人在现场都听得懂但散会之后如果没有一份结构清晰的纪要那些讨论就变成了一堆碎片。我见过太多团队的开会状态是“会上很热闹会后很沉默”最后只能靠一两个人凭记忆补纪要补出来的东西还经常对不上。我给自己定了个目标做一个“会议小帮手”它要有三个最基本的能力——把录音变成逐字稿、在逐字稿里标出发言人和时间、把关键信息整理成可复制的纪要。听起来不复杂但真正落地时你会发现逐字稿只是第一步如何从一大段口语文本里筛出行动项和负责人才是决定这个工具有没有用的关键。1.1 会议场景对语音识别的特殊要求通用的语音识别和会议场景的语音识别完全是两种难度。会议场景有几个很典型的特征第一口语化严重。大家说话都是半句话起头、语气词一堆、“那个”“这个”满天飞识别引擎如果只做“字对字”的转写出来的稿子会很碎几乎没有可读性。第二专业术语密集。技术评审会上全是“接口”“灰度”“配额”“单元测试”产品会上是“埋点”“漏斗”“转化率”这些词在通用语料里出现频率低默认模型很容易听错。第三多人轮流说话。一次周会通常五到八个人没有说话人分离的转写稿就是一团浆糊你根本不知道哪句话是谁拍板的。第四时长不短。会议录音通常二三十分钟起步长的可能两三个小时这远远超过普通语音识别接口对音频时长的限制。所以我在选型阶段就明确了一件事不能拿一个简单的“一句话识别”就往上套我必须找一个支持长音频、带热词定制、能分说话人的识别方案。腾讯云AI语音识别的录音文件识别接口恰好覆盖了这些点这是它进入我视野的直接原因。1.2 需求拆解最小可用版本MVP该长什么样做工具最忌讳一上来就规划一堆花活。我给自己框了个最小可用版本先跑通再迭代自动转写录音文件上传后能异步识别输出带标点的逐字稿。说话人分离识别结果能区分“发言人A”“发言人B”而不是一段混沌的文本。热词定制把团队内部的专有名词、人员昵称预先塞进模型减少错词。时间戳定位每句话带时间轴点一下就能回到录音对应位置。Markdown导出识别结果清洗后输出为带标题、带列表的纪要可以直接贴到群里。实时语音识别也就是边说边出字我暂时没做因为日常会议场景通常是录完再整理异步方案更稳也能少写很多WebSocket长连接代码。后来证明这个决策是对的后面我还会细说。2. 技术选型为什么是云端API而不是本地部署模型我最早考虑的其实不是云API而是本地开源的语音识别模型。那阵子我正好有一台还不错的显卡机器觉得可以自己部署一个模型一劳永逸数据还不出内网。真上手之后就发现理想和现实差距很大。2.1 本地开源方案与云API的对比这里我直接用一张表说清楚我的权衡过程对比维度本地开源模型云端API腾讯云AI语音识别部署门槛需要中高端GPU显存不够直接跑不起来注册账号、拿到密钥就能调用环境依赖Python环境、依赖包、模型文件动辄几个GB只需要SDK和网络中文效果要看具体模型很多开源模型泛化一般面对中文做了定向优化效果稳定热词定制自己调模型或做外挂词典工程量大控制台建热词表任务直接挂载长音频支持需要自己写切片、拼接逻辑异步任务接口原生支持长音频维护成本模型升级、机器坏了、系统重装都得自己扛平台负责按量付费不占用本地资源用本地方案最大的问题不在“能不能跑”而在“跑了谁维护”。模型精度不够理想时我要去微调要准备语料要训练这对于一个“给团队做个辅助工具”的项目来说成本直接失控了。相比较而言云端API打开就能用。我把精力省下来去解决会议场景真正的问题——文本清洗、发言人标注、纪要生成。这跟“用别人造好的轮子开车”是一个道理你不需要从矿石开始炼铁。2.2 在多个云端产品里怎么选录音文件识别还是实时语音识别腾讯云AI语音识别下面有多个产品我实际调研后筛掉了一大半最后就剩两个候选录音文件识别和实时语音识别。录音文件识别是异步的你把音频文件的URL交上去它后台慢慢算算完了你轮询拿结果。适合非实时的长音频处理二三十分钟的周会录音完全没压力还自带标点和说话人分离能力。实时语音识别是流式的麦克风一边收音一边返回文字适合做字幕、现场速记、直播字幕这类对实时性要求高的场景。它的优点是真实时但代价是需要维护WebSocket长连接还要自己处理音频流的分帧和断线重连复杂度高不少。我的判断是会议小帮手的第一版先做“录完转写”保证结果的稳定性和可读性实时字幕放到后期再做。实际开发的时候录音文件识别的调用方式也更简单就是“提交任务—轮询状态—取结果”三步整个项目的代码量可以控制得很小。还有人问为什么不用一句话识别我多说一句一句话识别适合很短的人机交互比如语音搜索、语音指令音频时长限制卡得死死的根本接不住一整个会议的录音。长音频场景想都不要想直接选录音文件识别。3. 整体架构设计与核心功能拆解这个项目的定位决定了它的架构不需要很复杂但它必须是一条完整的链路从录音文件到最终纪要中间每个环节都要有人在管。3.1 数据流转一条从录音到纪要的路径整个系统的数据流是这样的本机录音 → 上传到对象存储COS → 拿到音频URL → 调用录音文件识别接口提交任务 → 轮询任务状态 → 任务完成后拉取识别结果JSON → 后处理脚本清洗文本、补上发言人标签 → 生成Markdown纪要 → 发送到工作群或邮件。用对象存储做中转这个选择很关键。一开始有人建议直接把录音文件的内容Base64之后塞进请求里我当时就否决了——一个三十分钟的会议音频至少几十MB直接塞请求体里既不优雅也不稳定上传一个大文件还可能把请求超时拖死。而对象存储给的是标准URL识别服务从URL拉数据两边都轻松。这条链路还有个好处原始录音文件被永久留存在对象存储里纪要里每句话都带着时间戳事后想核对原话点开录音定位一下就找到了。这一点在“背锅”场景下特别好用谁说了什么白纸黑字加录音不用争。3.2 让会议词不被听错热词定制的使用逻辑热词定制是这个项目里性价比最高的功能没有之一。它的原理可以理解成给识别模型提前发一份“这次会议可能出现的词汇表”提高这些词在解码过程中的权重。我遇到过很典型的例子“灰度发布”被识别成“归途发布”“微服务”变成“微冠军”“腾讯云”三个字都能被拆成“田讯云”。团队内部的黑话、产品代号、同事昵称这些词在通用语料里的出现概率太低识别器完全没有概念。解决方案也很简单把常见词拉一个清单比如项目名、模块名、专有名词、几个人名的简短叫法加进热词表然后提交识别任务时挂载这个热词表。我实际用下来的感受是加了热词表之后专有名词的识别错误率明显下降虽然不敢说100%准确但至少“灰度发布”不会再变成“归途发布”了。需要注意的是热词表不是越大越好。我在调试时把一堆低频长句也塞了进去结果反而引入了一些奇怪的替换。最终我控制在每个词不超过十个字一次只加几十个词效果最稳。3.3 从逐字稿到会议纪要文本后处理怎么做语音识别拿到的是逐字稿严格来说它还不是“纪要”。口语稿里有大量的语气词、口头禅、重复表达直接发到群里没人看得下去。我的后处理分成三步走。第一步是清洗去掉“嗯”“啊”“那个”之类的填充词合并重复的短句。第二步是结构化把连续文本按“发言人标签时间戳内容”拆成一条条Markdown里就变成了引用块或者列表。第三步是提取行动项我用规则扫描文本凡是出现“下周”“周五前”“麻烦你”“需要跟进”这类关键词的句子我就单独拎出来凑成“待办事项”列表。用规则而不是用大模型做摘要是刻意的选择。会议纪要最重要的是准确规则引擎虽然傻但它不会“自由发挥”不会把没说过的话脑补进去。标配的AI摘要适合给领导汇报用但是作为给团队自己看的记录逐字稿加行动项才是最可靠的形态。4. 从零搭建实操流程与关键代码下面这部分是整篇文章最“干”的地方我会把从开通服务到生成纪要的完整步骤写出来代码都是我在本地实际跑过的结构你拿过去改改参数就能用。4.1 环境准备开通服务、准备密钥与热词表首先去腾讯云控制台开通AI语音识别服务这个操作没什么难度实名认证后进产品页点开通就行。你大概率还会被要求开通对象存储COS因为录音文件识别要求音频是个“可访问的URL”我们得先把文件存到对象存储里。然后需要拿两样东西一个是SecretId和SecretKey用于调用API的身份认证另一个是热词表ID如果在控制台创建了热词表提交任务时可以直接关联。顺便说一句密钥千万不要硬编码在代码里更别传给别人我身边已经有人因为密钥泄露被刷了好几笔账单这条真金白银的教训不比识别效果次要。Python环境准备好之后安装以下依赖pip install tencentcloud-sdk-python tencentcloud-sdk-python-asr cos-python-sdk-v54.2 把录音文件上传到对象存储并获取URL这一步做的是“中转存储”。我会在COS桶里建一个meeting/目录每次会议录音按日期命名。from qcloud_cos import CosConfig, CosS3Client secret_id 你的SecretId secret_key 你的SecretKey region ap-guangzhou bucket my-bucket-1250000000 config CosConfig(Regionregion, SecretIdsecret_id, SecretKeysecret_key) client CosS3Client(config) # 上传录音文件 client.put_object( Bucketbucket, Bodyopen(meeting_20250101.wav, rb), Keymeeting/meeting_20250101.wav, ContentTypeaudio/wav ) # 生成一个短期内有效的下载URL私有读桶也能用 url client.get_presigned_download_url( Bucketbucket, Keymeeting/meeting_20250101.wav, Expired3600 ) print(url)这里有个细节值得说一下如果桶是公有读直接把拼接URL当作用户输入即可如果桶是私有读就必须用临时签名URL。千万不要为了省事把桶设成公有读会议录音是敏感数据直接裸奔在公网上我是强烈不建议的。4.3 提交录音文件识别任务并轮询结果有了包含音频的URL接下来就是调用录音文件识别接口。这个接口是异步的提交后会返回一个TaskId然后用这个TaskId反复查状态直到返回“已完成”。import time import json 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 secret_id 你的SecretId secret_key 你的SecretKey cred credential.Credential(secret_id, secret_key) http_profile HttpProfile(endpointasr.tencentcloudapi.com) client_profile ClientProfile() client_profile.httpProfile http_profile client asr_client.AsrClient(cred, ap-guangzhou, client_profile) audio_url 上传后拿到的音频URL data_len 1024 * 1024 * 15 # 音频文件的字节数根据实际文件填充 # 提交任务 req models.CreateRecTaskRequest() req.EngineModelType 16k_zh # 16k采样率中文通用模型 req.ChannelNum 1 # 单声道 req.ResTextFormat 0 # 0返回词级结果2返回一句话文本 req.SourceType 1 # 1表示URL req.Url audio_url req.DataLen data_len req.FilterPunc 1 # 过滤标点符号0不过滤1过滤 # 如果有热词表这里可以挂载req.HotwordId 你的热词表ID resp client.CreateRecTask(req) task_id resp.Data.TaskId print(task_id:, task_id) # 轮询结果 while True: time.sleep(5) qreq models.DescribeTaskStatusRequest() qreq.TaskId task_id qresp client.DescribeTaskStatus(qreq) status qresp.Data.Status # 0: 任务进行中1: 成功2: 失败 if status 1: result json.loads(qresp.Data.Result) break elif status 2: raise Exception(识别任务失败) print(json.dumps(result, ensure_asciiFalse, indent2))轮询间隔我设成5秒实际使用中你可以根据音频时长调整音频越长轮询等待时间越久。不要用太短的间隔去打接口没必要给自己制造并发压力。4.4 解析识别结果生成Markdown纪要识别任务完成后拿到的Result是一段JSON里面包含了逐字文本、时间戳以及说话人标签如果开启了说话人分离。我的后处理脚本大致长这样def build_markdown(result): lines [] for item in result.get(ResultDetail, []): speaker item.get(Speaker, 未知) start_ms item.get(StartMs, 0) text item.get(SliceSentence, ).strip() if not text: continue start_sec start_ms / 1000 minutes int(start_sec // 60) seconds int(start_sec % 60) ts f[{minutes:02d}:{seconds:02d}] lines.append(f- {ts} **{speaker}**{text}) return \n.join(lines) markdown build_markdown(result) print(markdown)最终生成的纪要大概是这样的效果[00:03]发言人A这个版本我们先考虑把核心流程跑通。[00:42]发言人B灰度发布的时间窗口我觉得放在周五比较合适。[01:20]发言人C那这个接口我来负责下周给到联调版本。这种格式的好处是结构简单、信息可跳转队友看到时间戳能快速定位到录音对应位置比自己全程盯着一份纯文字稿效率高太多了。5. 踩坑实录真实项目中遇到的常见问题做这个项目看起来就是“调接口写脚本”但真正跑起来之后各种奇奇怪怪的问题还是层出不穷。我把遇到的坑和排查思路整理成一个速查表再挑几个高频问题展开讲讲。5.1 常见问题速查表问题现象常见原因我的解决办法识别结果乱断句、错字多音频采样率不对或原文件是AAC压缩格式用ffmpeg转成16k单声道WAV再上传任务一直停留在“进行中”音频太长、URL无法访问、长时间没查状态检查URL可访问性轮询间隔拉长超时后重新提交任务任务返回“失败”对象存储桶权限不够URL有效时间太短改用临时签名URL有效期设长一些专有名词全部识别错热词表没传或权重不够控制台建热词表提交任务时关联表ID说话人标成一团乱多人同时说话、背景有混响开会时每人离麦近一点避免交叉抢话关闭标点后文本黏在一起FilterPunc参数设为0了需要正常标点时设为1音频文件很大上传极慢本地宽带不够或文件格式冗余转成16k wav后体积通常能缩到原来的三分之二左右5.2 高频问题详细拆解第一个高频坑是音频格式。录音文件识别对音频格式有要求我一开始直接从手机录音App导出一个M4A文件直接丢上去识别结果文本稀碎。后来乖乖用ffmpeg统一转成16kHz、单声道、16bit的WAV格式识别率立刻上升了一个台阶。转换命令很简单ffmpeg -i input.m4a -ac 1 -ar 16000 -acodec pcm_s16le output.wav这里面的原理在于16kHz采样率足以覆盖语音的主要频段而WAV/PCM是未压缩格式识别器处理起来没有解码损耗。也不是非要WAVPCM也可以但统一用WAV最省事少踩格式兼容的坑。第二个高频坑是热词表不生效。一开始我怎么调都觉得“腾讯云”还是会被识别成别的词后来发现是我传的词表ID写错了位置任务提交时压根没挂上。确认热词表真正生效之后识别结果立刻不一样了。还有一个细节是腾讯云AI语音识别的热词表通常是中文词或短语我塞英文缩写时效果不稳定后来尽量写中文全称识别率更可控。第三个有点隐蔽的坑是音量太小导致识别失败。有一次同事用笔记本自带麦克风录了一整场会声音又小又闷。我后来养成了一个习惯转码后先用ffmpeg跑一个响度归一化把音量拉到合适的范围再上传。ffmpeg -i output.wav -af loudnormI-16:TP-1.5:LRA11 output_normal.wav这个操作相当于是给音频“洗了个澡”让识别器能把说话人的内容听得更清楚。宁可归一化多花几秒也不要让一整段录音因为音量问题变成废数据。第四个问题是时间戳不对齐。我在早期版本里曾经用一句话的时间戳去对应整段文本结果发现最终纪要里没法准确跳转。后来我意识到录音文件识别返回的是“句级”时间戳每个句子有自己的起始时间要把时间戳挂到句子上而不是挂到整段文本上定位才会准。这一点如果你自己写解析脚本建议多看一眼字段结构。6. 项目回顾与可以继续扩展的方向这个“会议小帮手”到目前为止已经稳定跑了三个月我最大的变化是散会之后不再焦虑“纪要写不出来”。原始录音早就在对象存储里归档好了识别任务跑完纪要自动生成群里的小伙伴也能自己翻阅。它从一个可有可无的小脚本慢慢变成了团队开会的默认配置。如果后面想继续扩展我觉得有三条路都很值得走。一条是把实时语音识别接上做成会议现场的字幕或者实时速记这个对远程会议尤其有价值因为线上参会的人经常跟不上现场语速。另一条是接入智能摘要把逐字稿喂给大语言模型让它提炼出一页纸的“决策摘要”和“遗留问题”但摘要结果必须经过人工校对不能直接发布。还有一条是打通任务看板从纪要里识别出的行动项自动创建为待办任务分配负责人和截止日期形成“会议—任务—执行”的闭环。最后聊聊我的个人体会。这个项目让我印象最深的不是技术本身而是“工具思维”这件事。会议纪要的痛点每个人都知道但多数人选择忍受或者靠更勤奋地记录去对抗。真正有效的解法是把会议转成结构化的数据再用自动化的方式让这些数据为人服务。你不用多聪明只要把流程拆开把每一段都做扎实就能省下大把时间。遇到识别不准的时候我也不会慌因为我手里有原始录音有带时间戳的逐字稿随时可以回溯这已经比任何凭记忆整理的纪要都强得多了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑