- 用
/audio/transcriptions转录一段录音 - 请求带时间戳,而不是每个模型都会给你
- 按 schema 提取决策与行动项
- 处理转录稿里从来不会告诉你是谁在说话这件事
- 切分较长的录音,并且不丢失时钟
准备工作
你需要 Python 3.9 或更高版本、requests 包,以及一个 Venice API key。如果你还没有,请参见生成 API Key。准备一段对话的录音,格式为 wav、mp3、m4a、flac、aac、mp4、ogg 或 webm 均可。
1. 转录这段录音
/audio/transcriptions 与 OpenAI 兼容,接收 multipart 上传。文件必须是真正的 file part,因为这个 endpoint 不接受 base64。
用
GET /models?type=asr 拉取最新列表,而不是把上面这些写死,因为目录会变。
2. 请求带时间戳
时间戳是让会议记录可核实的关键,所以这是最重要的一个选择,而默认情况下并不会给你时间戳:timestamps 是一个对象而不是列表,而里面的 key 名字取决于模型。Whisper 按短语分组,Scribe 按单词分组:
3. 提取会议记录
把你想要的会议记录形式描述成一个 schema,这样返回的就是一条记录,而不是需要你去解析的散文:disable_thinking 出现在这里的原因,和它出现在任何抽取步骤里的原因是一样的。schema 已经决定了答案的形态,让一个推理型模型再针对它反复思考就毫无收获,而且会让每次运行的成本和上一次都不一样。从文档中抽取结构化数据量化了这个差别。
对一段五十三秒的站会跑一下,返回的会议记录带着时钟:
spoken_at 都是真实的。跳到 19.6 秒,你就能听到那句促成这项任务的话。
把不带时间戳的行喂给模型,返回的每个
spoken_at 都会是 0。这个字段是必填的,模型没有东西可以放进去,而一个必填字段是让它生成点什么的指令,而不是让它说”我不知道”的邀请。任何时候一个 schema 看起来在正常工作,都值得记住这一点:形状对了,不等于值就对了。4. 没人被标注
上面那份输出里有两处错误,两处都来自同一个原因。 发布负责人是May。她的名字其实是 Mei。语音识别在专有名词上最不可靠,而名字恰恰是归属所需要的,所以这是你应该预料到的失败,而不是运气不好。
最后一项是 unassigned,尽管显然有人接下了它。那句话是 “I’ll ask legal today and report back tomorrow”,转录稿只记下了这些词,却没记下是谁说的。
第二个问题不是你能修的 bug。Venice 的转录模型都不做说话人分离(diarization),所以任何一个模型上都没有 speaker 字段可以取。转录稿是一整段没有声音归属的文本流,任务只能在有人喊出名字的时候才能被归属出去,比如 “Tomas, can you put the deprecation notice in the changelog”。
第一个问题你可以修,只要告诉模型房间里都有谁:
May 被解析成 Mei Lin,因为模型现在有了一个短名单可以对照,负责人的名字也变成了任务追踪工具能查得到的全名。第三项仍然是 unassigned,这是对的。花名册能修正听错,但没有任何东西能挽回录音里从未承载过的信息。
5. 一次请求装不下
上传限制在 25 MB,未压缩的音频到这个上限的速度比你想象的要快,而且一场长会议本身就值得切开来处理,这样一次失败也不会让你损失整场转录。 对于 WAV 文件,标准库就够了,不需要 ffmpeg:timed_lines 里 offset 参数的用途:
awesome.,把一句话变成了三句。
时间戳仍然是对的,会议记录那一步也仍然能找到这项任务。它丢掉的是那个名字,而归属恰恰依赖名字。
压缩格式没法这样切,因为你没法用标准库在帧边界上切开 MP3。这种情况用 ffmpeg:
把这些串起来
下一步
- 把行动项发到你的任务追踪工具里,用花名册解析出来的负责人姓名。
- 用文本转语音把摘要念给错过会议的人听。
- 把转录稿用嵌入存起来,实现对历次会议的搜索。
- 让一个智能体自行决定什么时候该转录、什么时候直接从已有的会议记录里作答,参见使用函数调用构建能使用工具的智能体。
语音转文本
转录 endpoint 的参考文档。
从文档中抽取结构化数据
同样的 schema 优先抽取方法,用在文件上。
结构化响应
json_schema 如何约束一次 completion。
声音克隆
给摘要一个属于它自己的声音。