2

架构全景图

约 20000 字DraftOpenClaw Book

第 2 章 · 架构全景图

OpenClaw 的架构可以浓缩成一句话:一个网关守护进程,管着一组消息通道的连接,通过 WebSocket 把所有客户端(人、手机、浏览器)连在一起,然后在一个嵌入式的 Agent 运行时里执行"接收 → 思考 → 工具调用 → 回复"的循环。

本章你将学到

  • Gateway 的组件结构和连接模型
  • Agent Loop 的完整生命周期——从消息进入到回复发出
  • WebSocket 协议的设计和角色/权限体系
  • 多 Agent 路由的工作原理
  • 数据如何在系统中流转

2.1 全局架构:一张图看懂 OpenClaw

在拆解每个组件之前,先看全局。下面这张 ASCII 图展示了 OpenClaw 的核心架构:

┌─────────────────────────────────────────────────────────────────────┐
│                        外部世界                                      │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐           │
│  │ WhatsApp │  │ Telegram │  │ Discord  │  │ iMessage │  ...      │
│  │  (Baileys)│ │ (grammY) │  │  (D.js)  │  │ (AppleScript)│      │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └────┬─────┘           │
└───────┼──────────────┼──────────────┼──────────────┼───────────────┘
        │              │              │              │
        └──────────────┴──────┬───────┴──────────────┘
                              │
                    ┌─────────▼──────────┐
                    │                    │
                    │     Gateway        │  ← 单个长驻进程
                    │   (守护进程)        │
                    │                    │
                    │  ┌──────────────┐  │
                    │  │ 通道管理器    │  │  管理所有通道连接
                    │  └──────────────┘  │
                    │  ┌──────────────┐  │
                    │  │ 路由引擎      │  │  消息 → Agent 分配
                    │  └──────────────┘  │
                    │  ┌──────────────┐  │
                    │  │ 会话管理器    │  │  会话生命周期
                    │  └──────────────┘  │
                    │  ┌──────────────┐  │
                    │  │ Agent Runtime│  │  Agent 执行环境
                    │  └──────────────┘  │
                    │  ┌──────────────┐  │
                    │  │ WS 服务      │  │  WebSocket 控制面
                    │  └──────────────┘  │
                    │  ┌──────────────┐  │
                    │  │ HTTP API     │  │  OpenAI 兼容端点
                    │  └──────────────┘  │
                    │                    │
                    └────────┬───────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
       ┌──────▼──────┐ ┌───▼────┐  ┌──────▼──────┐
       │ CLI / WebUI │ │ macOS  │  │ iOS/Android │
       │ (operator)  │ │  App   │  │   (nodes)   │
       └─────────────┘ └────────┘  └─────────────┘

整个系统围绕一个单进程、单端口的 Gateway 展开。这个 Gateway 做了所有事情:维护消息通道连接、路由消息、管理 Agent 执行、暴露控制面 API。没有微服务,没有消息队列,没有分布式协调——简单直接。

这个架构选择背后的逻辑是:OpenClaw 的目标用户是个人或小团队,不需要分布式架构的复杂性。一台机器、一个进程、一个端口,够了。

2.1.1 单端口多路复用

Gateway 默认监听 127.0.0.1:18789,这个端口同时承载:

  • WebSocket 连接——CLI、Web 控制面板、macOS App、iOS/Android Node 都通过 WebSocket 连接
  • HTTP API——包括 OpenAI 兼容端点(/v1/chat/completions/v1/models 等)
  • Web 控制面板——静态 HTML/JS/CSS 文件
  • Canvas 宿主——Agent 可编辑的 HTML/CSS/JS 画布
  • 健康检查端点——/healthz(存活)和 /readyz(就绪)

这种"一个端口搞定一切"的设计大大简化了部署和运维。你不需要为 WebSocket 开一个端口、为 HTTP 开另一个端口、再为静态文件开第三个。一个端口,全部搞定。

2.2 Gateway 深度解析

2.2.1 Gateway 的职责

Gateway 是 OpenClaw 的"中枢神经系统"。它负责:

通道连接管理——维持与 WhatsApp、Telegram、Discord 等消息平台的持久连接。每个通道有自己的连接逻辑(WhatsApp 用 Baileys 库、Telegram 用 grammY 框架),但 Gateway 对上层提供统一的抽象。

消息路由——当一个消息从某个通道到达时,Gateway 需要决定:这个消息应该交给哪个 Agent?路由决策基于绑定规则(Bindings),支持按通道、账号、发送者 ID、Discord 服务器/角色等条件匹配。

Agent 执行编排——Gateway 接收消息后,调用 Agent Runtime 执行"思考 → 工具调用 → 回复"的循环,然后把结果发送回对应的通道。

控制面——通过 WebSocket 向 CLI、Web UI、macOS App 等客户端暴露状态查询、配置修改、会话管理等 API。

配置热重载——监听配置文件变化,自动应用安全变更(如修改通道设置),在需要时自动重启(如修改端口或绑定地址)。

2.2.2 Gateway 的生命周期

Gateway 的运行模型很简单:一个前台进程,持续运行直到被停止。

# 启动 Gateway
openclaw gateway --port 18789

# 带详细日志启动
openclaw gateway --port 18789 --verbose

# 强制杀掉占用端口的进程再启动
openclaw gateway --force

在生产环境中,你需要用进程管理器来保持 Gateway 持续运行:

  • macOS:通过 openclaw gateway install 安装 LaunchAgent(launchd 管理)
  • Linux:通过 openclaw gateway install 安装 systemd user unit
# 安装为系统服务
openclaw gateway install

# 检查状态
openclaw gateway status

# 重启
openclaw gateway restart

# 停止
openclaw gateway stop

Gateway 配置了热重载机制,默认模式是 hybrid:安全的变更(如修改通道配置、Agent 设置)立即生效;需要重启的变更(如修改端口、绑定地址)自动重启。

重载模式 行为
hybrid(默认) 安全变更热应用,需要重启的变更自动重启
hot 只热应用安全变更,需要重启时仅记录警告
restart 任何变更都重启
off 关闭文件监听,变更在下次手动重启时生效

⚠️ 踩坑记录

问题:修改了 gateway.port 后,Gateway 看起来重启了,但客户端连不上。

原因:在 hybrid 模式下,端口变更触发重启,但重启需要一点时间。如果客户端在重启完成前尝试连接,会失败。

解决:等待 5-10 秒后重试。可以用 openclaw gateway status 确认 Gateway 已经就绪。

2.2.3 Gateway 绑定模式

Gateway 的网络绑定是一个容易混淆的点。OpenClaw 使用语义化的绑定模式名称,而不是直接让你写 IP 地址:

模式 含义 适用场景
loopback(默认) 只监听 127.0.0.1 个人使用,只有本机访问
lan 监听 0.0.0.0 局域网内其他设备需要访问
custom 监听自定义地址 特殊网络配置
tailnet 监听 Tailscale 地址 通过 Tailscale 远程访问
auto 自动检测 让系统决定

安全规则:如果绑定模式不是 loopback(即对外开放),Gateway 强制要求认证(token 或 password)。在 loopback 模式下,认证默认启用但可以关闭(不推荐)。

绑定模式选择逻辑:

你的 Gateway 需要从其他设备访问吗?
├── 不需要 → loopback(默认,最安全)
├── 需要,在同一局域网 → lan + token 认证
└── 需要,从互联网 → Tailscale/VPN + token 认证

2.3 WebSocket 协议:Gateway 的语言

所有客户端(CLI、Web UI、macOS App、iOS/Android Node)都通过 WebSocket 与 Gateway 通信。这个协议是 OpenClaw 的"母语"——理解它,你就理解了 Gateway 的一切交互方式。

2.3.1 协议基础

传输层:WebSocket,文本帧,JSON 载荷。

每一帧要么是一个请求(Request)、一个响应(Response)、还是一个事件(Event)。

请求帧格式:
{
  "type": "req",        // 请求
  "id": "唯一ID",       // 用于匹配响应
  "method": "方法名",    // 如 "connect", "agent", "health"
  "params": { ... }     // 方法参数
}

响应帧格式:
{
  "type": "res",        // 响应
  "id": "对应的请求ID",
  "ok": true,           // 或 false
  "payload": { ... }    // 成功时的数据
  // 或 "error": { ... } // 失败时的错误信息
}

事件帧格式:
{
  "type": "event",      // 事件
  "event": "事件名",     // 如 "agent", "presence", "tick"
  "payload": { ... },   // 事件数据
  "seq": 42,            // 可选,序列号
  "stateVersion": 7     // 可选,状态版本
}

2.3.2 握手流程

连接建立后,客户端发送的第一帧必须connect 请求。这是协议的铁律——任何非 connect 的首帧都会导致连接被关闭。

握手流程如下:

客户端                              Gateway
  │                                    │
  │  ← connect.challenge (nonce) ────  │  1. Gateway 发送挑战码
  │                                    │
  │  ─── req: connect ──────────────→  │  2. 客户端发送 connect 请求
  │     (含签名、角色、设备ID)          │     (签名是对挑战码的响应)
  │                                    │
  │  ←── res: hello-ok ─────────────  │  3. Gateway 验证通过
  │     (含协议版本、策略、设备令牌)     │
  │                                    │
  │  ←── event: presence ────────────  │  4. 推送在线状态
  │  ←── event: tick ────────────────  │  5. 推送心跳
  │                                    │
  │  ─── req: agent ────────────────→  │  6. 客户端发起 Agent 请求
  │  ←── res: {accepted} ────────────  │  7. Gateway 确认接受
  │  ←── event: agent (streaming) ──  │  8. 流式推送 Agent 输出
  │  ←── res: agent (final) ────────  │  9. 最终结果

握手中有几个关键概念:

挑战-签名机制:Gateway 先发一个随机数(nonce),客户端必须用设备密钥对它签名后返回。这防止了中间人攻击和重放攻击。

角色(Role):客户端在 connect 时声明自己的角色——operator(操作者,如 CLI/Web UI)或 node(节点,如 iOS/Android 设备)。不同角色有不同的权限。

设备身份(Device Identity):每个客户端有一个基于密钥对指纹的设备 ID。新设备需要配对批准,批准后 Gateway 颁发一个设备令牌(Device Token),后续连接可以用这个令牌简化认证。

2.3.3 角色与权限

OpenClaw 的 WebSocket 协议定义了两种角色:

Operator(操作者)——控制面客户端。包括 CLI、Web 控制面板、自动化脚本。Operator 有以下权限作用域(Scopes):

Scope 权限
operator.read 读取状态、会话、配置
operator.write 修改配置、发送消息
operator.admin 管理性操作(持久化配置修改)
operator.approvals 审批执行请求
operator.pairing 管理设备配对

Node(节点)——能力宿主。包括 macOS App(作为节点时)、iOS App、Android App。Node 声明自己提供的能力(Capabilities)和命令(Commands):

  • caps: 高级能力类别(camera、canvas、screen、location、voice)
  • commands: 可调用的命令列表(camera.snap、canvas.navigate、screen.record)
  • permissions: 细粒度权限控制(camera.capture: true, screen.record: false)

Gateway 把 Node 的 caps/commands/permissions 声明视为声明(Claims),服务端有独立的允许列表来强制执行。这意味着 Node 不能通过声明超出它实际能力的权限来提升权限。

2.3.4 Agent 运行的两阶段模型

Agent 运行在协议中是一个两阶段过程,这个设计对理解 OpenClaw 的行为至关重要:

阶段一:接受确认。客户端发送 agent 请求后,Gateway 立即返回 {runId, status: "accepted"}。此时 Agent 可能还没开始处理(消息还在队列里),但客户端已经得到了一个 runId,可以用来追踪这次运行。

阶段二:流式执行 + 最终结果。Agent 开始执行后,Gateway 通过 agent 事件流式推送执行状态——思维过程(thinking)、工具调用(tool)、文本输出(assistant)。执行完成后,Gateway 发送最终的 res:agent,包含 {runId, status: "ok"|"error", summary}

时间线:

客户端发送 agent 请求
    │
    ▼
Gateway 立即返回 accepted ←── 阶段一(毫秒级)
    │
    ▼
Agent 开始执行...
    │
    ├── event: agent (lifecycle: start)
    ├── event: agent (streaming: thinking)
    ├── event: agent (streaming: tool call)
    ├── event: agent (streaming: tool result)
    ├── event: agent (streaming: assistant text)
    ├── event: agent (lifecycle: end)
    │
    ▼
Gateway 返回最终结果 ←── 阶段二(秒到分钟级)

这个两阶段设计的好处是:客户端可以立即知道"请求被接受了",而不需要等 Agent 执行完。对于 UI 来说,这意味着可以立刻显示"正在思考..."的状态。

2.3.5 幂等性保护

对于有副作用的请求(如 sendagent),协议要求客户端提供一个幂等键(Idempotency Key)。Gateway 维护一个短期去重缓存,如果收到重复的幂等键,会返回之前的结果而不是重复执行。

这解决了一个实际问题:网络不稳定时,WebSocket 连接可能断开重连,客户端可能会重发同一个请求。没有幂等性保护,你的 Agent 可能会对同一条消息处理两次——在 WhatsApp 上回复两次,或者执行两次相同的文件操作。

2.4 Agent Runtime:大脑壳

Agent Runtime 是 OpenClaw 的"大脑壳"——它不是大脑本身(那是 LLM),而是大脑运行的容器。就像 Docker 容器封装了一个应用的所有依赖一样,Agent Runtime 封装了 Agent 运行所需的一切。

2.4.1 工作空间(Workspace)

每个 Agent 有一个工作空间目录,这是 Agent 的"家"——它的所有文件操作都在这个目录里进行。

默认路径:~/.openclaw/workspace

工作空间里有几个特殊的文件,OpenClaw 在每次新会话的第一轮会自动把它们注入到 Agent 的上下文中:

文件 用途 类比
SOUL.md Agent 的人格、边界、语气 "我是谁"
AGENTS.md Agent 的行为规则和记忆 "我应该怎么做"
TOOLS.md 用户维护的工具使用说明 "工具使用指南"
IDENTITY.md Agent 的名字、风格、emoji "我叫什么"
USER.md 用户画像和称呼偏好 "你是谁"
HEARTBEAT.md 心跳任务的指令 "定期检查什么"
MEMORY.md 长期记忆(可选,不自动创建) "我记住了什么"
BOOTSTRAP.md 首次运行的仪式(完成后删除) "第一次见面做什么"

空白文件会被跳过,不浪费 Token。过大的文件会被截断,保持 Prompt 精简。文件缺失时,OpenClaw 注入一个"缺失文件"标记。

这种"文件即人格"的设计有几个好处:

可版本控制——把工作空间做成 Git 仓库,你的 Agent 人格就有了完整的变更历史。今天改了什么、为什么改、改了之后效果如何——全部可追溯。

可移植——换一台机器?把工作空间目录拷过去就行。所有的"个性"都在文件里。

可读——打开 SOUL.md 就能看到 Agent 的人格设定,比任何 UI 面板都直观。

💡 提示

建议把工作空间初始化为 Git 仓库(git init),并设为私有仓库。OpenClaw 在创建新工作空间时会自动初始化 Git(如果系统安装了 Git)。

2.4.2 技能(Skills)的加载机制

Skills 从三个位置加载,优先级从低到高:

  1. 内置技能——随 OpenClaw 安装包一起发布
  2. 全局技能——~/.openclaw/skills/,所有 Agent 共享
  3. 工作空间技能——<workspace>/skills/,仅当前 Agent 使用

同名技能按工作空间 > 全局 > 内置的优先级覆盖。这意味着你可以在工作空间里放一个与内置技能同名的文件来"覆盖"默认行为。

2.4.3 模型引用的解析

OpenClaw 使用 provider/model 格式引用模型,解析规则很简单:按第一个 / 分割

anthropic/claude-opus-4-6    → provider: anthropic, model: claude-opus-4-6
openai/gpt-5.2               → provider: openai, model: gpt-5.2
openrouter/moonshotai/kimi-k2 → provider: openrouter, model: moonshotai/kimi-k2

如果模型 ID 本身包含 /(如 OpenRouter 风格),必须显式指定 provider 前缀。如果省略 provider,OpenClaw 把整个字符串当作默认 provider 的模型 ID。

2.5 Agent Loop:从消息到行动的完整旅程

Agent Loop 是 OpenClaw 最核心的运行机制。它是"接收一条消息 → 思考 → 可能调用工具 → 生成回复"的完整过程。理解 Agent Loop,你就理解了 OpenClaw 的一切行为。

2.5.1 Loop 的完整生命周期

┌──────────────────────────────────────────────────────────────────┐
│                      Agent Loop 生命周期                          │
│                                                                  │
│  ┌───────────┐                                                   │
│  │ 消息到达   │ ← 来自通道或心跳/Cron                              │
│  └─────┬─────┘                                                   │
│        │                                                         │
│        ▼                                                         │
│  ┌───────────┐                                                   │
│  │ 1. 参数校验 │ ← 验证消息格式、权限                               │
│  └─────┬─────┘                                                   │
│        │                                                         │
│        ▼                                                         │
│  ┌───────────┐                                                   │
│  │ 2. 会话解析 │ ← 确定使用哪个会话(新建 or 复用)                   │
│  └─────┬─────┘                                                   │
│        │                                                         │
│        ▼                                                         │
│  ┌───────────┐                                                   │
│  │ 3. 上下文组装│ ← 加载会话历史 + 注入 Bootstrap 文件              │
│  │            │   (SOUL.md, AGENTS.md, USER.md 等)                 │
│  └─────┬─────┘                                                   │
│        │                                                         │
│        ▼                                                         │
│  ┌───────────┐                                                   │
│  │ 4. Prompt  │ ← 构建系统提示词                                   │
│  │    构建    │   (基础 Prompt + 技能 + 上下文 + 覆盖)              │
│  └─────┬─────┘                                                   │
│        │                                                         │
│        ▼                                                         │
│  ┌──────────────────────────────────────────┐                     │
│  │                                          │                     │
│  │  5. 模型推理 ──────────────────────┐     │                     │
│  │                                    │     │                     │
│  │     ┌──────────────────────┐      │     │                     │
│  │     │  LLM 推理              │      │     │                     │
│  │     │  (Anthropic/OpenAI/   │      │     │                     │
│  │     │   Google/...)          │      │     │                     │
│  │     └──────────┬───────────┘      │     │                     │
│  │                │                   │     │                     │
│  │        需要调用工具?               │     │                     │
│  │          ├── 否 → 跳到步骤 7        │     │                     │
│  │          └── 是 ↓                  │     │                     │
│  │                                │     │                     │
│  │  6. 工具执行                    │     │                     │
│  │     ┌──────────────────┐      │     │                     │
│  │     │ 工具调用          │      │     │                     │
│  │     │ (exec/read/write │      │     │                     │
│  │     │  /browser/...)   │      │     │                     │
│  │     └───────┬──────────┘      │     │                     │
│  │             │                  │     │                     │
│  │     工具结果注入上下文          │     │                     │
│  │             │                  │     │                     │
│  │             └──→ 回到步骤 5    │     │                     │
│  │                                │     │                     │
│  └────────────────────────────────┘     │                     │
│        │                                 │                     │
│        ▼                                 │                     │
│  ┌───────────┐                           │                     │
│  │ 7. 回复组装│ ← 文本 + 工具摘要 + 错误处理  │                     │
│  └─────┬─────┘                           │                     │
│        │                                 │                     │
│        ▼                                 │                     │
│  ┌───────────┐                           │                     │
│  │ 8. 回复发送│ ← 通过通道发回给用户         │                     │
│  └─────┬─────┘                           │                     │
│        │                                 │                     │
│        ▼                                 │                     │
│  ┌───────────┐                           │                     │
│  │ 9. 持久化  │ ← 保存会话记录到 JSONL 文件   │                     │
│  └───────────┘                           │                     │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

2.5.2 入口点

Agent Loop 可以从两个地方启动:

Gateway RPC:通过 WebSocket 协议的 agentagent.wait 方法。这是最常见的方式——消息从通道到达后,Gateway 内部调用 Agent Loop。

CLI:通过 openclaw agent 命令。这让你可以在终端里直接跟 Agent 对话,不需要通过消息通道。

2.5.3 队列与并发控制

Agent Loop 有一个重要的设计约束:每个会话同一时间只运行一个 Loop

这通过一个 per-session 的队列实现。如果 Agent 正在处理一条消息,又来了新消息,新消息会排队等待。队列模式有三种:

模式 行为
collect(默认) 等当前 Loop 结束后,把排队的消息作为新的一轮输入
steer 在当前 Loop 的下一个模型调用边界注入新消息("转向"当前执行)
followup 等当前 Loop 结束后,为每条排队的消息分别启动新 Loop

这个设计防止了工具和会话状态的竞争条件。如果两条消息同时触发 Agent 去修改同一个文件,没有序列化就会导致冲突。

除了 per-session 队列,还有一个可选的全局队列(Global Lane),可以限制整个 Gateway 同时运行的 Agent Loop 数量。

2.5.4 上下文组装与 Prompt 构建

每次 Agent Loop 执行时,系统需要构建一个完整的 Prompt 发送给 LLM。这个 Prompt 由以下部分组成:

┌─────────────────────────────────────────┐
│              完整 Prompt 结构            │
├─────────────────────────────────────────┤
│  1. OpenClaw 基础系统提示词              │
│     (框架内置的行为约束和格式规范)        │
│                                         │
│  2. 技能提示词                           │
│     (已安装技能的使用说明)               │
│                                         │
│  3. Bootstrap 上下文文件                 │
│     - SOUL.md    (人格)                 │
│     - AGENTS.md  (行为规则)              │
│     - TOOLS.md   (工具说明)              │
│     - IDENTITY.md (身份)                │
│     - USER.md    (用户信息)              │
│     - MEMORY.md  (长期记忆,如果存在)     │
│                                         │
│  4. 会话历史                             │
│     (之前的对话记录,可能经过压缩)        │
│                                         │
│  5. 当前用户消息                         │
│                                         │
│  6. per-run 覆盖                         │
│     (本次运行的特殊指令或上下文)          │
└─────────────────────────────────────────┘

上下文组装有一个重要的优化:自动压缩(Compaction)。当会话历史太长、接近模型的上下文窗口限制时,OpenClaw 会自动压缩旧的对话记录,保留关键信息,腾出空间给新的对话。

2.5.5 工具执行与安全

当 LLM 决定调用工具时,OpenClaw 有一层安全检查:

工具策略(Tool Policy)——可以全局或 per-Agent 设置工具的 allow/deny 列表。

{
  agents: {
    list: [
      {
        id: "family",
        tools: {
          allow: ["read", "exec"],           // 只允许读和执行
          deny: ["write", "edit", "browser"], // 禁止写、编辑、浏览器
        },
      },
    ],
  },
}

执行审批(Exec Approval)——当 exec 工具需要执行命令时,如果命令不在自动批准列表里,Gateway 会广播一个 exec.approval.requested 事件,等待 Operator 客户端批准。

沙箱(Sandbox)——Agent 的工具执行可以被隔离在 Docker 容器里,即使 Agent 被注入恶意指令,也无法影响宿主机。

2.5.6 回复组装与抑制

Agent 的最终回复不是简单地"把 LLM 的输出原样发回去"。回复经过多层处理:

文本提取——从 LLM 的结构化输出中提取纯文本。

工具摘要合并——如果开启了 verbose 模式,工具调用的摘要会包含在回复中。

NO_REPLY 过滤——如果 Agent 的输出是 NO_REPLY(一个静默标记),这条消息不会被发送。这允许 Agent 在执行了某些操作(如发送了消息、修改了文件)后选择不额外回复。

消息去重——如果 Agent 通过消息工具(messaging tool)已经发送了一条消息,回复中的重复内容会被移除。

媒体提取——如果回复中包含 MEDIA:<path-or-url> 标记,OpenClaw 会提取这些标记并把对应的媒体文件作为附件发送。

2.5.7 流式输出

Agent Loop 的输出是流式的。LLM 生成的文本不是等全部生成完才发送,而是边生成边推送:

event: agent
payload: { stream: "assistant", delta: "我来帮你..." }

event: agent
payload: { stream: "assistant", delta: "查看一下那个文件..." }

event: agent
payload: { stream: "assistant", delta: "的内容。" }

对于消息通道,OpenClaw 支持两种流式模式:

Delta 模式(默认)——每次生成一小段文本就发送一次。在 WhatsApp/Telegram 上,你会看到消息"一个字一个字地出现"(实际上是一小段一小段地更新)。

Block 模式——等一个完整的段落或消息块生成完毕后再发送。这减少了消息更新次数,更适合 Discord 等对频繁更新不友好的通道。

2.5.8 超时与提前终止

Agent Loop 不会永远运行。以下情况会导致 Loop 提前终止:

  • Agent 超时——默认 600 秒(10 分钟),可通过 agents.defaults.timeoutSeconds 调整
  • 取消信号——Operator 可以通过 WebSocket 发送取消请求
  • Gateway 断连——WebSocket 连接断开时,正在运行的 Loop 会被中断
  • agent.wait 超时——等待 Agent 完成的请求有独立超时(默认 30 秒)
超时层次:

agent.wait 超时: 30s (仅影响等待,不停止 Agent)
    │
    ▼
Agent Runtime 超时: 600s (实际停止 Agent 执行)
    │
    ▼
Gateway 断连: 立即中断

⚠️ 踩坑记录

问题:Agent 在处理复杂任务时经常超时被中断。

原因:默认超时 600 秒对于简单对话够用,但涉及浏览器自动化、大量文件操作或需要多轮工具调用的任务可能不够。

解决:在配置中增加超时时间。对于需要长时间运行的任务,建议设置 timeoutSeconds: 1800(30 分钟)或更高。

2.6 Hook 系统:在关键时刻插入自定义逻辑

OpenClaw 有两套 Hook 系统,分别用于不同的场景。

2.6.1 内部 Hook(Gateway Hooks)

这些是事件驱动的脚本,在特定事件发生时执行。主要用于运维和自动化。

  • agent:bootstrap——在构建 Bootstrap 文件时运行,可以添加或移除上下文文件
  • 命令 Hook——/new/reset/stop 等命令触发时执行

2.6.2 插件 Hook(Plugin Hooks)

这些是更细粒度的拦截点,深入 Agent Loop 的内部。它们可以:

  • 修改 Prompt——before_prompt_build Hook 可以在 Prompt 发送给 LLM 之前注入额外的上下文
  • 拦截工具调用——before_tool_call / after_tool_call 可以检查或修改工具的输入输出
  • 观察 Agent 生命周期——agent_end 可以在 Agent 完成执行后检查最终结果
  • 控制消息收发——message_received / message_sending / message_sent 可以拦截或修改消息

Hook 的决策规则是"否决权"模型:

  • before_tool_call 返回 {block: true}阻止工具调用(终端操作),返回 {block: false} 是空操作(不清除之前的阻止)
  • message_sending 返回 {cancel: true}取消消息发送(终端操作)

这意味着多个 Hook 可以同时注册,高优先级的 Hook 可以否决低优先级的决定,但低优先级的不能覆盖高优先级的否决。

2.7 多 Agent 路由详解

多 Agent 是 OpenClaw 区别于大多数 Agent 框架的核心特性。它不是简单的"多个 Bot 实例",而是一套完整的隔离和路由体系。

2.7.1 "一个 Agent"的定义

在 OpenClaw 中,一个 Agent 是一个完全独立的"大脑",拥有:

  • 独立的工作空间——自己的 SOUL.mdAGENTS.mdUSER.md
  • 独立的状态目录——自己的认证配置、模型注册表
  • 独立的会话存储——对话历史互不可见
  • 独立的认证——每个 Agent 读自己的 auth-profiles
~/.openclaw/
├── openclaw.json                    # 全局配置
├── workspace/                       # 默认 Agent (main) 的工作空间
│   ├── SOUL.md
│   ├── AGENTS.md
│   └── skills/
├── agents/
│   ├── main/                        # Agent "main"
│   │   ├── agent/
│   │   │   └── auth-profiles.json   # 认证配置
│   │   └── sessions/                # 会话记录
│   │       ├── session-1.jsonl
│   │       └── sessions.json
│   ├── work/                        # Agent "work"
│   │   ├── agent/
│   │   │   └── auth-profiles.json
│   │   └── sessions/
│   └── family/                      # Agent "family"
│       ├── agent/
│       │   └── auth-profiles.json
│       └── sessions/
└── workspace-work/                  # Agent "work" 的工作空间
    ├── SOUL.md
    └── AGENTS.md

2.7.2 路由规则:最具体匹配优先

路由是确定性的——相同的输入永远路由到同一个 Agent。匹配规则按优先级排列:

优先级(从高到低):

1. peer 匹配(精确的 DM/群组/频道 ID)
2. parentPeer 匹配(线程继承)
3. guildId + roles(Discord 角色路由)
4. guildId(Discord 服务器级别)
5. teamId(Slack 团队级别)
6. accountId 匹配(通道账号级别)
7. 通道级别匹配(accountId: "*")
8. 默认 Agent(agents.list[].default,否则第一个,默认 "main")

如果在同一优先级有多个绑定匹配,配置文件中靠前的优先。如果一个绑定了多个匹配字段(如同时指定 peerguildId),所有字段都必须匹配(AND 语义)。

2.7.3 典型路由场景

场景一:按通道分流

// WhatsApp → 快速日常 Agent (Claude Sonnet)
// Telegram → 深度工作 Agent (Claude Opus)
{
  agents: {
    list: [
      { id: "chat", model: "anthropic/claude-sonnet-4-6", workspace: "~/.openclaw/workspace-chat" },
      { id: "opus", model: "anthropic/claude-opus-4-6", workspace: "~/.openclaw/workspace-opus" },
    ],
  },
  bindings: [
    { agentId: "chat", match: { channel: "whatsapp" } },
    { agentId: "opus", match: { channel: "telegram" } },
  ],
}

场景二:按发送者分流

// 同一个 WhatsApp 号码,不同人发消息路由到不同 Agent
{
  agents: {
    list: [
      { id: "alex", workspace: "~/.openclaw/workspace-alex" },
      { id: "mia", workspace: "~/.openclaw/workspace-mia" },
    ],
  },
  bindings: [
    { agentId: "alex", match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551230001" } } },
    { agentId: "mia", match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551230002" } } },
  ],
}

场景三:家庭群组 + 严格沙箱

// 家庭群组绑定到一个独立的 Agent,开启沙箱和严格工具限制
{
  agents: {
    list: [
      {
        id: "family",
        workspace: "~/.openclaw/workspace-family",
        sandbox: { mode: "all", scope: "agent" },
        tools: {
          allow: ["read", "exec"],
          deny: ["write", "edit", "browser"],
        },
      },
    ],
  },
  bindings: [
    { agentId: "family", match: { channel: "whatsapp", peer: { kind: "group", id: "120363...@g.us" } } },
  ],
}

2.8 数据流全景

把所有组件串起来,看一条消息从进入到回复的完整数据流:

用户在 WhatsApp 发送消息:"帮我查一下明天北京到上海的航班"
    │
    ▼
[1] WhatsApp 服务器 → Baileys 库 → Gateway 通道管理器
    │  消息被解析为统一的内部格式
    │
    ▼
[2] Gateway 路由引擎
    │  根据 bindings 规则,匹配到 Agent "main"
    │  会话管理器找到或创建对应的会话
    │
    ▼
[3] Agent Runtime
    │  加载会话历史
    │  注入 Bootstrap 文件 (SOUL.md, AGENTS.md, USER.md, ...)
    │  加载技能
    │  构建 Prompt
    │
    ▼
[4] LLM 推理 (Anthropic API)
    │  模型决定需要调用 web_search 工具
    │
    ▼
[5] 工具执行
    │  Agent 调用 web_search 工具
    │  工具通过 Brave API 搜索航班信息
    │  搜索结果注入上下文
    │
    ▼
[6] LLM 再次推理
    │  基于搜索结果生成回复文本
    │
    ▼
[7] 回复组装
    │  提取文本,过滤 NO_REPLY,去重
    │
    ▼
[8] 回复发送
    │  通过 Baileys 发送 WhatsApp 消息
    │  同时通过 WebSocket 推送事件给所有已连接的客户端
    │
    ▼
[9] 持久化
    │  会话记录保存到 JSONL 文件
    │  ~/.openclaw/agents/main/sessions/<sessionId>.jsonl

2.9 OpenAI 兼容端点

Gateway 暴露了一组 OpenAI 兼容的 HTTP API 端点。这不是"兼容"这么简单——它意味着任何支持 OpenAI API 的客户端(Open WebUI、LobeChat、LibreChat、Cursor 等)都可以直接连接到 OpenClaw,把 OpenClaw 当作一个"有工具能力的 OpenAI 代理"来使用。

可用的端点:

  • GET /v1/models — 返回 OpenClaw Agent 作为"模型"(openclaw/defaultopenclaw/<agentId>
  • POST /v1/chat/completions — 标准 Chat Completions API
  • POST /v1/embeddings — 嵌入向量 API
  • POST /v1/responses — Agent-native 的 Responses API

这个特性的实用价值在于:你可以在现有的 AI 工具链中使用 OpenClaw 的 Agent 能力。比如在 Cursor 里用 OpenClaw Agent 来做代码审查,在 Open WebUI 里用 OpenClaw Agent 来做知识库问答。

本章小结

  • OpenClaw 的架构围绕一个单进程、单端口的 Gateway 展开,简单直接
  • Gateway 同时管理消息通道、Agent 执行、WebSocket 控制面和 HTTP API
  • WebSocket 协议使用请求/响应/事件三种帧类型,强制握手和设备认证
  • Agent Loop 是核心运行机制:消息 → 上下文组装 → Prompt 构建 → 模型推理 → 工具调用(可能多轮)→ 回复组装 → 持久化
  • 每个 Loop 是 per-session 序列化的,防止并发冲突
  • 多 Agent 路由基于确定性绑定规则,最具体匹配优先
  • Hook 系统允许在 Agent Loop 的各个阶段插入自定义逻辑

下一步

你已经理解了 OpenClaw 的架构全貌。下一章,我们会把理论付诸实践——安装 OpenClaw、完成配置、接入消息通道、发送第一条消息。从"知道它是什么"到"亲手让它跑起来"。


💡 经验之谈

很多新手试图一次性理解 OpenClaw 的全部架构,然后感到困惑。我的建议是:先用起来,再理解细节。你不需要理解 Agent Loop 的每一个阶段才能发送第一条消息。但当你遇到奇怪的行为(比如"为什么 Agent 没有回复"或"为什么回复被截断了")时,回来查看这一章的对应部分,会非常有帮助。

把这一章当作"参考手册"而不是"必须从头读到尾的教程"。