我平时最怕的不是开会,而是开完会以后翻笔记,发现自己什么都没记下来。通勤路上想复盘只能凭印象拼大段对话,想找某个人上个月的承诺翻了三个文档也找不到原话。后来我干脆做了一个“会议小帮手”:录音、上传、自动转写、按说话人分段、带时间轴回放。底层识别引擎用的就是腾讯云 AI 语音识别。这篇不聊太宏大的架构,只讲我实现这个小工具的真实过程——从接口选型到踩坑,从录音格式到热词调优,适合正在做会议纪要、访谈转写、播客整理这类需求的朋友参考。
1. 为什么我给会议做了个“自动记录员”:痛点与功能边界
1.1 记不全、听不懂、找不到:会议记录的三个痛点
先说一个很实在的场景。每周的例会大概40分钟,涉及三个模块的进度同步、两个待办确认、一个延期解释。如果是线下开会,我还能边听边记关键词;一旦改成线上会议,我基本只能盯着屏幕,手根本腾不出来。等会议结束打开笔记本,上面只有几个零散名词:二期、接口、周五,完全拼不出完整逻辑。
第二个痛点是专业术语。我们业务里经常出现英文缩写和内部代号,比如某些业务单元名称、版本号、功能英文名。我自己听得懂是一回事,但转述给别人的时候经常卡壳,原话到底是谁说的、怎么拼的、出现在哪一分钟,全靠模糊印象。很多次为了确认一句话,还要回去翻聊天记录和邮件,效率很低。
第三个痛点是“事后检索”。项目做到第三个月,要追溯某次会议里定下的责任人和截止时间,才发现过往会议的录音散落在聊天文件、手机录音、云盘的各个角落,文件名都是“语音0701.m4a”这种。遇到这种情况,就算找到录音,40分钟听完才能确认一句话,成本实在太高。开会记录、长期检索、内容沉淀,本质上这不是工具问题,是信息管理能力的问题。
1.2 工具最终长什么样:录音、转写、整理三步闭环
基于上面三个痛点,我给“会议小帮手”定了一个比较朴素的闭环:录音文件进来,转写文本出去,再按说话人和时间轴整理成可检索的会议纪要。
具体功能可以拆成三层。采集层负责录音,我用手机自带录音或电脑会议的录制文件都能作为输入;转写层是核心,调用腾讯云 AI 语音识别的录音文件识别接口,输出带时间戳和说话人标签的逐句文本;整理层做格式化,把“谁(SpeakerId)在什么时间(StartMs/EndMs)说了什么(Text)”渲染成一份带标题、段落、高亮要点的页面,方便每个人复盘和检索。
这里要说明一下,这个工具不是用来替代人工记录的,而是做“原始语料沉淀”。机器转写结果天然存在错误率,不可能完全替代人类判断,但它能保证“事实原话”不会再丢。哪怕转写错了,听众也能根据大致内容回到音频里去二次确认,这就比“完全没有记录”好非常多。我最初的目标就是把“无记录状态”变成“有记录可查”。
1.3 先定边界再写代码:不做实时字幕,先做离线转写
动手之前还有个选择:要不要做实时字幕?会议室大屏上实时飘字幕看起来确实酷,但落地成本完全不同。实时转写需要处理好流式连接、半句结果、麦序冲突和网络抖动,工程复杂度比离线文件转写高一截。而且对于大多数会议复盘场景来说,会后10分钟出完整文本完全够用,没必要为了“实时”而实时。
所以第一版我明确画了一条边界:先做离线转写,不做实时字幕;先支持单音频文件,不追求多通道麦克风矩阵;先保证中文普通话准确率,不做中英混说的特殊优化。这条边界帮我省掉了一堆不必要的复杂度,也让我能把精力集中在一件事上:把腾讯云 AI 语音识别接口用对、用透。后面我发现这个“先做减法”的决策特别重要,因为识别接口本身就有不少参数坑,如果一开始再叠实时语音识别,排查问题的难度会翻倍。
2. 腾讯云语音识别的能力怎么选:离线转写与实时流式两条路
2.1 三种接口的适用场景对比
腾讯云 AI 语音识别对外提供的能力可以按交互方式粗分成三类:一句话识别、录音文件识别、实时语音识别。我第一反应是用实时识别啃下所有场景,但认真对比后发现各有分工,不是越“实时”越高级。
| 能力类型 | 交互方式 | 适合场景 | 响应特点 | 对会议的适配度 |
|---|---|---|---|---|
| 一句话识别 | 一次性上传短音频(≤15秒左右) | 语音搜索、指令控制、语音红包 | 秒级返回一次性结果 | 基本不适合长会 |
| 录音文件识别 | 提交音频URL或BASE64,异步返回任务结果 | 会议录音、访谈、客服质检、播客转写 | 分钟级出全部结果 | 最适合复盘场景 |
| 实时语音识别 | WebSocket流式上传,边录音边识别 | 实时字幕、同传、会议中投屏 | 边说边出中间结果 | 适合直播场景但工程量大 |
这三类我在选型时给自己提了一个问题:“会议小帮手”最需要的上下文是什么?是整场会议的完整脉络。一句话识别把音频切成碎片,丢失了前后文;实时识别强调的是时效性,而不是完整内容;录音文件识别天然输出整场会议的全部句子,还带时间轴和说话人信息。所以离线转写这个底座,从一开始就比较稳。
2.2 为什么最终以录音文件识别为底座
录音文件识别接口的核心逻辑是异步任务:你提交一个音频地址,服务端排队处理,过一会儿再查询结果。这种异步模式让它可以承载长时间音频,处理几十兆到几G的文件都很稳。我在实测中提交过一个多小时、约90MB的会议录音,从提交到出结果大约两分钟,体验可以接受。
另一个重要原因是它能输出结构化结果。识别结果不只是“一整段文本”,而是拆成逐句列表,每一句都有开始时间、结束时间和说话人标识。这些字段是后续做会议纪要页面、做聚合统计的基础材料。比如我可以直接统计某个人发言了多长时间、哪个时间段讨论最密集,这样信息维度就上来了。
当然,用录音文件识别也有代价,就是不能边会议边看字幕。我一度焦虑这一点,后来想通了:apple 无纸化不是目的,能把信息沉淀下来才是目的。会后的完整转写文本,在日常检索场景里有不可替代的价值,所以我坚持“离线为主”。项目稳定运行一段时间后,如果确实需要实时字幕作为辅助,再另起一套实时方案也不迟。
2.3 引擎模型、采样率、音频格式:先搞懂这几个关键概念
进入代码之前,得先解释几个识别接口绕不开的概念,特别是对小白读者。我第一次对接时就在这些术语上绕了不少时间。
引擎模型,也就是EngineModelType参数,决定了识别引擎面向的音频特征。会议场景一般用16k_zh,也就是16kHz采样率的中文模型。16kHz不代表更清晰,识别引擎只要“够用”的频段,电话语音8kHz就够,开会用16k比较合适;如果音频是44.1kHz的CD音质,可以先降采样到16k,既能降低文件大小和队列等待时间,也不会降低识别效果。
采样率,简单说就是每秒采样多少个点来记录声音。44.1kHz和16kHz听起来差别不大,但16k文件的体积只有前者的三分之一左右。转码工具推荐用FFmpeg,一句命令解决:ffmpeg -i input.m4a -acodec pcm_s16le -ar 16000 -ac 1 output.wav。识别服务对格式也很挑剔,优先推荐WAV或PCM,其次是压缩格式;很多莫名其妙的失败都是音频格式不规范导致的。
还有一个重要概念是声道数。网上常见的双声道立体声录音听起来效果好,但识别模型大多针对单声道优化。多声道音频最好先合并成单声道再做识别,这个细节我后面单独讲,因为它影响说话人分离的准确率。总之,音频预处理的思路是:统一到16k采样率、单声道、WAV或PCM,这是最省心的识别输入。
3. 录音文件识别(离线转写)从申请到出稿的完整流程
3.1 开通服务与密钥准备
实操第一步是开通云语音识别服务,拿到密钥。这个过程不难,但有两个小细节值得注意。一个是密钥权限,项目里最好单独建一个子账号,只授予语音识别相关接口权限,不要直接把主账号的密钥写在代码里。另一个是地域,不同地域的API入口可能不一样,录音文件识别目前在实际使用中一般选广州地域(ap-guangzhou),其他地域的兼容性需要以官方文档为准。
密钥拿到手之后,我不建议自己手写签名逻辑。腾讯云API 3.0的签名流程是TC3-HMAC-SHA256,手动实现要处理时间戳、拼接字符串、哈希计算好几步,我第一次手写花了大半天,后来才发现官方提供了Python SDK,直接封装好了签名和重试逻辑。项目里用SDK比自己拼HTTP请求省事得多,也少踩不少“签名过期”“SecretId格式不对”之类的坑。
准备工作完成后再确认一下账号是否开通了对应服务,避免首次请求被拦截。很多老手容易被“开通服务”这一步坑到:界面有个明显的开通入口,但开通之后并不是立刻生效,个别账号需要等几分钟到十几分钟。建议提前开通,不要等到项目联调时才发现没有权限。
3.2 提交转写任务:CreateRecTask的几个参数
录音文件识别入口是CreateRecTask接口,异步提交任务并返回TaskId。核心参数我用一张表列出来,都是我实际调过的:
| 参数名 | 我使用的值 | 含义与说明 |
|---|---|---|
| EngineModelType | 16k_zh | 引擎模型,会议中文场景常用 |
| ChannelNum | 1 | 声道数,1为单声道,多声道需先混音或拆分 |
| ResTextFormat | 1 | 0为纯文本,1将数字转成阿拉伯数字写法 |
| SourceType | 1 | 1表示传URL,0表示BASE64上传 |
| Url | 公网可访问地址 | 音频文件地址,SourceType=1时必须填 |
| Data | BASE64串 | SourceType=0时填,适合小文件 |
| SpeakerDiarization | 1 | 开启说话人分离,会议场景强烈建议开 |
| SpeakerNumber | 3 | 预估说话人数,需大于实际人数一个以上 |
| FilterPunc | 1 | 过滤标点符号,对纪要页面不是必需 |
传URL有个容易忽略的前提:URL必须是公网可访问的,内网地址提交上去任务会一直失败。我第一版就把音频传到内网测试服务器,结果所有任务都卡在“处理中”,排查了半小时才发现是URL可达性问题。如果用BASE64上传,文件长度限制比较严格,常规会议录音一般超过限制,所以实践中我会把录音传到对象存储,再拿对象存储地址提交识别。
SpeakerNumber这个参数也值得一提。它不是实际发言人的精确数量,而是模型声学聚类的一个参考值。我踩过的坑是会议室里只有3个人说话,但SpeakerNumber只填了2,结果识别引擎把一人的发言强行拆成两段。建议预估人数时比实际人数多填1到2个,多出来的类别会在后处理时合并掉,比少填更稳妥。
3.3 查询任务状态与解析结果
任务提交后返回TaskId,接下来就是轮询查询。用的接口是DescribeTaskStatus,传入TaskId,返回状态码和结果数据。状态码常见三个:0表示排队中,1表示识别成功,2表示识别失败。查询间隔我设置为2秒一次,前几次查询基本是排队状态,等状态变成1后,一次性把Result拉下来存好。
Result结构里有一个ResultText字段,这是整场会议的完整文本,适合快速预览;更有价值的是Sentence列表,每个元素包含StartMs、EndMs、SpeakerId、Text四个核心字段,分别代表本句起始毫秒、结束毫秒、说话人编号、句子内容。我拿到Sentence列表后,会把它存成JSON,方便后续页面渲染和检索。
失败状态的处理也要注意。状态为2时,返回的ErrorMsg会给出失败原因,常见有音频格式不支持、文件过大、URL不可访问等。我的程序里会给ErrorMsg单独做个告警,这样即使凌晨跑批量转写,第二天检查日志也能一眼定位问题。轮询逻辑可以用循环加sleep实现,也可以放到后台队列里慢慢查,不影响主流程。
3.4 把流程串起来的完整示例
下面是我整理的完整调用流程,基于官方Python SDK,代码按实际使用简化过。核心就是三步:初始化客户端、提交任务、轮询结果。
import time from tencentcloud.common import credential from tencentcloud.asr.v20190614 import asr_client, models # 1. 初始化,这里建议用子账号密钥 cred = credential.Credential("你的SecretId", "你的SecretKey") client = asr_client.AsrClient(cred, "ap-guangzhou") # 2. 提交录音文件识别任务 create_req = models.CreateRecTaskRequest() create_req.EngineModelType = "16k_zh" create_req.ChannelNum = 1 create_req.ResTextFormat = 1 create_req.SourceType = 1 create_req.Url = "https://your-bucket.example.com/meeting_20250101.wav" create_req.SpeakerDiarization = 1 create_req.SpeakerNumber = 4 create_resp = client.CreateRecTask(create_req) task_id = create_resp.Data.TaskId print("TaskId:", task_id) # 3. 轮询查询转写结果,间隔2秒 query_req = models.DescribeTaskStatusRequest() query_req.TaskId = task_id result_data = None while True: query_resp = client.DescribeTaskStatus(query_req) data = query_resp.Data if data.Status == 1: # 识别成功 result_data = data break elif data.Status == 2: # 识别失败 raise RuntimeError("识别失败: %s" % data.ErrorMsg) time.sleep(2) # 排队中,继续等待 # 4. 输出关键结果 result = result_data.Result print("完整文本:", result.ResultText) for sentence in result.Sentence: print(f"[{sentence.StartMs}-{sentence.EndMs}ms] 说话人{sentence.SpeakerId}: {sentence.Text}")这段代码跑通之后,“会议小帮手”的雏形就算出来了。剩下的工作就是围绕这个核心流程做工程化封装,比如批量处理录音文件、存储结果、生成会议页面。核心代码长度不过几十行,但它在实际跑任务时能稳定出结果,比一堆臃肿的界面代码实在得多。
4. 实时语音识别接入的难点:WebSocket流式通信与VAD节奏控制
4.1 为什么会议场景最终还是要接实时识别
离线转写跑通之后,我开始认真考虑实时识别的需求。最大的驱动来自“边开会边查词”的场景:有同事说了一个新项目代号,与会者想立刻知道是哪几个字,离线转写要到会议结束才能看到,而实时识别可以在发言同时打出中文字幕,所有人能当场确认。这个功能对一些内部术语较多的团队来说,确实能改善沟通效率。
另一个驱动是敏捷性。会议进行到一半,如果某部分讨论特别重要,我希望立刻标记“从第几分钟开始是重点”,而不是会后在长文本里翻找。实时识别的中间结果配合我的标记动作,能实现“边开会边给重点画线”的效果。这个体验和纯粹会后转写完全不同,更像一个辅助人类理解的工具,而不是历史记录机。
不过我要先说明:实时识别的结果通常带大量中间修正。人说话是连续的,机器需要边听边给它分词、断句、纠错,所以同一句话可能先输出半截,再补充成完整句。如果直接把这些中间结果投到屏幕上,观众会觉得字幕一直在跳动。这对系统的容错和展示策略提出了更高要求,不能用离线转写的思路来写实时代码。
4.2 握手与消息格式:一个请求连接说清楚
实时语音识别走的是WebSocket协议,整体流程比较清晰:建立连接、发送音频帧、服务端持续返回中间结果、关闭连接。连接地址上会带一长串参数,包括密钥信息、引擎模型类型、是否需要中间结果等。第一次看官方文档会觉得参数很密,我用下来真正核心的就是几个:音频格式、采样率、是否需要长连接。
连接建立完成后,客户端以二进制帧的形式持续发送音频数据。这里有个对很多人来说不太直观的点:WebSocket理论上也可以发文本,但音频必须是二进制帧;发送节奏要尽量贴近录音的实时速率。比如你录音设备每20毫秒产生一小块PCM数据,那发送间隔就应该和录音速率匹配,不能一次性把整块音频全推过去,否则服务端可能来不及处理。
结束时也要规范:先发完最后一小块音频,再发送一个约定好的结束指令(通常是JSON消息,里面只有type字段标志结束),然后等待服务端返回最终结果,最后才断开连接。这样服务端才能把上下文收尾,输出完整的最终句子。很多新手直接在录音停止后立刻断开WebSocket,结果最后几秒的内容丢失或没有产出最终结果,这是实时识别最常见的坑。
4.3 VAD与静音截断:控制“说完了吗”和“还在说”的边界
实时识别的流畅体验,很大程度取决于对语音端点的处理。专业点叫VAD(Voice Activity Detection),通俗点说就是让系统判断“这句话说完了没”。常见误区是有人在会议里停顿几秒翻材料,客户端就把这当成一句话的结束,导致句子被硬切;反过来,如果VAD太保守,连续几句话又会糊成一大段,影响阅读。
我的做法是“本地判断+服务端判断”双保险。客户端侧先做一个简单的静音检测:当检测到持续静音超过800毫秒时,不立即切断,而是启动一个“疑似结束”计时器;之后再听到新语音,就认为话还在继续,合并到当前段落;如果计时器超过1.5秒仍然没有新语音,才判定句子真正结束,发送结束指令。这种做法可以让中断概率大幅下降。
服务端也有自己的VAD逻辑,客户端不能太激进。我在实际联调中把静音阈值从500毫秒调到1000毫秒,误切率明显降低,但实时性变差了一点。这个阈值的取舍取决于会议场景:领导讲话停顿多,可以调大一点;两个同事交互频繁,停顿短,可以调小一点。我最后的经验是,宁可让句子略长,也不要切碎,因为后续人工校对长句比拼接碎句容易得多。
4.4 实时字幕的降级方案:先给原文,会后换精修
实时识别和离线转写的准确率存在明显差距。离线识别有整段上下文,可以反复修正;实时识别只能靠局部信息,必然有更多错误。这让我想通了一个方案:实时字幕和离线转写不要互斥,而是做成“先粗后精”的流水线。
会上投屏展示的字幕,只作为“当下辅助理解”的粗稿,允许出现个别错字。等会议结束音频上传后,离线转写任务自动运行,几分钟后生成精修文本,再覆盖掉之前展示的粗糙字幕。这样既满足了实时需求,又保住了最终纪要的准确率。用户看到最终文本时,不会在意它是怎么一步步生成的。
这个方案实现起来也不复杂:实时识别过程中,所有中间结果存入Redis,带上voiceId标识;会议结束后,录音文件触发另一个离线识别任务;精修文本生成后,覆盖同一个voiceId对应的内容。前端展示层不需要感知背后的替换逻辑,只要每次拉取数据时检查是否有“已精修”标记即可。我管这个叫“粗字幕占位、精修稿转正”,非常实用。
5. 实际使用中踩过的一串坑:格式、时长、热词与说话人分离
5.1 首当其冲:录音格式与转码的坑
第一坑就是格式。手机录音常见m4a,电脑录屏常见mp4,会议软件常导出mp3,这些格式不是不能识别,但兼容性不如WAV和PCM。我在批量处理一批录音时,出现了一个奇怪的规律:mp3文件的识别成功率低于wav文件,偶尔还会报“音频解码失败”。排查半天,发现有些mp3文件的编码参数不规范,服务端解码器不认。
解决方式很简单,统一转码。我写了段Python脚本,用FFmpeg把目录下所有音频都转成“16k采样率、单声道、PCM编码的WAV”再上传。转码之后问题基本消失,识别成功率稳定了很多。这个预处理步骤看起来多一道流程,实际能把后续的失败率压到最低,强烈建议在项目初期就固定下来。
这里还得提一个容易被忽略的点:识别时长和计费。同样的音频,转码成16k单声道后体积变小,上传时间和存储成本也下降。如果直接把44.1kHz的双声道立体声WAV传给识别服务,不仅没带来多少准确率提升,反而增加排队时间。识别引擎关心的是语音内容特征,而不是音乐级音质,这一点我一开始也想反了。
5.2 长音频分段:单次识别时长限制与自动切分
会议录音动辄一小时以上,虽然录音文件识别支持长音频,但单次任务通常有文件大小或时长的隐含上限。我在测试某次两小时培训录音时,任务提交成功后一直排队,最后报“时长超限”。为了避免这种情况,我加了分段机制。
分段策略是按时间切,比如每30分钟一段。切割工具还是FFmpeg,关键是切割点要选在静音处或说话间隙,尽量避免从词语中间拦腰截断。实际操作时,我先用FFmpeg静音检测命令找出静音段,然后在静音段中间切一刀,这样每段的开头和结尾都相对干净,不会出现“上半句在一段、下半句在另一段”的问题。
分段之后还有个处理要点:后处理时要复用每个分段的SpeakerId,但我们知道不同分段的编号可能会错位,第一段的“说话人1”未必是第二段的“说话人1”。我的办法是提交分段任务时都开启说话人分离,然后在合并阶段做一个简单的聚类匹配。匹配特征可以用语音时长比例和出现位置做启发式判断,也可以用更复杂的方式按声纹比对,这里不做展开。总之,分段合并是整个应用里最容易乱的一环,建议尽早设计好映射规则。
5.3 热词为什么“没用”:热词表要用对姿势
“热词”是我早期最期待的优化手段,但实际效果一度让我怀疑人生。我们团队经常提到一个内部系统代号,比如“星辰平台”,转写结果永远是“星辰平台”的谐音错别字。我按照文档要求创建了热词表,提交任务时也填上了VocabId,可第二次识别还是错的。仔细检查后,发现我把热词表放在“全局生效”的位置,但文档明确说明热词是需要通过特定接口以词表形式绑定到识别请求里的,不绑定等于没用。
正确姿势是:先调用热词表创建接口,把需要强化的短语以词表形式传进去,拿到一个热词表ID;然后在提交CreateRecTask时,把该ID填到VocabId参数里。热词数量不是越多越好,每个词表控制在几百条内效果比较稳定;同一个词表适用于常用场景,不需要每次识别都重建。
还有一个很重要但很少被提及的点:热词对“标准发音”的词汇提升最明显,但对带方言口音的词条帮助有限。比如“星辰平台”大家都用标准普通话读,热词能拉回来;如果某个流程名带着浓重方言味,热词表也可能救不回来。我后来做了一层兜底:转写完成后,在文本后处理阶段用关键词替换规则把已知专名强制纠正一遍,效果稳定多了。热词负责“从源头救回”,后处理负责“兜底再捞一遍”。
5.4 说话人分离的误差来源与缓解手段
说话人分离是会议场景的核心功能,但它的准确率得看“声音条件”吃饭。安静会议室、三人轮流发言、麦克风距离相近,分离效果很好;一旦出现两个人同时说话、或者一个人离麦很近而另一个人离麦很远,分离就很容易出错。
我的缓解手段有三个层次。第一,预处理阶段把音频压成单声道时要小心,如果原始录音是“分轨”的,也就是不同麦克风录到不同声道,不要盲目标成单声道,否则会丢失分离线索。这种情况下可以考虑提交双声道识别参数,让引擎基于轨道差异做区分。可惜我们常见录音不是分轨的,这一招有时使不上。
第二,SpeakerNumber的取值技巧,前面已经说过,宁可多填不可少填。多填出来的类别在人工后处理阶段能手动合并,但少填会让不同说话人的声音被强行揉到一起,基本没法拆。第三,后处理阶段增加一个“按段落连续性校验”的逻辑:如果同一SpeakerId的发言时长忽短忽长、且文本内容看不出连续性,就标记为“疑似切错”,方便人工复核。这里必须坦白,说话人分离不可能做到100%精准,我的工具定位是辅助人工,而不是替代人工判断。
6. 从“转写字幕”到“会议纪要”:结果结构化与后续扩展
6.1 用时间戳和SpeakerId重组发言段落
拿到了逐句识别的Sentence列表,会议纪要的骨架就有了。我的做法是先按SpeakerId分组排序,统计每个说话人的发言时间、句子数、发言时长占比,生成一个“发言概况”模块。这个模块能直观反映一场会议上谁主导讨论、谁基本在旁听,对复盘会议效率很有参考意义。
接着我按时间顺序渲染完整时间轴:每一句前面带上“00:12:34”的时间戳和“发言人A”标签,点击时间戳可以直接跳转到录音对应位置。这个交互特别有用,因为转写文本偶尔有错时,听众能马上回到原声确认,形成“听读对照”。实现这套交互不难,大部分前端播放器都支持按秒定位播放。
最后是全文检索。转写文本存进一个简单搜索引擎之后,“找某句话”的成本从打开录音反复拖拽,变成一次秒级关键词查询。配合时间戳跳转,整个“回溯承诺、核实数据、回顾讨论”的链路就封闭了。到这里,“会议小帮手”才真正从“录音转文字”工具升级成了会议知识库的入口。
6.2 轻量级的要点抽取规则
很多人会觉得生成会议纪要要点必须用大模型,其实在转写文本基础上,可以先用轻量规则拿到七成的有效信息。我的规则组合是:高权重关键词命中、发言时长阈值、动词开头的动作短语、时间相关词(比如周五、下周一、月底)。前两项容易理解,后两项需要说明一下。
会议中有价值的表述通常包含动作和责任,比如“我来负责接口联调”“周五之前给排期”。这类句子一般以第一人称代词加动词开头,长度适中。我写了一套简单的规则模板去匹配,命中的句子自动提到“行动项”分组。准确率大概七成,剩下的三成通过人工确认即可。大模型摘要适合作为后续增强手段,但要控制成本,不能每条录音都跑一遍大长文摘要。
用规则而不是大模型的另一个原因是稳定。规则结果高度可复现,同样的输入永远得到同样的输出;而大模型每次生成结果可能不同,给用户带来困惑。我并不是排斥大模型,而是建议先建立结构化信息底座,再考虑是否叠加智能摘要。处理完“谁在什么时间说了什么”之后,再上摘要也只是锦上添花。
6.3 后续可以往哪些方向扩展
第一版稳定运行后,我规划了几个扩展方向。方向一是和日历打通,会议结束自动创建纪要文档,关联会议邀请中的参与者;方向二是加“重点标记”入口,开会时随手双击某个时间点就能插入一个mark,纪要里自动展示高亮段落;方向三是多语言支持,目前只处理中文,后续可以接中英混说引擎模型,覆盖外企团队的会议需求。
还有一个思考是隐私问题。所有音频上传到云端转写,对某些团队来说是敏感操作。我的做法是把数据尽量保存在自己的存储桶,并设置生命周期规则,一段时间后自动清理原始音频,只保留转写文本。这里需要提醒使用云端语音识别服务的朋友:处理好数据边界和留存策略,是最容易被忽略但非常重要的环节。
如果沿着这个方向继续做下去,整个项目会越来越像一个轻量级的智能会议知识库。不过我始终提醒自己,工具的核心价值是“把原话留住”,其他都是加分项。这个判断标准,让我在后续添加各种小功能时不会跑偏,也让我愿意一直维护这个小帮手。
最后分享一个实操体会:语音识别类项目,真正难的不是调用接口,而是把输入音频整理干净、把输出结果结构化、再把前后步骤串成完整闭环。先用最简单的思路跑通一遍离线转写,再去考虑实时能力和复杂报表,推进会顺利得多。希望这篇记录能帮正准备做类似会议录音整理工具的朋友少走几个弯路。