系统架构详解

深入理解 OpenClaw 的 Gateway、Pi Agent、WebSocket 与会话模型

21 min read Part of OpenClaw 龙虾篇 · Ch. 3
← 上一层级:学习路径 · Part 04 · OpenClaw 实战

🦞 从消息进来到回复出去,OpenClaw 真正做的不是“调一次模型”,而是协调一整套长期运行的控制面、会话、队列、工具和渠道系统。

系统架构详解

flowchart LR
  A["系统架构详解"]
  A --> B["分类:OpenClaw"]
  A --> C["关键词:OpenClaw"]
  A --> D["关键词:架构"]
  A --> E["关键词:Gateway"]
  A --> F["关键词:Pi Agent"]

前两篇文章分别回答了两个问题:OpenClaw 是什么,以及怎么把它跑起来。到了这一篇,重点要从“使用者视角”切换到“系统视角”。

因为一旦你开始接真实频道、跑常驻 Gateway、调试消息延迟、分析为什么某条消息没有触发 agent、或者为什么群组里行为和私聊不同,你就会发现:OpenClaw 的复杂度并不主要来自模型本身,而是来自它如何组织消息、会话、工具、节点和控制面。

理解它的架构,不是为了背组件名,而是为了回答这些真正会在实践里遇到的问题:

  • 一条消息为什么会进到这个 session,而不是那个 session?
  • 为什么有时候消息会被合并,有时候会立刻触发?
  • 为什么一个渠道能流式输出,另一个却只能分块?
  • 为什么同样是“发消息给 agent”,CLI、WebChat 和真实渠道的路径并不完全一样?
  • 为什么 OpenClaw 如此强调 Gateway,而不是把所有逻辑都塞进一个前端或一个脚本里?

这篇文章会把 OpenClaw 拆成几个核心层面来看:Gateway、Pi 集成、WebSocket 协议、消息与会话模型、媒体与流式传输、配置与安全边界。 你不需要记住每一个细节,但需要建立一个足够准确的整体心智模型。

一张更接近真实的架构图

如果只用一句话概括 OpenClaw 的主干架构,可以写成:

消息渠道负责接入,Gateway 负责拥有状态与控制平面,agent 运行时负责推理与工具执行,结果再经由 Gateway 回写到原始渠道。

但这句话还不够具体。更接近实际情况的结构,大致是这样:

Channels (WhatsApp / Telegram / Slack / Discord / Signal / WebChat / ...)


Gateway (single long-running daemon)
  - WS control plane
  - HTTP endpoints
  - session ownership
  - routing / queue / presence / health
  - config / hooks / cron

        ├── Embedded Pi agent sessions
        ├── CLI clients
        ├── Control UI / WebChat
        ├── macOS app
        └── Nodes (macOS / iOS / Android / headless)

这里最关键的不是“组件有多少”,而是三件事:

  1. Gateway 是单个长期运行的中心进程
  2. 客户端和节点都主要通过同一个 WebSocket 控制面接入
  3. agent 能力不是外挂在外面的一个聊天脚本,而是被嵌进整个 Gateway 架构里。(docs.openclaw.ai)

这三点一旦理解了,后面很多设计就都顺了。

Gateway:OpenClaw 真正的中枢,不只是“一个端口服务”

官方架构文档对 Gateway 的定义很直接:单个长期运行的 Gateway 拥有所有消息平台连接,客户端通过配置的绑定主机上的 WebSocket 接入,节点也通过同一个 WS 服务器连接。每台主机通常只有一个 Gateway,它是唯一打开 WhatsApp 会话的位置。(docs.openclaw.ai)

这段话非常关键,因为它说明 Gateway 不是简单的“API 网关”或“CLI 后端”,而是整个系统的状态拥有者。

Gateway 具体负责什么

如果把它的职责拆开,可以大致分成六类:

1. 渠道连接的拥有者

WhatsApp、Telegram、Slack、Discord、Signal、WebChat 等渠道,最终都不是由前端页面直接连,而是由 Gateway 持有连接、维护登录态、处理入站与出站。(docs.openclaw.ai)

这意味着渠道状态是中心化持有的。 好处是统一、可管理;代价是 Gateway 成为关键点。

2. WebSocket 控制平面

Gateway 暴露类型化的 WS API,请求、响应和服务器推送事件都通过这个协议完成。文档里明确提到它会发出诸如 agentchatpresencehealthheartbeatcron 等事件,并对入站帧按 JSON Schema 做校验。(docs.openclaw.ai)

从工程上看,OpenClaw 的“控制层语言”不是某个前端内部状态,而是一个正式的协议层。

3. 会话和消息路由的拥有者

Session 由 Gateway 拥有,而不是由某个客户端拥有。无论消息来自哪一个前端、哪一个渠道、哪一个设备,最终会不会落到同一段对话上下文,是由 Gateway 的会话键与路由逻辑决定的。(docs.openclaw.ai)

4. 事件与自动化入口

Gateway 不只是管聊天。它还承担:

  • hooks / Webhooks
  • cron 触发
  • health / heartbeat
  • presence
  • 某些系统级状态广播(docs.openclaw.ai)

这意味着它更像“消息和自动化总线”,而不只是“对话代理”。

5. HTTP 与 UI 承载

当前版本里,Gateway 绑定到同一端口时,不只是提供 WebSocket,也同时承载 HTTP 服务,包括控制界面、hooks、A2UI 等;Canvas 文件服务则默认跑在单独端口 18793。(docs.openclaw.ai)

这比“WS 在一个端口、HTTP 在另一个端口”的印象更准确。 OpenClaw 这里用的是单端口多路复用思路。

6. 配置加载与热重载

官方 Gateway 手册明确写到,配置默认会监视 ~/.openclaw/openclaw.json,采用 gateway.reload.mode="hybrid":安全变更会热应用,关键变更则触发重启;也可以显式关闭热重载。(docs.openclaw.ai)

这一点值得特别纠正。很多旧式理解会写成“改配置后通常需要手动重启 Gateway”。这不能算完全错,但已经不够准确了。更成熟的表述应该是:

OpenClaw 现在并不是“完全静态配置 + 全量手动重启”的模型,而是部分支持配置热重载,只是关键变更仍可能触发进程内重启。

这在实际运维里是很重要的差别。

Gateway 为什么必须是“一个长期运行的中心进程”

这不是实现偏好,而是架构要求。

如果 Gateway 不是长期运行、集中拥有状态,那么下面这些事情都会变得混乱:

  • 每个渠道都要自己维护登录态
  • 会话会被不同前端各自切碎
  • 队列和去重没法统一
  • 多个设备很难共享同一个助手状态
  • Webhooks / Cron 无法和消息上下文自然衔接
  • 节点配对、设备信任、presence、health 都难以形成统一事实来源

所以你可以把 Gateway 理解成: OpenClaw 的“系统内核”不在 GUI,而在守护进程。

这也是为什么官方一直推荐你把它当作用户服务来运行,而不是只在终端里临时敲一条命令。(docs.openclaw.ai)

Pi Agent:最值得纠正的点,不是“它很强”,而是“它不是 RPC 外挂进程”

原文里把 Pi Agent 写成“以 RPC 模式运行,并由 Gateway 按需唤起”,这个说法在今天已经不准确了。

官方的 Pi 集成架构 页面写得很明确: OpenClaw 不是把 pi 作为子进程生成,也不是通过 RPC 模式使用,而是通过 createAgentSession() 直接导入并实例化 pi 的 AgentSession(docs.openclaw.ai)

这意味着什么?

1. Pi 更像嵌入式运行时,而不是外部服务

这是一条非常重要的架构线索。 很多系统会把 agent 当作“外部 worker”或“独立推理服务”,然后由控制层通过 RPC 去调它。OpenClaw 当前不是这么做的。它更像是:

  • Gateway 拥有总体状态
  • agent session 作为运行时实体被嵌入进来
  • OpenClaw 在其上注入渠道特定工具、系统提示和会话逻辑

这种方式的直接收益包括:

  • 更强的会话生命周期控制
  • 更容易做渠道与上下文定制
  • 更容易注入本地工具与系统能力
  • 持久化、压缩、分支等会话机制更自然

2. “嵌入式”比“RPC”更利于 OpenClaw 这种场景

对于一个强调:

  • 多渠道
  • 会话归属
  • 流式事件
  • 工具调用
  • 设备节点
  • 长期上下文

的系统来说,嵌入式 agent 有很强的工程合理性。因为这类系统的复杂度主要不是“单次推理吞吐”,而是“协调一致性”。

RPC 架构当然也有好处,例如更容易做强进程隔离、独立扩缩容、跨主机分布式运行。但 OpenClaw 当前显然更重视的是:

  • 对会话内部状态的掌控
  • 对事件与工具流的整合
  • 对上下文与渠道行为的精细定制

3. 这也解释了为什么它不是“纯模型路由器”

如果 Pi 是嵌入式 session,而不是外部模型代理,那么 OpenClaw 的 agent 层就不只是“把 prompt 发给模型再拿回文本”。它还承担:

  • 会话状态维护
  • 系统提示拼装
  • 工具注入
  • 分支与压缩
  • provider/profile 选择和切换
  • 流式事件回传

这比“转发一条模型请求”要深得多。(docs.openclaw.ai)

会话模型:OpenClaw 真正复杂的地方之一

OpenClaw 的消息与投递文档里有一句非常关键的话:

Session 由 Gateway 拥有,而不是客户端。直接聊天会合并到 agent 主会话键,群组 / 频道则有各自的会话键。(docs.openclaw.ai)

这句话看起来抽象,但它实际上决定了很多用户会直接感知到的行为差异。

1. 私聊与群组,本来就不是同一种会话

在很多普通聊天产品里,用户容易把“一个联系人”和“一个会话”画等号。 在 OpenClaw 里,这种等号并不稳定。

更准确的理解是:

  • 私聊 往往会落到主会话键或其直接派生
  • 群组 / 频道 / 房间 会有独立会话键
  • 不同渠道 也可能映射到不同的 session scope
  • 同一 agent 可以服务多个会话,但上下文并不会自动全局共享

这背后有很强的工程理由。 因为群组和私聊在触发规则、上下文语义、历史长度、回复线程和风险模型上都不一样。如果强行把它们塞进同一会话,结果通常会更差。

2. “主会话”不是“唯一会话”,而是默认锚点

很多文章会把 main session 写成“默认主会话”,这没错,但还不够。

更接近实际的说法是: 主会话是默认锚点,而不是系统里唯一重要的会话。

因为一旦进入:

  • 群组
  • 多线程
  • 多渠道
  • 节点触发
  • 自动化触发
  • 后台任务

会话就会开始分化。 这也是为什么 Control UI 和 TUI 被官方文档称为 Gateway 支持的会话记录“事实来源”。(docs.openclaw.ai)

3. 历史上下文并不等于“所有消息无脑拼接”

文档专门区分了:

  • Body:发给 agent 的提示文本
  • CommandBody:用于命令解析的原始文本
  • RawBody:兼容保留字段(docs.openclaw.ai)

同时,当渠道能提供历史时,OpenClaw 会用统一包装器,把“上次回复以来的聊天消息”和“当前消息”区分开来。对于群组消息,还会对当前消息加发送者标签前缀。(docs.openclaw.ai)

这说明一件非常重要的事:

OpenClaw 的会话不是“把所有文本堆进 prompt”这么简单,而是对当前消息、历史、命令文本和渠道包络做了分层处理。

这也是它能在多渠道、多会话场景里保持相对一致行为的原因之一。

入站消息不是立刻就跑 agent:去重、防抖、队列才是真实路径

如果你以为消息一到,马上就直接触发一轮推理,那会把 OpenClaw 理解得过于理想化。

真实路径里,至少还有三层重要机制:

1. 入站去重

官方文档指出,渠道在重连后可能重复投递消息,因此 OpenClaw 会维护一个短期缓存,以“渠道 / 账户 / 对端 / 会话 / 消息 ID”为键,避免重复消息触发第二次 agent 运行。(docs.openclaw.ai)

这类细节很不起眼,但在真实 IM 平台里极其重要。 没有去重,任何网络波动都可能导致“为什么它回复了两次”。

2. 入站防抖

同一发送者快速连续发多条纯文本消息时,OpenClaw 可以按渠道与会话作用域做防抖,把它们合并成同一个 agent 轮次;媒体或附件则会立即刷新,不参与纯文本防抖。(docs.openclaw.ai)

这个设计的意义非常现实:

  • 用户发三四条碎片消息,不必触发三四轮推理
  • 渠道体验更接近真人聊天节奏
  • 上游模型调用压力更低
  • 也减少了多轮连续回复造成的噪音

3. 队列与并发控制

当一个会话已经有运行中的任务时,新消息不一定会立即开第二轮。OpenClaw 的队列文档说明,它通过一个小型进程内队列来序列化入站自动回复运行,按会话键入队,保证每个会话同一时间只有一个活动运行;然后再进入全局通道,受 agents.defaults.maxConcurrent 等限制。(docs.openclaw.ai)

同时,它支持多种队列模式,例如:

这里最值得强调的不是模式名,而是背后的架构取舍:

OpenClaw 不是盲目追求“每条消息立刻并发处理”,而是优先避免会话冲突、共享资源竞争和上游限流。

这对一个执行型 agent 系统来说,往往比“极限并发”更重要。

“流式输出”不是单一功能,而是一整套分块与协议设计

原文提到 tool streaming 和 block streaming,这个方向是对的,但可以写得更准确一些。

官方消息文档明确说明:

  • OpenClaw 支持分块流式传输
  • 分块遵循渠道文本限制
  • 尽量避免拆分 fenced code block
  • blockStreamingDefaultblockStreamingBreakblockStreamingChunkblockStreamingCoalescehumanDelay 等配置项
  • 各渠道还可有自己的覆盖配置(docs.openclaw.ai)

这意味着“流式输出”在 OpenClaw 里不是“模型有 token 就往外吐”这么简单,而是至少涉及四层权衡:

1. 模型输出节奏

模型产生文本块、推理片段、工具事件。

2. Gateway 事件流

这些输出要被包装成 WS 事件或渠道输出节奏,供 CLI、WebChat、桌面端或真实渠道消费。

3. 渠道约束

不同渠道对长度、分块、编辑能力、草稿态支持不同。 比如某些渠道更适合真正的 streaming,某些则只能模拟成分块发送。(docs.openclaw.ai)

4. 可读性与噪音控制

流得太碎,读者体验很差;流得太粗,又失去“正在思考 / 正在执行”的即时感。 所以 OpenClaw 需要在“低延迟”和“可读性”之间找平衡。

这也是为什么它的 streaming 配置看起来比很多聊天产品更细。因为它面对的不是单一网页窗口,而是多个差异很大的渠道。

WebSocket 协议:OpenClaw 的真正控制语言

官方架构页已经把协议摘要写得相当明确:

  • 传输层是 WebSocket
  • 文本帧承载 JSON 载荷
  • 第一帧必须是 connect
  • 握手后使用请求 / 响应 / 事件三种语义
  • 如果设置了 OPENCLAW_GATEWAY_TOKEN 或配置 token,connect.params.auth.token 必须匹配,否则套接字关闭
  • 有副作用的方法如 sendagent 需要幂等键以便安全重试
  • 节点连接时必须声明 role: "node" 与能力/权限(docs.openclaw.ai)

这些信息很值得展开,因为它揭示了 OpenClaw 不只是“用 WebSocket 通信”,而是把 WebSocket 做成了系统级控制协议

为什么要强调“第一帧必须是 connect”

这不是形式主义。 它意味着:

  • 会话建立是显式的
  • 鉴权在协议一开始就发生
  • 客户端身份、节点身份、能力声明都在握手阶段完成
  • 服务端可以在非常早的阶段决定是否接受连接

对于一个既接客户端、又接节点、还允许远程接入的系统来说,这是很必要的严谨性。

事件驱动为什么重要

协议里不仅有 req / res,还有 event。 这背后的含义是:OpenClaw 不是简单的“客户端请求,服务端回一句”。它还需要持续推送:

这是一种非常典型的控制面思路: 状态不是只在请求时拉取,而是通过事件流持续同步。

对 CLI、Control UI、WebChat、节点来说,这比纯 REST 风格更自然。

幂等性为什么要进协议层

官方文档特别提到,有副作用的方法需要幂等键以安全重试。(docs.openclaw.ai)

这是一个很工程化、也很容易被忽略的点。 因为消息发送、agent 触发这类操作,一旦网络抖动、客户端超时重发,如果没有幂等保护,就会出现:

  • 发两次消息
  • 重跑两次 agent
  • 工具执行重复触发
  • 用户看到莫名其妙的重复响应

把幂等性纳入协议,而不是留给客户端“自己小心”,说明 OpenClaw 的架构已经在面对真实系统问题。

节点:不是普通客户端,而是能力提供者

架构文档里指出,节点通过同一个 WebSocket 服务器接入,但会在 connect 时声明 role: node,并暴露如 canvas.*camera.*screen.recordlocation.get 等命令。(docs.openclaw.ai)

这和普通客户端的差别非常大。

普通客户端主要是:

  • 发请求
  • 订阅事件
  • 展示状态
  • 触发 agent 或消息

而节点更像是:

  • 把设备能力注册给系统
  • 接受 Gateway 下发的能力调用
  • 成为 agent 可以使用的外设或传感器

从工程上看,节点是能力面的一部分,不只是另一个 UI。

这也解释了为什么 OpenClaw 会单独设计设备配对,而不是把它们一律当成普通登录设备。因为节点一旦被错误接入,风险不是“多了一个读消息的人”,而是“多了一个可被 agent 驱动的设备能力入口”。

配对与信任模型:OpenClaw 的架构里,安全不是后补功能

OpenClaw 的安全模型并不是只靠“你别暴露公网”一句话来兜底,而是在协议和渠道层都插入了显式的信任步骤。

官方配对文档把配对分成两类:

  1. 私信配对:谁被允许和机器人对话
  2. 节点配对:哪些设备 / 节点被允许加入 Gateway 网络(docs.openclaw.ai)

私信配对

当渠道配置为 DM pairing 策略时,未知发送者不会直接进入正常处理流程,而是先收到一个短代码,必须经所有者批准。文档中还给出了实现细节:

  • 配对码为 8 位大写字符
  • 1 小时过期
  • 默认每个渠道待处理请求有数量上限(docs.openclaw.ai)

这本质上是在做一件很重要的事: 把“谁能触发你的 agent”从默认开放,改成显式批准。

节点配对

节点作为 role: node 设备接入 Gateway 时,也会生成待批准请求,必须显式 approve。(docs.openclaw.ai)

从架构角度看,这一步极其必要。 因为节点能力一旦被误接入,后果远比“看见聊天历史”严重,可能涉及摄像头、屏幕、定位等敏感接口。

所以,如果要概括 OpenClaw 的安全姿态,一个准确的说法是:

它并不假设“网络是可信的”,而是尽量把关键入口变成显式信任建立过程。

媒体处理:真正难的不是“支持图片音频”,而是把异构输入变成统一上下文

原文提到媒体管道,但还可以写得更工程化一点。

对于 OpenClaw 来说,媒体处理的核心难题不是“会不会收附件”,而是如何把来自不同渠道、不同格式、不同大小限制、不同元数据质量的输入,整理成 agent 能稳定理解和工具能稳定消费的统一形态。

在文档中,这一层主要散落在消息、渠道与工具行为里,没有被包装成一个神秘的“智能媒体系统”。这反而说明它更务实:媒体是消息系统的一部分,不是一个独立炫技模块。(docs.openclaw.ai)

从架构角度看,它至少要处理这些问题:

1. 媒体到达方式不同

不同渠道可能给你:

  • 文件 ID
  • 下载 URL
  • 临时附件引用
  • 直接字节流
  • 不同质量的 mime/type 元数据

所以 Gateway 或渠道适配层必须先把这些差异抹平。

2. 媒体与文本触发规则不同

文档明确提到:纯文本可以防抖合并,但媒体/附件会立即刷新。(docs.openclaw.ai)

这背后其实是在说: 媒体在消息语义上通常是“强事件”,不能像碎片文本那样随便合并。

3. 临时文件与清理是现实问题

媒体不可能永远常驻内存;下载、缓存、转写、传给模型或工具之后,就会涉及临时文件管理和生命周期问题。 这一层往往不会体现在 flashy demo 里,但在长期运行的系统里非常关键。

4. agent 不应该感知每个渠道的媒体怪癖

媒体层做得好的标志,不是支持了多少文件格式,而是上层 agent 不必理解“这是从 Telegram 来的语音还是从 Slack 来的音频附件”。 它最好只看到一个统一后的输入对象或上下文包装。

Control UI、WebChat、CLI:三者都是客户端,但角色不同

很多人初看 OpenClaw,会把 CLI、WebChat、Control UI 当成“不同入口而已”。 这没错,但还不够。

更准确地说,它们都是 Gateway 控制面的客户端,只是各自承担不同角色:

CLI

最接近系统管理与脚本化控制。 适合:

  • onboarding
  • health / doctor
  • agent 测试
  • message send
  • pairing / devices
  • 配置与运维命令

WebChat

最轻量的聊天入口。 官方文档明确说,它是基于 Gateway WS API 的网页聊天界面,也是无需先配置真实频道就能快速开始第一次聊天的入口之一。(docs.openclaw.ai)

Control UI

更偏系统管理视图。 不是单纯“聊天窗口”,而是让你观察会话、状态、连接和某些配置层面的控制面界面。

这三者共用同一套控制平面,而不是各自有独立后端。 这正是 OpenClaw 架构一致性的体现。

配置系统:不是随便一个 JSON,而是受 schema 严格约束的系统契约

OpenClaw 的配置页明确说明: OpenClaw 只接受完全符合 schema 的配置。未知键、类型错误或无效值都会让 Gateway 拒绝启动;唯一的根级例外是 $schema(docs.openclaw.ai)

这件事很值得强调,因为它和很多“随便写点 JSON 试试看”的开源项目很不一样。

这背后有两层好处:

1. 配置变成系统契约,而不是模糊约定

这会提高:

  • 可验证性
  • 工具兼容性
  • 配置迁移可靠性
  • 出错时的明确性

2. 也会提高新手的失败感

这是实话。严格 schema 的代价是: 你写错不会“凑合运行”,而是直接拒绝启动。

但对 Gateway 这种关键系统来说,这通常是更好的选择。 因为“带着错误配置勉强跑起来”往往比“启动时就失败”更难查。

配置和热加载的关系

因为 Gateway 支持监视配置文件并按 hybrid 模式热应用或重启,所以配置不只是启动时读取一次的静态输入,而是运行时控制面的组成部分。(docs.openclaw.ai)

这意味着你应该把配置看作:

  • 系统行为定义
  • 安全边界定义
  • 渠道能力与默认值定义
  • agent / tools / queue / streaming 行为定义

而不是“几项安装参数”。

沙箱、工具策略、提权:三者不要混为一谈

原文提到“非 main session 可通过 Docker sandbox 运行”,方向对,但可以讲得更清楚。

官方安全文档明确区分了三套不同控制:

  1. 沙箱:工具在哪里运行(主机还是 Docker)
  2. 工具策略:哪些工具被允许调用
  3. 提权:仅限 exec 的主机逃逸通道(docs.openclaw.ai)

这三个概念在实践里极容易混淆。

沙箱不是“权限总开关”

你把 session 放进沙箱,解决的是执行位置与文件系统隔离的问题; 它不自动决定某个工具是否可用。

工具策略不是“运行位置”

allow / deny、profile、sandbox tools policy 决定的是哪些能力存在、哪些被硬阻止。 即使你 /exec,也不能绕过被 deny 的工具。(docs.openclaw.ai)

提权不是“万能后门”

提权只影响 exec,而且主要用于在沙箱隔离时允许某些命令在主机上执行;它不是 skill 级别的超级权限,也不会覆盖工具 allow/deny。(docs.openclaw.ai)

这一层架构设计说明 OpenClaw 在安全边界上相当克制: 它没有用一个模糊的“权限开关”糊弄过去,而是把“在哪里运行”“能运行什么”“是否允许某种逃逸”拆开建模。

一条消息到底是怎么走完的

到这里,可以把前面的组件重新串起来,看一条典型消息的真实生命周期。

1. 消息进入渠道适配层

用户在 WhatsApp、Telegram、Slack、Discord、Signal、WebChat 或其他渠道发送一条消息。

渠道适配层负责:

  • 接收平台事件
  • 解析平台身份和消息元数据
  • 下载或引用附件
  • 生成统一的入站表示

2. Gateway 做去重、绑定和会话键计算

Gateway 拿到入站后,不会立刻盲跑 agent,而是先做:

  • 去重
  • 防抖判断
  • 路由 / 绑定
  • 会话键映射
  • 触发规则判断(例如群组、提及、回复等)

3. 进入队列与并发控制

如果当前会话已有运行中的任务,新的入站要根据 queue mode 决定是:

  • steer 当前运行
  • followup 下一轮
  • collect 合并后续消息
  • 或采用其他变体(docs.openclaw.ai)

4. 组装当前 prompt / body

Gateway 会把:

  • 当前消息
  • 必要历史
  • 渠道包络
  • 发送者标签
  • 命令文本与正文区分
  • 会话状态

整理成 agent 需要的输入。

5. 嵌入式 Pi agent session 开始运行

此时才真正进入推理与工具执行阶段。 agent session 可以:

  • 调模型
  • 用工具
  • 发出流式文本块
  • 产生工具事件
  • 更新会话状态
  • 触发分支或压缩逻辑(docs.openclaw.ai)

6. Gateway 把流式事件转成客户端 / 渠道可消费形式

对于 CLI、WebChat、Control UI,会更多体现为 WS 事件流。 对于真实消息渠道,则要受平台能力约束,可能是:

  • 真流式
  • 分块发送
  • 编辑式更新
  • 或退化成最终整段发送

7. 出站回复写回原渠道

最后,Gateway 负责:

  • 使用原始渠道的发送能力回写
  • 维持 reply/thread 关系
  • 处理长度限制和 chunking
  • 根据平台能力做错误重试或退化处理

这整条链路的意义在于: OpenClaw 的“回复”不是某个模型函数返回一个字符串,而是一个跨渠道、跨协议、跨队列、跨会话的系统动作。

为什么理解这套架构后,排错会容易很多

如果你把 OpenClaw 理解成“一个本地聊天机器人”,排错时很容易陷入混乱。 但如果你把它理解成“消息入口 + 控制平面 + 会话系统 + 嵌入式 agent + 工具层”的组合,很多问题会自然被拆开。

例如:

“为什么没有回复?”

可能是:

  • 渠道没进来
  • 被去重了
  • 被防抖合并了
  • 会话队列在等
  • 提及门控没满足
  • Gateway 连通性问题
  • provider 配置问题
  • agent 跑了,但出站被平台限制

“为什么回复重复?”

可能是:

  • 幂等键没处理好
  • 渠道重投递但去重范围不符合预期
  • queue mode 带来 steer + backlog 双阶段响应
  • 某个客户端重试了有副作用调用

“为什么群组表现和私聊不一样?”

可能是:

  • 会话键不同
  • 群组历史包装不同
  • 触发条件不同
  • 队列策略或 per-channel 配置不同
  • 沙箱模式对 non-main session 生效

一旦你知道这些层是独立存在的,定位就会比“感觉哪里怪怪的”有效得多。

小结

OpenClaw 的架构看起来复杂,是因为它解决的问题本来就不是“做一个更顺手的聊天 UI”,而是:

  • 让 AI 长期在线
  • 接多个消息渠道
  • 统一拥有会话与状态
  • 通过正式控制协议协调客户端和节点
  • 在真实消息系统里处理去重、防抖、队列和流式输出
  • 在工具、安全、沙箱和设备能力之间建立可控边界

所以,最值得记住的几个结论是:

  • Gateway 是系统中枢,不只是一个端口
  • Pi 集成当前是嵌入式 session,不是外部 RPC worker
  • Session 由 Gateway 拥有,客户端只是接入面
  • 消息在触发 agent 之前,会经过去重、防抖、队列和路由
  • WebSocket 不是附属通信方式,而是 OpenClaw 的控制协议核心
  • 安全边界被拆成配对、沙箱、工具策略、提权等多个层面,而不是一个模糊开关

理解了这几件事,后面你再看频道配置、调试日志、工具调用、安全模型和部署策略,就不会只看到一堆命令,而会看到它们在系统里的位置。