1. 从一场两小时的会议录音说起
上周帮一个做企业培训的朋友处理了一批会议录音,六场会,每场平均一个半小时,录完往硬盘里一扔就没人管了。他跟我说:"每次开完会都想着整理纪要,但一看到那堆音频文件就犯怵,手动听打至少要花三倍的时间。"这个场景我太熟悉了——会议记录这件事,几乎每个团队都在做,但真正做得好的没几个。要么是没人愿意记,要么是记了但信息遗漏严重,要么是记完就躺在共享盘里再也没人翻。
后来我用腾讯云的AI语音识别服务搭了一套自动化流程,从音频上传到输出带时间戳和说话人区分的文字稿,整个过程压缩到几分钟。这篇文章就把这套方案完整拆开讲一遍,包括为什么选这个服务、怎么配置参数、实际跑下来哪些地方容易翻车、以及怎么把识别结果进一步加工成可用的会议纪要。如果你也在被会议记录折磨,或者想找一个靠谱的语音转文字方案,这篇内容应该能帮你省不少时间。
核心关键词就几个:腾讯云AI语音识别、会议记录自动化、语音转文字、说话人分离、实时识别与录音文件识别。整套方案不复杂,但细节很多,我会把每个环节的坑都标出来。
2. 为什么是腾讯云AI语音识别,而不是其他方案
2.1 先搞清楚会议场景对语音识别的真实需求
很多人一上来就问"哪个语音识别准确率最高",这个问题其实问偏了。会议场景的需求跟通用语音转文字完全不一样,你得先把自己的场景拆清楚。
我总结下来,会议记录对语音识别有五个硬性要求:
- 长音频支持:一场会动辄一两个小时,很多免费或轻量级方案对音频时长有限制,超过就截断或者直接失败。
- 说话人分离:会议是多个人交替发言,如果转出来的文字分不清谁说的,那基本没法用。
- 专业术语识别:每个行业都有自己的黑话,通用模型很容易把"日活"听成"日活"(这个还好),把"转化率"听成"转花率"就麻烦了。
- 时间戳:做纪要的时候需要定位到具体时间点,方便回溯原音。
- 批量处理能力:不是一场会,是每周好几场,得能批量跑。
腾讯云的录音文件识别服务在这几个维度上覆盖得比较完整。它支持最长5小时的音频文件,自带说话人分离功能,可以上传自定义词表来提升专业术语的识别率,输出结果带逐句时间戳。这些能力组合起来,刚好匹配会议记录的核心诉求。
2.2 录音文件识别和实时语音识别,到底选哪个
腾讯云AI语音识别下面有两个主要产品线:实时语音识别和录音文件识别。很多人搞不清楚该用哪个,我一开始也纠结过。
实时语音识别的逻辑是音频流一边传输一边出结果,延迟很低,适合直播字幕、实时会议字幕这种场景。但它的缺点是说话人分离能力弱,而且对网络稳定性要求高,一旦断流就得重来。
录音文件识别的逻辑是先把音频文件传到对象存储,然后提交识别任务,异步等待结果。它的优势是支持说话人分离、支持更大的音频文件、识别准确率在长音频上更稳定。缺点是没法实时出结果,得等。
对于会议记录这个场景,我的建议很明确:用录音文件识别。原因很简单——会议记录不是要实时看字幕,而是要事后整理出一份准确的纪要。准确率和说话人分离比实时性重要得多。而且录音文件识别可以批量提交任务,你周五下班前把一周的会议录音全部提交,周一早上来收结果就行。
注意:如果你的场景是线上会议需要实时字幕辅助,那实时语音识别更合适。但如果是会后整理纪要,录音文件识别是更优解。两者可以结合使用,实时识别做辅助,录音文件识别做最终稿。
2.3 成本账要算清楚
说到成本,腾讯云语音识别的计费方式是按音频时长计费。录音文件识别的价格大概在每小时几块钱的量级(具体价格以官网为准,不同套餐有差异)。一场一小时的会议,识别成本大概就是一杯奶茶的钱。
但这里有个容易被忽略的点:音频预处理会直接影响成本。如果你上传的是双声道音频,每个声道分别识别,那计费时长会翻倍。所以在上传之前,最好先把音频转成单声道,能省一半的钱。这个细节后面讲实操的时候会详细说。
另外,腾讯云经常有新用户免费额度,刚开始试的时候可以先薅一波免费额度,跑通流程再正式付费。对于小团队来说,一个月的会议识别成本可能也就几十块钱,比请人专门做会议记录划算太多了。
3. 从音频到文字稿的完整链路拆解
3.1 整体架构:四个环节串起来
这套方案的整体链路不复杂,但每个环节都有讲究。我把它拆成四步:
- 音频采集与预处理:把会议录音整理成符合要求的格式
- 上传到对象存储:腾讯云语音识别需要音频文件有一个可访问的URL
- 提交识别任务:调用API提交任务,配置识别参数
- 获取与后处理结果:拉取识别结果,做进一步加工
这四步里面,第一步和第四步是最容易被低估的。很多人以为调个API就完事了,实际上音频预处理和结果后处理才是决定最终可用性的关键。
3.2 音频预处理:别小看这一步
腾讯云录音文件识别对音频格式有明确要求,支持的格式包括wav、mp3、m4a、aac等,采样率建议16kHz,位深16bit。但实际拿到的会议录音往往五花八门——有的是手机录的m4a,有的是会议系统导出的wav,还有的是从视频里提取的音频。
我一般会用ffmpeg做统一预处理,核心命令如下:
# 转成16kHz单声道wav格式 ffmpeg -i input.m4a -ar 16000 -ac 1 -acodec pcm_s16le output.wav这条命令做了三件事:把采样率统一到16000Hz,把声道数强制为1(单声道),把编码格式转成PCM 16位小端。为什么要这么做?
- 采样率16kHz:这是语音识别的标准采样率,再高对识别准确率提升有限,反而增加文件体积和上传时间。
- 单声道:前面说了,双声道会按两倍时长计费,而且会议录音通常不需要立体声。
- PCM编码:无损格式,识别引擎处理起来最稳定,不会因为压缩算法引入的失真影响识别效果。
如果原始音频噪音比较大,还可以加一步降噪:
# 使用ffmpeg的降噪滤镜 ffmpeg -i input.wav -af "afftdn=nf=-25" -ar 16000 -ac 1 output_denoised.wavafftdn是FFT降噪滤镜,nf=-25表示噪声底限设为-25dB。这个参数需要根据实际录音环境调整,如果会议室空调声大,可以适当调高。
实操心得:降噪不是越强越好。过度降噪会把人声的细节也抹掉,反而降低识别准确率。我的经验是,如果原始录音信噪比还可以,直接跳过降噪;如果背景噪音明显,用轻度降噪即可,不要追求"干净得像录音棚"。
3.3 上传到对象存储:URL是必须的
腾讯云录音文件识别要求音频文件必须有一个可访问的URL,所以你需要先把文件上传到对象存储(COS)。这一步看起来简单,但有几个细节要注意:
- 存储桶权限:识别服务需要能读取到这个文件,所以存储桶的权限设置要允许读取。但也不要设成公开读写,那样有安全风险。建议用临时签名URL或者给识别服务授权。
- 文件命名:建议用有意义的命名规则,比如
meeting_20240115_产品评审.wav,方便后续管理和追溯。 - 区域选择:存储桶的区域最好和语音识别服务的区域一致,这样内网传输速度快,也不会有跨区费用。
上传的代码示例(Python):
from qcloud_cos import CosConfig, CosS3Client secret_id = '你的SecretId' secret_key = '你的SecretKey' region = 'ap-shanghai' token = None scheme = 'https' config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token, Scheme=scheme) client = CosS3Client(config) # 上传文件 response = client.upload_file( Bucket='your-bucket-125xxxxxxx', LocalFilePath='meeting_audio.wav', Key='meetings/20240115_product_review.wav', PartSize=1, MAXThread=10, EnableMD5=False ) print(response['ETag'])上传完成后,你需要生成一个可访问的URL。如果存储桶是私有读,可以用get_presigned_url生成带签名的临时URL:
url = client.get_presigned_url( Method='GET', Bucket='your-bucket-125xxxxxxx', Key='meetings/20240115_product_review.wav', Expired=3600 # URL有效期1小时 )3.4 提交识别任务:参数配置是核心
这是整个流程最关键的一步。腾讯云录音文件识别的API叫CreateRecTask,核心参数包括:
| 参数名 | 说明 | 推荐值 |
|---|---|---|
| EngineModelType | 引擎模型类型 | 16k_zh(中文通用) |
| ChannelNum | 声道数 | 1 |
| ResTextFormat | 结果文本格式 | 0(基础格式)或2(带时间戳和说话人) |
| SpeakerDiarization | 是否开启说话人分离 | 1(开启) |
| SpeakerNumber | 说话人数量 | 根据实际人数填,不填则自动判断 |
| FilterDirty | 是否过滤脏词 | 1(过滤) |
| FilterModal | 是否过滤语气词 | 1(过滤) |
| HotwordId | 热词表ID | 提前创建好的热词表 |
ResTextFormat这个参数特别重要。如果你只需要纯文本,填0就行;但如果要做会议纪要,强烈建议填2,这样返回的结果会包含每个句子的开始时间、结束时间和说话人ID。
SpeakerDiarization开启后,识别结果会把不同说话人标记为"说话人0""说话人1"这样的ID。但要注意,它只能区分"不同的人",不能告诉你具体是谁。所以会后还需要人工把说话人ID映射到具体人名,这个后面会讲怎么高效处理。
热词表是提升专业术语识别率的关键。你可以在控制台创建一个热词表,把团队常用的专业术语、产品名、人名都加进去。比如"日活跃用户""转化漏斗""OKR对齐"这些词,通用模型可能会识别错,但加到热词表里就能显著改善。
# 提交识别任务的示例代码 import json from tencentcloud.common import credential from tencentcloud.asr.v20190614 import asr_client, models cred = credential.Credential('你的SecretId', '你的SecretKey') client = asr_client.AsrClient(cred, 'ap-shanghai') req = models.CreateRecTaskRequest() req.EngineModelType = '16k_zh' req.ChannelNum = 1 req.ResTextFormat = 2 req.SourceType = 0 req.Url = '你的音频文件URL' req.SpeakerDiarization = 1 req.SpeakerNumber = 0 req.FilterDirty = 1 req.FilterModal = 1 req.HotwordId = '你的热词表ID' resp = client.CreateRecTask(req) print(resp.TaskId)提交成功后会返回一个TaskId,后续用这个ID去查询识别结果。
3.5 拉取结果:异步等待与轮询
录音文件识别是异步任务,提交后需要轮询查询状态。腾讯云提供了DescribeTaskStatus接口,传入TaskId即可查询。
# 轮询查询任务状态 import time req = models.DescribeTaskStatusRequest() req.TaskId = 你的TaskId while True: resp = client.DescribeTaskStatus(req) status = resp.Data.Status if status == 2: # 2表示识别完成 print(resp.Data.Result) break elif status == 3: # 3表示识别失败 print('识别失败:', resp.Data.ErrorMsg) break else: print('识别中,等待10秒...') time.sleep(10)状态码的含义:0是等待中,1是识别中,2是识别完成,3是识别失败。一般一小时的音频,识别时间在几分钟左右,具体取决于排队情况。
返回的结果是一个JSON字符串,里面包含了逐句的识别文本、时间戳和说话人ID。拿到这个结果之后,就可以进入后处理环节了。
4. 识别结果的后处理:从文字稿到可用纪要
4.1 说话人ID映射:把"说话人0"变成具体人名
识别结果里的说话人ID是"说话人0""说话人1"这样的匿名标识,直接看根本不知道谁是谁。这一步没有自动化的捷径,但可以做得高效一些。
我的做法是:先让识别结果按说话人分组,把每个说话人的所有发言片段提取出来,然后快速浏览一遍,根据发言内容判断是谁。比如提到"我们技术这边"的大概率是技术负责人,提到"预算"的大概率是财务或管理层。
具体操作上,可以用Python把结果按说话人聚合:
import json result = json.loads(resp.Data.Result) sentences = result['ResultDetail'] # 按说话人分组 speaker_segments = {} for sent in sentences: speaker = sent['SpeakerId'] if speaker not in speaker_segments: speaker_segments[speaker] = [] speaker_segments[speaker].append({ 'start': sent['StartTime'], 'end': sent['EndTime'], 'text': sent['FinalSentence'] }) # 输出每个说话人的发言摘要 for speaker, segments in speaker_segments.items(): print(f'=== 说话人{speaker} ===') for seg in segments[:5]: # 只看前5句 print(f"[{seg['start']}ms] {seg['text']}")看完每个说话人的前几句话,基本就能判断出身份了。然后建一个映射字典,把ID替换成人名。
4.2 时间戳对齐:快速定位原音
时间戳的价值在于回溯。当纪要有疑问的时候,可以根据时间戳快速定位到原音的具体位置。我一般会在最终的文字稿里保留时间戳,格式是[HH:MM:SS]。
def format_time(ms): seconds = ms // 1000 hours = seconds // 3600 minutes = (seconds % 3600) // 60 secs = seconds % 60 return f"{hours:02d}:{minutes:02d}:{secs:02d}" for sent in sentences: time_str = format_time(sent['StartTime']) speaker_name = speaker_map.get(sent['SpeakerId'], f"说话人{sent['SpeakerId']}") print(f"[{time_str}] {speaker_name}: {sent['FinalSentence']}")这样输出的文字稿,每一句都有时间戳和说话人,看起来就很清晰了。
4.3 自动摘要:用大模型做二次加工
文字稿有了,但一份一小时的会议文字稿可能有上万字,没人愿意从头读到尾。这时候可以用大语言模型做自动摘要。
我的做法是把文字稿按议题切分,然后让模型对每个议题生成摘要和待办事项。提示词大概是这样:
以下是一场会议的记录,请帮我: 1. 按讨论的议题分段 2. 每个议题提炼3-5个关键结论 3. 提取所有待办事项,标注负责人(如果提到的话) 4. 标记有争议或未达成一致的点 会议记录: {文字稿内容}这样处理之后,一份可读性很强的会议纪要就出来了。当然,自动摘要的结果需要人工复核,但至少省去了从零开始整理的时间。
实操心得:自动摘要的提示词里一定要强调"只基于原文内容,不要自行推断"。否则模型很容易脑补出一些会上根本没说的内容,那就麻烦了。
5. 实际跑下来容易翻车的几个地方
5.1 音频质量问题导致的识别错误
这是最常见的问题。会议录音如果是在大会议室用单麦克风录的,离得远的人说话声音小,识别准确率会明显下降。我遇到过最夸张的情况是,坐在角落的同事发言被识别成了"嗯嗯啊啊"一堆无意义的字。
解决办法有两个:一是尽量用每个人的独立麦克风(比如线上会议每个人用自己的电脑麦克风),二是如果只能用一个麦克风,尽量放在桌子中间,并且开会前测试一下录音效果。
另外,如果会议中有大量重叠发言(几个人同时说话),说话人分离的效果会大打折扣。这种情况下,识别结果可能会出现说话人ID跳来跳去的情况。我的建议是,如果会议讨论很激烈,可以在会后结合录音人工校对关键部分。
5.2 热词表不是万能的
热词表能提升专业术语的识别率,但也不是加了就万事大吉。我测试下来,热词表对"词"的识别提升明显,但对"短语"和"句子"的提升有限。而且热词表有数量限制,不能无限加。
我的策略是:只加最高频、最容易识别错的词。比如产品名称、核心技术术语、团队成员的姓名。那些偶尔出现的词,加了反而可能干扰其他词的识别。
还有一个坑:热词表创建后需要一定时间生效,不是立即就能用的。如果你刚创建完就提交识别任务,可能热词还没加载进去。建议创建后等几分钟再提交任务。
5.3 长音频的断句问题
一小时的音频,识别结果可能会有几百个句子。有时候断句会不太合理,比如把一句话拆成两半,或者把两句话合并成一句。这会影响后续的摘要质量。
腾讯云的识别结果里有一个FinalSentence字段,是经过断句处理的。如果对断句要求高,可以在后处理阶段用标点符号和语义做二次断句。不过大多数情况下,原生的断句已经够用了。
5.4 并发限制和排队
腾讯云录音文件识别有并发任务数限制,具体数量取决于你的套餐。如果你一次性提交太多任务,后面的任务会排队等待。对于小团队来说,一般不会碰到这个限制,但如果是大规模批量处理,需要提前规划好提交节奏。
我的做法是分批提交,每批不超过并发上限,等前一批完成后再提交下一批。这样虽然总时间长了点,但不会因为超限导致任务失败。
6. 把这套流程串起来:一个可复用的脚本框架
6.1 目录结构设计
为了让这套流程可复用,我把它整理成了一个Python脚本,目录结构如下:
meeting-asr/ ├── config.py # 配置文件(密钥、存储桶信息等) ├── preprocess.py # 音频预处理 ├── upload.py # 上传到COS ├── recognize.py # 提交识别任务并轮询结果 ├── postprocess.py # 结果后处理(说话人映射、时间戳格式化) ├── summarize.py # 自动摘要 └── main.py # 主流程入口6.2 主流程代码框架
# main.py import os from preprocess import preprocess_audio from upload import upload_to_cos from recognize import submit_task, poll_result from postprocess import format_result from summarize import generate_summary def process_meeting(audio_path, meeting_title): # 1. 预处理 processed_path = preprocess_audio(audio_path) # 2. 上传 url = upload_to_cos(processed_path) # 3. 提交识别 task_id = submit_task(url) # 4. 轮询结果 raw_result = poll_result(task_id) # 5. 后处理 formatted_text = format_result(raw_result) # 6. 保存文字稿 output_path = f"output/{meeting_title}_transcript.txt" with open(output_path, 'w', encoding='utf-8') as f: f.write(formatted_text) # 7. 生成摘要 summary = generate_summary(formatted_text) summary_path = f"output/{meeting_title}_summary.md" with open(summary_path, 'w', encoding='utf-8') as f: f.write(summary) print(f"处理完成:{output_path}") return output_path, summary_path if __name__ == '__main__': process_meeting('raw_audio/meeting_0115.m4a', '产品评审会')这个框架跑通之后,每周的会议录音只需要把文件丢到raw_audio目录,改一下文件名,运行脚本就行。整个过程从原来的几小时压缩到十几分钟。
6.3 批量处理的优化
如果一周有好几场会,可以写一个批量处理的脚本:
import os from main import process_meeting audio_dir = 'raw_audio/' for filename in os.listdir(audio_dir): if filename.endswith(('.wav', '.mp3', '.m4a')): audio_path = os.path.join(audio_dir, filename) meeting_title = os.path.splitext(filename)[0] try: process_meeting(audio_path, meeting_title) except Exception as e: print(f"处理 {filename} 失败: {e}")这样一次性把所有会议录音都处理完,省时省力。
7. 一些关于准确率和效率的实测数据
7.1 不同音频条件下的识别准确率
我用自己的几场会议录音做了对比测试,结果如下:
| 音频条件 | 大致准确率 | 主要问题 |
|---|---|---|
| 单人麦克风,安静环境 | 95%以上 | 偶尔专业术语错误 |
| 会议室单麦克风,正常讨论 | 85%-90% | 远场发言识别率下降 |
| 会议室单麦克风,激烈讨论 | 75%-85% | 重叠发言导致说话人混乱 |
| 手机录音,有背景噪音 | 70%-80% | 噪音干扰明显 |
这个数据只是我个人的测试结果,实际准确率会因会议内容、口音、语速等因素有较大差异。但整体趋势是明确的:音频质量决定识别上限。
7.2 时间成本对比
| 处理方式 | 一小时会议的处理时间 |
|---|---|
| 人工听打 | 3-4小时 |
| 人工听打+校对 | 4-5小时 |
| 本方案自动识别 | 5-10分钟 |
| 本方案+人工校对 | 30-40分钟 |
自动识别把时间压缩了90%以上,即使加上人工校对,效率提升也非常明显。
7.3 成本对比
以一小时会议为例:
| 项目 | 成本 |
|---|---|
| 人工听打(按市场价) | 100-200元 |
| 本方案识别费用 | 几元 |
| 本方案+人工校对 | 几十元 |
对于需要频繁整理会议记录的团队来说,这套方案的性价比很高。
8. 后续可以继续优化的方向
8.1 实时字幕与录音识别的结合
目前这套方案是会后处理,如果线上会议需要实时字幕,可以接入腾讯云的实时语音识别。实时识别出初步结果,会后用录音文件识别出精校版,两者互补。
8.2 与会议系统的深度集成
如果团队用的是固定的线上会议系统,可以研究一下是否有API可以自动获取会议录音,省去手动上传的步骤。不过这个取决于会议系统是否开放接口。
8.3 知识库沉淀
识别出来的会议纪要可以进一步结构化,比如按项目、按议题、按决策类型分类,沉淀到团队的知识库里。这样以后回溯某个决策的时候,可以直接搜索到相关的会议记录。
8.4 多语言支持
腾讯云语音识别支持多种语言和方言。如果团队有跨语言沟通的需求,可以在提交任务时指定不同的EngineModelType。比如英语会议用16k_en,粤语会议用16k_yue。
这套方案我自己跑了大半年,从最初的每次手动处理,到现在基本全自动化,省下来的时间相当可观。最关键的体会是:音频预处理和热词表这两个环节,投入产出比最高。花十分钟把音频转成标准格式,花五分钟配置热词表,能让识别准确率提升一大截。至于后处理和摘要,可以根据团队的实际需求逐步完善,不用一开始就追求大而全。