OpenClaw 源码解析(七):Gateway 控制平面与 WebSocket RPC 机制 1. 本期目标上一期我们分析了openclaw agent --message ...的执行链路。表面上看这条命令只是从命令行发送一条消息但源码中实际经历了参数校验、Agent 选择、Session 选择、模型覆盖、Gateway 调用、本地 fallback 等多个环节。这一期继续往下看为什么 OpenClaw 要把 Agent 请求交给 Gateway Gateway 在系统里到底扮演什么角色 CLI、Web UI、移动端节点、Channel 为什么都要连接 Gateway Gateway 的 WebSocket RPC 请求是如何组织的 Gateway 如何根据 method 找到对应处理函数OpenClaw 官方架构文档明确说明Gateway 是一个长期运行的进程负责维护消息渠道连接并通过 WebSocket API 暴露请求、响应和服务端推送事件CLI、Web UI、macOS app、自动化任务等控制平面客户端都通过 WebSocket 连接到 Gateway默认地址是127.0.0.1:18789。(OpenClaw)所以本期的核心问题可以概括为一句话Gateway 是 OpenClaw 的控制平面它把 CLI、Channel、Agent、Tools、Sessions、Nodes 和 Control UI 统一连接到一个长期运行的本地服务中。2. 为什么要单独讲 Gateway前面几期已经反复提到 Gateway但还没有真正拆开它。在 OpenClaw 里Gateway 不是一个普通 HTTP 后端也不是单纯的模型转发服务。它更像整个个人 AI 助手系统的“中枢”。官方架构文档中写到Gateway 是一个单一、长期运行的进程负责所有消息入口例如 WhatsApp、Telegram、Slack、Discord、Signal、iMessage、WebChat 等同时控制平面客户端通过 WebSocket 接入 Gateway节点设备也通过同一个 WebSocket 服务接入只是会声明role: node并携带自己的能力和命令。(OpenClaw)可以先用下面这张图理解CLI Web UI macOS App iOS / Android Node Telegram / Slack / Discord / WebChat Automation / Cron ↓ Gateway ↓ Agent Runtime Sessions Tools Skills Channels Nodes Config也就是说Gateway 的作用不是“替模型回答问题”而是统一接收请求 统一验证身份和权限 统一管理会话和状态 统一调度 Agent 运行 统一处理 Channel 连接 统一推送事件给客户端。这也是为什么上一期openclaw agent默认优先走 Gateway而不是直接在 CLI 进程里调用模型。3. Gateway 的运行模型OpenClaw 的 Gateway 是一个 always-on 进程。官方 Gateway runbook 中说明它是一个长期运行的进程承担路由、控制平面和 Channel 连接同一个端口同时承载 WebSocket 控制 / RPC、OpenAI-compatible HTTP API、插件 HTTP 路由、Control UI 和 hooks默认绑定模式是 loopback并且默认需要认证。(OpenClaw)这说明 Gateway 至少承担四类能力第一类控制平面 RPC 例如 health、status、agent、send、config、sessions、tools 等。 第二类消息渠道连接 例如 Telegram、Slack、Discord、WebChat 等。 第三类前端和可视化 例如 Control UI、Canvas、A2UI 等。 第四类兼容接口和插件接口 例如 /v1/models、/v1/chat/completions、/tools/invoke、插件 HTTP 路由等。所以它不是一个“启动一次就结束”的命令而是一个长期存在的服务进程。可以把 Gateway 理解成OpenClaw CLI 负责发起操作。 OpenClaw Gateway 负责维护长期状态、连接和运行时环境。 OpenClaw Agent 负责具体理解和执行任务。4. Gateway 为什么使用 WebSocketOpenClaw Gateway 的协议文档说明Gateway WS protocol 是 OpenClaw 的单一控制平面和节点传输协议。CLI、Web UI、macOS app、iOS / Android nodes、headless nodes 都通过 WebSocket 连接并在握手时声明自己的角色和权限范围。(OpenClaw)这里的关键点是OpenClaw 需要的不只是一次请求一次响应。它还需要Agent 执行过程中的流式事件 工具调用过程中的实时状态 会话变化通知 节点状态变化 Channel 连接状态 Gateway 健康状态 任务执行进度 长时间运行任务的事件推送。HTTP 请求当然也能做一部分工作但 WebSocket 更适合长期连接和服务端主动推送。官方架构文档中的单客户端连接生命周期也体现了这一点客户端先connectGateway 返回hello-ok随后 Gateway 可以推送 presence、tick 等事件客户端发送req:agent后Gateway 先返回 accepted ack再通过event:agent推送流式执行事件最后返回 final summary。(OpenClaw)可以画成Client ↓ connect Gateway ↓ hello-ok Client ↓ req: agent Gateway ↓ res: accepted Gateway ↓ event: agent streaming Gateway ↓ res: final summary这就是 Gateway 不只是“收一个请求回一个结果”的原因。5. WebSocket 协议的基本格式OpenClaw Gateway 协议使用 WebSocket 文本帧内容是 JSON payload。协议文档中明确说明第一个 frame 必须是connect请求握手成功后请求、响应和事件分别使用固定结构。(OpenClaw)基本结构可以简化为{ type: req, id: ..., method: agent, params: {} }响应结构是{ type: res, id: ..., ok: true, payload: {} }事件结构是{ type: event, event: agent, payload: {} }官方架构文档也把 wire protocol 总结为请求是{type:req, id, method, params}响应是{type:res, id, ok, payload|error}事件是{type:event, event, payload, seq?, stateVersion?}。(OpenClaw)这三个结构非常重要。后续看源码时只要看到某个方法名比如agent chat.send sessions.list config.get tools.invoke channels.status就可以理解为客户端发出一个 method Gateway 根据 method 找到对应 handler handler 执行后通过 res 或 event 返回结果。6. Gateway 握手为什么第一个请求必须是 connectGateway 协议规定第一个 frame 必须是connect请求。握手时客户端会声明协议版本、客户端信息、role、scopes、caps、commands、auth、device 等信息Gateway 返回hello-ok其中包含协议版本、server 信息、features、snapshot、auth 和 policy。(OpenClaw)可以理解为普通请求 我想调用某个功能。 connect 请求 我是谁 我是什么角色 我有哪些权限 我支持哪些能力 我如何认证 Gateway 当前支持哪些方法和事件握手成功后Gateway 才能知道这是 CLI 还是节点 这是 operator 还是 node 它有没有 operator.read / operator.write 权限 它能不能调用 agent 它能不能调用 config.set 它能不能作为 node 暴露 camera、canvas、screen 等命令所以 Gateway 的第一步不是“执行请求”而是“建立可信连接上下文”。7. role、scope 和 device pairingOpenClaw Gateway 不是所有连接都一视同仁。官方架构文档说明所有 WebSocket 客户端都需要携带 device identity新 device id 需要 pairing approval直接本地 loopback 连接可以自动批准以保证同主机体验流畅非本地连接仍然需要显式批准Gateway auth 对本地和远程连接都适用。(OpenClaw)这里可以拆成三层role 连接是什么角色例如 operator 或 node。 scope 这个角色具体有哪些权限例如 operator.read、operator.write、operator.admin。 device identity / pairing 这个连接来自哪个设备这个设备是否被批准。对于 CLI 来说它通常是控制平面客户端。对于移动端节点来说它会声明role: node并声明自己具备哪些 caps 和 commands例如 camera、canvas、screen、location、voice 等。协议文档给出的 node 示例中node 会在 connect 参数中声明caps和commands。(GitHub)这说明 Gateway 的设计不是只靠一个 token而是结合了连接认证 角色识别 权限范围 设备身份 设备配对 本地 / 远程连接策略。这对于一个可以控制本地工具、消息渠道和设备节点的个人 AI 助手系统非常重要。8. Gateway 方法method 是如何被分发的理解 Gateway 的关键是理解 method 到 handler 的映射。在src/gateway/server-methods.ts中OpenClaw 定义了大量 Gateway RPC 方法并通过createLazyCoreHandlers进行懒加载注册。例如源码中可以看到health、status、channels.status、chat.history、chat.send、config.get、config.set、tools.invoke、sessions.list、sessions.send、agent、agents.list等方法都会被注册到coreGatewayHandlers中。(GitHub)这说明 Gateway 的方法体系大致是这样的health / status ↓ 健康检查和状态查询 channels.* ↓ 渠道状态、启动、停止、登出 chat.* ↓ 聊天历史、发送、注入、终止 config.* ↓ 配置读取、修改、schema 查询 sessions.* ↓ 会话列表、会话创建、会话发送、会话重置、会话压缩等 tools.* ↓ 工具目录、有效工具、工具调用 agent / agent.wait ↓ 触发 Agent run 或等待 Agent 结果 agents.* ↓ Agent 列表、创建、更新、删除、文件读写换句话说Gateway 并没有把所有请求都写在一个巨大的switch里而是通过 method registry 和 handler 模块组织起来。9. lazy handler 的意义源码中server-methods.ts使用了lazyHandlerModule和createLazyCoreHandlers。从代码结构看每一类 handler 都通过动态 import 加载例如 agent 相关方法加载./server-methods/agent.jssessions 相关方法加载./server-methods/sessions.jsconfig 相关方法加载./server-methods/config.js。(GitHub)这是一种很常见的大型系统设计Gateway 启动时 先建立方法注册表。 真正请求到来时 再加载对应 handler 模块。这样做有几个好处第一降低启动时加载压力。 第二让不同方法模块保持独立。 第三避免所有 Gateway 功能挤在一个文件里。 第四插件或扩展方法更容易接入。 第五后续调试时可以按 method 找到对应源码文件。比如当我们看到method: agent就可以沿着源码找到src/gateway/server-methods.ts ↓ loadAgentHandlers ↓ src/gateway/server-methods/agent.ts当看到method: sessions.send就可以找src/gateway/server-methods.ts ↓ loadSessionsHandlers ↓ src/gateway/server-methods/sessions.ts这样就形成了非常清晰的源码阅读路径。10.handleGatewayRequest的处理流程Gateway 收到一个请求后并不是直接执行 handler。server-methods.ts中的handleGatewayRequest会先构建或获取 method registry然后做权限校验、启动期不可用判断、控制平面写操作限流、handler 查找最后才调用具体 handlerhandler 调用还会包在 plugin runtime gateway request scope 里以便插件运行时和子 Agent 等场景可以在正确的 Gateway 请求上下文中执行。(GitHub)可以简化成下面这条链路收到 req ↓ 读取 method ↓ 构建 method registry ↓ authorizeGatewayMethod 权限检查 ↓ 判断 Gateway startup 阶段该 method 是否可用 ↓ 如果是控制平面写操作检查 rate limit ↓ 根据 method 找 handler ↓ 找不到则返回 unknown method ↓ 找到则执行 handler ↓ 返回 res 或推送 event这说明 Gateway 请求处理至少有四层保护第一层连接握手和认证。 第二层role / scope 方法权限检查。 第三层启动状态和方法可用性检查。 第四层控制平面写操作限流。所以 Gateway 不是简单“method 反射调用”而是带有控制面安全边界的 RPC 分发系统。11.agentRPC 在 Gateway 中的位置上一期我们讲过openclaw agent默认会通过 Gateway 发送一次agent请求。在 Gateway 方法注册表里agent、agent.identity.get、agent.wait会被挂载到 agent handler 模块。server-methods.ts中可以看到这几个 method 由loadAgentHandlers负责加载。(GitHub)而src/gateway/server-methods/agent.ts中可以看到大量与 Agent run 相关的逻辑包括 session key 解析、agent workspace 解析、sandbox 配置、模型支持能力、delivery plan、chat abort controller、任务状态跟踪、dedupe key、dispatchAgentRunFromGateway等。(GitHub)所以agentRPC 不是简单调用一次模型而是会处理这次请求属于哪个 agent 这次请求属于哪个 session 是否需要模型覆盖 是否需要 reset session 是否需要处理附件 是否需要 delivery 是否需要 task tracking 是否需要去重 是否需要处理 abort 是否需要更新 session 状态 是否需要广播 sessions.changed 是否需要通过 Channel 投递回复。这也解释了为什么 OpenClaw 要通过 Gateway 来承载 Agent run因为 Agent 的运行涉及的不只是模型本身还涉及系统状态、会话、投递、任务、权限、工具、渠道和事件。12. accepted、streaming 和 finalOpenClaw 的 Agent 请求不是只有一个最终响应。官方架构文档中的连接生命周期显示客户端发送req:agent后Gateway 会先返回一个 accepted ack其中包含runId和状态随后通过event:agent推送流式执行事件最后返回 final其中包含 runId、status 和 summary。(OpenClaw)这可以理解为三阶段第一阶段accepted Gateway 已经接受请求并分配 runId。 第二阶段streaming events Agent 运行中不断产生增量事件、状态事件或工具事件。 第三阶段final Agent run 结束返回最终状态和摘要。这种设计特别适合长任务。比如用户让 Agent 做一个多步骤任务读取文件 调用工具 分析日志 生成报告 发送到某个 Channel。如果只等最终响应用户体验会很差。通过 event stream客户端可以实时展示Agent 开始运行 正在调用工具 工具返回结果 正在生成回答 任务已完成或失败。13. Gateway 和配置热重载Gateway 还有一个非常重要的能力配置热重载。官方 runbook 中说明Gateway 会监听 active config file path默认 reload mode 是gateway.reload.modehybrid。第一次成功加载后运行中的进程会服务 active in-memory config snapshot后续成功 reload 会原子替换这个 snapshot。(OpenClaw)这说明 OpenClaw 的配置不是“启动时读一次就结束”。它的模型更接近openclaw.json ↓ Gateway 读取配置 ↓ 形成内存配置快照 ↓ 运行时使用该快照 ↓ 配置文件变更 ↓ 校验成功后原子替换快照这个设计很适合长期运行的个人助手系统。因为用户可能随时调整模型 provider 默认 agent Channel allowlist dmPolicy sandbox 配置 tools 配置 cron 任务 UI / media / networking 配置。如果每次改配置都要手动重启 Gateway使用体验会比较差。14. Gateway 和 Control UI 的关系第二期和第三期提到过 Control UI。官方配置文档说明用户可以打开127.0.0.1:18789使用 Config tabControl UI 会根据 live config schema 渲染表单并提供 Raw JSON editorGateway 还暴露config.schema.lookup来获取某个路径下的 schema 节点和子项摘要。(OpenClaw)这说明 Control UI 不是独立系统而是 Gateway 的一个可视化客户端。可以理解为Control UI ↓ WebSocket / Gateway API Gateway ↓ config.get / config.set / config.schema.lookup ↓ openclaw.json / in-memory config snapshot所以后续分析 UI 时重点不是“前端页面怎么画”而是前端调用了哪些 Gateway method Gateway 返回了哪些 schema 和状态 配置修改如何通过 Gateway 写回 Gateway 如何热重载这些配置。15. Gateway 和 Channel 的关系OpenClaw 的 Channel例如 Telegram、Slack、Discord、WebChat 等也由 Gateway 统一管理。官方架构文档中说Gateway 维护 provider connections并发出 agent、chat、presence、health、heartbeat、cron 等事件。(OpenClaw)这意味着 Channel 并不是各自独立调用模型。更合理的理解是外部平台消息 ↓ Channel adapter ↓ Gateway ↓ Session / Agent / Tools ↓ Gateway ↓ Channel adapter ↓ 外部平台收到回复所以 Gateway 是消息入口与 Agent Runtime 之间的中间层。这也是 OpenClaw 能够支持多渠道接入的原因不同 Channel 只负责把平台消息转换成内部消息事件核心调度逻辑仍然回到 Gateway。16. Gateway 和 Nodes 的关系Nodes 是 OpenClaw 的另一类重要客户端。官方架构文档说明macOS、iOS、Android、headless nodes 也连接到同一个 WebSocket 服务但会声明role: node并带有明确的 caps 和 commands例如canvas.*、camera.*、screen.record、location.get等。(OpenClaw)这说明 Gateway 还承担“设备能力路由”的角色。可以理解为Agent 想拍照 / 读取位置 / 操作 Canvas ↓ Gateway 判断哪个 node 有能力 ↓ 向 node 发送命令 ↓ node 执行后返回结果 ↓ Gateway 把结果交给 Agent这也是 OpenClaw 和普通 Chatbot 的差别之一它不仅能在文本里回答还能通过节点设备扩展输入输出能力。17. 从 Gateway 看 OpenClaw 的系统边界通过这一期可以看到OpenClaw 的系统边界非常清楚CLI 发起命令适合人类或脚本操作。 Gateway 长期运行维护连接、状态、RPC、事件、安全边界。 Agent 负责理解任务、调用模型、组织工具和技能。 Channel 负责连接外部消息平台。 Node 负责暴露设备能力。 Control UI 负责可视化管理 Gateway 和 Agent 状态。最核心的是 Gateway。它把这些模块连接起来CLI / UI / Node / Channel ↓ Gateway ↓ Agent / Session / Tool / Config / Delivery所以读 OpenClaw 源码时Gateway 这一层必须先看懂。18. 本期源码阅读建议这一期建议重点看下面几个文件src/gateway/server-methods.ts ↓ 看 Gateway method 如何注册、懒加载、鉴权和分发。 src/gateway/server-methods/agent.ts ↓ 看 agent RPC 如何转化为一次 Agent run。 src/gateway/server/ws-connection.ts ↓ 看 WebSocket 连接如何建立和管理。 src/gateway/server/ws-connection/message-handler.ts ↓ 看 Gateway 如何处理 WebSocket frame、connect、request 和 event。 docs/gateway/protocol.md ↓ 对照理解 Gateway WebSocket 协议。 docs/concepts/architecture.md ↓ 从整体架构角度理解 Gateway、Client、Node、WebChat 的关系。阅读时可以带着几个问题1. connect 请求什么时候完成认证 2. method 是如何映射到 handler 的 3. agent 请求为什么需要 idempotencyKey 4. Gateway 什么时候返回 accepted什么时候推送 event 5. 哪些请求属于控制平面写操作 6. role 和 scope 如何限制 method 调用 7. sessions、tools、config、channels 方法分别对应哪些 handler 文件19. 我的理解我认为 Gateway 是 OpenClaw 最关键的架构层。如果没有 GatewayOpenClaw 很容易退化成一个 CLI 包装的大模型调用工具openclaw agent ↓ 调用模型 ↓ 输出文本但有了 Gateway 之后OpenClaw 才真正变成一个个人 AI 助手系统长期运行 多渠道接入 多客户端连接 多会话管理 工具调用 设备节点 事件推送 配置热重载 权限控制 任务追踪。所以理解 OpenClaw不能只盯着“模型怎么调用”还要看“Gateway 如何把模型调用放进一个长期运行的系统里”。20. 本期重点理解这一期可以总结为五点第一Gateway 是 OpenClaw 的控制平面是一个长期运行的本地服务进程。 第二CLI、Web UI、macOS app、iOS / Android nodes、headless nodes 都通过 WebSocket 连接 Gateway。 第三Gateway 协议使用 req / res / event 三类 JSON frame第一个请求必须是 connect。 第四Gateway 通过 method registry 和 lazy handler 把 agent、sessions、config、tools、channels 等方法分发到不同源码模块。 第五agent RPC 不是简单模型调用而是会涉及 session、delivery、dedupe、task tracking、abort、workspace、sandbox 和事件推送等系统级逻辑。一句话概括Gateway 把 OpenClaw 从一个命令行 AI 工具提升为一个长期运行、多客户端、多会话、多渠道、可扩展的个人 AI 助手系统。21. 本期小结本期主要分析了 OpenClaw 的 Gateway 控制平面。Gateway 是一个长期运行的本地服务默认通过127.0.0.1:18789对 CLI、Web UI、macOS app、iOS / Android nodes 和 headless nodes 提供 WebSocket 控制平面。Gateway 协议使用 JSON 文本帧包含req、res和event三类消息连接建立时必须先发送connect请求以声明客户端身份、角色、权限、认证和设备信息。源码中Gateway 通过server-methods.ts注册大量 RPC method并使用 lazy handler 将不同方法分发到agent、sessions、config、tools、channels等模块。对于agent请求Gateway 会进一步处理 session、workspace、delivery、dedupe、task tracking 和事件推送因此它承载的是完整 Agent run而不是一次简单模型补全。这一期可以用一句话总结OpenClaw 的 Gateway 是整个系统的运行中枢所有客户端先连接 Gateway所有核心能力通过 Gateway method 进入系统所有长期状态和实时事件也由 Gateway 统一维护。下一期可以继续分析OpenClaw 源码解析八Session 会话模型与 sessionKey 设计下一期重点看sessionKey、sessionId、main session、channel session、subagent session、sessions store、transcript 文件和sessions.*Gateway 方法理解 OpenClaw 如何把不同用户、不同渠道、不同 Agent run 组织成可追踪的长期会话。