第 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 幂等性保护
对于有副作用的请求(如 send、agent),协议要求客户端提供一个幂等键(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 从三个位置加载,优先级从低到高:
- 内置技能——随 OpenClaw 安装包一起发布
- 全局技能——
~/.openclaw/skills/,所有 Agent 共享 - 工作空间技能——
<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 协议的 agent 和 agent.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_buildHook 可以在 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.md、AGENTS.md、USER.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")
如果在同一优先级有多个绑定匹配,配置文件中靠前的优先。如果一个绑定了多个匹配字段(如同时指定 peer 和 guildId),所有字段都必须匹配(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/default、openclaw/<agentId>)POST /v1/chat/completions— 标准 Chat Completions APIPOST /v1/embeddings— 嵌入向量 APIPOST /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 没有回复"或"为什么回复被截断了")时,回来查看这一章的对应部分,会非常有帮助。
把这一章当作"参考手册"而不是"必须从头读到尾的教程"。