一文看懂OpenClaw是如何处理飞书消息任务的

14 阅读5分钟

一、飞书消息任务流程分析

把整个系统想象成一个「餐厅」

用户 ──────► 飞书服务器 ──► OpenClaw Gateway ──► AI Agent ──► 飞书服务器 ──► 用户
            (外卖平台)       (餐厅前台)          (后厨)         (外卖平台)

1. 消息接收:「接单」

场景: 顾客在外卖平台下单

用户发送消息
        │
        ▼
   飞书服务器 ──收到订单──► OpenClaw Gateway
   (外卖平台)                (餐厅前台)

做了什么:

  • 飞书服务器收到用户消息
  • 通过 WebSocketWebhook 推送给 OpenClaw
  • 就像外卖平台把订单推送给餐厅

关键文件: extensions/feishu/src/monitor.transport.ts


2. 消息验证:「验单」

场景: 前台收到订单,检查订单是否有效

inboundDebouncer.enqueue()  ── 消息去重/防抖
        │
        ▼
handleFeishuMessage()      ── 解析消息 + 权限检查
        │
        ├── 是群聊?需要 @ 机器人吗?
        ├── 用户在白名单吗?
        └── 机器人有没有回复权限?

关键文件: extensions/feishu/src/bot.ts


3. 路由到 Agent:「分单到后厨」

场景: 前台确认订单有效,交给后厨

dispatchReplyFromConfig()  ── 消息分发给 AI
        │
        ▼
getReplyFromConfig()      ── 准备做饭(加载会话、解析指令)
        │
        ▼
runPreparedReply()        ── 把食材准备好

关键文件: src/auto-reply/reply/dispatch-from-config.ts


4. AI 处理:「炒菜」

场景: 后厨开始做菜

runReplyAgent()            ── 启动做菜流程
        │
        ▼
runEmbeddedPiAgent()      ── 调用 AI 模型
        │
        ▼
        ┌─────────────────────────────┐
        │        AI 模型              │
        │   "分析消息,生成回复"       │
        └─────────────────────────────┘

AI 做了什么:

  • 理解用户说的话
  • 查看对话历史(记忆)
  • 生成合适的回复

关键文件: src/agents/pi-embedded.ts


5. 回复发送:「出餐」

场景: 菜做好了,交给外卖小哥

ReplyPayload 生成         ── 装盘
        │
        ▼
sendMessageFeishu()       ── 发送文本消息
        或
sendCardFeishu()          ── 发送卡片消息(带格式)
        或
FeishuStreamingSession    ── 流式发送(边做边上菜)
        │
        ▼
飞书 API                  ── 外卖小哥取餐
        │
        ▼
   用户收到回复

关键文件: extensions/feishu/src/send.ts


完整流程图

用户: "帮我翻译 Hello"
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│ 第一步:接单(消息接收)                                      │
│                                                             │
│   飞书服务器 ─── WebSocket/Webhook ───► OpenClaw Gateway    │
│                                                             │
│   📁 extensions/feishu/src/monitor.transport.ts              │
└─────────────────────────────────────────────────────────────┘
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│ 第二步:验单(权限检查)                                      │
│                                                             │
│   - 是群聊吗?需要 @ 我吗?                                   │
│   - 用户有没有权限?                                          │
│   - 机器人能不能回复?                                        │
│                                                             │
│   📁 extensions/feishu/src/bot.ts                           │
└─────────────────────────────────────────────────────────────┘
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│ 第三步:分单(路由到 Agent)                                  │
│                                                             │
│   dispatchReplyFromConfig()                                 │
│                                                             │
│   📁 src/auto-reply/reply/dispatch-from-config.ts           │
└─────────────────────────────────────────────────────────────┘
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│ 第四步:炒菜(AI 处理)                                       │
│                                                             │
│   ┌─────────────────────────────────────────┐              │
│   │  AI 模型: "Hello""你好"              │              │
│   └─────────────────────────────────────────┘              │
│                                                             │
│   📁 src/agents/pi-embedded.ts                             │
└─────────────────────────────────────────────────────────────┘
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│ 第五步:出餐(发送回复)                                      │
│                                                             │
│   sendMessageFeishu() ───► 飞书 API ───► 用户               │
│                                                             │
│   📁 extensions/feishu/src/send.ts                         │
└─────────────────────────────────────────────────────────────┘
       │
       ▼
用户: "你好"

二、飞书消息任务源码分析

1. 飞书 Extension 入口和消息接收点

关键文件:

  • extensions/feishu/src/channel.ts - 飞书 channel 插件主入口
  • extensions/feishu/src/monitor.ts - 消息监控入口
  • extensions/feishu/src/monitor.transport.ts - WebSocket/Webhook 传输层
  • extensions/feishu/src/monitor.account.ts - 单账号消息处理

消息接收流程:

Gateway 启动
    │
    ▼
monitorFeishuProvider() [monitor.ts:31]
    │
    ├── monitorSingleAccount() [monitor.account.ts:634]
    │       │
    │       ├── fetchBotIdentityForMonitor() - 获取机器人身份
    │       ├── warmupDedupFromDisk() - 加载去重缓存
    │       ├── createEventDispatcher() - 创建 Lark SDK 事件分发器
    │       ├── registerEventHandlers() - 注册事件处理器
    │       │
    │       └── 根据 connectionMode 选择传输方式:
    │               │
    │               ├── WebSocket: monitorWebSocket() [monitor.transport.ts:84]
    │               │               └── wsClient.start({ eventDispatcher })
    │               │
    │               └── Webhook: monitorWebhook() [monitor.transport.ts:129]
    │                               └── HTTP Server 处理 webhook 请求

事件注册点 [monitor.account.ts:410-619]:

eventDispatcher.register({
  "im.message.receive_v1": ...     // 接收消息
  "im.message.reaction.created_v1": ...  // 表情反应
  "im.message.reaction.deleted_v1": ...  // 删除反应
  "application.bot.menu_v6": ...         // Bot 菜单
  "card.action.trigger": ...             // 卡片交互
})

2. 消息如何路由到 Agents

关键文件:

  • extensions/feishu/src/bot.ts - 消息处理和路由核心逻辑
  • extensions/feishu/src/policy.ts - 权限策略检查
  • extensions/feishu/src/session-route.ts - Session 路由解析

路由流程:

im.message.receive_v1 事件
    │
    ▼
inboundDebouncer.enqueue() [monitor.account.ts:419] - 消息去重/防抖
    │
    ▼
handleFeishuMessage() [bot.ts:221]
    │
    ├── finalizeFeishuMessageProcessing() - 去重确认
    ├── parseFeishuMessageEvent() - 解析消息内容
    │
    ├── 权限检查:
    │   ├── 群组检查: isFeishuGroupAllowed() - 检查 groupAllowFrom
    │   ├── DM 检查: resolveFeishuAllowlistMatch() - 检查 dmPolicy
    │   └── 配对检查: createChannelPairingController() - 处理未配对用户
    │
    ├── 消息路由:
    │   ├── resolveAgentRoute() - 解析 agent 路由
    │   ├── resolveConfiguredBindingRoute() - ACP 会话绑定
    │   └── buildFeishuAgentBody() - 构建发送给 agent 的消息体
    │
    └── dispatchReplyFromConfig() [dispatch-from-config.ts:149]
            │
            └── getReplyFromConfig() [get-reply.ts:106]
                    │
                    └── 传递给 Agent 执行

消息解析 [bot.ts:130-174]:

parseFeishuMessageEvent(event, botOpenId, botName)
├── parseMessageContent() - 解析消息内容
├── checkBotMentioned() - 检查是否 @机器人
├── normalizeMentions() - 规范化提及
└── 返回 FeishuMessageContext

权限策略 [policy.ts]:

  • groupPolicy: "open" | "allowlist" - 群组策略
  • dmPolicy: "open" | "pairing" | "allowlist" - DM 策略
  • requireMention: 群组是否需要 @机器人才响应

3. Agent 如何处理并生成回复

关键文件:

  • src/auto-reply/reply/get-reply.ts - 回复生成入口
  • src/auto-reply/reply/get-reply-run.ts - 回复运行逻辑
  • src/auto-reply/reply/agent-runner.ts - Agent 执行器
  • src/auto-reply/reply/agent-runner-execution.ts - 实际 LLM 调用
  • src/agents/pi-embedded.ts - Embedded PI Agent

Agent 处理流程:

dispatchReplyFromConfig()
    │
    ├── 快速中止检查: tryFastAbortFromMessage()
    ├── 发送策略检查: resolveSendPolicy()
    ├── ACP 分发: tryDispatchAcpReply() - ACP 会话处理
    │
    └── getReplyFromConfig() [get-reply.ts:106]
            │
            ├── finalizeInboundContext() - 规范化上下文
            ├── applyMediaUnderstandingIfNeeded() - 媒体理解
            ├── applyLinkUnderstandingIfNeeded() - 链接理解
            ├── initSessionState() - 初始化会话状态
            ├── resolveReplyDirectives() - 解析指令
            │       ├── /think - 思考级别
            │       ├── /verbose - 详细模式
            │       └── /reset - 重置会话
            │
            └── runPreparedReply() [get-reply-run.ts:204]
                    │
                    ├── 构建 prompt
                    ├── 解析队列设置
                    └── runReplyAgent() [agent-runner.ts:110]
                            │
                            └── runAgentTurnWithFallback() [agent-runner-execution.ts:84]
                                    │
                                    └── runEmbeddedPiAgent() [pi-embedded.ts]
                                            │
                                            └── 调用 LLM API 生成回复

消息格式化 [bot.ts:176-219]:

buildFeishuAgentBody()
├── 添加引用内容 [Replying to: "..."]
├── 添加发送者标识 "sender: message"
├── 添加提及提示 [System: ...]
└── 添加消息 ID [message_id: xxx]

4. 回复如何传回飞书

关键文件:

  • extensions/feishu/src/reply-dispatcher.ts - 回复分发器
  • extensions/feishu/src/send.ts - 消息发送
  • extensions/feishu/src/outbound.ts - 出站适配器
  • extensions/feishu/src/streaming-card.ts - 流式卡片

回复发送流程:

Agent 生成 ReplyPayload
    │
    ▼
createFeishuReplyDispatcher() [reply-dispatcher.ts:95]
    │
    ├── 创建 typing controller (打字指示器)
    └── 创建 ReplyDispatcher
            │
            ├── dispatcher.sendToolResult() - 工具结果
            ├── dispatcher.sendBlockReply() - 块回复(流式)
            └── dispatcher.sendFinalReply() - 最终回复
                    │
                    ▼
            reply-dispatcher.ts deliver()
                    │
                    ├── 流式卡片: streaming.start() / streaming.update() / streaming.close()
                    │           或
                    ├── 文本卡片: sendStructuredCardFeishu()
                    │           或
                    └── 纯文本: sendMessageFeishu()
                            │
                            ▼
                    sendReplyOrFallbackDirect() [send.ts:121]
                            │
                            ├── 尝试回复: client.im.message.reply()
                            └── 失败时直接发送: client.im.message.create()

发送函数链 [send.ts]:

sendMessageFeishu() 
    → sendReplyOrFallbackDirect()
        → client.im.message.reply() (回复)
        → client.im.message.create() (直接发送)
        
sendStructuredCardFeishu()
    → sendCardFeishu()
        → sendReplyOrFallbackDirect()

sendMediaFeishu() [media.ts]
    → 上传媒体到飞书
    → 发送消息

流式卡片 [streaming-card.ts]:

FeishuStreamingSession
├── start() - 创建卡片消息
├── update() - 更新卡片内容(partial)
└── close() - 关闭并发送最终内容

完整消息流时序图

┌─────────────────────────────────────────────────────────────────────────┐
│                           飞书服务器                                     │
│  im.message.receive_v1 event ──────────────────────────────────────┐   │
└─────────────────────────────────────────────────────────────────────┼───┘
                                                                      │
                    WebSocket / Webhook                                ▼
┌───────────────────────────────────────────────────────────────────────────┐
│                          OpenClaw Gateway                                │
│                                                                           │
│  monitor.ts: monitorFeishuProvider()                                      │
│       │                                                                  │
│       ▼                                                                  │
│  monitor.account.ts: monitorSingleAccount()                               │
│       │                                                                  │
│       ▼                                                                  │
│  monitor.account.ts: registerEventHandlers()                              │
│       │  注册 im.message.receive_v1                                       │
│       ▼                                                                  │
│  monitor.account.ts: inboundDebouncer.enqueue()                            │
│       │  消息防抖/去重                                                    │
│       ▼                                                                  │
│  bot.ts: handleFeishuMessage()                                           │
│       │  - 解析消息                                                       │
│       │  - 权限检查                                                       │
│       │  - 路由解析                                                       │
│       ▼                                                                  │
│  dispatch-from-config.ts: dispatchReplyFromConfig()                       │
│       │  - 插件钩子                                                       │
│       │  - 快速中止检查                                                   │
│       │  - ACP 分发                                                       │
│       ▼                                                                  │
│  get-reply.ts: getReplyFromConfig()                                      │
│       │  - 会话初始化                                                     │
│       │  - 指令解析                                                       │
│       ▼                                                                  │
│  get-reply-run.ts: runPreparedReply()                                     │
│       │  - 构建 prompt                                                   │
│       ▼                                                                  │
│  agent-runner.ts: runReplyAgent()                                        │
│       │                                                                  │
│       ▼                                                                  │
│  agent-runner-execution.ts: runAgentTurnWithFallback()                    │
│       │  - 模型选择                                                       │
│       │  - 错误重试                                                       │
│       ▼                                                                  │
│  pi-embedded.ts: runEmbeddedPiAgent()                                    │
│       │  LLM API 调用                                                     │
│       ▼                                                                  │
│  ┌───────────────────────────────────────────────────────────────────┐    │
│  │                        AI Model                                    │    │
│  └───────────────────────────────────────────────────────────────────┘    │
│       │                                                                  │
│       ▼                                                                  │
│  ReplyPayload 生成                                                       │
│       │                                                                  │
│       ▼                                                                  │
│  reply-dispatcher.ts: createFeishuReplyDispatcher()                       │
│       │  - 创建 typing controller                                        │
│       │  - 处理流式/块/最终回复                                           │
│       ▼                                                                  │
│  send.ts: sendMessageFeishu() / sendStructuredCardFeishu()               │
│       │                                                                  │
│       ▼                                                                  │
│  client.im.message.reply() / client.im.message.create()                   │
│       │  调用飞书 API                                                     │
└───────────────────────────────────────────────────────────────────────────┘
                                                                      │
                    飞书 API                                               ▼
┌───────────────────────────────────────────────────────────────────────────┐
│                           飞书服务器                                     │
│                        消息发送到用户                                    │
└───────────────────────────────────────────────────────────────────────────┘

关键文件路径汇总

阶段文件路径核心函数
Channel 入口extensions/feishu/src/channel.tsfeishuPlugin - 插件定义
监控启动extensions/feishu/src/monitor.tsmonitorFeishuProvider()
单账号监控extensions/feishu/src/monitor.account.tsmonitorSingleAccount(), registerEventHandlers()
传输层extensions/feishu/src/monitor.transport.tsmonitorWebSocket(), monitorWebhook()
消息处理extensions/feishu/src/bot.tshandleFeishuMessage(), parseFeishuMessageEvent()
权限策略extensions/feishu/src/policy.tsresolveFeishuReplyPolicy(), isFeishuGroupAllowed()
回复分发extensions/feishu/src/reply-dispatcher.tscreateFeishuReplyDispatcher()
消息发送extensions/feishu/src/send.tssendMessageFeishu(), sendCardFeishu()
出站适配extensions/feishu/src/outbound.tsfeishuOutbound
流式卡片extensions/feishu/src/streaming-card.tsFeishuStreamingSession
核心分发src/auto-reply/reply/dispatch-from-config.tsdispatchReplyFromConfig()
回复生成src/auto-reply/reply/get-reply.tsgetReplyFromConfig()
Agent 运行src/auto-reply/reply/agent-runner.tsrunReplyAgent()
LLM 执行src/auto-reply/reply/agent-runner-execution.tsrunAgentTurnWithFallback()