Google 3月底甩出Gemini 3.1 Flash Live,主打"让音频AI更自然可靠"。实时双向语音、低延迟、可打断、多语言——参数表漂亮得像产品经理的PPT。
但我的LINE Bot项目(linebot-helper-python)正面临一个尴尬现实:它能处理文本、图片、URL、PDF、YouTube,唯独对语音消息装死。用户发语音,Bot沉默。
这次要补上这块拼图,顺便记录几个踩过的坑。
坑一:用直播相机拍照片
Gemini 3.1 Flash Live的设计场景是实时流式对话,但LINE的语音消息是预录制的m4a文件。两者就像直播相机和单反——都能成像,但工具逻辑完全不同。
用Flash Live处理预录音频,相当于拿着电影机去拍证件照。技术上能跑通,但属于错配。
最终方案:标准Gemini API,直接把音频字节作为内联数据(inline data)传入,一次调用拿回转录文本。更简单,也更贴合场景。
坑二:一次回复 token 的极限操作
项目已有完整的Orchestrator架构,能根据消息内容自动路由到不同Agent(Chat、Content、Location、Vision、GitHub)。语音功能的目标很明确:
转文字 → 当成普通文本塞进Orchestrator → 现有功能自动支持语音输入。
用户说"帮我搜附近加油站" → 转录成文本 → Orchestrator识别为位置查询 → LocationAgent处理。不需要为语音单独写逻辑。
但LINE的回复机制有个陷阱:每个消息只有一个reply token,用掉就没了。而用户需要看到两步反馈——先确认"我听懂了你说的话",再等Orchestrator返回最终结果。
解法:拆成两次回复。第一次用reply_message()消耗token,展示转录文本;第二次改用push_message(),把转录后的文本重新抛给Orchestrator处理。
代码路径长这样:
AudioMessage → handle_audio_message() → LINE SDK下载音频字节 → Gemini转录(gemini-3.1-flash-lite-preview)→ 回复#1"你说的是:{转录结果}" → 调用handle_text_message_via_orchestrator() → push_message()推送最终结果。
坑三:MIME类型的隐形契约
LINE的语音消息永远是m4a格式,MIME类型固定为audio/mp4。这个细节在文档里不会加粗,但传错就直接报错。
转录函数的核心逻辑很干净:初始化genai.Client,构造Part.from_bytes(audio_bytes, mime_type="audio/mp4"),塞进Content对象,调用client.models.generate_content()。模型选的是flash-lite-preview——比Live版便宜,对预录音频足够用。
完整实现放在tools/audio_tool.py,不到50行。但调试时花了两小时才发现:LINE的iter_content()返回的是异步生成器,需要async for攒成bytes,不能直接await。
一个反直觉的发现
测试时发现,Gemini对中文口语的容错率比预期高。"那个那个帮我查一下附近的那个加油站"这种含混表达,转录后Orchestrator仍能正确识别意图。
但英文混中文的场景会翻车。"Check一下我的GitHub"被转录为"Check一下我的github"——小写的github让GitHubAgent的匹配正则漏掉,最后 fallback 到普通ChatAgent。
修复方式:转录后统一做大小写归一化,再加一层同义词映射。这是语音交互特有的脏活:文本输入时用户会自己修正,语音输入时系统得替用户擦屁股。
目前代码已推送到仓库。下一步想试的是:让Bot主动发语音回复,把Gemini的流式输出接进LINE的音频消息API。但LINE的音频上传有20MB限制,而长文本转语音很容易超——这又是个待填的坑。
你的Bot是怎么处理语音的?是直接调Whisper API,还是也踩过类似的格式陷阱?
热门跟贴