模型切换与飞书集成:一个晨会前的排查

模型切换与飞书集成:一个晨会前的排查
早晨 8:30,晨会还有十五分钟。产品经理在飞书群里喊:"机器人怎么没反应?昨天还能用的。" 我打开终端,开始了一段紧张但有序的排查之旅。问题是:刚切换了模型,飞书集成就罢工了。
这篇文章记录这次排查的全过程,提炼出三个典型故障场景和一套可复用的排查方法。希望能帮到同样在折腾 Hermes + 飞书集成的同学。
场景一:Provider 配置不对——OpenRouter 没配 API Key
先交代背景。Hermes AI Agent 支持通过不同的 provider 调用大模型。我们团队之前一直用 deepseek-chat 直连,因为想试试其他模型,就改成了 OpenRouter——它能聚合几十个厂商的模型,灵活切换很方便。
修改配置后,重启服务,飞书群里发消息——没反应。日志里只有一行报错:
ERROR provider/openrouter.go:42 OpenRouter API key is not set
问题很清楚:provider 切换了,但对应的 API key 没配。Hermes 在 config.yaml 里通过 provider 字段指定使用哪个模型服务商,每个 provider 有自己的 credential 配置。如果配了 provider: openrouter 却没给 openrouter.api_key,Hermes 启动时不会报错(因为懒加载),但实际发请求时就挂了。
model:
provider: openrouter
name: deepseek/deepseek-chat
openrouter:
api_key: sk-or-v1-xxxxxxxxxxxxxxxxx
base_url: https://openrouter.ai/api/v1
openai、deepseek、openrouter、anthropic,每家的配置字段都不一样。如果开了多个 provider 作为 fallback,要确保每个都有有效 key。
修复起来简单:加上 api_key 重启即可。但为什么会忘?因为之前用 deepseek 直连时,API key 是在 deepseek.api_key 字段里的,换了 OpenRouter 就得换字段。这种"隐蔽的依赖"是配置类问题最常见的坑。
场景二:模型 ID 大小写问题——deepseek-chat vs deepseek-Chat
修复了 API Key,重启,发消息——嗯,有反应了,但返回的是 404 model not found。
日志里看到 Hermes 确实发出了请求:
DEBUG httpclient/client.go:87 POST https://openrouter.ai/api/v1/chat/completions
body: {"model":"deepseek-Chat","messages":[...]}
注意 "deepseek-Chat",字母 C 是大写的。OpenRouter 的模型 ID 是大小写敏感的,它只认 deepseek/deepseek-chat 或 deepseek-chat(全小写)。大小写错了就是 404。
我在 config.yaml 里配的是 model.name: deepseek-chat(全小写),但 Hermes 的某个内部处理环节把首字母大写了。这其实是 Hermes 的一个小 bug——它在构建请求 body 时,对模型名做了 strings.Title() 处理。
// hermes/internal/provider/openrouter.go
func (p *Provider) buildRequest(msg []Message) Request {
return Request{
Model: strings.Title(p.config.ModelName), // 这里!
Messages: msg,
}
}
strings.Title()、strings.ToUpper()、strings.ToLower() 之类的转换调用。对于 OpenRouter 这类代理服务,模型 ID 必须逐字精确匹配,大小写差一个都不行。
临时绕过去的方法:配置里写成 deepseek-Chat(首字母大写),让 strings.Title() 恰好命中正确格式。但这是 dirty fix,正确做法是修复代码,去掉大小写转换,或者改用 strings.ToLower() 统一小写。我们是修了代码提了 PR 的。
场景三:飞书集成无回应——三路排查法
前两个问题修完,模型能正常对话了。但飞书群里发消息——还是没反应。这就怪了,API 都通了啊。
这时候不能乱试,得有条理地排查。我总结了一套 "三路排查法",每次都能救命:
第一路:检查哪个进程在跑
先搞清楚当前系统里有哪些相关进程在运行:
# 查看所有 hermes 相关进程
ps aux | grep hermes
# 或者用 systemctl
systemctl status hermes-gateway
好,hermes-gateway 在跑,端口监听正常。但问题依然存在。这就引出了一个关键发现——
实际处理飞书消息的不是 hermes-gateway,而是 openclaw-gateway。 Hermes 的架构里,hermes-gateway 主要负责 HTTP API 和 WebSocket,而飞书/钉钉/企微等 IM 平台的集成是交给 openclaw-gateway 这个独立服务处理的。OpenClaw 是一个"通道层",负责把 IM 平台的 webhook 事件转换成 Hermes 能理解的消息格式。
# 看看 openclaw 在不在
ps aux | grep openclaw
果然——openclaw-gateway 根本没在跑。刚才重启服务时只重启了 hermes-gateway,忘了 openclaw 也需要重启。
飞书 → openclaw-gateway → hermes-gateway → provider(OpenRouter/deepseek)。openclaw 负责接收飞书 webhook,做消息解析和会话管理,然后把结构化请求发给 hermes。任何一端出问题,飞书就没反应。
第二路:检查 config
启动了 openclaw-gateway,但还是没反应?继续查配置。OpenClaw 需要知道自己要连接哪个 hermes 实例,以及飞书相关的凭证:
gateway:
hermes:
endpoint: http://localhost:8080 # hermes-gateway 地址
timeout: 30s
feishu:
app_id: cli_xxxxxxxxxxxxxxxx
app_secret: xxxxxxxxxxxxxxxxxxxxxxxxxx
webhook_path: /webhook/feishu
这里常见的坑有两个:一是 hermes.endpoint 地址错了(比如端口不对),二是飞书的 app_secret 过期了。飞书的应用凭证有效期最长 1.5 年,但如果你重新发布了飞书应用,app_secret 就会变。
还有一个隐蔽的问题:hermes-gateway 换了端口但 openclaw 配置没更新。我们在排查场景一二时调试过 hermes,临时改了监听端口,忘了改回来。openclaw 连不上 hermes,自然无法处理消息。
验证方法很简单:
# 测试 openclaw 能否连通 hermes
curl http://localhost:8080/health
# 如果 hermes 没换端口但 curl 不通,检查 hermes 是否在监听 0.0.0.0(默认)还是 127.0.0.1
ss -tlnp | grep hermes
第三路:检查 OpenClaw 配置
进程在跑,端口对的,config 也没问题,但飞书还是没反应?那问题可能出在 OpenClaw 自身的路由配置上。
OpenClaw 有一个 rules 或 routes 配置段,定义了什么样的消息转发到哪个后端。如果你加了新的飞书 bot 或改了消息类型,但路由规则没跟上,消息就会被丢进黑洞。
routes:
- match:
source: feishu
type: message
action: process
target:
service: hermes
endpoint: http://localhost:8080/api/chat
检查点:
match.source是不是feishu(有的配成了lark,飞书国际版的标识)match.type是不是正确的消息类型(文本消息是message,图片是image等)target.service是不是正确的后端服务名target.endpoint路径对不对(hermes 的 chat API 路径)
我们那次的问题出在 match.type 上——测试时发了张图片,但路由只配了 type: message,图片消息被丢弃了。加一条图片路由就好了。
排查总流程图
关键发现总结
这是架构上最容易误解的点。很多人以为 hermes-gateway 是"网关"所以 IM 消息也在它这处理,但 Hermes 的设计把通道层(OpenClaw)和推理层(Hermes)分开了。排查时先检查 openclaw 在不在。
模型切换不是改一个字段就完事的。Provider 的 API key 要补,模型 ID 大小写要对,而且 hermes-gateway 和 openclaw-gateway 都要重启——因为 openclaw 会缓存 hermes 的配置信息。
任何 IM 集成问题都按这个顺序来,不会漏。第一路看服务在不在,第二路看配置对不对,第三路看消息能不能走到正确的路由。90% 的问题都能在这三步内定位。
附录:排查命令速查
# 1. 检查进程
ps aux | grep -E "hermes|openclaw"
systemctl status hermes-gateway openclaw-gateway
# 2. 检查端口监听
ss -tlnp | grep -E "8080|9090"
# 3. 检查 hermes 健康状态
curl http://localhost:8080/health
# 4. 查看 hermes 日志
journalctl -u hermes-gateway --no-pager -n 50
# 5. 查看 openclaw 日志
journalctl -u openclaw-gateway --no-pager -n 50
# 6. 测试飞书 webhook(从 openclaw 日志里拿 request_id)
tail -f /var/log/openclaw/gateway.log | grep "request_id"
# 7. 直连测试(绕过飞书)
curl -X POST http://localhost:8080/api/chat \
-H "Content-Type: application/json" \
-d '{"message":"hello","source":"test"}'
这次排查从 8:30 到 8:58,赶在晨会前修好了。三个问题里两个是改模型配置时引入的,一个是架构认知偏差。记录下来,希望下次换模型时能省下这半小时。
毕竟,晨会前的每一分钟都很宝贵。