资讯详情

资讯详情

腾讯云AI语音识别实战:从录音到会议纪要的完整方案

1. 会议记录这件事到底卡在哪儿了开完一场两小时的跨部门对齐会回到工位上盯着空白的会议纪要文档脑子里只剩下零星的几个关键词——这种体验我相信大多数职场人都经历过。更别提那些远程会议录音文件躺在硬盘里想回头找某个决策点得从头到尾拖进度条效率低到让人抓狂。我身边不少朋友试过各种办法有人专门安排一个同事做记录结果那个人全程低头打字根本没法参与讨论有人用手机录音事后靠耳朵一句句听两小时的会要花三四个小时整理还有人干脆放弃记录全凭记忆推进工作最后扯皮的时候谁也说不清当时到底定了什么。这个项目的出发点特别朴素能不能让机器帮我们把会议内容自动转成文字顺便把关键信息也提取出来答案是可以的而且门槛比很多人想象的低得多。腾讯云AI语音识别提供了一套完整的语音转文字能力我们只需要围绕它搭一个轻量的“会议小帮手”就能实现录音上传、自动转写、关键词提取、摘要生成这一整条链路。说白了就是把原本需要人工花几个小时做的事情压缩到几分钟内自动完成。这篇文章适合谁看如果你是开发者想找一个完整的语音识别落地案例练手这篇会给你从架构设计到代码实现的全部细节如果你是产品经理或团队负责人想了解这套方案能做到什么程度、成本大概多少也能从中找到答案哪怕你只是对AI语音识别好奇想自己动手做一个能用的工具跟着走一遍也能跑通。我会把踩过的坑、参数怎么调、代码怎么写、效果怎么优化全部摊开来讲不藏私。2. 整体方案设计为什么这样搭最省事2.1 核心需求拆解与功能边界先把需求理清楚。一个“会议小帮手”到底要做什么我把它拆成了四个核心模块音频输入支持上传本地录音文件也支持实时录制。实际场景里大多数会议是通过会议软件录制的导出为mp3或wav格式所以文件上传是刚需。语音转文字这是核心中的核心把音频里的说话内容转成带标点的文本最好还能区分不同说话人。文本后处理转写出来的原始文本是一大坨需要做断句、分段、关键词提取、摘要生成才能变成人能快速阅读的会议纪要。结果输出生成结构化的会议纪要文档支持导出和分享。功能边界也要明确我们不做实时字幕那个对延迟要求太高架构完全不同不做多语言翻译虽然腾讯云支持但会增加复杂度不做声纹识别区分具体是谁说的那个需要预先注册声纹。先把核心链路跑通再考虑扩展。2.2 技术选型为什么是腾讯云AI语音识别市面上语音识别的方案不少开源的像Whisper、Kaldi云服务像腾讯云、阿里云、讯飞。我选腾讯云AI语音识别主要基于几个实际考量第一中文识别准确率。我实测过几段带口音的会议录音腾讯云在中文场景下的表现明显更稳。特别是涉及到专业术语和缩略词的时候它的一站式识别能力比通用模型强不少。Whisper虽然开源免费但中文标点符号的处理一直是个痛点经常出现整段没有标点的情况后处理成本很高。第二说话人分离能力。腾讯云的录音文件识别支持开启说话人分离返回的结果里会标注每句话是谁说的speaker 0、speaker 1……。这个功能对会议场景太重要了否则转出来的文本就是一团乱麻根本分不清谁提出了什么观点。第三接入成本低。腾讯云提供完整的SDK和API文档Python SDK几行代码就能调通。对于个人开发者和小团队来说不需要自己训练模型、不需要买GPU服务器按量付费用多少算多少。第四生态完整。除了语音识别腾讯云还有自然语言处理、文本摘要等能力后续要做关键词提取和摘要生成可以直接在同一个体系里完成不用来回切换平台。当然选型不是绝对的。如果你对数据隐私要求极高必须本地部署那开源方案更合适如果你预算充足且对实时性要求高可以考虑实时语音识别接口。但对于大多数会议记录场景录音文件识别后处理的方案性价比是最高的。2.3 系统架构与数据流转整个系统的架构不复杂我画一个文字版的流程用户上传音频 → 对象存储COS→ 调用语音识别API → 获取转写结果JSON → 文本清洗与分段 → 关键词提取 → 摘要生成 → 生成会议纪要 → 前端展示/导出这里有几个关键设计决策为什么用对象存储中转腾讯云的录音文件识别API要求音频文件可以通过URL访问。直接把本地文件传上去不行得先传到对象存储COS生成一个临时链接再把链接传给识别接口。这个设计虽然多了一步但好处是识别任务可以异步处理不用一直等着。为什么用异步识别而不是同步同步接口有时长限制一般60秒以内会议录音动辄半小时起步必须用异步接口。异步接口的流程是提交任务→轮询结果→获取转写文本。虽然多了一次轮询但稳定性和成功率都高得多。后处理为什么单独做语音识别返回的结果是带时间戳的句子列表直接拼接可读性很差。需要做几件事按时间间隔和语义断句、合并同一说话人的连续发言、去掉语气词和重复内容。这些逻辑放在后端单独处理方便调试和优化。3. 核心细节解析从音频到文字的每一步3.1 音频格式与参数选择音频质量直接决定识别准确率。我踩过的坑里至少有一半是因为音频本身有问题。先说格式腾讯云支持wav、mp3、m4a、aac等常见格式但推荐用wav或无损格式因为mp3是有损压缩高频部分被砍掉后会影响识别。如果原始录音是mp3尽量找高码率的版本128kbps以下的建议先转码。采样率方面16kHz是黄金标准。人声的主要频率范围在300Hz到3400Hz之间16kHz采样率完全覆盖而且文件体积适中。如果录音是8kHz电话录音常见识别率会明显下降特别是区分“四”和“十”这种发音相近的词。如果是48kHz的高保真录音建议降采样到16kHz既减小文件体积又不影响识别效果。声道数也有讲究。单声道比立体声更适合语音识别。立体声虽然听起来更有空间感但两个声道的内容如果相位不一致反而会干扰识别引擎。如果原始录音是立体声建议在预处理阶段合并为单声道。还有一个容易被忽略的点音量归一化。有些会议录音里离麦克风远的人声音很小近的人声音很大。识别引擎对音量过小的片段识别率很差。我一般会用音频处理库做一次音量归一化把所有片段的音量调整到大致相同的水平。这个操作在Python里用pydub或者librosa都能做几行代码的事。3.2 调用语音识别接口的关键参数腾讯云录音文件识别的接口参数不少但真正影响效果的就这么几个参数名推荐值作用不设置的后果EngineModelType16k_zh指定中文通用模型默认可能是英文模型中文识别乱码ChannelNum1声道数立体声不合并会报错或识别混乱ResTextFormat2返回带时间戳的详细结果只返回纯文本无法做说话人分离SpeakerDiarizationtrue开启说话人分离分不清谁说的纪要没法整理SpeakerNumber0自动判断说话人数手动指定容易猜错自动更稳FilterDirty1过滤脏词会议里偶尔的脏话会被转出来FilterModal1过滤语气词“嗯”“啊”“那个”全保留文本很啰嗦FilterPunc0保留标点设成1会把标点全去掉可读性极差ConvertNumMode1数字转阿拉伯数字“一百二十三”不如“123”直观这些参数里SpeakerDiarization和ResTextFormat是最关键的两个。说话人分离不开会议纪要就是一团乱麻ResTextFormat不设成2拿不到时间戳后续做分段和摘要都无从下手。还有一个隐藏参数HotwordList。这是热词表可以传入会议中可能出现的专业术语、人名、产品名。比如你们公司内部有个项目叫“天枢”识别引擎可能听成“天数”“天书”把“天枢”加到热词表里识别准确率立刻上去。热词表最多支持1000个词每个词可以设置权重权重越高越容易被识别出来。我一般会把公司名、产品名、常见人名、行业术语都加进去效果提升非常明显。3.3 说话人分离的原理与局限说话人分离Speaker Diarization听起来很玄乎其实原理不复杂先提取音频中的声纹特征然后用聚类算法把相似的声纹归为一类每一类就是一个说话人。腾讯云用的是深度学习模型做声纹提取比传统的MFCCGMM方案准确率高不少。但要注意几个局限第一说话人数超过5个之后准确率下降。这是聚类算法的通病类别越多越容易混淆。如果会议超过5个人建议提前指定SpeakerNumber或者接受一定的误判率。第二短发言容易被归错。如果一个人只说了“对”“好的”这种一两个字的回应声纹特征太少很容易被归到别人身上。这个目前没有太好的解决办法只能在后处理阶段人工修正。第三重叠说话处理不好。两个人同时说话的时候识别引擎只能选一个主要声源另一个人的话会丢失。这是所有语音识别系统的通病不是腾讯云独有的问题。如果会议经常出现抢话的情况建议在会议规范上要求轮流发言。3.4 文本后处理的核心逻辑语音识别返回的原始结果大概长这样{ Result: { Sentences: [ {Text: 那个我们今天讨论一下这个季度的目标, StartTime: 0, EndTime: 2500, SpeakerId: 0}, {Text: 好的我觉得应该先把上个月的复盘做一下, StartTime: 2600, EndTime: 5200, SpeakerId: 1}, ... ] } }直接把这些句子拼起来可读性很差。后处理要做几件事断句与分段按时间间隔断句如果两句话之间间隔超过1.5秒认为是两个独立的段落。同时按说话人分段同一个人连续说的几句话合并成一个段落。语气词过滤虽然接口有FilterModal参数但过滤得不够干净。我会在后处理阶段再用正则表达式去掉“嗯”“啊”“那个”“就是说”这类词。注意不要过度过滤有些语气词去掉后句子会不通顺。重复内容合并会议里经常有人说“对对对”“好好好”这些重复内容要合并成“对”“好”。标点修正识别引擎加的标点不一定准确特别是问号和句号容易混淆。我会用简单的规则做二次修正比如句尾有“吗”“呢”“吧”的改成问号。数字与单位规范化把“百分之三十”转成“30%”“三点五”转成“3.5”这些细节能大幅提升可读性。4. 实操过程从零搭建会议小帮手4.1 环境准备与依赖安装先列一下需要的东西Python 3.8以上腾讯云账号开通语音识别和对象存储服务腾讯云Python SDKpip install tencentcloud-sdk-python音频处理库pip install pydub librosaWeb框架可选pip install flask获取密钥的步骤我就不赘述了在腾讯云控制台的访问管理里创建API密钥拿到SecretId和SecretKey。建议把这些敏感信息放在环境变量里不要硬编码在代码中。对象存储需要创建一个存储桶Bucket记住Bucket名称和所属地域Region。上传文件后生成的临时链接有效期默认是1小时足够识别任务完成了。4.2 音频上传与预处理代码先写一个音频预处理的函数把各种格式统一成16kHz单声道的wavfrom pydub import AudioSegment import os def preprocess_audio(input_path, output_path): 将音频统一转换为16kHz单声道wav格式 audio AudioSegment.from_file(input_path) # 转为单声道 if audio.channels 1: audio audio.set_channels(1) # 设置采样率为16kHz audio audio.set_frame_rate(16000) # 音量归一化目标音量-20dBFS target_dBFS -20.0 change_in_dBFS target_dBFS - audio.dBFS audio audio.apply_gain(change_in_dBFS) # 导出为wav audio.export(output_path, formatwav) return output_path这段代码里音量归一化那一步很关键。我试过不归一化直接识别远场说话人的内容经常丢失。归一化之后识别完整度提升了大概20%。4.3 调用识别接口的完整流程接下来是核心的识别调用。腾讯云的异步识别分三步提交任务、查询状态、获取结果。from tencentcloud.common import credential from tencentcloud.asr.v20190614 import asr_client, models import json import time def create_asr_task(audio_url): 提交录音文件识别任务 cred credential.Credential( os.environ.get(TENCENT_SECRET_ID), os.environ.get(TENCENT_SECRET_KEY) ) client asr_client.AsrClient(cred, ap-guangzhou) req models.CreateRecTaskRequest() req.EngineModelType 16k_zh req.ChannelNum 1 req.ResTextFormat 2 req.SourceType 0 req.Url audio_url req.SpeakerDiarization True req.SpeakerNumber 0 req.FilterDirty 1 req.FilterModal 1 req.FilterPunc 0 req.ConvertNumMode 1 # 热词表 req.HotwordList [天枢, 星链计划, OKR, 复盘] resp client.CreateRecTask(req) return resp.Data.TaskId def wait_for_result(task_id, max_wait600): 轮询识别结果最多等待10分钟 cred credential.Credential( os.environ.get(TENCENT_SECRET_ID), os.environ.get(TENCENT_SECRET_KEY) ) client asr_client.AsrClient(cred, ap-guangzhou) start_time time.time() while time.time() - start_time max_wait: req models.DescribeTaskStatusRequest() req.TaskId task_id resp client.DescribeTaskStatus(req) status resp.Data.Status if status 2: # 识别完成 return json.loads(resp.Data.Result) elif status 3: # 识别失败 raise Exception(f识别失败: {resp.Data.ErrorMsg}) time.sleep(5) # 每5秒轮询一次 raise Exception(识别超时)这里有几个实操细节值得说轮询间隔设5秒比较合适。设太短浪费请求次数设太长结果出来得慢。实测下来1小时的录音大概3-5分钟能识别完5秒轮询完全够用。热词表要提前准备。我一般会在项目启动时维护一个热词文件每次调用时读取。热词不要太多100个以内效果最好太多会稀释权重。错误处理要做好。识别失败的原因很多音频格式不对、文件太大、网络超时。每种错误要有对应的提示方便排查。4.4 文本后处理与纪要生成拿到识别结果后开始后处理import re def merge_sentences(sentences, gap_threshold1500): 按时间间隔和说话人合并句子 merged [] current None for sent in sentences: if current is None: current { speaker: sent[SpeakerId], text: sent[Text], start: sent[StartTime], end: sent[EndTime] } else: gap sent[StartTime] - current[end] same_speaker sent[SpeakerId] current[speaker] if same_speaker and gap gap_threshold: current[text] sent[Text] current[end] sent[EndTime] else: merged.append(current) current { speaker: sent[SpeakerId], text: sent[Text], start: sent[StartTime], end: sent[EndTime] } if current: merged.append(current) return merged def clean_text(text): 清洗文本去语气词、合并重复、修正标点 # 去掉常见语气词 fillers [嗯, 啊, 那个, 就是说, 然后呢, 这个呢] for filler in fillers: text text.replace(filler, ) # 合并重复字符 text re.sub(r(.)\1{2,}, r\1, text) # 修正标点 text re.sub(r([吗呢吧])。, r\1, text) # 去掉多余空格 text re.sub(r\s, , text) return text.strip()后处理完之后生成结构化的会议纪要def generate_minutes(merged_sentences): 生成会议纪要 minutes [] speaker_map {} # 说话人ID映射为发言人A/B/C for sent in merged_sentences: speaker_id sent[speaker] if speaker_id not in speaker_map: speaker_map[speaker_id] f发言人{chr(65 len(speaker_map))} speaker_name speaker_map[speaker_id] text clean_text(sent[text]) if text: # 过滤空内容 minutes.append(f**{speaker_name}**{text}) return \n\n.join(minutes)4.5 关键词提取与摘要生成会议纪要光有全文还不够最好能自动提取关键词和摘要。腾讯云的自然语言处理接口可以做这件事但为了减少依赖我用了一个轻量方案基于TF-IDF的关键词提取。from sklearn.feature_extraction.text import TfidfVectorizer import jieba def extract_keywords(text, top_k10): 基于TF-IDF提取关键词 # 分词 words jieba.lcut(text) words [w for w in words if len(w) 1] # 过滤单字 # 用TF-IDF计算权重 vectorizer TfidfVectorizer() tfidf_matrix vectorizer.fit_transform([ .join(words)]) # 获取特征词和对应的TF-IDF值 feature_names vectorizer.get_feature_names_out() tfidf_scores tfidf_matrix.toarray()[0] # 排序取Top K word_scores list(zip(feature_names, tfidf_scores)) word_scores.sort(keylambda x: x[1], reverseTrue) return [word for word, score in word_scores[:top_k]]摘要生成用TextRank算法原理是把句子作为节点句子之间的相似度作为边然后迭代计算每个句子的重要性。这个算法在jieba里已经有实现import jieba.analyse def generate_summary(text, top_k5): 基于TextRank生成摘要 # 分句 sentences re.split(r[。], text) sentences [s.strip() for s in sentences if len(s.strip()) 10] if len(sentences) top_k: return 。.join(sentences) 。 # 用TextRank提取关键句 summary_sentences jieba.analyse.textrank( 。.join(sentences), topKtop_k, withWeightFalse ) return 。.join(summary_sentences) 。这两个方案虽然不是最先进的但胜在轻量、无需额外API调用、效果也够用。如果对摘要质量要求更高可以接入腾讯云的文本摘要接口那个是基于大模型的效果更好但成本也更高。5. 常见问题与排查技巧实录5.1 识别准确率低的排查思路识别准确率低是最常见的问题原因可能有很多。我整理了一个排查清单现象可能原因排查方法解决方案整段识别为空音频格式不支持用播放器确认文件能正常播放转码为16kHz单声道wav部分内容丢失音量过小用音频软件查看波形做音量归一化专业术语识别错未配置热词检查热词表是否包含该词添加热词并设置高权重说话人混淆说话人数过多确认实际说话人数指定SpeakerNumber或减少参会人数标点混乱FilterPunc设置错误检查参数是否为0设为0保留标点数字识别成中文ConvertNumMode未开启检查参数设为1转阿拉伯数字我遇到最坑的一次是音频文件本身有问题——录音设备设置了自动增益导致音量忽大忽小识别引擎在音量骤降的片段直接跳过了。后来在预处理阶段加了动态范围压缩问题才解决。5.2 接口调用超时与限流处理腾讯云语音识别接口有默认的QPS限制具体数值取决于你的账号等级。如果并发提交太多任务会返回RequestLimitExceeded错误。解决办法有两个第一加队列。把识别任务放进消息队列控制并发数。我用的是简单的Redis队列每次只提交3个任务完成一个再提交下一个。第二加退避重试。遇到限流错误时等待一段时间再重试等待时间指数增长import time def call_with_retry(func, max_retries3): for i in range(max_retries): try: return func() except Exception as e: if RequestLimitExceeded in str(e) and i max_retries - 1: wait_time 2 ** i # 1s, 2s, 4s time.sleep(wait_time) else: raise5.3 长音频处理的注意事项超过1小时的音频识别时间会比较长而且失败率也会上升。我的经验是超过2小时的音频建议先切分。用音频处理库按静音片段切分成30分钟左右的段落分别识别后再合并。这样即使某一段失败也不用全部重来。切分点要选在静音处。如果在说话中间切开两段的首尾都会丢失内容。用pydub的silence检测功能可以找到静音点from pydub import AudioSegment from pydub.silence import detect_silence def split_audio(audio_path, min_silence_len1000, silence_thresh-40): audio AudioSegment.from_file(audio_path) silences detect_silence(audio, min_silence_lenmin_silence_len, silence_threshsilence_thresh) # 在静音中点切分...5.4 成本控制与优化建议腾讯云语音识别按音频时长计费具体价格在官网可以查到。如果会议量大成本还是要算一算的。几个省钱技巧第一能复用的不重复识别。同一段音频不要重复提交结果存数据库下次直接查。第二短会议用同步接口。如果会议在1分钟以内用同步接口更便宜同步接口按次计费异步按分钟计费。第三非关键会议降低采样率。8kHz的识别费用比16kHz低如果只是内部同步会对准确率要求不高可以用8kHz。第四定期清理对象存储。识别完成后音频文件就没用了及时删除避免存储费用累积。5.5 几个让我印象深刻的坑坑一中文文件名导致上传失败。对象存储的URL不支持中文上传前要把文件名转成英文或拼音。我一开始没注意调试了半天才发现是文件名的问题。坑二时区问题导致时间戳错乱。识别结果的时间戳是相对于音频开始的毫秒数但如果你在展示时用了本地时区转换会全部错位。记住时间戳就是毫秒数不要做任何时区转换。坑三并发写入导致数据覆盖。多个识别任务同时完成如果都往同一个文件里写结果会互相覆盖。解决办法是每个任务写独立的文件最后再合并。坑四热词权重设置过高导致误识别。热词权重最高可以设到10但设太高会让引擎强行把不相关的词识别成热词。我一般设到5-8之间平衡准确率和误识别率。6. 后续可以怎么扩展这套方案跑通之后能扩展的方向很多。比如接入实时语音识别接口做会议实时字幕比如接入大模型做智能问答会后直接问“这次会议关于预算的结论是什么”比如对接企业微信或飞书自动把会议纪要推送到群里。我个人最想加的功能是自动待办提取。会议里经常有人说“这个事我来跟进”“下周三之前给方案”这些承诺如果能自动识别出来并生成待办列表那会议小帮手就真的变成生产力工具了。技术上不难用正则匹配“我来”“我负责”“之前给”这类模式再结合时间实体识别就能提取出待办事项和截止时间。还有一个方向是多会议关联分析。同一个项目每周开一次会把历次会议纪要串起来就能看到决策的演变过程。这个需要做会议主题聚类和时序分析复杂度高一些但价值也更大。最后分享一个小技巧如果你觉得识别出来的文本还是太啰嗦可以在后处理阶段加一个“压缩”步骤用大模型把每段话压缩成一句话。我试过用提示词“把以下会议发言压缩成不超过20字的要点”效果出奇地好纪要篇幅直接减半可读性反而提升了。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →