乐于分享
好东西不私藏

OpenClaw / Feishu Channel / Agent Runtime 三者关系

OpenClaw / Feishu Channel / Agent Runtime 三者关系

最近虎sir在docker容器以及vmware 虚拟机中安装了openclaw,解决了一些实际问题,聊天通道为飞书,配置飞书机器人与后台Agent进行交互,下面就用户与openclaw的交互做一个简要概述。

一、三层角色

┌────────────────────────────────────────────────────────────────────────────┐│ Layer 1: 用户层 — Feishu Backend (飞书开放平台)                                ││   domain = "open.feishu.cn"  |  https://open.feishu.cn/open-apis/...        ││   协议:WebSocket (LarkWSClient) + HTTPS REST                                 ││   持有:appId/appSecret、事件订阅、消息存储、卡片数据                          │└──────────────────────────────┬─────────────────────────────────────────────┘                               │ WebSocket 事件推送 + HTTPS API 调用                               ▼┌────────────────────────────────────────────────────────────────────────────┐│ Layer 2: 通道层 — Feishu Channel Plugin (openclaw-lark 插件)                  ││   位置: /config/.openclaw/extensions/openclaw-lark/                          ││   进程: 嵌入 openclaw-gateway (PID 30112, 单一 Node 进程)                     ││   职责: 收消息 → 解析 → 安全闸门 → 富化 → 转发给 Agent                         ││   关键模块:                                                                   ││     src/channel/monitor.js      ←  WebSocket 入口, 创建 LarkClient + WSClient ││     src/channel/event-handlers.js  ← 注册飞书事件路由                          ││     src/channel/plugin.js       ← ChannelPlugin 接口实现                      ││     src/messaging/inbound/handler.js  ← 7 阶段流水线                           ││     src/messaging/inbound/dispatch.js ← 调用 runtime.channel.reply           ││     src/messaging/outbound/*    ← 发送消息/卡片/媒体                            ││     src/core/lark-client.js     ← SDK 封装                                    ││     src/tools/oapi/index.js     ← 注册 OAPI 工具 (calendar, task)            ││     src/tools/mcp/doc/index.js  ← 注册 MCP 文档工具                           │└──────────────────────────────┬─────────────────────────────────────────────┘                               │ dispatchToAgent → core.channel.reply.dispatch*                               │ (进程内函数调用 + 事件总线)                               ▼┌────────────────────────────────────────────────────────────────────────────┐│ Layer 3: Agent Runtime — OpenClaw Gateway                                    ││   进程: openclaw-gateway (PID 30112)                                          ││   Agents:                                                                      ││     /config/.openclaw/agents/main          ← 我 (当前 webchat session)        ││     /config/.openclaw/agents/joshua-work   ← 飞书 "work" 账号绑定的 agent      ││     /config/.openclaw/agents/joshua-life   ← 飞书 "life" 账号绑定的 agent      ││     /config/.openclaw/agents/joshua-invest ← 飞书 "invest" 账号绑定的 agent    ││   每个 agent 持有: agent/ 目录、sessions/ 历史、模型配置 (models.json)         ││   凭据: /config/.openclaw/credentials/lark.secrets.json (4 个 appSecret)      │└────────────────────────────────────────────────────────────────────────────┘

二、协议 / 数据流详解

入站:你发消息给我

[你]  1. 在飞书 DM 发文本消息 "查余额"    飞书 IM 服务器  2. 通过 WebSocket (Lark/Event v2 envelope) 推送事件     URL: wss://open.feishu.cn/...   (resolveBrand('feishu'))     协议: larksuite open SDK WSClient 内部的长连接     负载示例 (简化):     {       "schema""2.0",       "header": { "app_id""cli_xxx""event_type""im.message.receive_v1" },       "event": {         "message": { "message_id""om_...""chat_id""oc_...""content""{\"text\":\"查余额\"}" },         "sender": { "sender_id": { "open_id""ou_..." } }       }     }    [Feishu Channel Plugin - monitor.js]  3LarkClient.startWS() 创建 WSClient,     EventDispatcher.register({ "im.message.receive_v1": handleMessageEvent })     入站事件分发到 handleMessageEvent    [event-handlers.js]  4. isEventOwnershipValid 检查 app_id 匹配 (防串号)     MessageDedup 去重 (TTL 缓存, 防 WS 重连导致重复投递)     用 chat_id (+ thread_id) 走 chat-queue.js 串行化 (避免同一个 chat 并发混乱)    [handler.js - 7 阶段流水线]  5. ① 解析账号 (accounts.getLarkAccount  joshua-work/life/invest)     ② parseMessageEvent  MessageContext (合并转发消息展开)     ③ enrich.resolveSenderInfo (批量预取用户姓名)     ④ gate.checkMessageGate   权限闸门:          - 白名单 allowFrom          - 群策略 requireMention (群消息必须 @机器人)          - 多账号隔离: accountScopedCfg 替换 cfg.channels.feishu     ⑤ 富化 (内容解析, 媒体下载, 引用消息展开)     ⑥ chat-queue 排队     ⑦ dispatch.dispatchToAgent  构造 agent envelope    [dispatch.js]  6. dc.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({         ctx: { from, to, rawBody, senderId, chatType, accountId, ... },         cfg: accountScopedCfg,         replyOptions: { skillFilter }    决定 agent 能用哪些 skill     })     这一步是进程内调用 + 事件总线, 走 OpenClaw plugin-sdk 的     "channel.reply" 子系统    [Agent Runtime - openclaw-gateway]  7. runtime 找到目标 agent (按 accountId 绑定到 joshua-* agent)     agent 加载自己的:       - workspace (AGENTS.md, SOUL.md, USER.md, IDENTITY.md, BOOTSTRAP.md)       - sessions 历史       - skills (本会话上下文已加载: feishu-*, htsc-*)       - 模型 (models.json)  8. agent 调 LLM (我用的是 minimax/MiniMax-M3)  9. agent 决定调工具: 例如 feishu_im_user_message (以用户身份发),     或我装好的 htsc a-share-paper-trading    [工具调用路径 - 任选]  A. 飞书 OAPI 工具 (feishu_* lark-client.js  HTTPS POST     https://open.feishu.cn/open-apis/...     AuthorizationBearer <tenant_access_token | user_access_token>  B. 飞书 MCP 工具 (doc/wiki/drive)  MCP 协议 (model-context-protocol)  C. 本地/扩展 skill (htsc-* 直接 HTTP POST 后端 (例如 ai.zhangle.com)    [Outbound 写回]  10. agent 的回复被 reply-dispatcher 接管      - 流式卡片 (Streaming Card)   updateCardFeishu (PATCH card)      - 普通文本                      sendMessageFeishu / sendTextLark      - 富卡片                        sendCardLark      - 图片/文件/音频                 uploadImageLark + sendImageLark 等   [飞书 IM 服务器  你的飞书客户端]   11. 卡片/文本/图片渲染在你的飞书里

三、协议清单(按层)

协议 / 接口
方向
端点
谁用
WebSocket (Lark/Event v2)
飞书 → 插件
wss://open.feishu.cn/...
飞书 → LarkClient.WSClient(事件推送)
HTTPS REST /open-apis/im/v1/...
插件 → 飞书
https://open.feishu.cn/open-apis/im/v1/messages
发送/编辑消息、上传媒体
HTTPS REST /open-apis/authen/v2/oauth/token
插件 → 飞书
https://open.feishu.cn/open-apis/authen/v2/oauth/token
OAuth device-flow 拿用户 access_token
HTTPS REST /open-apis/doc/v2/...
插件 → 飞书
doc/wiki/drive API
文档读写
HTTPS REST /open-apis/bitable/v1/...
插件 → 飞书
多维表格
bitable skill
HTTPS REST /open-apis/calendar/v4/...
插件 → 飞书
日历
calendar skill
HTTPS REST /open-apis/task/v2/...
插件 → 飞书
任务
task skill
MCP (Model Context Protocol)
插件 → 飞书
MCP endpoint
文档类工具走 MCP
OAuth 2.0 Device Flow
浏览器 ↔ 飞书
用户手动扫码授权
把 user_access_token 存到本机,给“以用户身份发消息”用
进程内函数调用
Plugin → Gateway
直接 require / 事件总线
dispatchToAgent → runtime.channel.reply.dispatch*
HTTPS REST
Plugin → 第三方后端
https://ai.zangle.com

 等
htsc skill 的 query-indicator / financial-analysis 等
本地 HTTP
shell — exec — Python
本机 stdio
我刚才装 htsc 时通过 exec 跑 python3

四、关键约束(从代码里挖出来的)

1.单进程:所有 plugin + agents + WebSocket 连接都在一个 openclaw-gateway (PID 30112) 里跑(cwd 是 /home/joshua)。2.多账号 = 多 agent:4 个飞书 app 配置(main / work / life / invest),凭据存 /config/.openclaw/credentials/lark.secrets.json,每个账号绑定一个独立 agent(joshua-work 等)。账号之间通过 accountScopedCfg 严格隔离配置。3.WebSocket 模式:connectionMode 默认且当前唯一支持的是 websocket;webhook 模式 README 标注未实现。4.串行化:同一个 chat_id + thread_id 通过 chat-queue.js 排队,避免同一会话并发引发乱序。5.去重:MessageDedup 在 WS 重连时按 message_id 去重(默认 TTL 默认上限)。6.安全闸门:gate.js 强制 requireMention(群消息不 @ 机器人就丢)+ allowFrom 白名单。7.两套工具注册    OAPI 工具(calendar/task)走飞书 Open API    MCP 工具(doc/wiki/drive)走 Model Context Protocol    OAuth 工具单独注册8.卡片 vs 文本:回复走流式卡片(dispatchReplyWithBufferedBlockDispatcher),支持 Thinking/Generating/Complete 三态 + 流式打字效果

五、用一句话总结

Feishu Backend 通过 WebSocket 推事件给 openclaw-lark 插件的 WSClient,插件在进程内用 7 阶段流水线(解析 → 富化 → 权限闸门 → 排队)处理后,通过 runtime.channel.reply.dispatch* 把消息塞进对应 agent(按账号绑定到 joshua-work/life/invest),agent 在自己 workspace 里跑 LLM + 工具(OAPI / MCP / 本地 skill),最终回复经 reply-dispatcher 流式卡片或文本回写飞书 IM。