飞书群聊消息无法接收 — 问题排查与解决记录

日期: 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. 经验教训

  1. 先查配置默认值。 很多"莫名其妙的静默行为"来自默认策略过于保守。
  2. DM 通 ≠ 配置正确。 不同消息通道在代码中走不同的检查路径。
  3. WebSocket 帧 ≠ 能被处理。 即使 SDK 层收到了帧,上层可能还有策略过滤。
  4. 永久写库文件的风险。 在 venv 的第三方库中写文件日志,升级库时会被覆盖。排查完应及时清理。