日期: 2026-05-31
影响范围: Hermes 团队助手 (The Cutting Edge · OMS) 飞书群聊
现象: DM 私聊可正常触发机器人回复,群聊中 @机器人 无任何反应
1. 问题表现
- DM 私聊: ✅ 正常。@机器人发送消息 → Hermes 收到事件 → 调用 DeepSeek → 返回回复
- 群聊: ❌ 完全不通。@机器人发送消息 → 无日志、无事件、无响应
- Gateway 日志显示 WebSocket 连接成功:
connected to wss://msg-frontier.feishu.cn/ws/v2 - 飞书开放平台侧检查:事件订阅
im.message.receive_v1已开启,权限im:message.group_at_msg已授权,应用已发布
2. 排查过程
2.1 第一轮:WebSocket 原始帧日志
在 lark_oapi/ws/client.py 的 _handle_data_frame() 方法中添加文件日志,记录所有 WebSocket 数据帧:
with open('/tmp/feishu_ws_frames.log', 'a') as f:
f.write(f"{time.time()}|type={message_type.value}|msg_id={msg_id}\n")
结果: DM 消息能捕获到 type=event 帧,但群聊消息完全无帧。初步怀疑飞书 msg-frontier 节点未将群聊事件路由到此 WebSocket。
2.2 第二关:网关重启后日志丢失
Gateway 因故重启后,发现 /tmp/feishu_ws_frames.log 为空。原因为 lark_oapi 的日志是永久写入库文件的(非内存注入),但在排查过程中 gateway 多次重启,消息恰好落在重启窗口内。
2.3 第三轮:Hermes 层事件日志
在 feishu.py 的 _on_message_event() 入口添加日志,捕获经 lark_oapi SDK 解析后到达 Hermes 的事件:
with open("/tmp/feishu_ws_frames.log", "a") as f:
f.write(f"{time.time()}|type=message_event|chat_type={...}|...\n")
结果: 日志仍为空 → 消息事件根本没到达 _on_message_event。
2.4 关键发现:群聊策略默认值
经代码审查发现:Hermes 的 Feishu 适配器中,FEISHU_GROUP_POLICY(对应配置键 group_policy)默认值为 allowlist。
当策略为 allowlist 时,必须配合 FEISHU_ALLOWED_USERS 白名单使用。team 配置中此白名单为空 → 所有群聊消息在到达 _on_message_event 后被静默丢弃。
DM 不受此策略影响,因此 DM 一直正常。这也解释了为什么 WebSocket 原始帧(lark_oapi 层)能捕获 DM 事件——帧在 SDK 层已到达,但在 Hermes 层被群聊策略检查拦截。
3. 根本原因
FEISHU_GROUP_POLICY 默认值 = allowlist
FEISHU_ALLOWED_USERS 未设置 = 空
→ 群聊消息全部被 Hermes 群聊策略检查静默丢弃
→ DM 绕过群聊策略检查 → 正常
不是飞书平台问题,不是 WebSocket 连接问题,是 Hermes 配置缺失。
4. 解决方案
在 ~/.hermes/profiles/team/config.yaml 中添加:
feishu:
enabled: true
extra:
app_id: cli_aa916b6465b9dcd9
app_secret: xxx
require_mention: true
group_policy: open # ← 新增此行
可选值:
open— 允许所有群聊消息(团队助手推荐)allowlist— 仅允许白名单用户(需配合FEISHU_ALLOWED_USERS)
5. 清理
问题解决后,清理了所有调试代码:
| 文件 | 清理内容 |
|---|---|
gateway/platforms/feishu.py |
移除 _on_message_event 中的临时文件日志 |
lark_oapi/ws/client.py |
移除 _handle_data_frame 中的文件日志 |
/tmp/feishu_ws_frames.log |
删除临时日志文件 |
6. 经验教训
- 先查配置默认值。 很多"莫名其妙的静默行为"来自默认策略过于保守。
- DM 通 ≠ 配置正确。 不同消息通道在代码中走不同的检查路径。
- WebSocket 帧 ≠ 能被处理。 即使 SDK 层收到了帧,上层可能还有策略过滤。
- 永久写库文件的风险。 在 venv 的第三方库中写文件日志,升级库时会被覆盖。排查完应及时清理。