Hermes Agent Telegram 语音转文字(STT)问题排查记录:Groq 正常,但 Gateway 未自动触发转写
-
Hermes Agent Telegram 语音转文字(STT)问题排查记录:Groq 正常,但 Gateway 未自动触发转写
一、环境信息
硬件 / 系统
- NAS 环境运行 Hermes Agent
- Docker 容器部署
- s6 作为进程管理
- Hermes Gateway 通过 s6 启动
启动方式:
hermes gateway run --replace
后续调试增加:
hermes gateway run --replace -vv
开启 DEBUG 日志。
⸻
二、目标
希望 Telegram 发送语音消息后:
Telegram Voice
↓
Hermes Gateway
↓
STT 转文字
↓
LLM 理解文本
↓
Telegram 返回回复STT 使用 Groq Whisper API。
⸻
三、最初问题表现
Telegram 发送语音:
日志:
Cached user voice at /opt/data/cache/audio/xxxx.ogg
但是之后没有:
Transcribing user voice
也没有:
Transcribed via command STT provider
最终表现:
- Gateway 收到语音

- 音频缓存成功

- 但是没有自动转写

- LLM 收不到语音内容

⸻
四、首先排查 Groq API
测试结果
Groq API 通过代理可以正常访问。
问题不是:
- API Key 错误
- 网络限制
- Groq 服务异常
原因:
国内环境直接访问 Groq 可能失败,但代理访问正常。
⸻
五、发现 OpenAI SDK 代理问题
Hermes 内部 STT 使用:
OpenAI(
api_key=...,
base_url="https://api.groq.com/openai/v1"
)没有传:
http_client=httpx.Client(proxy=...)
导致:
- curl 可以访问 Groq
- Hermes 内部 OpenAI SDK 不走代理
解决思路:
原本考虑:
- 修改 Hermes 源码增加 http_client
- monkey patch OpenAI SDK
- sitecustomize
- PYTHONSTARTUP
但是最终没有采用。
⸻
六、采用更稳定方案:command STT provider
改为:
stt.provider = groq-proxy
通过:
调用 Groq。
架构:
Hermes Gateway
↓
command STT provider
↓
groq-stt-wrapper.sh
↓
httpx 显式代理
↓
Groq Whisper API⸻
七、验证 Groq STT 链路
手动调用:
transcribe_audio()
成功。
结果:
你好介绍一下你自己
确认:
- Groq

- Proxy

- Wrapper

- command provider

⸻
八、发现新的矛盾
虽然:
transcribe_audio()
成功。
但是 Telegram 语音仍然:
Cached user voice
之后没有:
Transcribing user voice
说明:
问题不是 STT。
问题在:
Gateway 收到语音
↓
调用 STT这一段。
⸻
九、开启 DEBUG 日志
发现:
成功转写日志:
logger.debug()
失败日志:
默认日志级别看不到成功过程。
修改启动:
原:
hermes gateway run --replace
改:
hermes gateway run --replace -vv
确认 DEBUG 生效。
⸻
十、DEBUG 测试结果
发送语音:
Cached user voice (audio_xxxx.ogg)
但是仍然没有:
Transcribing user voice
结论:
Gateway 没有进入:
_enrich_message_with_transcription()
或者:
_prepare_inbound_message_text()
中的 STT 分支。
⸻
十一、进一步定位方向
怀疑:
可能 1
event.media_urls 丢失
流程:
Telegram Adapter
↓
下载音频
↓
event.media_urls
↓
handle_message()可能:
音频缓存成功,但是:
event.media_urls=[]
导致:
if event.media_urls:
不成立。
⸻
可能 2
MessageType 不正确
例如:
收到:
VOICE
但是 event 中:
message_type != MessageType.VOICE
导致跳过。
⸻
可能 3
实际运行路径不同
源码显示:
Telegram Adapter
↓
handle_message()
↓
_prepare_inbound_message_text()但实际运行可能走另一条路径。
需要确认:
- Telegram Voice 实际进入哪个函数
- 调用栈
- event 在哪里丢失
⸻
十二、调试限制
容器权限:
- Hermes 用户运行
- /opt/hermes 部分文件属于 root
- 无 sudo
- 无法直接修改:
/opt/hermes/gateway/run.py
尝试:
cp
dd
tee
shutil.copy2均失败。
⸻
十三、尝试过的方法
失败
- PYTHONSTARTUP
原因:
只对交互式 Python 生效。
Gateway 非交互启动,不加载。
⸻
- .pth 文件
原因:
Python import 路径固定:
/opt/hermes
无法覆盖已安装 package。
⸻
- PYTHONPATH shadow
失败原因:
模块已经从:
/opt/hermes
加载。
⸻
- launcher wrapper
创建:
尝试启动前 patch。
风险:
改变 s6 启动链。
未采用。
⸻
十四、目前状态
已确认:
项目 状态
Telegram 收到语音
音频缓存
Groq API
Groq Proxy
Wrapper
command STT provider
transcribe_audio()
Gateway 自动调用 STT
⸻
十五、下一步计划
需要一次最小源码日志修改。
目标:
只在:
_prepare_inbound_message_text()
增加:
logger.warning("[STT_TRACE] ...")
观察:
- event.media_urls
- event.message_type
- audio_paths
- 是否调用 _enrich_message_with_transcription()
不修改业务逻辑。
⸻
当前核心问题
不是:
- Groq
- 网络
- API
- 权限
- STT provider
而是:
Hermes Gateway 收到 Telegram Voice 后,为什么没有把音频路径传入 STT 转写流程。
希望有熟悉 Hermes Gateway / Telegram Adapter 的朋友帮忙确认:
- Voice 消息正常情况下 event.media_urls 应该在哪里生成?
- _prepare_inbound_message_text() 是否是唯一 STT 入口?
- Telegram Voice 是否存在独立处理路径?
- 是否有配置项会导致 Voice 只缓存、不转写?
感谢。
-
@Bukong Li 排查得很细致,我对 Gateway STT 流程有一些了解,针对你的问题逐一回复:
-
event.media_urls 在哪里生成
Telegram Adapter 收到 Voice 消息时,会在 adapter 层下载音频文件(OGG 格式),存到配置的 cache 目录下,然后把本地路径写入 event.media_urls。你在日志里看到的 "Cached user voice at /opt/data/cache/audio/xxxx.ogg" 就是这个步骤完成的,说明 adapter 层下载成功了。 -
_prepare_inbound_message_text() 是否是唯一 STT 入口
是的,这是 Gateway 层调用 STT 的唯一入口。它内部检查 event.media_urls 是否有内容,如果有则调用 _enrich_message_with_transcription() 进行转写。如果 media_urls 有文件但没触发转写,问题可能出在这个方法内部的判断逻辑上。 -
Telegram Voice 是否存在独立处理路径
没有完全独立的路径。Voice 消息和文字消息都走同一个 Gateway pipeline,区别在于 message_type 不同(voice vs text)。在 _prepare_inbound_message_text() 中,逻辑大致是:如果 event.media_urls 不为空 → 调用 STT → 把转写结果放到 message content 中。所以关键是 media_urls 有没有被正确传入 Gateway 层。 -
可能阻止转写的配置
几个关键配置项:
- gateway 配置中的 stt_enabled: 必须为 true
- stt.provider 必须正确配置(你用的 Groq 没问题)
- telegram adapter 配置中是否有 voice 相关的过滤选项
- 如果你用了 --replace 启动,确认是新的配置文件生效,不是旧配置缓存
你下一步计划加 logger.warning 是最直接有效的排查方式。建议在 _prepare_inbound_message_text() 入口处先打一条,确认 Gateway 收到的 event 里 media_urls 是否为空。如果 Gateway 层没收到 media_urls,问题就在 adapter → gateway 的序列化传递环节。
-