Skip to content

第二章 整体架构解析

在第一章,我们从设计哲学的角度理解了 OpenClaw 的核心定位:本地优先、智能体循环、工具驱动。这些选择回答了"为什么"的问题。

现在我们要回答"是什么"的问题——这些设计是如何在代码层面落地的?当你启动 openclaw gateway 后,系统内部到底在发生什么?

1. 从一个问题开始

想象一下这个场景:

你正在使用 OpenClaw,突然遇到了奇怪的行为。你让它"查找项目里所有 TODO 注释",它却回复"我找不到任何文件"。你知道项目里明明有几十个 TODO,问题出在哪里?

作为用户,你只能换个说法再试一次。但如果你理解架构,你就能定位问题:

  • 是 Gateway 没有正确接收你的消息?
  • 是 Command Queue 把消息路由到了错误的会话?
  • 是 Agent 调用了 Glob 但模式匹配失败?
  • 还是 LLM 返回的结果解析出错?

这就是本章的目标:从"再试一次"到"我知道为什么"。


2. 架构全景:WebSocket Gateway

OpenClaw 采用 WebSocket Gateway 架构,核心组件包括:

  ┌─────────────────────────────────────────────────────────────┐
  │                      Clients                                │
  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
  │  │  Control UI │  │    CLI      │  │   Web Dashboard     │  │
  │  │   (macOS)   │  │  (Terminal) │  │    (Browser)        │  │
  │  └─────────────┘  └─────────────┘  └─────────────────────┘  │
  └─────────────────────────────────────────────────────────────┘

                                │ HTTP / WebSocket

  ┌─────────────────────────────────────────────────────────────┐
  │                     Gateway (WebSocket)                     │
  │ ┌─────────────────────────────────────────────────────────┐ │
  │ │              Message Router & Session Mgr               │ │
  │ └─────────────────────────────────────────────────────────┘ │
  └─────────────────────────────────────────────────────────────┘
         ▲                    ▲                    ▲
         │                    │                    │
  ┌─────────────┐    ┌─────────────┐    ┌─────────────────────┐
  │  Telegram   │    │   Discord   │    │  WhatsApp (Baileys) │
  │   Channel   │    │   Channel   │    │      Channel        │
  │  (extension)│    │  (extension)│    │    (built-in)       │
  └─────────────┘    └─────────────┘    └─────────────────────┘
         ▲                    ▲                    ▲
         │                    │                    │
     Telegram            Discord API           WhatsApp Web
      Bot API            (HTTP/WebSocket)       (WebSocket)

                              │ node.invoke (WebSocket)

  ┌─────────────────────────────────────────────────────────────┐
  │                      Nodes (Device Agents)                  │
  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
  │  │  macOS Node │  │   iOS Node  │  │   Android Node      │  │
  │  │(system.run) │  │(camera.snap)│  │  (screen.record)    │  │
  │  └─────────────┘  └─────────────┘  └─────────────────────┘  │
  └─────────────────────────────────────────────────────────────┘

一条消息的典型生命周期:

  1. 用户通过 Telegram/Discord/Slack 等渠道发送消息
  2. Gateway 接收消息,通过对应平台的 SDK 处理
  3. 消息进入 Command Queue,按会话排队
  4. Agent 取出消息,进入 Agent Loop 进行决策
  5. 需要时调用 LLM,需要时调用工具(read、write、edit、exec)
  6. 结果通过 Gateway 返回给用户

3. Gateway:统一入口与多平台接入

3.1 为什么需要 Gateway

Gateway 是 OpenClaw 的唯一入口点,负责管理所有消息渠道和控制平面客户端。

如果没有 Gateway,每个渠道都需要独立的连接管理、身份验证和消息处理逻辑,这会导致:

  • 复杂度爆炸:每加一个渠道,核心代码都要改一次
  • 状态分散:WhatsApp 会话、Telegram 连接分散管理
  • 安全风险:多个入口点意味着多个攻击面

Gateway 通过统一 WebSocket 接口解决了这些问题。

3.2 多平台接入

OpenClaw 直接使用各平台的 SDK 接入,而非自行实现协议:

平台接入方式
TelegramWebhook / 长轮询
DiscordWebSocket
SlackEvents API / WebSocket
WhatsAppWebSocket
WebChatWebSocket

这些 SDK 负责处理平台特定的协议细节,Gateway 专注于消息路由和会话管理。

3.3 连接模式与内网部署

不同渠道的接入方式差异很大:

模式原理实时性公网要求
Webhook平台主动 HTTP 请求需要公网地址
WebSocketGateway 主动保持长连接不需要
长轮询Gateway 定期查询不需要

对于 Webhook 模式,大多数个人用户没有公网服务器。解决方案包括:

  • Tailscale:推荐方案,建立加密 mesh 网络
  • SSH 隧道:建立端口转发隧道

这让 OpenClaw 可以安全地运行在内网服务器甚至笔记本上,而不需要公网 IP。

3.4 身份识别与会话隔离

Gateway 负责进行路由寻址。不同平台的用户 ID 格式各异,但都遵循统一规范:

  • agent:main:telegram:123456789🧵42 - Telegram 话题线程
  • agent:main:discord:channel:987654321 - Discord 频道
  • agent:main:whatsapp:dm:+15551234567 - WhatsApp 私聊
  • agent:ops:slack:cron:job:daily-report:run:uuid - Cron 任务会话

这个统一的身份标识决定了消息进入哪个会话(Session),是实现多用户隔离的基础。不同用户的消息被路由到独立的处理队列中,不会混淆。


4. Command Queue:命令队列与并发控制

4.1 为什么需要 Command Queue

直接让 Gateway 调用 Agent 会有三个问题:

问题后果Command Queue 的解决
并发冲突多个消息同时处理导致资源竞争按会话序列化处理
无缓冲高峰期消息涌入导致崩溃提供队列缓冲
难以控制无法控制并行度可配置并发上限

Command Queue 是一个进程内队列,基于"泳道"模型实现并发控制,纯 TypeScript 实现,无外部依赖。

4.2 泳道模型:并发与顺序的平衡

Command Queue 的核心设计是 泳道模型

  泳道内:串行保证(按 Session Key)       泳道间:并行处理
  ┌──────────────────────────┐            ┌──────────┐ ┌──────────┐
  │ Session A 的消息队列      │            │ 泳道 A   │ │ 泳道 B   │
  │ ──────────────────────── │            │(SessionA)│ │(SessionB)│
  │ 1. 创建 report           │            │ ──────── │ │ ──────── │
  │ 2. 写入内容 ←─────────────┼── 排队等待  │ 长任务   │ │ 短查询   │
  └──────────────────────────┘            │ (5分钟)  │ │ (1秒)    │
                                          │ ──────── │ │ ──────── │
                                          │          │ │ 立即响应 │
                                          └──────────┘ └──────────┘

泳道内的串行保证:同一用户的消息按顺序处理。比如用户连续发送"创建 report.txt"和"写入内容",第二条会等待第一条完成后再执行,防止因文件不存在而失败。

泳道间的并行处理:不同用户互不等待。Session A 的 5 分钟代码分析不会阻塞Session B 的天气查询。

4.3 消息处理方式

Command Queue 支持多种消息处理方式,可通过配置或命令切换:

方式行为适用场景
collect(默认)合并所有队列消息为单条跟进大多数场景
steer立即注入当前运行需要实时干预
followup排队等待下一轮顺序处理
steer-backlog立即注入 + 保留跟进复杂交互
steer+backlog与 steer-backlog 类似复杂交互
queue标准队列模式顺序处理
interrupt中断当前运行紧急干预

配置示例(openclaw.json):

json5
{
  messages: {
    queue: {
      mode: "collect",
      debounceMs: 等待毫秒数,
      cap: 队列长度上限,
      drop: "summarize",
      byChannel: { discord: "collect" },
    },
  },
}

会话内临时切换:发送 /queue steer/queue collect

4.4 队列选项

  • debounceMs:等待安静期后开始处理(防止"继续、继续"的连续消息)
  • cap:每会话最大队列长度
  • drop:溢出策略(old 丢弃旧消息 / new 丢弃新消息 / summarize 生成摘要)

5. Agent:决策核心与 Agent Loop

5.1 不是脚本,是循环

消息通过 Command Queue 后,到达系统的"大脑"——Agent。Agent 是 OpenClaw 最核心的组件,负责理解用户需求、规划任务、选择工具、协调执行。

传统的程序是线性的:输入、处理、输出。Agent 不是线性的,它是循环的。这个循环被称为 Agent Loop(智能体循环)。

Agent Loop 的工作流程:

        ┌──────────┐
        │  观察     │◄──────────────────────────┐
        │ (Observe)│                           │
        └────┬─────┘                           │
             │ 接收当前状态                      │
             │ (用户输入、工具结果、环境信息)       │
             ▼                                 │
        ┌──────────┐     ┌──────────┐          │
        │  思考     │────►│  行动    │          │
        │ (Think)  │     │  (Act)   │          │
        └──────────┘     └────┬─────┘          │
                              │                │
                              │ 执行决策        │
                              │ (调用工具/回复)  │
                              └────────────────┘

循环持续进行,直到:

  • 任务完成 → 生成最终回复
  • 遇到错误 → 无法继续
  • 达到最大循环次数 → 强制终止

这就是 OpenClaw 能处理复杂任务的原因。它不是一次性生成答案,而是像人类一样,一步步探索、验证、调整,直到达成目标。

5.2 工具选择:如何让 LLM 做出正确决策

在每次思考阶段,Agent 需要让 LLM 选择工具。Agent 会把当前状态、可用工具列表(包含每个工具的名称、描述和参数格式)发送给 LLM,LLM 根据任务需求选择最合适的工具并生成参数。

这个过程面临的三个挑战:

挑战问题描述解决方案
描述质量LLM 根据描述理解工具,模糊描述导致错误选择精心设计工具描述
上下文限制工具多时占用大量上下文空间智能选择展示哪些工具
幻觉错误LLM "幻觉"出不存在的工具或格式错误验证白名单,错误反馈

5.3 状态管理:记忆的维护

Agent 需要维护状态才能进行多轮推理。状态包括:

  • 对话历史:用户和 Agent 之前的交互记录,作为短期记忆让 Agent 理解上下文
  • 工具结果:最近调用的工具及其返回值,成为下一次思考的依据
  • 环境信息:当前工作目录、系统状态、用户偏好等

状态管理的一个核心挑战是上下文窗口限制。LLM 能处理的文本长度是有限的(比如 4K、8K、128K Token)。当对话变长或任务变复杂时,状态可能超出这个限制。

Agent 采用了几种策略来应对:

  • 截断:丢弃或压缩最旧的对话历史,重点保留最近的若干轮
  • 摘要:对早期的对话进行压缩,用更短的文本保留关键信息
  • 提取:将重要的事实(如用户偏好)提取出来,存储到长期记忆(MEMORY.md)中

5.4 安全边界

Agent 具有调用工具执行任意操作的能力,这既是优势也是风险。OpenClaw 设计了三层安全机制:

  • Security 模式deny / allowlist / full 三级控制
  • Ask 模式off / on-miss / always 确认级别
  • Safe Bins:stdin-only 工具限制

6. LLM 集成与错误处理

6.1 多模型支持

Agent 做出了决策,但它需要调用外部的 LLM 来获得推理能力。

OpenClaw 支持接入多种 LLM,包括 OpenAI 的 GPT 系列、Anthropic 的 Claude、Google 的 Gemini,以及本地模型。系统负责屏蔽它们的差异:

  • 封装不同的 API 格式
  • 适配不同的能力(有的支持函数调用,有的不支持)
  • 管理故障转移(当主模型不可用时自动切换到备用模型)

6.2 错误处理与自愈

LLM 的输出是不确定的,经常会遇到各种问题。系统实现三层错误自愈机制:

问题处理方式示例
格式错误自动修复{ "tool": "bash" → 补全 }
模型故障切换认证档案或使用备用模型换一个Key或换一个模型
网络超时指数退避重试自动重试

这种机制让 OpenClaw 在面对不稳定的模型输出时,依然能保持极高的任务成功率。

6.3 Token 管理与成本控制

每次调用 LLM 都会产生 Token 消耗,系统从四个方面管理成本:

策略作用
用量统计记录每次调用的 input/output Token
预算控制设置上限,接近时发出警告
提示词压缩压缩旧对话,保留新消息
响应缓存缓存重复查询,避免重复调用

7. 场景化理解:当 things go wrong

理解了架构之后,让我们看看几个真实场景中如何定位问题。

场景一:消息没有响应

你发送了一条消息,Agent 完全没有反应。

排查思路

  1. 检查 Gateway:日志中是否收到了消息?如果没有,检查网络连接和渠道配置。
  2. 检查 Command Queue:消息是否进入了正确的泳道?队列是否堆积?
  3. 检查 Agent:是否正在处理其他消息?是否陷入了长时间循环?

常见原因

  • Gateway 的 WebSocket 连接断开,没有重连成功
  • Command Queue 已满,新消息被延迟处理
  • Agent 正在处理一个耗时任务,你的消息在泳道中排队等待
场景二:响应非常慢

一条简单的查询等了十几秒才回复。

排查思路

  1. Gateway 到 Queue:通常是毫秒级,不会是瓶颈
  2. Queue 排队:检查是否有其他消息在队列中等待
  3. Agent 处理:检查 Agent Loop 轮数,是否在反复尝试
  4. LLM 调用:检查 LLM 的响应时间,是否模型过载

常见原因

  • LLM 服务响应慢(尤其是高峰期)
  • Agent 陷入工具调用的循环,反复尝试失败的命令
  • 上下文过长,每次调用都携带大量历史记录
场景三:工具选择错误

你让它查找文件,它却一直用 Bash 运行 find 命令,而不是更适合的 Glob。

排查思路

  1. 工具描述:系统提示词中 Glob 的描述是否清晰?
  2. 上下文限制:是否因为工具太多,Glob 没有被展示给 LLM?
  3. 历史惯性:是否之前的对话让 LLM 形成了使用 Bash 的习惯?

解决方案

  • 在 TOOLS.md 中明确描述 Glob 的适用场景
  • 如果工具太多,考虑精简可用工具列表
  • 在对话中明确提示"请使用 Glob 工具"

8. 组件协作示例

消息旅程(用户发送:"查找所有包含 TODO 的 Python 文件"):

Step 1: Gateway          Step 2: Command Queue     Step 3: Agent (Loop)
───────────────          ───────────────────       ─────────────────────
Telegram SDK ─────▶      确定会话标识              第1轮:观察 → 思考 → 行动
提取消息内容      ───▶    放入队列 ─────────▶       (调用Glob) 得50个文件
                         调度给Agent               第2轮:观察 → 思考 → 行动
                                                   (调用Grep) 得12个匹配
                                                   第3轮:观察 → 思考 → 行动
                                                   任务完成,生成回复

Step 4: Command Queue    Step 5: Gateway
───────────────────      ───────────────
回复放入队列  ─────────▶   通过 Telegram SDK
                          发送给用户

异常处理

  • Gateway 网络问题 → 重试或返回错误
  • Queue 溢出 → 根据 drop 策略处理
  • Agent 死循环 → Tool Loop Detection 终止
  • LLM 拒绝 → 错误自愈重试

9. 消息通信格式

Gateway 与客户端之间的通信使用标准的消息格式:

typescript
type 消息类型 = "lifecycle" | "tool" | "assistant" | "error" | (string & {});

type 消息内容 = {
  runId: string;           // 运行ID
  seq: number;             // 序列号(严格递增)
  stream: 消息类型;         // 消息类型
  ts: number;              // 时间戳(毫秒)
  data: Record<string, unknown>; // 消息数据
  sessionKey?: string;     // 会话标识
};

消息类型说明:

  • lifecycle:运行生命周期事件(开始、结束)
  • tool:工具调用事件
  • assistant:助手回复事件
  • error:错误事件
  • compaction:状态压缩事件
  • (string & {}):允许扩展其他自定义消息类型

10. 本章小结

通过这一章,我们从架构层面理解了 OpenClaw 的核心组件:

组件核心职责解决的关键问题
GatewayWebSocket 网关,管理所有渠道连接统一入口、多平台接入、身份识别
Command Queue命令队列,按会话序列化处理并发控制、顺序保证、流量缓冲
Agent理解需求、规划任务、选择工具Agent Loop 决策、状态管理、安全边界
LLM 集成连接多种模型,处理错误与重试多模型适配、错误自愈、Token 管理

这些组件通过定义良好的协议协同工作,构成了 OpenClaw 的"数字大脑"。

架构设计的核心洞察

OpenClaw 的架构体现了"关注点分离"的设计原则。每个组件只负责一件事,并且把这件事做好:

  • Gateway 只关心如何接收和路由消息,不关心消息如何处理
  • Command Queue 只关心如何排队和调度,不关心消息内容是什么
  • Agent 只关心如何决策和行动,不关心消息从哪里来
  • LLM 集成 只关心如何调用模型,不关心调用的结果如何使用

这种分离带来了极大的灵活性。你可以:

  • 接入新的渠道,而不影响其他组件
  • 使用不同的 LLM,Agent 的逻辑完全不用修改
  • 在一台机器上运行多个实例,共享同一个 Gateway

理解了这个架构,你就能诊断问题发生在哪个环节:

  • 消息没收到是 Gateway 的问题
  • 消息没处理是 Queue 或 Agent 的问题
  • 回复质量差是 LLM 或 Agent 的问题

在下一章,我们将深入 Agent 的"灵魂"——提示词系统。你会看到 SOUL.md、USER.md、TOOLS.md 等文件如何被组合成系统提示词,热更新机制如何让修改立即生效,以及如何通过调整提示词精细控制 Agent 行为。