用腾讯云AI语音识别,自动生成会议纪要与待办事项
开完会一小时的录音真到写纪要的时候脑子里只剩“刚才说了一堆具体谁负责什么来着”这种模糊印象。这种场景我经历过太多次所以干脆把腾讯云 AI 语音识别接到自己的流程里做了一个能自动出转写、能分说话人、能提取待办事项的会议小帮手。这篇文章会从需求说起把整体方案怎么设计、接口怎么调、参数怎么定、识别率怎么调优、以及我实际踩过的坑全部讲清楚。适合手里有真实会议录音场景的运营、市场、产品同学也适合想快速给内部工具加一个语音转写能力的开发者。不写泛泛的API文档直接以一套可以落地的“录音文件转写纪要生成”方案为主线。1. 从想清楚“会议小帮手”要解决什么开始1.1 会议场景里真正让人头疼的三件事会议数据至少有三种形态大家嘴里说的话、共享屏幕上展示的信息、以及最终要落到执行层的行动项。第一种形态的信息损耗最严重。嘴上说的是“下周排期要提前小王你跟进一下”到纪要里往往就变成“下周排期要提前”至于“谁跟进”这个关键信息经常直接消失。做成会议小帮手第一步不是选接口而是把需要解决的问题拆开。我按照自己的使用体验归纳成三个核心诉求把语音变成文字而且要分得出是谁在说话从大段口语化转写稿里提炼出纪要级信息把“待办”“负责人”“时间节点”这类信息结构化提取出来这三个诉求对应着完全不同的技术动作。语音转写靠的是ASR引擎分说话人靠的是声纹特征纪要提炼和信息结构化靠的则是后处理规则或大模型。如果一开始就把所有目标寄托在某一个API上后面很容易被需求带偏。1.2 为什么用云API而不是本地语音识别方案我在做这个小帮手之前先花了一个周末尝试本地语音识别方案。那条路不是说走不通而是维护成本高得离谱。中文识别效果要追平云端商用接口需要下载几个GB的模型还要配好GPU推理环境而一旦遇到会议室这种多人混音场景识别率会直线下降。云API在会议室场景有几个很实在的优势。首先中文普通话识别效果经过大量真实语料打磨对会议中常见的口语、语速变化、专业词汇处理得更稳。其次云接口天然支持长音频异步转写不用自己处理分段和拼接。最后按量付费的模式对低频自用场景非常友好我一个月可能就识别十几个小时的会议录音成本完全在一个可接受的范围内。1.3 这个“小帮手”最终长什么样先描述一下最终效果大家更容易理解后面的技术选型。我的流程是会议结束后把录音文件丢进一个脚本脚本上传音频并创建转写任务等待识别完成后自动拉取带说话人标记和时间戳的转写文本然后通过一套规则把文本切成段落、去掉语气词、抽出含有关键动作词的句子最后生成一个包含“背景、结论、行动项、负责人”的Markdown会议纪要同步到团队的文档空间。整个过程从丢入录音到拿到纪要大约需要几分钟到十几分钟取决于音频长度和队列排队情况。后面所有技术细节都是在为这个目标服务。2. 整体方案设计从录音采集到纪要输出2.1 音频采集端决定了一半的识别率我刚开始测试时犯过一个典型错误随手用手机录了段会议音频格式是微信语音转出来的MP3码率低、人声混杂结果识别出来的文字基本没法看。后来才想明白ASR引擎再强输入音频的质量不行输出就一定不行。会议录音采集有几个硬性指标需要明确采样率建议16kHz或以上。语音识别引擎对采样率有明确要求低于8kHz会损失大量高频信息中文的声母、韵母识别会明显变差声道单声道。双声道如果两个人离麦克风距离不同反而会造成能量不均编码格式WAV/PCM是首选无损压缩。MP3在低码率下会丢失细节高码率的M4A/AAC也能接受录制距离麦克风离说话人越近越好多人会议建议使用全向麦克风或专用会议麦如果手里已经有一份不太合格的录音可以用FFmpeg做一次简单的预处理把格式统一成16k单声道WAV。这条命令我基本每次都会用到ffmpeg -i input.m4a -ar 16000 -ac 1 -f wav output.wav参数解释-ar 16000重采样到16kHz-ac 1强制转成单声道-f wav输出WAV容器格式有人会问为什么不直接用48kHz音频提交很多云端ASR接口会限制采样率范围超过上限反而要做二次转码与其让它转不如自己在本地用FFmpeg统一处理还能顺便把音量标准化做了。2.2 选哪个识别产品实时还是录音文件识别腾讯云语音识别在会议场景里主要涉及两条产品线选型的核心判断依据是“音频是不是已经存在”。录音文件识别异步任务式适合会议已经结束、手里有完整录音文件的场景。提交音频URL或base64后返回一个TaskId轮询状态拉取结果。支持长音频还能选说话人分离、词级时间戳等能力实时语音识别流式适合会中场景比如现场大屏显示字幕、实时关键词提醒。通过WebSocket或长连接持续上传语音流返回实时转写片段对于我自己的会议小帮手主线走的是录音文件识别因为实际处理的是“会议结束后”的场景不追求实时性。如果后续想做成会中工具再扩展实时识别也不迟因为两者的结果格式和下游处理逻辑是可以共用的。表里的对比可以帮你快速判断该用哪条路对比维度录音文件识别实时语音识别使用场景会后转写、离线纪要会中字幕、实时提醒音频输入完整文件URL或base64流式音频帧调用方式创建任务轮询WebSocket长连接结果粒度完整文本、句级时间戳、说话人增量文本、句级时间戳适合开发门槛较低封装清晰较高需要处理连接生命周期2.3 下游结果处理不能把转写文本直接当纪要ASR接口拿到的是“人说了什么”的原始转写文本不是“会议决定了什么”。两者之间差着一个完整的后处理步骤。会议室里的真实语音充满冗余比如“呃”“然后”“那个”还有自我修正、话题跳转。直接把原始转写贴出去阅读体验非常糟糕。我的做法是分三层处理转写结果清洗层去掉语气词、重复片段按句末标点重新分段结构层利用说话人标记把文本按发言者归组方便看出谁主导了什么话题提取层设定关键词规则比如“需要跟进”“下周五之前”“由XX负责”把行动项和负责人抽出来这三层用一小段Python脚本就能有一个不错的雏形后面会给出代码示例。如果团队对纪要质量要求更高还可以把清洗后的文本交给大模型做摘要和结构化效果会比纯规则好很多不过那已经是另一个话题了。2.4 成本和并发限制要先算清楚接入云API之前先估一下自己的使用量级。我自己的模型很简单一个月10场会议每场1小时合计600分钟语音转写量。按云端语音识别常见的按分钟计费方式这个量级产生的费用非常可控。刚开始测试阶段尽量利用免费额度和低价档位试运行确定稳定了再批量处理历史录音。并发和任务数限制也是容易被忽略的点。录音文件识别通常有单账户并发任务上限如果一次性灌入几十个录音文件需要做一个简单的提交队列控制每次最多提交两三个任务否则会撞到频控报错。这块在第四部分会给出实际建议。3. 动手接入用Python调通录音文件识别接口3.1 安装SDK、开通服务和获取密钥腾讯云的语音识别接口走的是云API 3.0体系Python SDK可以直接通过pip安装。我这里安装的是包含语音识别组件的包pip install tencentcloud-sdk-python-asr如果你习惯装全量包用tencentcloud-sdk-python也可以只是体积会大不少。建议按产品线装精简包部署依赖的时候能省很多事。开通服务这块流程很固定登录控制台找到语音识别产品完成开通然后创建一个用于调用API的子账号密钥。我强烈建议不要直接用主账号的密钥而是创建一个只授权了语音识别相关服务权限的子账号这样就算密钥不小心泄露风险也被限制在一个产品范围内。控制台里能看到两种密钥形式SecretId和SecretKey。这两个信息需要保存在服务端环境变量或配置文件中不要写进代码仓库。我自己的习惯是放在环境变量里部署的时候再注入。3.2 录音文件识别的完整调用代码录音文件识别是一次典型的异步任务。核心流程分三步创建任务、轮询状态、拉取结果。代码如下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 你的SecretId SECRET_KEY 你的SecretKey AUDIO_URL https://your-public-host/meeting-20250106.wav # 1. 初始化客户端 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) # 2. 创建识别任务 req models.CreateRecTaskRequest() req.EngineModelType 16k_zh_large req.ChannelNum 1 req.ResTextFormat 1 req.SourceType 0 # 0音频URL1base64 req.Url AUDIO_URL resp client.CreateRecTask(req) task_id resp.Data.TaskId print(TaskId:, task_id) # 3. 轮询任务状态 while True: treq models.DescribeTaskStatusRequest() treq.TaskId task_id tresp client.DescribeTaskStatus(treq) status tresp.Data.StatusStr if status success: print(识别完成) print(tresp.Data.Result) break elif status failed: raise RuntimeError(识别任务失败) time.sleep(3)几个参数值得展开说明EngineModelType引擎模型。我用的16k_zh_large对应16k采样率的中文普通话大模型会议场景选这个通用性最好。如果音频本身是8k采样率需要选用对应的8k引擎否则识别率会受影响ChannelNum声道数。前面已经预处理成单声道这里填1。如果填了2引擎会按双声道分别识别反而可能导致输出混乱ResTextFormat结果文本格式。填1会输出带标点的文本会议纪要场景必须开这个功能SourceType音频来源。0表示通过公网URL提交1表示base64编码提交。URL方式对大文件更友好DescribeTaskStatus轮询返回的Result字段是一个JSON字符串里面包含了完整的转写结果通常还有句级时间戳、说话人等信息的具体结构取决于创建任务时是否开启对应参数。脚本里直接把结果打印出来了实际使用中可以把它作为字符串传给下游解析。轮询间隔我这里用的是3秒实际使用时可以根据任务时长动态调整一开始可以短一点1分钟后再拉长间隔减少无效请求。3.3 大文件音频怎么提交换行吗录音文件识别对base64直传的大小有限制超过限制需要用URL方式或者把音频先放到对象存储COS生成一个带有临时签名的访问地址再提交。这也是SourceType0存在的意义。如果不想开通对象存储也可以找一个临时文件托管服务但要注意两点一是URL必须公网可访问二是服务稳定性要足够否则轮询到一半文件被删了任务就直接失败。我后来还是选择了对象存储COS原因很简单上传完成后生成一个带有效期的签名URL识别完成后再收回权限全流程安全可控。提交代码大概是这个思路# 使用对象存储SDK上传文件获取临时URL from tencentcloud.cos import CosConfig, CosS3Client # 上传后调用generate_presigned_url生成预签名URL # 再把URL传给CreateRecTaskRequest的Url字段3.4 从转写文本到会议纪要的小脚本识别结果到手之后我通常会做一轮后处理。先写一个简单的会议纪要生成脚本它做的事情很直接按行读取转写文本过滤掉语气词和无意义短句再用规则提取行动项。import re import json FILTER_WORDS [呃, 嗯, 然后, 那个, 这个, 就是] def clean_line(line: str) - str: for w in FILTER_WORDS: line line.replace(w, ) return line.strip() def detect_action(line: str) - bool: action_pattern re.compile(r(需要|负责|跟进|待办|下[周月天]|今天|明天|后天|尽快)) return bool(action_pattern.search(line)) def build_notes(result_text: str) - dict: raw_lines result_text.strip().split(\n) clean_lines [clean_line(l) for l in raw_lines if len(clean_line(l)) 5] actions [l for l in clean_lines if detect_action(l)] return { clean_text: \n.join(clean_lines), action_items: actions, } # 假设识别结果存到了变量result_json result_data json.loads(result_json) transcript result_data.get(text, ) notes build_notes(transcript) print(notes[action_items])这个脚本非常基础但已经能应付真实场景中的大多数简单提取需求。如果想更精确地识别“负责人”还可以维护一个人名名单通过正则匹配“由某某负责”“让某某跟进”这类句式。更进一步把清洗后的文本发给大模型让它输出JSON格式的纪要这是效果最好的方式。我在内网工具里两种方式都保留了规则版速度快、稳定LLM版理解更准确适合长会议。4. 识别率优化让AI听懂你的会议黑话4.1 用16k引擎、开对参数剩下的交给热词表很多刚接入语音识别的开发者识别率不理想第一反应是换引擎其实优先应该检查输入音频。音频本身16k采样率、单声道、信噪比足够的话识别率通常已在一个不错的水准。接下来最值得投入的是热词表也就是定制词库。会议场景里一定会有产品名、人名、地名、项目代号这些词通用模型不一定认识。腾讯云的语音识别提供了热词表能力可以在控制台或通过API维护一个词表把公司产品、同事昵称、项目代号、业务黑话加进去。比如我的词表里会有“排期”“链路”“灰度发布”“北极星指标”这类词。加了热词之后转写文本里这些词的准确率明显提升。4.2 说话人分离不是每个场景都能开会议纪要最希望输出的是“谁说了什么”这需要语音识别接口开启说话人分离能力。在录音文件识别里这通常对应一个参数开启后会在结果中按说话人编号分组你能拿到每一句对应的SpeakerId。但这个能力有两个限制要提前知道。一是对话轮次重叠严重时说话人分离准确率会下降比如几个人同时抢话引擎容易把人声轨迹分错。二是开启说话人分离对音频质量的要求更高如果录音是远场拾音或者环境噪声大效果反而不如普通转写。我实际验证下来麦克风质量好的会议室录音开启说话人分离体验很好能清楚看到“说话人1: 下周三之前要完成上线”“说话人2: 我这边人手不够”。如果是手机录的现场音频建议先用普通转写把文本跑通不要强行追求说话人标签。4.3 通过音频预处理提高识别率的三个细节预处理不只是格式转换下面几个细节对识别率有实质性帮助音量归一化会议录音里经常有人说话声音忽大忽小音频开头和中间响度不均匀。用FFmpeg的loudnorm滤镜做一次响度归一化能让引擎在统一音量下工作裁剪静音段会议中途长时间安静这个段落不会产生有效文本但会拉长任务处理时间空闲占用。可以先做静音检测把超过2秒的静音段压缩去除回声和背景音乐如果录音里有持续的背景音乐或风扇噪声可以用降噪滤镜处理但注意不要过度降噪导致语音本身受损降噪和音量处理的FFmpeg命令示例ffmpeg -i input.wav -af loudnormI-16:LRA11:TP-1.5,highpassf200,lowpassf8000 -ar 16000 -ac 1 output_clean.wav这条命令做了三件事响度归一化、高通滤波滤掉低频噪声、低通滤波滤掉高频干扰同时保证语音主要能量集中在200Hz到8kHz这个范围内。5. 高频报错与避坑实录照着查能省半天5.1 录音文件识别常见问题速查表现象可能原因处理方法创建任务报错提示认证失败SecretId或SecretKey不正确或子账号没有语音识别权限检查密钥给子账号授权相关产品策略任务一直处于等待状态提交的音频URL不能公网访问或URL本身失效换成可公网访问的URL检查CDN或跨域限制识别结果空白或只有少量文本音频是纯静音或音量极低识别引擎没捡到有效语音帧先用播放器确认音频有声再做音量归一化任务处理时间异常长音频时长太长或提交任务过多触发排队检查单次音频时长限制控制并发提交数量报错提示采样率不支持音频实际采样率与EngineModelType不匹配统一用16k代码里加校验逻辑转写文本没有标点ResTextFormat没设置为带标点格式在创建任务时检查该参数5.2 我踩过的三个坑第一个坑是采样率不统一导致的识别率暴跌。我有一批老录音是24kHz当时直接提交给16k大模型结果很多低频词汇直接被吞掉。后来把预处理流程固定为强制重采样到16k问题才消失。现在我的工具链里任何音频进来第一步永远是统一格式不做完这件事坚决不调用API。第二个坑是任务并发限制。有次我要转写一个季度累积的二十多段会议录音脚本里没有加并发控制一次性把二十多个任务全部提交结果中途大量任务失败。排查之后才意识到需要给提交逻辑加上一个简单的队列流量控制把并发控制在两三个以内还要在失败时做指数退避重试。第三个坑是回调地址的问题。录音文件识别支持设置回调URL我一开始图省事在自己的内网测试环境里放了一个回调地址结果根本收不到通知。排查后发现回调地址必须公网可达而且服务端需要返回HTTP 200否则会判定回调失败。后来我改成轮询模式开发阶段最稳定也最容易定位问题。5.3 轮询比回调省心但要注意写对状态判断在开发阶段我更推荐轮询逻辑直观、不依赖外网回调。轮询时状态判断只认两个最终状态success和failed。中间状态还有类似waiting、processing之类但没必要为每个中间状态写分支只有最终状态才需要处理。为了减少无效请求轮询间隔可以做成动态的前三次间隔2秒之后每次间隔5秒超过两分钟还没完成就改成10秒。识别一个一小时的录音完整等待时间通常在几分钟量级。5.4 把脚本固化成一个命令行工具调用逻辑验证通过后我把它封装成一个简单的命令行工具方便每次会议结束后直接调用。命令大概是python meeting_assistant.py --input ./meeting_0115.wav --output ./notes_0115.md脚本内部做五件事预处理音频、上传对象存储、创建识别任务、轮询结果、生成纪要Markdown。命令行入口的好处是团队的同事不需要理解API细节只要把录音文件路径传进去就能得到纪要。这其实就是“会议小帮手”项目最核心的交付物不复杂但很实用。使用了一段时间之后我最大的体会是语音识别本身已经足够成熟真正决定这个工具好不好用的永远是前面音频是否合格、后面结果是否做了有效的结构化解读。如果你也想做一个类似的会议小帮手我建议动手时先跑通一条最小链路拿一段清晰的录音走通提交到出文本再花点时间调后处理规则。先把这段路走顺了,再去纠结说话人分离、热词优化这些进阶能力会遇到更多具体问题到那时手里已经有了能复现真实场景的基础设施做任何调优都会有的放矢。