目录
前言
1.Gateway 网关层
1.1 什么是Gateway?
1.2 Gateway的6大普适化功能
1.3 Gateway 十大常见误解
1.4 三种路径在gateway中的流程
Bundle Channel :Telegram 与 Gateway 同进程函数调用
HTTP/IPC(非 WebSocket):独立 Channel 进程与 Gateway
WebSocket (18789):只服务于 Device Nodes
三种路径对比总结
1.5 Registry
1.5.1 Registry的核心作用
1.5.2 发现与扫描:Registry 从哪找到插件?
1.5.3 加载机制:同进程 import() 与依赖注入
1.5.4 契约验证:Registry 不是”见文件就加载”
1.5.5 能力注册:Registry 是”能力注册中心”(Registry as Capability Registry)
1.5.6
安全策略与诊断:加载时的”安检”,不是运行时的”隔离”
Registry 小节的一句话收尾
1.6 Router
1.6.1 定位
1.6.2 核心源码
1.6.3 关键行为
1.6.3.1 路由优先级链(从高到低)
1.6.3.2 ResolvedAgentRoute 输出结构
1.6.3.3 SessionKey 的生成规则
1.6.4 常见误解
一句话总结
1.7 Dispatcher
1.7.1 定位
1.7.2 核心源码
1.7.3 关键行为
1.7.3.1 同进程函数调用(不是独立服务)
1.7.3.2 会话级串行化(Concurrency Control)
1.7.3.3 流式输出缓冲
1.7.3.4 全局跟踪与优雅关闭
常见误解
一句话总结
1.8 WebSocket Hub
1.8.1 定位
1.8.2 核心源码
1.8.3 关键行为
1.8.3.1 默认地址与端口
1.8.3.2 连接的两类对象
1.8.3.3 协议握手与通信模式
1.8.3.4 node.invoke 的完整调用链
1.8.3.5 配对与信任
1.8.4 常见误解
一句话总结
1.9 Session Store
1.9.1 定位
1.9.2 核心源码
1.9.3 关键行为
文件系统持久化(不是数据库)
维护的核心状态
并发安全:写锁机制
缓存与性能优化
磁盘预算与自动清理
常见误解
一句话总结
Config & Cron
定位
核心源码
关键行为
全局配置源 OpenClawConfig
Cron 服务:CronService
Cron 的会话模型
Webhook 通知与失败告警
MCP Loopback Server
Control UI 的 HTTP 入口
常见误解
一句话总结
总结
在 OpenClaw 的架构中,Gateway 是最容易被误解的一层。它名字里带着“网关”,却不是你熟悉的那种微服务 API Gateway;它内部跑着 WebSocket 服务,却又不连接 Telegram 或 Discord;它负责把消息送进 Agent,却从不理解消息本身的语义。很多开发者在第一次接触代码时,会本能地把 Gateway 想象成“协议翻译官”或“消息中转站”,并据此画出一张张并不准确的架构图。
这种误解并非没有代价:当你认为 Channel 和 Gateway 之间隔着网络调用时,你就读不懂 telegramDeps.dispatchReplyWithBufferedBlockDispatcher 为什么只是一个函数指针;当你把 WebSocket 与“渠道长连接”画上等号时,你就会对 Device Node 的注册机制视而不见;当你把 Gateway 当成一个独立的微服务时,你就很难理解 Registry、Router、Dispatcher 为什么能在毫秒级完成路由与派发。
本文的目的是从源码出发,还 Gateway 一个清晰、准确的技术画像。我们将逐个拆解 Gateway 的六大核心职能,纠正关于它的十大常见误解,深入 Registry、Router、Dispatcher、WebSocket Hub、Session Store 与 Config & Cron 的实现细节,并通过三种通信路径(同进程函数调用、HTTP/IPC、WebSocket)的对比,帮你建立对 Gateway runtime 的完整认知。
读完本文,你应该能够准确地回答以下问题:Gateway 到底是什么?它加载了什么、路由了什么、派发了什么?它与 Channel Plugin、Agent Runtime、Device Node 之间的边界在哪里?以及,为什么 OpenClaw 要坚持“Gateway 是宿主和路由器,不是翻译官”?
The Gateway is OpenClaw’s WebSocket server (channels, nodes, sessions, hooks).
注意:此处的
channels指 Gateway 通过 WebSocket 暴露给控制平面的渠道抽象接口(如 Web UI 实时状态同步、hooks 事件订阅),不是 Telegram/Discord 的消息入站管道。

The Gateway is the central control plane for OpenClaw, responsible for managing WebSocket connections, dispatching messages, coordinating messaging channels, orchestrating agent runs, and maintaining system state. It runs as a single multiplexed process serving protocol clients (CLI, native apps, Control UI) and channel providers simultaneously.
网关是OpenClaw的中央控制平面,负责管理WebSocket连接、调度消息派发、协调消息通道、协调代理运行以及维护系统状态。它作为一个单一复用进程运行,同时为协议客户端(CLI、本地应用、控制界面)和通道提供者提供服务。
以下是源码,因为太长了,我就不放出来了,可以点击下方链接直接看
前往源码 :src/gateway/server.impl.ts
这里是我的网关的日志,证明Gateway是一个单一复用进程,它同时启动HTTP server, MCP server,heartbeat,health-monitor,最后才启动channels。可以说明 Gateway 的”宿主”角色——它先把自己搭好,再去加载插件。
10:02:39 [gateway] loading configuration… 10:02:41 [gateway] resolving authentication…
10:02:41 [gateway] starting… 10:02:45 [gateway] starting HTTP server… 10:02:45 [gateway] MCP loopback server listening on http://127.0.0.1:9692/mcp 10:02:45 [heartbeat] started 10:02:45 [health-monitor] started 10:02:45 [gateway] ready (9 plugins, 4.1s) 10:02:46 [gateway] starting channels and sidecars…
注意⚠
Gateway 是宿主和路由器,不是翻译官、不是智能体、不是微服务网关、不是 WebSocket 接入层(除 Device Node 外)。它加载插件、路由会话、缓冲派发、管理设备和配置——但绝不直接触碰外部聊天平台的 API,也绝不理解消息内容的语义。
职责 说明 实现方式
插件宿主 加载、验证并运行 Channel Plugin 与 Skill Plugin Registry — 动态 import() + 契约验证 (defineBundledChannelEntry)
消息路由 根据 SessionKey / AccountId 将消息派发给目标 Agent Router — resolveAgentRoute (binding → default 优先级链)
派发缓冲 按 Session 串行化排队,管理流式输出的缓冲与回传 Dispatcher — dispatchReplyWithBufferedBlockDispatcher
会话管理 维护会话状态、历史记录、last route 与线程隔离 Session Store — 文件系统持久化 (loadSessionStore / recordInboundSession)
设备编排 注册 Device Node,代理 Agent 对其发起远程工具调用 WebSocket Hub — ws://127.0.0.1:18789 (node.invoke ↔ Device Node)
配置与调度 统一配置源、Cron 定时任务、Control UI / Webhook 入口 Config & Cron — OpenClawConfig / Cron Runner / HTTP API
- Plugin Host (插件宿主)
功能:加载、验证、运行 Channel Plugin 和 Skill Plugin。
抽象行为:
- Gateway 启动时扫描插件目录,执行契约验证。
- 对于 bundled plugins,直接动态加载到同进程内存中运行。
- 对于 external plugins,可以通过子进程或 HTTP API 接入,但 Gateway 仍是统一的宿主和调度者。
关键边界:
- Gateway 不直接对接 Telegram/微信/Discord 的 API。
- Gateway 只对接 Channel Plugin 的标准接口(入站上下文提交、出站回复交付)。
src / infra / outbound / deliver.ts // Channel docking: outbound delivery delegates to plugin.outbound adapters. async function createChannelHandler(params: ChannelHandlerParams): Promise
= await loadChannelBootstrapRuntime();
bootstrapOutboundChannelPlugin({ channel: params.channel, cfg: params.cfg, }); outbound = await loadChannelOutboundAdapter(params.channel);
} const handler = createPluginHandler({ …params, outbound }); if (!handler) {
throw new Error(`Outbound not configured for channel: ${params.channel}`);
} return handler; }
插件提供的标准出站接口实现:
attachedResults: {
channel: "telegram", sendText: async ({ cfg, to, text, accountId, deps, replyToId, threadId, silent, gatewayClientScopes, }) => await sendTelegramOutbound({ cfg, to, text, accountId, deps, replyToId, threadId, silent, gatewayClientScopes, }), ...
还是以我的网关为例
10:02:45 [gateway] [plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load: openclaw-weixin 10:02:45 [gateway] feishu_doc: Registered feishu_doc, feishu_app_scopes 10:02:45 [gateway] feishu_chat: Registered feishu_chat tool 10:02:45 [gateway] feishu_wiki: Registered feishu_wiki tool … 10:03:23 [plugins] openclaw-weixin: loaded without install/load-path provenance; treat as untracked local code
- 证明 Gateway 启动时会扫描并加载 bundled plugins(feishu 系列)和 non-bundled plugins(openclaw-weixin)。
plugins.allow is empty的警告可以说明 Registry 有安全契约验证机制——不信任的本地代码会被标记为 untracked。
- Routing & Dispatch (路由与派发)
功能:把从任何渠道进来的消息,精准地分发给对应的 Agent。
抽象行为:
- 收到 Channel Plugin 提交的
ctxPayload(包含SessionKey)。 - 查询路由策略(bindings → default),解析出目标
agentId。 - 通过派发器(Dispatcher)将消息投入 Agent Runtime 的输入队列。
- 管理会话级并发控制:同一个 Session 的消息串行处理,不同 Session 可并行。
关键边界:
- Gateway 不做消息内容的语义理解(那是 Agent 的事)。
- Gateway 只看路由元数据(
SessionKey、AccountId、PeerId、Channel)。
- Session Management (会话管理)
功能:维护跨渠道、跨 Agent 的持久化会话状态。
抽象行为:
- 持久化
lastRoute(这个会话上次由哪个 Agent 处理)。 - 管理会话历史(History)的读取与更新。
- 处理线程/话题隔离(Topic/Thread Session Keys)。
- 保证同一个 Session 的上下文连续性,即使消息来自不同平台。
关键边界:
- Session Store 是 Gateway 维护的,但历史内容格式由 Channel Plugin 初始化时提供。
- Device Orchestration (设备编排)
功能:管理所有接入的执行节点(Node),并让 Agent 能够远程调用它们。
抽象行为:
- 通过 WebSocket 服务(默认
ws://127.0.0.1:18789)接受 Device Node 注册。 - 维护设备能力表(capabilities)和在线状态。
- 当 Agent 调用
node.invoke(toolName, params)时,Gateway 负责:
解析目标设备 通过 WebSocket 推送指令 接收执行结果 将结果回注 Agent 的 ReAct 循环
关键边界:
- WebSocket 只连接 Device Nodes,不连接外部聊天平台。
- Gateway 是 Agent 与物理世界(摄像头、GPS、本地文件系统)之间的唯一通道。
- Configuration & Control Plane (配置与控制平面)
功能:作为整个系统的统一配置源和管控入口。
抽象行为:
- 加载并维护全局
OpenClawConfig,向所有 Plugin 和 Agent 提供只读配置。 - 运行 Cron 任务调度器。
- 暴露 Webhook 入口(供外部系统回调,如 ClawHub、第三方集成)。
- 提供 Control UI(部分版本的 Gateway 带内置 Web UI)。
- Inter-layer Mediation(层间中介)
功能:作为 Channel ↔ Agent ↔ Node 之间的唯一中转站。
抽象数据流:
Inbound: Channel Plugin → Gateway → Agent Runtime Outbound: Agent Runtime → Gateway → Channel Plugin Node Call: Agent Runtime → Gateway → Device Node → Gateway → Agent Runtime
为什么必须有一个中介?
- 解耦:Channel 不需要知道 Agent 的存在,Agent 不需要知道消息来自哪个平台。
- 扩展性:新增一个渠道或 Agent,只需要接入 Gateway 的标准接口,不需要改动其他层。
- 一致性:所有会话状态、路由策略、安全策略在 Gateway 处统一强制执行。
连接关系 是否存在长连接 说明
Gateway ↔ 外部平台(Telegram/微信/Discord) ❌ 不存在 这是 Channel Layer(Monitor)的职责
Gateway ↔ Bundled Channel Plugin ❌ 不存在 同进程内存加载,是函数调用关系
Gateway ↔ External Channel Plugin ⚠️ 可能存在 HTTP/IPC 但这不是"长连接",而是请求-响应式 API
Gateway ↔ Device Node ✅ WebSocket 长连接 这是 Gateway 唯一维持的网络长连接
- Gateway 通过 WebSocket 与消息渠道连接
误解 Gateway 用 WebSocket 维持与 Telegram/Discord 等渠道的长连接。
真相 Gateway 的 WebSocket 只服务于 Device Node(手机/电脑客户端)。消息渠道(如 Telegram)与 Gateway 之间是 同进程函数调用(import() + 注入的 dispatchReplyWithBufferedBlockDispatcher)。
源码依据 extensions/telegram/src/bot-message-dispatch.ts:602(直接函数调用);WebSocket 默认端口 18789 仅在 Device Node 注册逻辑中出现。
- Gateway 负责将原始消息标准化为 Envelope
误解 消息进入 Gateway 后,Gateway 把它打包成统一的 Envelope DTO。
真相 Envelope 标准化发生在 Channel 层的 Context Builder,不是 Gateway。Gateway 收到的已经是 MsgContext(一个扁平对象),其中 Body 字段是 formatInboundEnvelope() 生成的一段字符串。
源码依据 extensions/telegram/src/bot-message-context.session.ts:271-284(formatInboundEnvelope 调用);src/auto-reply/envelope.ts:156-195(字符串格式化逻辑)。
- Gateway 内部存在 sequential / concurrent / sliding 三种队列模式
误解 Gateway 按 session queue 策略提供三种排队模式:sequential、concurrent、sliding。
真相 源码中完全不存在这三种模式。实际只有一个 dispatchReplyWithBufferedBlockDispatcher,它的核心作用是按 sessionKey 做会话级串行化缓冲,同时支持 block/partial streaming 的回传。
源码依据 全局搜索 sequential、concurrent、sliding 与 session/dispatch 相关组合,无匹配源码;bot-message-dispatch.ts:602 为唯一派发入口。
- Gateway 是一个独立的微服务,Channel 通过 RPC 调用它
误解 Gateway 是独立部署的微服务,Telegram Channel 作为客户端通过 RPC/HTTP 向 Gateway 注册和发消息。
真相 Gateway 是一个 宿主进程(Host Process)。对于 bundled channels(如 Telegram),Channel Plugin 被 Gateway 动态 import() 加载到同一个 Node.js 进程中运行,调用是内存级别的函数指针传递。
源码依据 extensions/telegram/index.ts 使用 defineBundledChannelEntry;src/channels/plugins/ 负责同进程加载验证。
- Monitor 是 Gateway 的一个子组件
误解 Monitor 属于 Gateway 层,是 Gateway 内部负责监听外部消息的模块。
真相 Monitor 属于 Channel 层,不是 Gateway。每个 Channel Plugin 自带自己的 Monitor(如 Telegram 的 monitor.ts),负责与外部平台建立连接(grammy long polling / webhook)。Gateway 不直接触碰任何外部平台的网络 API。
源码依据 extensions/telegram/src/monitor.ts(文件路径在 extensions/telegram/ 下,不在 src/gateway/ 下)。
- Gateway 产生智能(LLM 推理在 Gateway 中发生)
误解 Gateway 不仅路由消息,还负责 LLM 推理和工具选择。
真相 Gateway 不产生智能。LLM 推理、ReAct 循环、工具选择全部发生在 Agent Runtime 层。Gateway 只负责把 MsgContext 递给 Agent,再把 Agent 生成的 ReplyPayload 传回 Channel。
源码依据 dispatchReplyWithBufferedBlockDispatcher 只处理派发和流式缓冲,不包含任何模型调用逻辑;模型调用在 Agent Runtime 内部完成。
- Gateway 保存并理解会话历史的具体内容
误解 Gateway 深度理解会话历史内容,基于历史内容做路由决策。
真相 Gateway 管理 Session Store(持久化文件/键值存储),但它只保存元数据(如 lastRoute、timestamp、transcript 引用)。历史消息的具体内容格式由 Channel Plugin 的 Context Builder 初始化,Gateway 本身不解析历史内容的语义。
源码依据 src/routing/session-key.ts;extensions/telegram/src/bot-message-context.session.ts:407-442(recordInboundSession 只更新路由元数据)。
- Gateway 直接对接 Telegram/Discord/微信的 API 服务器
误解 Gateway 进程直接发起 HTTP 请求到 api.telegram.org 或 Discord Gateway。
真相 Gateway 从不直接调用外部聊天平台的 API。所有与 Telegram/Discord/WhatsApp 的网络通信都由各自 Channel Plugin 的 Monitor 和 Outbound Adapter 完成。Gateway 只与 Channel Plugin 的标准接口交互。
源码依据 extensions/telegram/src/monitor.ts(grammy 连接 Telegram API);extensions/telegram/src/outbound-adapter.ts(直接调用 sendMessageTelegram)。
src/gateway/server-channels.ts
const startChannelInternal = async (
channelId: ChannelId, accountId?: string, opts: StartChannelOptions = {},
) =>
const { preserveRestartAttempts = false, preserveManualStop = false } = opts; const cfg = loadConfig(); resetDirectoryCache({ channel: channelId, accountId }); const store = getStore(channelId); const accountIds = accountId ? [accountId] : plugin.config.listAccountIds(cfg); if (accountIds.length === 0) { return; }
extensions/twitch/src/plugin.ts
gateway: );
ctx.log?.info(`Starting Twitch connection for ${account.username}`); // Lazy import: the monitor pulls the reply pipeline; avoid ESM init cycles. const { monitorTwitchProvider } = await import("./monitor.js"); await monitorTwitchProvider({ account, accountId, config: ctx.cfg, runtime: ctx.runtime, abortSignal: ctx.abortSignal, }); },
- Gateway 重启后,Channel 的长连接需要”重新注册”到 Gateway
误解 如果 Gateway 崩溃重启,Telegram 等渠道需要重新向 Gateway 建立连接或注册。
真相 对于 bundled channels,不需要重新注册。因为 Channel Plugin 被 import() 进 Gateway 进程,Gateway 重启时会重新加载所有 bundled channels,Monitor 随 Gateway 同生同灭,自动重新启动其对外连接。
源码依据 Registry 在 Gateway 启动时扫描并加载 extensions/ 目录,defineBundledChannelEntry 同步触发 Monitor 启动逻辑。
- Gateway 的 Dispatcher 是一个独立的网络组件/服务
误解 Dispatcher 是 Gateway 内部一个独立的子服务或网络代理。
真相 Dispatcher 不是独立服务,它是 Gateway 核心在初始化 Channel Plugin 时注入的一个 JavaScript 函数(dispatchReplyWithBufferedBlockDispatcher)。Telegram Plugin 收到消息后,直接 await 调用这个函数,把消息送进 Gateway 的消息处理循环。
源码依据 extensions/telegram/src/bot-message-dispatch.ts:602(telegramDeps.dispatchReplyWithBufferedBlockDispatcher({ ctx: ctxPayload }))。
一句话速记
Gateway 是宿主和路由器,不是翻译官、不是智能体、不是微服务网关、不是 WebSocket 接入层(除 Device Node 外)。它加载插件、路由会话、缓冲派发、管理设备和配置——但绝不直接触碰外部聊天平台的 API,也绝不理解消息内容的语义。
接下来这一章,我们来讲同进程函数调用,WebSocket长连接和HTTP/IPC请求响应在gateway中是如何流转的。
我们先看telegram,telegram在这里是bundled channel,由Gateway 进程直接启动(日志前缀都是 [gateway] 或 [telegram],没有网络连接日志)。
10:03:25 [telegram] [bot1] starting provider 10:03:25 [telegram] [bot2] starting provider 10:03:26 [telegram] menu text exceeded the conservative 5700-character payload budget…
接下来是独立进程/HTTP,
10:03:22 [openclaw-weixin] [fc64ff9de28d-im-bot] starting weixin provider 10:03:22 [openclaw-weixin] weixin monitor started (https://ilinkai.weixin..com) 10:03:23 [plugins] openclaw-weixin: loaded without install/load-path provenance
openclaw-weixin 是 non-bundled plugin,Gateway 发现并加载了它,但它维护自己的 monitor(连接 ilinkai.weixin..com),这正是 external channel 的典型特征。
最后是 WebSocket:Device Node 与 Control UI,
10:03:22 [openclaw-weixin] [fc64ff9de28d-im-bot] starting weixin provider 10:03:22 [openclaw-weixin] weixin monitor started (https://ilinkai.weixin..com) 10:03:23 [plugins] openclaw-weixin: loaded without install/load-path provenance
client=openclaw-control-ui证明 WebSocket 上的连接者是控制平面客户端,不是 Telegram。node.list、device.pair.list证明 WebSocket 上跑的是设备管理和系统状态查询的 RPC-like 调用。
关键对比:
10:03:23 [feishu] feishu[default]: starting WebSocket connection…
特别强调:这是 feishu channel 自己与飞书服务器建立的 WebSocket,和 Gateway 的 WebSocket 完全不是一回事。这正好用来反驳”Gateway 用 WebSocket 连接所有渠道”的误解。

-
Bundle Channel :Telegram 与 Gateway 同进程函数调用
入站数据流
出站数据流
源码证据:extensions/telegram/index.ts
import { defineBundledChannelEntry } from “openclaw/plugin-sdk/channel-runtime”; export default defineBundledChannelEntry({ name: “telegram”, // … });
Gateway 加载方式: Gateway 启动时会扫描 extensions/ 目录,对每个 bundled channel 执行动态 import(),然后把它注册到当前 Node.js 进程中。
派发调用:extensions/telegram/src/bot-message-dispatch.ts:602
({ queuedFinal } = await telegramDeps.dispatchReplyWithBufferedBlockDispatcher({
ctx: ctxPayload,
cfg,
dispatcherOptions: { … },
})); 这里的 telegramDeps.dispatchReplyWithBufferedBlockDispatcher 不是通过网络发请求,而是 Gateway 核心在启动 Telegram Plugin 时直接注入到 telegramDeps 对象里的函数指针。它们在同一进程内存空间中,调用就是一次同步/异步的函数调用。
这意味着什么?
- Telegram 收到的消息 → Context Builder 组装
ctxPayload→ 直接调用注入的dispatchReplyWithBufferedBlockDispatcher→ Gateway 把消息送进 Agent Runtime。 - 全程没有 HTTP 请求,没有 WebSocket,没有 socket 连接。
- grammY 的 Long Polling / Webhook 连接的是
api.telegram.org,那是 Telegram 对外的 API,跟 Gateway 无关。
-
HTTP/IPC(非 WebSocket):独立 Channel 进程与 Gateway
虽然 Telegram 是 bundled channel(同进程),但 OpenClaw 架构也支持外部 Channel Plugin。这类外部 channel 会作为一个独立进程运行,通过以下方式之一与 Gateway 通信:
- HTTP API:Gateway 暴露本地 HTTP endpoint,外部 channel 进程调用
/dispatch提交ctxPayload。 - Unix Domain Socket / Named Pipe:同机进程间通信。
- Child Process IPC:Gateway
fork()出 channel 子进程,通过 Node.jsprocess.send()通信。
关键区别:即使走 HTTP/IPC,这种通信也是请求-响应式的 API 调用,不是持久连接的 WebSocket。外部 channel 进程本质上是一个”Gateway 的客户端”。
-
WebSocket (18789):只服务于 Device Nodes
WebSocket 上跑的既不是 Telegram,也不是你的聊天机器人渠道,而是你自己的设备:
- macOS 桌面端 App
- iOS 手机 App
- Android 手机 App
- 甚至可以是树莓派等边缘设备
架构角色
Device Node (你的 iPhone) │
│ WebSocket 长连接
▼
Gateway (ws://127.0.0.1:18789)
│
│ 函数调用
▼
Agent Runtime
为什么 Device Node 需要 WebSocket?
因为 Agent Runtime 可能跑在云端服务器(你的 VPS 上),但它需要调用你本地手机的摄像头、GPS、剪贴板、本地 shell。这就需要:
- 设备注册:你的 iPhone App 启动时,通过 WebSocket 向 Gateway 注册自己,声明能力(capabilities)。
- 指令下发:Agent 推理后决定”调用手机摄像头拍照”,通过 Gateway 把
node.invoke指令推送到 WebSocket。 - 结果回传:iPhone 拍完照,把图片数据通过同一个 WebSocket 连接发回 Gateway,Gateway 再转给 Agent。
源码中的体现
在 src/plugin-sdk 和 Gateway 代码中,node.invoke 的调用链最终会把消息序列化,通过 WebSocket 推送到对应的 device session。而 Telegram Channel 完全不走这条路径。
-
三种路径对比总结
维度
路径 A:Bundled Channel
路径 B:WebSocket
路径 C:HTTP/IPC
代表对象
Telegram、Discord(内置)
iPhone、macOS、Web UI
外部渠道、CLI、Webhook
连接方式
同进程函数调用
WebSocket 长连接
HTTP/IPC 请求-响应
Gateway 角色
宿主进程(直接加载执行)
WebSocket 服务器(路由指令/状态)
HTTP API 服务器(接收/响应)
谁主动发起通信
双向函数调用
设备/Web UI 先连接,之后 Gateway 可主动推送
客户端始终主动请求
是否需要序列化
不需要(共享内存对象)
需要(WebSocket 帧)
需要(HTTP body JSON)
延迟
最低(微秒级)
低(毫秒级)
中(毫秒-百毫秒级)
典型数据
ctxPayload、ReplyPayload
node.invoke 指令、实时状态事件
ctxPayload、配置命令、Webhook payload
在OpenClaw Gateway中,registry(注册表)是插件系统的核心组件,负责管理和维护所有已加载插件的状态和能力。
Gateway 不是凭空运行的,它首先是一个插件的”宿主”和”安检员”。很多人讲 Gateway 直接从”路由”开始,这是错的——没有 Registry,Gateway 根本不知道有哪些 Channel 和 Agent 可用。
1.5.1 Registry的核心作用
- 插件生命周期管理
Registry通过PluginRegistry类管理插件的完整生命周期 。当Gateway启动时,loadOpenClawPlugins函数会创建并返回一个注册表实例 。
src/plugins/loader.ts loader.ts:1082-1087 export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegistry
- 能力注册中心
Registry存储插件注册的各种能力:
- Providers: LLM提供商、语音(TTS/STT)、图像生成等
- Tools: 代理工具
- Channels: 消息渠道集成(Telegram、Discord等)
- Hooks: 生命周期钩子
- 运行时状态管理
通过RuntimeTrackedPluginRegistry类型,Registry维护插件的运行时状态 runtime-state.ts:5-11 ,包括:
- 活动注册表引用
- 版本控制
- 工作空间目录
- 子代理运行模式
export type RuntimeTrackedPluginRegistry = PluginRegistry; export type RegistrySurfaceState = { registry: RuntimeTrackedPluginRegistry | null; pinned: boolean; version: number;
- Gateway集成
在Gateway启动过程中,注册表被用于:
- 初始化插件系统 server-plugin-bootstrap.ts:1-12
- 配置绑定规则的编译 configured-binding-compiler.ts:180-199
- 提供插件运行时服务
1.5.2 发现与扫描:Registry 从哪找到插件?
要讲清楚两个来源:
- Bundled Plugins:主仓库
extensions/目录下,随 Gateway 一起编译分发 - External / Workspace Plugins:用户本地目录
/.openclaw/extensions/和/.openclaw/workspace/skills/
源码依据:
src/plugins/loader.ts:38, 41 import { discoverOpenClawPlugins } from “./discovery.js”; import from “./manifest-registry.js”;
Registry 扫描两类插件目录:
类型
路径
特点
Bundled
extensions/(随主仓库分发)
官方内置,同进程加载,默认信任
Non-bundled / External
~/.openclaw/extensions/、~/.openclaw/workspace/skills/
用户/第三方开发,需验证来源
日志证据
10:02:45 [gateway] feishu_doc: Registered feishu_doc, feishu_app_scopes
10:02:45 [gateway] feishu_chat: Registered feishu_chat tool
…
10:02:45 [gateway] [plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load: openclaw-weixin 对比说明:
feishu_doc等是 bundled plugin,直接Registered,没有任何安全警告openclaw-weixin是 non-bundled,Registry 额外输出了plugins.allow和untracked警告
Registry 的扫描范围分为两个世界: bundled 世界是主仓库自带的官方插件(如 feishu、telegram),它们随 Gateway 一起编译/分发,默认享有最高信任级别;non-bundled 世界是用户本地目录(~/.openclaw/extensions/)或工作区技能(workspace/skills/),Registry 会对它们执行更严格的来源检查。
1.5.3 加载机制:同进程 import() 与依赖注入
这是 Registry 最核心的技术细节:
- Bundled Channel 不是以独立服务运行的,而是被 Gateway 动态
import()加载到同一个 Node.js 进程 - Registry 不会直接调用插件的内部函数,而是将 Gateway 的核心能力注入到插件的依赖对象中
- 最典型的注入是
dispatchReplyWithBufferedBlockDispatcher——没有这个注入,Channel Plugin 就无法把消息送进 Gateway
源码依据
import { defineBundledChannelEntry } from “openclaw/plugin-sdk/channel-runtime”;
export default defineBundledChannelEntry({
name: “telegram”,
// …
});
- bundled channel 被 Registry 动态
import()加载到 Gateway 的同一个 Node.js 进程 - Telegram 的 grammY runner、微信的 monitor 都在 Gateway 进程内运行
- 但 Registry 不会直接调用插件内部函数,而是将 Gateway 的核心能力注入进去
- 最典型的注入就是为非 WS 路径准备的
fallbackGatewayContext——这样 Telegram Plugin 可以直接调用 Agent,无需经过 WebSocket
Registry 加载 bundled plugin 的方式是同进程动态导入。Telegram、微信等渠道的代码被
import()进 Gateway 的 V8 引擎后,Registry 通过buildPluginApi()将 Gateway 的运行时上下文注入给插件。官方源码中甚至专门注释说明:”非 WebSocket 路径(Telegram、WhatsApp 等)的渠道适配器直接调用 agent,不走handleGatewayRequest“。这意味着 Telegram 与 Gateway 之间是内存中的直接函数调用,不是网络 RPC。
1.5.4 契约验证:Registry 不是”见文件就加载”
Registry 不是随便加载一个 JS 文件就完事,它会验证插件是否导出了必要的契约接口。
- Channel Plugin 必须导出
defineBundledChannelEntry或符合channel-runtime的接口 - Skill Plugin 必须导出可被 Agent 调用的工具定义
- Managed Hook 必须符合 hook-runtime 的契约
源码依据
if (record.kind !== “bundled-channel-entry”) {
return null;
}
if (
typeof record.id !== “string” ||
typeof record.name !== “string” ||
typeof record.description !== “string” ||
typeof record.register !== “function” ||
typeof record.loadChannelPlugin !== “function”
) {
return null;
}
- 一个
.ts文件放在extensions/里不等于它就是 Channel Plugin - Registry 会检查导出的对象是否包含
kind: “bundled-channel-entry” - 还会逐一验证
id、name、description是字符串,register和loadChannelPlugin是函数 - 任何一项不满足,直接
return null,拒绝加载
常见误解纠正
误解:Registry 只加载 Channel Plugin。 真相:Registry 加载 Channel、Skill、Managed Hook、ACPX Runtime Backend 等多种插件类型。
日志里也有证据:
10:02:46 [hooks:loader] Loading managed hook code into the gateway process. Managed hooks are trusted local code.
10:03:22 [gateway] [plugins] embedded acpx runtime backend registered
Registry 的契约验证是”白名单式”的。对于 bundled channel,它要求模块必须导出符合
BundledChannelEntryContract的对象:必须有kind: “bundled-channel-entry”,必须有id、name、description字符串,必须有register和loadChannelPlugin函数。缺少其中任何一项,Registry 都会拒绝加载并记录诊断信息。
1.5.5 能力注册:Registry 是”能力注册中心”(Registry as Capability Registry)
源码依据
import { registerAgentHarness as registerGlobalAgentHarness } from “../agents/harness/registry.js”;
import { registerContextEngineForOwner } from “../context-engine/registry.js”;
import { registerInternalHook } from “../hooks/internal-hooks.js”;
import { registerCompactionProvider } from “./compaction-provider.js”;
import { registerMemoryEmbeddingProvider } from “./memory-embedding-providers.js”;
import { registerMemoryCapability, registerMemoryRuntime } from “./memory-state.js”;
import { registerPluginCommand } from “./command-registration.js”; Registry 不只是记录”哪个插件被加载了”,更是能力注册中心。当一个插件被加载后,它会通过 Registry 的 API 向系统注册自己的能力:
registerAgentHarness()—— 注册 Agent 运行时registerPluginCommand()—— 注册可执行命令registerMemoryEmbeddingProvider()—— 注册向量嵌入能力registerInternalHook()—— 注册事件钩子registerCompactionProvider()—— 注册上下文压缩器
这些注册信息构成了 Gateway 的运行时能力表。Router 在分发消息时、Agent 在选择工具时、Node 在执行指令时,都会查询这张表。
1.5.6 安全策略与诊断:加载时的”安检”,不是运行时的”隔离”
export function prepareGatewayPluginLoad(params: GatewayPluginBootstrapParams) {
const loaded = loadGatewayPlugins({ … });
params.beforePrimeRegistry?.(loaded.pluginRegistry);
if (loaded.pluginRegistry.diagnostics.length > 0) { logGatewayPluginDiagnostics({ ... });
} }
Registry 的安全模型是加载时准入控制,而非运行时沙箱隔离。由于所有 bundled plugin 都被 import() 进同一个 Node.js 进程,它们与 Gateway 共享内存空间,不存在容器级或进程级的隔离。
安全由以下策略保证:
plugins.allow白名单:只有明确信任的插件 ID 才能自动加载- Provenance 来源检查:检查插件是否有合法的安装记录或加载路径来源
- Diagnostics 诊断收集:每个插件的加载状态、错误、警告都会被记录到
pluginRegistry.diagnostics中,并通过 Gateway 日志输出
例如,当 plugins.allow 为空时,本地开发的 openclaw-weixin 会被标记为 untracked local code——Registry 不会阻止它运行,但会明确提醒用户尚未建立正式信任关系。
Registry 小节的一句话收尾
Registry 是 Gateway 的”免疫系统“和”能力注册中心”。它发现插件、验证契约、同进程加载、注入运行时依赖、注册系统能力、输出安全诊断——所有这些工作,都是为了让 Gateway 在收到第一条消息之前,就已经知道”有哪些 Agent 可用、有哪些工具可调用、有哪些渠道在线”。
1.6.1 定位
Router 是 Gateway 的”导航仪”。它不看消息内容的语义,只看 SessionKey、AccountId、PeerId 等路由元数据,决定这条消息应该交给哪个 Agent 处理。
1.6.2 核心源码
src/routing/resolve-route.ts—resolveAgentRoute(),路由决策核心src/routing/session-key.ts—buildAgentSessionKey(),buildAgentPeerSessionKey()
1.6.3 关键行为
1.6.3.1 路由优先级链(从高到低)
resolveAgentRoute() 内部按以下固定优先级匹配 bindings:
binding.peer— 精确匹配某个用户/群组/话题binding.peer.parent— 子话题继承父级绑定binding.peer.wildcard— 通配符匹配 peerbinding.guild+roles— Discord 按服务器+角色路由binding.guild— 按服务器路由binding.team— 按团队/工作区路由binding.account— 按账号级默认绑定binding.channel— 按渠道级默认绑定default— 配置中的全局默认 Agent
如果没有任何 binding 命中,最终回落到 default。
1.6.3.2 ResolvedAgentRoute 输出结构
Router 的产出不是一个简单的 agentId 字符串,而是一个结构化对象:
{
agentId: string; // 目标 Agent
channel: string; // 来源渠道
accountId: string; // 账号 ID
sessionKey: string; // 当前会话的持久化键
mainSessionKey: string; // 主会话键(DM 场景下的快捷别名)
lastRoutePolicy: “main” | “session”; // 哪个 session 接收 last-route 更新
matchedBy: “binding.peer” | … | “default”; // 匹配原因(用于调试)
} 1.6.3.3 SessionKey 的生成规则
buildAgentSessionKey() 根据 agentId、channel、accountId、peer 和 dmScope 生成标准化的 session key:
- 私聊(DM):
agent:main:telegram:456 - 群组:
agent:main:telegram:group:456 - 带 thread 隔离:
agent:main:telegram:456:thread:456:7
deriveLastRoutePolicy() 决定 lastRoute 应该更新到 mainSessionKey 还是当前 sessionKey,这直接影响多线程话题的上下文连续性。
1.6.4 常见误解
- 误解:Router 理解消息内容,基于语义做路由。
- 真相:Router 只看配置中的
bindings和消息元数据(chatId、threadId、senderId),完全不触碰MsgContext.Body。
一句话总结
Router 是 Gateway 的”地址簿”——它告诉 Dispatcher,这条消息应该投入哪个 Agent 的邮箱。
1.7.1 定位
Dispatcher 是 Gateway 的”交通信号灯“和”流式缓冲带”。它负责把 Channel 提交的消息有序地送进 Agent Runtime,同时把 Agent 的流式输出逐步回传给 Channel。
1.7.2 核心源码
src/auto-reply/reply/provider-dispatcher.ts—dispatchReplyWithBufferedBlockDispatcher()src/auto-reply/reply/dispatcher-registry.ts— 全局派发器跟踪src/gateway/server-lanes.ts— 并发车道控制
1.7.3 关键行为
1.7.3.1 同进程函数调用(不是独立服务)
dispatchReplyWithBufferedBlockDispatcher 是 Gateway 注入给 Channel Plugin 的函数。当 Telegram Plugin 收到消息后,直接 await 调用它:
// provider-dispatcher.ts
export async function dispatchReplyWithBufferedBlockDispatcher(params) {
return await dispatchInboundMessageWithBufferedDispatcher({ ctx: params.ctx,
cfg: params.cfg,
dispatcherOptions: params.dispatcherOptions,
}); }
全程没有 HTTP、没有 RPC、没有 WebSocket——就是一次函数调用。
1.7.3.2 会话级串行化(Concurrency Control)
Gateway 通过 Command Lane 机制控制并发:
// server-lanes.ts
setCommandLaneConcurrency(CommandLane.Main, resolveAgentMaxConcurrent(cfg));
setCommandLaneConcurrency(CommandLane.Cron, cfg.cron?.maxConcurrentRuns ?? 1);
setCommandLaneConcurrency(CommandLane.Subagent, resolveSubagentMaxConcurrent(cfg)); 这意味着:
- 同一个 Session 的消息是串行处理的(不会并发冲进同一个 Agent 会话导致上下文混乱)
- 不同 Session 的消息可以并行
- Cron 任务有独立的并发车道,避免阻塞主消息流
1.7.3.3 流式输出缓冲
Agent Runtime 生成回复时可能是流式的(block by block)。Dispatcher 内部维护缓冲逻辑:
- 接收
partial和block流式输出 - 按策略决定何时把中间状态推回 Channel 的
deliver回调 - Telegram 上你看到的”草稿气泡实时更新”,就是 Dispatcher 把 partial 输出推给 Channel 的结果
1.7.3.4 全局跟踪与优雅关闭
dispatcher-registry.ts 维护了一个全局的 activeDispatchers 集合:
export function getTotalPendingReplies(): number {
let total = 0;
for (const dispatcher of activeDispatchers) { total += dispatcher.pending();
} return total; }
这个机制确保 Gateway 重启前会等待所有正在处理的回复完成,避免消息半截中断。
-
常见误解
- 误解:Gateway 内部有 sequential / concurrent / sliding 三种队列模式。
- 真相:不存在这三种模式。实际的并发模型是 Command Lane 分级 + Session 级串行化。
一句话总结
Dispatcher 是 Gateway 的”调度员”——它按 Session 排队、按 Lane 分流、按 block 缓冲,确保消息不插队、回复不丢帧。
1.8.1 定位
WebSocket Hub 是 Gateway 与物理世界之间的唯一通道。它通过 WebSocket 连接 Device Nodes(你的手机/电脑)和 Control UI,让 Agent 能够远程调用本地硬件能力。
1.8.2 核心源码
src/gateway/server-ws-runtime.ts— WebSocket 处理器挂载src/gateway/server/ws-connection.ts— 连接生命周期管理docs/concepts/architecture.md— 官方协议说明
1.8.3 关键行为
1.8.3.1 默认地址与端口
WebSocket 服务与 Gateway HTTP 服务器共用同一端口(默认 ws://127.0.0.1:18789)。不需要额外端口。
1.8.3.2 连接的两类对象
类型
标识
用途
Device Node
role: "node" + caps/commands
提供摄像头、GPS、剪贴板、本地 shell 等硬件能力
Control UI
client=openclaw-control-ui
实时查看会话状态、发送控制命令、订阅系统事件
你的日志中可以看到:
[ws] webchat connected conn=5580007b… remote=127.0.0.1 client=openclaw-control-ui
[ws] ⇄ res ✓ node.list 1968ms
[ws] ⇄ res ✓ device.pair.list 1971ms 1.8.3.3 协议握手与通信模式
根据 docs/concepts/architecture.md,WebSocket 协议要求:
- 第一帧必须是
connect(带设备身份和 auth) - 握手后支持两种通信:
Request-Response:{type:"req", id, method, params} → {type:"res", id, ok, payload|error}
Server-Push Events:{type:"event", event, payload}
1.8.3.4 node.invoke 的完整调用链
当 Agent 调用本地设备时,数据流如下:
sequenceDiagram participant A as Agent Runtime
participant G as Gateway WebSocket Hub
participant DN as Device Node (iPhone)
participant HW as 本地硬件
A->>G: node.invoke("camera.takePhoto")
G->>G: 查找在线设备 session
G->>DN: WebSocket 推送指令帧
DN->>HW: 调用 iOS 相机 API
HW-->>DN: 照片数据
DN-->>G: WebSocket 返回结果帧
G->>A: 照片作为 Observation
注入 ReAct 循环
1.8.3.5 配对与信任
所有 WebSocket 客户端(无论是 Device Node 还是 Control UI)在 connect 时都必须提供设备身份。新设备需要配对批准(pairing approval),Gateway 会签发 device token 供后续连接使用。本地 loopback 连接可以自动批准,但 Tailnet/LAN 连接需要显式批准。
1.8.4 常见误解
- 误解:Gateway 的 WebSocket 是用来连接 Telegram/Discord 等消息渠道的。
- 真相:WebSocket 只连接 Device Node 和 Control UI。Telegram 的数据流完全不经过这里。
一句话总结
WebSocket Hub 是 Gateway 伸向物理世界的”手臂”——Agent 的
node.invoke指令通过它送达你的手机,设备的执行结果也通过它回到 Agent。
1.9.1 定位
Session Store 是 Gateway 的”记忆库”。它把会话状态、历史足迹、路由轨迹持久化到磁盘,保证 Gateway 重启或 Agent 切换后,上下文不会丢失。
1.9.2 核心源码
src/config/sessions/store.ts— 读写会话存储src/config/sessions/store-load.ts— 加载与缓存src/config/sessions/store-lock-state.ts— 并发锁控制src/plugin-sdk/reply-history.ts— 历史记录辅助函数
1.9.3 关键行为
-
文件系统持久化(不是数据库)
Session Store 默认使用文件系统存储(JSON 格式),路径由 resolveStorePath() 决定。每个 Agent 有自己的存储目录:
resolveStorePath(cfg.session?.store, { agentId })
-
维护的核心状态
每个 sessionKey 对应一个 SessionEntry,包含:
lastRoute/lastReply:上次由哪个 Agent 处理、上次回复内容transcript:会话转录摘要deliveryContext:包含 threadId、topic 等投递上下文systemSent:是否已发送过系统提示reasoningLevel:当前会话的推理级别设置
-
并发安全:写锁机制
由于多个消息可能并发触及同一个 session,Store 实现了写锁队列(acquireSessionWriteLock):
// store.ts import { acquireSessionWriteLock } from “../../agents/session-write-lock.js”; 同一个 session 的写操作会排队执行,避免文件竞争损坏。
-
缓存与性能优化
store-cache.ts 提供了内存缓存层:
getSerializedSessionStore()/setSerializedSessionStore()— 内存对象缓存writeSessionStoreCache()— 批量写回磁盘- 缓存失效与 Gateway 会话生命周期绑定
-
磁盘预算与自动清理
disk-budget.ts 实现了磁盘预算扫描:
- 自动清理过旧的 session 文件
pruneStaleEntries()删除长时间未活动的会话rotateSessionFile()轮转过大的存储文件
-
常见误解
- 误解:Gateway 深度理解并解析会话历史的内容语义。
- 真相:Gateway 只负责存储和检索历史记录的原始文本。历史内容的格式化(如 Envelope 字符串拼接)由 Channel Plugin 的 Context Builder 在入站时初始化。
一句话总结
Session Store 是 Gateway 的”硬盘”——它记住每个对话的轨迹,但不读信的内容,只负责把信封按地址归档和取出。
1.10.1 定位
Config & Cron 是 Gateway 的”中央司令部”。它维护全局配置,向所有插件和 Agent 提供统一的运行时参数,并调度定时任务(如 heartbeat、summary、cron job)。
1.10.2 核心源码
src/gateway/server-cron.ts— Cron 服务构建src/gateway/server-plugin-bootstrap.ts— 配置加载与插件启动src/gateway/server.impl.ts— Gateway 启动时配置初始化
1.10.3 关键行为
-
全局配置源
OpenClawConfig
Gateway 在启动时加载配置(loadConfig()),生成一个全局的 OpenClawConfig 对象。这个配置是只读的,Registry、Router、Agent Runtime 都会从中读取:
cfg.agents.list— 可用 Agent 列表cfg.bindings— 路由绑定规则cfg.channels— 渠道配置cfg.plugins— 插件安全策略(plugins.allow/plugins.deny)cfg.cron— 定时任务配置
-
Cron 服务:
CronService
buildGatewayCronService() 构建了一个完整的 Cron 调度器:
// server-cron.ts
const cron = new CronService({
storePath,
cronEnabled,
cronConfig: params.cfg.cron,
defaultAgentId,
// …
}); Cron 任务可以触发:
runHeartbeatOnce()— 定时 heartbeat(保持 Agent 活跃)runIsolatedAgentJob()— 运行隔离的 Agent 任务(如定时 summary、日报)enqueueSystemEvent()— 向系统事件队列注入 Cron 触发的事件
-
Cron 的会话模型
Cron 任务有自己独立的 session key:
- 默认格式:
cron:{job.id} - 也可以通过
sessionTarget: “session:xxx”指定复用某个现有会话 - Cron 运行时使用独立的 CommandLane.Cron,避免阻塞主消息流
-
Webhook 通知与失败告警
Cron 任务完成后支持多种通知方式:
delivery.mode = “webhook”— POST 到指定 URL(带 SSRF 防护)delivery.mode = “announce”— 通过 Channel 发送失败通知到指定聊天failureDestination— 自定义失败告警目标
// server-cron.ts 中的 SSRF 防护
await fetchWithSsrFGuard({ url: params.webhookUrl, init: { … } });
-
MCP Loopback Server
Gateway 还启动了一个 MCP(Model Context Protocol)回环服务器:
// 来自你的日志
[gateway] MCP loopback server listening on http://127.0.0.1:9692/mcp 这个本地 HTTP 服务为外部工具(如 IDE 插件)提供与 Agent 交互的标准化接口。
-
Control UI 的 HTTP 入口
除了 WebSocket 实时同步,Gateway 的 HTTP 服务器还提供:
/openclaw/canvas/— Canvas Host(Agent 可编辑的 HTML/CSS/JS)- Control UI 的静态资源和 API 端点
-
常见误解
- 误解:Cron 任务和正常消息共享同一个队列,会互相阻塞。
- 真相:Cron 运行在独立的
CommandLane.Cron上,有独立的并发配额(默认maxConcurrentRuns: 1),不会阻塞主消息通道。
一句话总结
Config & Cron 是 Gateway 的”大脑皮层和生物钟”——它记住所有规则、调度所有定时任务,并通过本地 HTTP 服务为外部控制界面提供入口。
总结
Gateway 的核心定位可以用一句话概括:它是 OpenClaw 的宿主进程与中央控制平面,负责插件加载、会话路由、消息派发、设备编排和全局配置管理,但绝不直接触碰外部聊天平台的 API,也绝不理解消息内容的语义。
回顾全文,我们围绕这一核心定位展开了三个层面的讨论:
第一,破除误解,建立正确的心智模型。 Gateway 不是微服务网关,不是 WebSocket 消息接入层(除 Device Node 外),也不是 LLM 推理的发生地。Bundled Channel(如 Telegram)与 Gateway 之间是同进程函数调用,而非 RPC 或 WebSocket;外部 Channel 进程可以通过 HTTP/IPC 接入,但本质上仍是 Gateway 的客户端。WebSocket 端口(默认 18789)只服务于 Device Node 和 Control UI,这是 Gateway 作为“设备编排者”的关键基础设施。
第二,深入内部机制,理解路由与派发的本质。 Registry 作为插件宿主,不仅加载 Channel Plugin 和 Skill Plugin,还扮演着能力注册中心与安全闸门的角色。Router 基于 binding.peer → binding.guild+roles → binding.account → binding.channel → default 的优先级链,将任意渠道进入的消息映射到目标 Agent。Dispatcher 则是注入到 Channel Plugin 中的函数,它把消息送入 Agent Runtime 的输入队列,并通过 CommandLane 实现会话级串行、跨会话并发的执行控制。
第三,看清状态与配置的边界。 Session Store 由 Gateway 维护,但历史内容的格式由 Channel Plugin 初始化时提供;它通过文件系统 JSON 与写锁机制保证持久化一致性。Cron 则运行在独立的 CommandLane.Cron 上,拥有专门的会话键和并发配额,与主消息流互不干扰。Config 作为全局只读配置源,贯穿插件、Agent 和调度器的整个生命周期。
理解 Gateway 的关键,在于始终把握“解耦”与“边界”这两个设计原则:Channel 不需要知道 Agent 的存在,Agent 不需要知道消息来自哪个平台,Device Node 不需要知道 Agent 跑在哪台服务器上——所有这些隔离与统一,都由 Gateway 在进程内部完成。它不是架构图中央那个最耀眼的明星,却是让整个 OpenClaw 系统得以稳定、可扩展运转的隐形中枢。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/271420.html