主题
微信语音到底是怎么变成文字的
我在给家里人配一个"发句话就能下电影"的助手。动手前最大的疑虑是:老人只会发语音,不会打字。
按常识,这需要装一个语音识别服务(Whisper 之类)—— 跑起来要几个 G 的模型、要 GPU 才不卡、还要处理微信的专有音频格式(SILK)。成本不低。
结果实测发现:完全不需要。语音已经有文字了。
第一次判断,我错了
我的验证方法是查媒体文件:如果语音消息被接收,应该有一个音频文件落盘。
bash
find media -newermt "-10 min" -type f空。 于是我的结论是"语音消息没被收到"。
这个结论是错的,而且错得很典型:我只查了"文件",没查"消息"。
正确的验证位置
消息通道的日志里,每条入站消息都会打一行,带类型:
bash
grep "inbound message" /tmp/openclaw/openclaw-YYYY-MM-DD.log | tail输出:
inbound message: from=xxx types=3
inbound message: from=yyy types=1types 是消息类型枚举,实测值:0 NONE / 1 TEXT / 2 IMAGE / 3 VOICE / 4 FILE / 5 VIDEO。
所以那两条 types=3 的,就是语音,收到了。 音频没有落盘,是因为根本不需要落盘。
为什么不需要落盘
因为在消息体里,语音已经带上了转写文字。
微信客户端(或者说它的服务端)会做语音识别,转写结果作为消息结构的一部分传上来。用插件源码看得很清楚:
js
// 大致逻辑:正文从消息项里取
function bodyFromItemList(items) {
for (const item of items) {
if (item.type === VOICE && item.voice_item?.text) {
return item.voice_item.text // 直接当正文用
}
}
}后续分支判断也一样:
js
// 只有当语音没有 text、且有可下载的媒体时,才走下载流程
if (!text && hasDownloadableMedia) {
await downloadMedia() // SILK → WAV
}于是链路分成两条:
- 有 text → 直接用文字当用户输入,
hasMedia = false,音频一次都不碰 - 无 text → 下载音频,用
silk-wasm把 SILK 转 WAV,交给 STT(本地命令或云端 API)
实测数据
两条语音消息的日志:
22:51:55 types=3 bodyLen=14 hasMedia=false → 正文「帮我找一下五月天转眼这首歌曲」
22:54:24 types=3 bodyLen=7 hasMedia=false → 正文「今天天气怎么样」第二次我故意没有手动点"转文字"。 文字照样来了。
这一条很重要:说明微信客户端的语音识别是自动的,不是"用户点了转文字按钮才有"。之前我以为必须用户配合,差点设计成"请先点一下转文字"的提示语——那是多余的门槛。
那什么时候才真的需要 STT
两种情况:
1. 语音消息没有 voice_item.text
不同客户端版本、不同平台(iOS/Android/PC),以及某些情况下转写会缺失。这时 bodyLen=0、hasMedia=true,你就得自己转。
2. 音频内容不是"话"
比如你想分析语音里的背景声、语气、音乐——转写出来的是文字,语气没了。
真需要时,方案是可以接的:
json
{
"tools": {
"media": {
"audio": {
"provider": "openai", // 或本地命令
"transcription": {
"command": "whisper --model small --language zh"
}
}
}
}
}但先确认第一条:打开日志看看 bodyLen 是不是 0。如果不为 0,你不需要 STT,你需要的是把已有文字用起来。
顺带一个容易搞错的点
验证时不要用"媒体目录里有没有新文件"当证据。当消息自带文字时,音频根本不会被下载,所以永远不会有文件。 这跟"消息没收到"看起来一模一样。
判断消息有没有到,就看日志的 inbound message 行;判断内容是什么,看同一行的 bodyLen;判断要不要下载媒体,看 hasMedia。
教训
这件事教我的,是一个比较通用的排查原则:
"我没看到证据"和"事情没发生"是两件事。
我第一次的判断链条是:查媒体目录 → 没文件 → 语音没收到。中间那一步"语音本来就不该有文件"我没验证过,却当成前提用。
现在的习惯是:碰到"东西没到",先找到能证明它到没到的那条日志,而不是猜它应该出现在哪里。
一句话版本:微信语音不用装 Whisper——转写文字跟着消息一起来了;去日志看
types=3和bodyLen,别去 media 目录找音频。