资讯动态

腾讯云语音识别实现会议录音转文字小工具

发布时间:2026/10/20 11:46:16 来源:尧图企业网站定制
如果你也经历过这种场景一定懂我为什么突然想做这个“会议小帮手”一场一小时的项目讨论结束之后大家各凭记忆行动有人记住了下周交付有人把负责人听岔了等到对需求的时候才发现每个人手里的版本都不一样。我以前试过边开会边手打纪要结果完全跟不上现场节奏一边是讨论已经跑到第三个话题一边是手指还停在上上句话里。后来我干脆放弃手工记录把精力转向语音识别用腾讯云 AI 语音识别做了一个自动转写会议录音的小工具。经过几个晚上的折腾这个工具已经能稳定把录音转成带时间戳的文本再经过一道简单的后处理就能变成一份勉强能发给同事的会议纪要。这篇文章就把我从选型、接口调用、代码实现到踩坑的完整过程记录下来。如果你是开发者想快速用云服务实现语音转文字可以参考里面的接口调用和后处理思路如果你不是技术人员只是受够了手动整理会议纪要也可以直接照抄思路找个会写脚本的同事帮你搭一个。1. 先想清楚会议纪要为什么需要一台“语音转写机”1.1 会议的三种痛点和语音识别的解法首先要承认不是所有会议都需要转写。五分钟同步一下进度的小会转不转意义不大。真正有价值的是那种信息密度高、参与人数多、时间又长的会议。这类会议有三个特别典型的问题。第一是记录跟不上。口头讨论的语速在每分钟两百字左右正常人手打每分钟也就四五十字差距摆在那里。第二是人脑天然会遗漏细节尤其是打断频繁的会议上一段还没落地下一段已经开始等回到刚才的话题时很多细节已经被冲掉了。第三是事后核对没有依据。明明会上说了会后有人说“没听到”这时候拿不出文本就只能靠争辩。语音转写解决的正是这三件事。录音交给云端识别转录出来的文本不管多少字都不嫌累等于给会议装了一台永恒完整的记录仪。文本带时间戳想确认某句话出现在第多少分钟拖一下播放器就能找到上下文谁说了什么、前后怎么衔接一目了然。1.2 为什么选腾讯云 ASR而不是自建模型或离线工具聊到语音识别方案时大家通常有三个方向自己训练模型、用本地开源工具、用云服务。我直接选了云服务里最省事的一条路。自建模型确实能做得非常“私有化”数据不出内网但代价也同样清晰需要准备带标注的语料、一台还过得去的训练机器以及一个能持续优化模型的团队。普通会议单位如果没有这样的条件第一步就卡住了。本地开源工具我也试过胜在免费、不用联网但配置门槛不低。轻量模型识别率一般上了大模型对 CPU 和内存又不友好而且会议录音里出现人名、产品名、口头语时开箱效果很难让人满意。更难受的是会议是突发需求不是每天都在处理没必要为它长期养一套本地识别环境。所以最终落到云服务上。腾讯云 AI 语音识别对我来说最大的优点是接口简单、模型已经训练得很成熟对中文口语、日常表述的容错度不错而且支持直接传录音文件不用自己去管分帧、解码、流式推送这些底层细节。对于“把会议录音变成文字”这个目标来说这是性价比最高的路径。1.3 整体方案架构确定了方向以后整个工具的结构就很清晰了大致分成四层。第一层是音频采集我用手机自带的录音功能或者电脑的会议录制功能把整场会议存成 mp3、m4a 或者 wav。完事后丢到一个临时目录里。第二层是音频预处理统一口径把文件转成腾讯云接口最友好的格式也就是 16kHz 采样率、单声道、wav 编码。第三层是云端识别通过腾讯云 ASR 的录音文件识别接口提交音频文件 URL然后轮询任务状态拿到转写结果。第四层是后处理把转写文本里明显的口语词清理掉利用时间戳分段落、提取高频关键词最后生成一份结构化文本。这样一来整个流水线就像一条组装流水线每一个环节都有明确输入输出出了问题也知道去哪排查。2. 接口选型录音文件识别还是实时语音识别2.1 两个接口的本质区别腾讯云语音识别的接口名很直白容易让人迷茫的是到底该用哪个。我在这上面花了点时间后来理清了。它主要分实时语音识别和录音文件识别两大方向。实时语音识别的特点是流式处理。麦克风录音的同时音频数据被切成小块不断送过去识别文本也在同步输出。典型场景是机器人对话、语音助手、实时字幕要求延迟尽量低用户能接受边说边出词。调用方式通常是 WebSocket 或者带回调的接口工程上要多处理一些状态同步。录音文件识别则是“离线任务”模式。你把一个完整的音频文件丢过去服务端异步开始识别过一段时间后再去查询结果。它不在乎是几分钟还是几小时的音频只在乎文件格式、大小和时长限制是否满足。因为不需要实时响应它能把整段音频跑得更充分稳定性也更高。会议转写显然属于这一类不是要现场出字幕而是要事后拿到完整文本。2.2 会议场景的正确选择当初我差点被实时接口吸引心想实时出字幕多酷。但仔细一想日常会议的录音通常是先录下来开完会再整理根本没有“边开边转”的强需求。即使有实时识别如果要稳定跑完一小时还要应对多人同时说话、网络抖动、麦克风远场等问题实现的复杂度会远超预期。所以最后选了录音文件识别接口把整段录音一次性交出去等它处理完再拉结果。录音文件识别的调用方式也很适合脚本化。不用维持长连接就是一个创建任务请求加一个轮询状态请求。如果音频在腾讯云对象存储上或者能被公网访问到直接传 URL 就能跑通。这样代码要维护的路径就少很多没有网络断连后重连的麻烦。2.3 开通服务与准备密钥在写代码之前需要先做三件事。注册腾讯云账号并完成实名认证这个不用多讲。然后在控制台找到语音识别服务开通对应的能力。最后在访问管理里创建一个 API 密钥拿到 SecretId 和 SecretKey这两个值就是后面代码里用于身份验证的凭证。这里提醒一下SecretKey 是敏感信息千万别硬编码进代码后传到什么公开仓库里。我自己用了一个临时环境变量来存脚本内部通过 os.environ 读取这样就算文件偶尔发给别人也不至于泄露密钥。密钥也能在控制台随时禁用或重建丢了不用太慌但尽量不要让这种事发生。3. 核心实现把录音变成带时间戳的文本3.1 环境准备我的运行环境是 Python 3.9。依赖方面主要安装腾讯云官方的 Python SDK命令行装一下即可pip install tencentcloud-sdk-python这个 SDK 会把语音识别的客户端和请求模型都带进来其它标准库基本够用。另外还需要准备一个命令行工具ffmpeg。它负责格式转换在 Mac 上可以用 brew 安装在 Windows 上可以下载静态编译版本把二进制加入 PATH。3.2 音频先用 ffmpeg 统一格式在我手机录音里微信语音、手机备忘录导出的文件格式五花八门有 m4a、mp3、amr。直接用这些格式去调接口不是不行但总有兼容性风险。索性我先统一转成 wav。ffmpeg -y -i input.m4a -ar 16000 -ac 1 -f wav output.wav这条命令的作用有几个-ar 16000把采样率统一成 16kHz符合识别模型的最佳输入-ac 1强制转成单声道避免双声道带来的数据量翻倍-f wav指定输出格式。如果源文件很长或者想降低传输体积也可以转成 mp3但 wav 在兼容性上更让我放心。转换完成后把文件放到一个临时目录里接下来就是要让云端能读到它。3.3 怎么把文件交给云端URL 方式的取舍录音文件识别接口支持两种方式传文件一种是把音频内容 Base64 编码后放在请求体里另一种是传一个公网可访问的 URL。Base64 方式对文件大小限制较严适合几十秒钟到几分钟的短音频。会议录音动辄几十分钟到几小时明显不适合硬塞在请求体里。所以我采用了 URL 方式。为了简单我临时把 wav 文件传到自己的对象存储桶里生成一个带鉴权签名的临时链接然后把链接传给接口。识别完成后再把临时文件删除。如果你手头没有对象存储也可以用一台带公网的服务器做静态文件服务。总之核心原则是识别服务需要有办法自己去下载这份录音。3.4 提交识别任务与轮询结果配置好密钥、准备好音频 URL 后就进入正式的接口调用环节。创建任务的关键代码如下import os import json import time 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 os.environ.get(TENCENTCLOUD_SECRET_ID) SECRET_KEY os.environ.get(TENCENTCLOUD_SECRET_KEY) cred credential.Credential(SECRET_ID, SECRET_KEY) http_profile HttpProfile() http_profile.endpoint asr.tencentcloudapi.com client_profile ClientProfile() client_profile.httpProfile http_profile client asr_client.AsrClient(cred, ap-guangzhou, client_profile) def create_task(audio_url): req models.CreateRecTaskRequest() req.EngineModelType 16k_zh req.ChannelNum 1 req.ResTextFormat 2 req.SourceType 1 req.Url audio_url req.FilterModal 1 req.ConvertNumMode 1 resp client.CreateRecTask(req) return resp.Data.TaskId def wait_for_result(task_id, timeout1800): start time.time() while time.time() - start timeout: req models.DescribeTaskStatusRequest() req.TaskId task_id resp client.DescribeTaskStatus(req) status resp.Data.Status if status 1: return resp.Data if status 2: raise RuntimeError(识别任务失败) time.sleep(3) raise TimeoutError(等待识别结果超时)这里解释几个参数。EngineModelType 16k_zh是最常用的中文普通话引擎适配 16kHz 采样率音频。ChannelNum 1表示单声道。ResTextFormat 2很关键它返回的是带词级时间戳的详细识别结果后处理阶段可以依据它定位每句话的时间位置。SourceType 1告诉接口我在传 URL而不是 Base64 数据。FilterModal 1负责过滤语气词像“嗯”“啊”这类词在转写结果里会少很多。ConvertNumMode 1会把数字转成标准写法比如“3.14”不会变成“三点一四”的奇怪形态。轮询的等待时长取决于音频长度。我自己的经验是十分钟左右的录音大约需要二到三分钟能返回结果稍微长一点的会议录音可能要等更久。这里有一个心态建设它不是实时接口等是正常机制不是卡住。3.5 解析返回结果任务成功后resp.Data里面会有识别文本。带时间戳的结果是一段 JSON 文本包含若干句子级别的对象每个对象里除了句子文本外还有每个词对应的起始和结束时间单位是毫秒。我习惯先把 JSON 解析出来再存成结构化文本def parse_results(data): result_text data.Result result_detail json.loads(data.ResultDetail) if data.ResultDetail else [] lines [] for item in result_detail: start item.get(StartTime, 0) / 1000 end item.get(EndTime, 0) / 1000 text item.get(Text, ) minutes int(start // 60) seconds int(start % 60) lines.append(f[{minutes:02d}:{seconds:02d}] {text}) return \n.join(lines), result_text这样处理后我拿到的不再是一整段糊在一起的文字而是一行一行带时间标记的转写稿。这一步是整个会议小帮手的关键也是后面做会议纪要有底气的原因。完整跑通的代码并不复杂几个函数加起来一百行左右。真正决定工具好用程度的反而是接下来这步对转写结果的二次加工。原始转写文本只能叫“记录”离“纪要”还有距离。4. 识别结果的二次加工让转写稿变成会议纪要素材4.1 时间戳的妙用拿到转写稿后我第一反应是“终于有了文本”但很快发现它还不能直接发到群里。一是夹杂着大量口语二是整篇没有分段。这时候时间戳就成了最好的线索。一般的会议流程都有内在节奏前几分钟同步上次进展中间讨论问题最后排布任务。利用时间戳我可以把转写稿按时间窗切成若干小段。比如每隔十分钟切一段分别浏览每段的核心内容很快就能抓住这段讨论的主题。也可以反过来搜索关键词比如搜“下一步”“截止”“负责人”然后利用时间戳定位到具体讨论位置把上下文拉出来看。我写了一个简单的“分段 段落摘要”的辅助逻辑大致逻辑是按预设时长窗口切分文本统计每段中出现的高频词作为候选标题。效果谈不上惊艳但足够用来快速定位会议的高光片段不用把整份录音从头到尾再听一遍。4.2 口语化文本的清洗转写结果里最常见的多余词是“那个”“然后”“就是说”“对吧”。虽然接口的 FilterModal 参数能过滤一部分语气词但不可能做得太激进因为一旦把所有候选语气词都删了可能误伤正常表达。所以我在自己的代码里再跑一轮轻量清洗。我用的是一个非常朴素的规则维护一个“弱语义词表”凡是命中这个词表的词直接删除。数量不用太多五六个高频词就够了。比如把文本里连续出现的“就是然后”替换成“然后”把句尾的“对吧”直接去掉。清洗完以后再按句号、问号、感叹号把长段切分成短句转写稿的可读性会提高不少。还有一个细节语音识别有时会输出重复句。比如发言人被卡住同一个词连说三遍转写稿就会连着出现三个相同的词。加一个基于编辑距离的简单去重函数对相邻短句做相似度比较相似度超过某个阈值就只保留第一句。这个做法对减少噪音非常有效。4.3 关键词与待办提取会议纪要最实用的部分是“接下来谁做什么”。转写稿不会主动告诉你这一点但可以通过关键词提取来辅助判断。我的做法是对清洗过的高频词做统计再人工重点查看包含“下周”“今天”“明天”“截止”“需要”这些词的句子。更省事的做法是直接把包含这些词的整句抽取出来拼成一个“动作项候选列表”然后自己快速浏览一遍。如果会议里有人明确说“由我来负责”“周三前给结论”这些句子几乎都能被捞到。虽然这谈不上什么智能分析但属于非常实用的小手段能省下不少反复听录音的时间。另外如果办公室里经常出现产品代号、英文名词腾讯云的识别结果偶尔会把它们转写成奇怪的中文。这种场景建议设置一个热词词表把常用术语丢进去。识别准确率会明显改善代价只是维护一份词表清单。5. 实测数据和踩坑记录5.1 识别准确率实测工具跑通之后我拿了真实会议录音做了几组测试。一段大约十分钟、说话人三位、普通话为主、室内安静环境的录音整段转写文本基本能覆盖绝大多数内容。若按照“关键意思是否正确表达”来评判我认为准确率在九成以上。个别地方会出现专有名词识别错、人名写错、数字听岔的情况但配合时间戳回到原音频很快就能纠正。如果环境嘈杂或者有两个人明显叠话转写质量会下降。我测过一段在开放式工区录的音频背景频繁有人走动和键盘声转写结果明显变差。这告诉我一个道理云端模型再强也架不住输入端埋雷。录音设备离说话人近一点环境安静一点带来的提升比换任何参数都明显。5.2 最常见的四类坑第一类坑是格式不服。初次测试时我直接传了一个 m4a 文件接口创建任务时没有报错但轮询结果状态码回了失败。排查下来发现是音频编码格式和接口预期不匹配。所以后来一律先过 ffmpeg把采样率、声道、编码统一好再传这个问题再也没出现过。第二类坑是文件体积与传输时长。录音文件全转成 wav 后体积很大一份一小时的会议 wav 文件可能超过三百兆。传对象存储时如果带宽不够上传本身就能花掉不少时间。后来我改成转成 mp3 格式再做识别体积小很多识别效果差异不明显。第三类坑是接口调用区域不一致。创建密钥和控制台资源所属区域之间如果不一致可能调用报错。我一开始下意识填了上海区域但资源在另一个区域接口也报了几次错。后来索性统一用资源所在的区域问题就消失了。第四类坑是轮询太频繁。我最初每秒钟查询一次任务状态跑了一段时间发现没有必要还容易触发接口限流。把轮询间隔放宽到三秒甚至五秒以后整个流程稳定得多心理上也舒服很多。5.3 成本和免费额度关于成本我只能说“按量付费价格不高”不展开具体的数字因为控制台的计价规则经常调整。实际的感受是一次一小时内的会议转写折算下来的费用远低于请一个人工速记员的零头。而且云服务通常有免费额度用来日常试用完全够。真到了量大的场景再去控制台看实时账单就好。6. 从“能跑”到“好用”后续可以怎么扩展6.1 目前做到什么程度我现在的版本基本就是一个命令行工具加一个后处理脚本。每次开完会把录音拖进来运行命令等几分钟输出一份带时间戳的转写稿和一份清洗后的纪要文本。对于个人使用来说这个程度已经让我很满足因为我不用再花半小时去回听录音。这个工具真正改变的是我对开会的态度。以前开会过程中我会一直焦虑“这得怎么记”现在我会放心地让录音记录一切自己更专注于讨论本身。这种心态上的变化比代码本身更有价值。6.2 扩展思路基于现在的转写文本可以扩展的方向还有不少。比如把转写文本接入大模型让它自动生成一份更正式的会议纪要并且列出待办项。原来的关键词提取只是粗糙方案交给大模型以后质量会提升很多。再比如对接日历。识别结果里如果出现了“明天下午三点”“周五上午”这类时间表达可以尝试解析成时间点给每个待办配上时间提醒。这个功能做出来就从一个“转写工具”进化成了真正意义上的“会议小帮手”。我也考虑过把接口替换成实时语音识别做成开会时旁边挂着一块屏幕实时显示字幕。这个方案对硬件和网络的依赖不小但万一以后团队需要远程参会同屏字幕是可以再花时间尝试的。根据我个人的经验这类工具最适合的落地方式不是做成一个庞大的系统而是先把“录音转文字”这条主干道跑通再根据实际使用场景慢慢加料。一次的会议录音从结束到拿到稿子整个过程控制在五分钟以内就已经超过很多手工整理了。如果你也有类似的痛点不妨参考这个思路挑一个周末把属于你自己的会议小帮手搭起来。

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

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

免费获取报价 →
↑