一、什么是 OpenClaw
OpenClaw 是一个开源的、自托管的 AI Agent 运行时框架,它将大语言模型(LLM)与真实世界的执行面(Execution Surfaces)连接起来:Shell 命令执行、文件系统访问、浏览器自动化、Docker 容器管理,以及超过 20 种第三方消息平台(WhatsApp、Telegram、Discord、Slack、飞书、企业微信等)。
二、整体架构设计
2.1 七层组件架构
OpenClaw 的架构可拆解为 7 个核心交互组件:
text
┌─────────────────────────────────────────────────────────────────┐
│ Channel System (消息渠道层) │
│ WhatsApp | Telegram | Discord | Slack | 飞书 | 企业微信 | ... │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Gateway (中央网关层) │
│ 消息路由 | 会话管理 | 工具分发 | Agent 编排 | 事件总线 │
│ WebSocket + HTTP 多路复用 (默认端口 18789) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Agent Runtime (智能体运行时) │
│ 多轮推理循环 | 工具调用执行 | 上下文组装 | 会话状态机 │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Plugin & │ │ Memory & │ │ LLM Provider│
│ Skill System│ │ Knowledge │ │ (模型层) │
│ (扩展系统) │ │ (记忆系统) │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Local Execution Environment (本地执行层) │
│ 主机 Shell | Docker 沙箱 | 浏览器 | 文件系统 | Node 设备 │
└─────────────────────────────────────────────────────────────────┘
2.2 Gateway:消息路由中枢 + 运行时编排引擎
Gateway 是 OpenClaw 的核心进程,是一个长运行的 Node.js 服务。它的职责远不止"消息路由",而是整个系统的中央调度器和运行时编排引擎。
为什么 Gateway 要包揽这么多职责?
OpenClaw 的设计哲学是"单进程自治"------Gateway 作为一个独立进程,不需要外部数据库、不需要 Redis、不需要 Nginx,自己就能完成所有核心功能。这种设计让部署极简(npm install -g openclaw && openclaw gateway start),但也意味着 Gateway 必须是"全能选手"。
| 职责 | 具体工作 | 为什么放在 Gateway |
|---|---|---|
| 消息路由 | 接收各渠道消息,按 binding 规则分发给对应 Agent | 所有渠道统一接入点 |
| 会话生命周期管理 | 维护 session 存储(sessions.json)、创建/过期/切换 sessionId | 会话是跨渠道的概念,必须由中央管理 |
| 工具分发 | 根据策略级联决定哪些工具对当前会话可用 | 工具权限是全局/Agent/Session 多层级的,需要中央裁决 |
| Agent 编排 | 管理主 Agent + 子 Agent 的生成、调度、通信 | Orchestrator 模式需要中央协调 |
| 心跳调度 | 执行 cron 定时任务和 heartbeat 轮询 | 定时任务需要独立于对话的调度器 |
| Web 服务 | 提供 Control UI 和 WebChat 界面 | Gateway 本身就是 HTTP/WebSocket 服务器 |
| 事件总线 | 处理内部消息、插件事件、跨会话通信 | 需要中央 Pub/Sub 机制 |
技术实现
- 单端口多路复用: WebSocket + HTTP 共享 18789 端口
- WebSocket 长连接: 支持实时双向通信(UI 与 Gateway 保持连接)
- 插件化注册表: 所有能力通过中央注册表暴露,避免直接耦合
- 状态唯一数据源: 所有会话状态由 Gateway 拥有,UI 只读 Gateway 的存储
三、Agent Runtime 详解
3.1 多轮推理循环 (Agent Loop)
OpenClaw 的 Agent Runtime 实现了一个经典 ReAct 风格的多轮推理循环:
text
用户输入/心跳触发
│
▼
┌─────────────────┐
│ 1. 上下文组装 │ ← 系统提示词 + 历史消息 + 注入的 Workspace 文件
└─────────────────┘
│
▼
┌─────────────────┐
│ 2. LLM 推理 │ ← 模型生成文本回复或工具调用 (Function Calling)
└─────────────────┘
│
▼
┌─────────────────┐
│ 3. 工具执行 │ ← 顺序执行工具调用,收集结果
└─────────────────┘
│
▼
┌─────────────────┐
│ 4. 结果回注 │ ← 将工具结果追加到上下文
└─────────────────┘
│
▼
是文本回复? ──是──→ 返回给用户
│否
▼
返回步骤 2 (继续循环)
关键设计:
- 工具调用是顺序执行的,而非并行(与一些框架不同)
- 最大循环深度可控: 防止无限循环
四、会话管理(Session)
4.1 Session:对话的持久化容器
Session = 一个特定对话的持久化容器,包含完整的对话历史(*.jsonl)、元数据和路由信息。
Session Key 的生成规则
Session Key 是 Gateway 用来标识"这是哪段对话"的字符串。不同场景生成不同 Key:
| 场景 | Session Key 格式 | 例子 | 说明 |
|---|---|---|---|
| 私信(默认) | agent:<agentId>:<mainKey> |
agent:main:main |
所有私信共享同一个 Key,跨平台互通 |
| 按发送者隔离 | agent:<agentId>:dm:<peerId> |
agent:main:dm:ou_xxx |
每个人单独一个 Session |
| 按渠道+发送者隔离 | agent:<agentId>:<channel>:dm:<peerId> |
agent:main:feishu:dm:ou_xxx |
微信和飞书分开 |
| 群聊 | agent:<agentId>:<channel>:group:<id> |
agent:main:feishu:group:oc_xxx |
每个群独立 |
| 定时任务 | cron:<jobId> |
cron:backup-daily |
每个定时任务独立 |
跨平台共享 Session
默认配置 session.dmScope = "main":
- 微信私信 →
agent:main:main - 飞书私信 →
agent:main:main
它们共享同一个对话历史! 你在微信说的事,飞书上继续聊,OpenClaw 能看到完整上下文。
如需隔离,改配置:
json
{
"session": {
"dmScope": "per-channel-peer"
}
}
4.2 Session 切换机制
什么时候切换 sessionId?
| 触发条件 | 说明 |
|---|---|
| 每日重置 | 默认凌晨 4:00(Gateway 本地时间)。如果最后更新时间 < 今天 4:00,下一条消息创建新 sessionId |
| 空闲重置 | idleMinutes 配置(如 120 分钟)。2 小时没消息,session 过期 |
| 手动重置 | 发 /new 或 /reset,立刻换新 sessionId |
| 首次对话 | 从来没聊过,创建新 Session |
切换的是什么?
- 切换前:Session Key=
agent:main:main→ sessionId=A → A.jsonl(存着昨天的聊天记录) - 切换后:Session Key=
agent:main:main→ sessionId=B → B.jsonl(空的,从零开始)
Session Key(门牌号)永远不变,变的是背后的 sessionId(房间号)和对应的 JSONL 文件。
所以:
- 不手动切,一天后也会自动切(凌晨 4:00)
- 切换后当前对话历史(Context)从零开始,旧 JSONL 不再加载到 LLM
- 但长期记忆通过 MEMORY.md 保留
五、系统提示词与上下文组装(Context)
5.1 系统提示词的动态组装机制
OpenClaw 的系统提示词不是静态模板,而是在每次运行(每次 LLM 调用前)由 Gateway 动态组装。这决定了模型"看到什么"、"知道什么"。
组装流程
text
Gateway 收到消息
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1. 加载基础系统提示词 │
│ - OpenClaw 运行规则(工具使用规范、安全边界) │
│ - 当前可用工具列表(名称 + 简短描述) │
│ - Skills 列表(名称 + 描述 + 路径,仅元数据不加载内容) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 组装项目上下文(Project Context) │
│ 从 workspace 目录读取以下文件,注入到系统提示词中: │
│ - AGENTS.md → 工作空间约定和规则 │
│ - SOUL.md → Agent 人格/语气定义 │
│ - USER.md → 用户信息(你是谁、偏好) │
│ - IDENTITY.md → Agent 身份定义 │
│ - TOOLS.md → 环境特定的工具配置 │
│ - MEMORY.md → 长期记忆(跨 Session 保留) │
│ - HEARTBEAT.md → 心跳任务清单 │
│ - memory/YYYY-MM-DD.md → 当日日记(短期记录) │
│ │
│ 约束:单文件最大 20,000 字符,总计上限 150,000 字符 │
│ 超出部分会被截断,截断信息在 /context 中可见 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. 加载运行时元信息 │
│ - 当前时间/时区 │
│ - 主机名、操作系统、Node 版本 │
│ - 当前模型名称、思考模式 │
│ - 沙箱状态、权限范围 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 组装对话历史(Context Window) │
│ 从当前 sessionId 对应的 JSONL 文件中读取: │
│ - 用户消息 │
│ - 助手回复 │
│ - 工具调用 / 工具结果 │
│ │
│ 如果接近窗口上限,先触发 Compaction(压缩旧消息为摘要) │
│ 如果超过窗口,旧内容会被剪枝(Pruning) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. 附加当前用户消息 │
│ 本次触发运行的用户输入内容 │
└─────────────────────────────────────────────────────────────┘
│
▼
发送给 LLM → 推理 → 返回工具调用或文本回复
Context 的完整组成
text
┌──────────────────────────────────────────────────────────────────────┐
│ ① 系统提示词(System Prompt) │
│ 由 Gateway 动态组装,约占总 token 的 30%-60% │
│ │
│ ├─ OpenClaw 运行规则(~2000 tokens) │
│ │ │
│ ├─ 可用工具列表(~1000-3000 tokens,取决于工具数量) │ │
│ │ - read: Read file contents. Supports text files and images... │
│ │ ...(每个工具名称 + 1-2句描述) │
│ │ │
│ ├─ Skills 元数据列表(~500-1000 tokens) │
│ │ ## Available Skills │
│ │ - feishu-bitable: 飞书多维表格... │
│ │ (仅加载名称+描述+路径,不加载 SKILL.md 全文) │
│ │ │
│ ├─ 项目上下文 Project Context(~2000-8000 tokens,取决于文件大小) │
│ │ ┌─────────────────────────────────────┐ │
│ │ │ AGENTS.md(工作空间约定) │ │
│ │ │ SOUL.md(人格/语气) │ │
│ │ │ USER.md(用户偏好) │ │
│ │ │ IDENTITY.md(身份定义) │ │
│ │ │ TOOLS.md(环境配置) │ │
│ │ │ MEMORY.md(长期记忆) │ │
│ │ │ HEARTBEAT.md(心跳清单) │ │
│ │ │ memory/2026-04-27.md(当日日记) │ │
│ │ └─────────────────────────────────────┘ │
│ │ 约束:单文件 ≤20,000 字符,总计 ≤150,000 字符 │
│ │ 超出部分截断,截断信息在 /context 中可见 │
│ │ │
│ ├─ 运行时元信息(~500 tokens) │
│ │ Current Date: Monday, April 27th, 2026 --- 2:41 PM (Asia/Shanghai)│
│ │ Runtime: agent=main | host=VM-58-41-ubuntu | os=Linux... │
│ │ Model: kimi-coding/k2p5 | Thinking: high │
│ │ Sandbox: none | Capabilities: none │
│ │ │
│ └─ 安全与自更新提示(~500 tokens) │
│ "Self-Update is ONLY allowed when the user explicitly asks..." │
│ "Get Updates is ONLY allowed when..." │
│ "Use config.schema.lookup with a specific dot path..." │
├──────────────────────────────────────────────────────────────────────┤
│ ② 对话历史(Conversation History) │
│ 从当前 sessionId 对应的 *.jsonl 文件中读取 │
│ 受模型上下文窗口限制,旧内容可能被压缩或剪枝 │
│ │
│ ├─ user: "帮我查一下今天的天气" │
│ ├─ assistant: "我来查一下天气信息" │
│ │ [tool call: web_search query="北京今天天气"] │
│ ├─ tool_result: "北京今天晴,25°C,西北风3级..." │
│ ├─ assistant: "今天北京晴天,25度..." │
│ ├─ user: "那明天呢" │
│ ├─ assistant: │
│ │ [tool call: web_search query="北京明天天气"] │
│ ├─ tool_result: "北京明天多云,22°C..." │
│ ├─ assistant: "明天多云,22度..." │
│ └─ ...(更多历史消息) │
├──────────────────────────────────────────────────────────────────────┤
│ ③ 当前用户消息(Current Input) │
│ 本次触发运行的用户输入内容 │
│ │
│ user: "继续之前 OpenClaw 的调研" │
└──────────────────────────────────────────────────────────────────────┘
5.3 上下文压缩与剪枝
当会话接近模型上下文窗口限制时,OpenClaw 有两层机制:
- Compaction(压缩) ------ 保存历史精华
- 将旧消息总结为压缩摘要
- 摘要写入 JSONL,原始消息被替换
- 保留最近 N 条消息完整内容
- 可手动触发:
/compact
- Pruning(剪枝) ------ 临时清理
- 仅清理内存中的旧工具结果(不修改 JSONL)
- 针对 Anthropic 缓存优化:缓存过期后减少重新缓存的 token 量
- 默认关闭,可通过
agents.defaults.contextPruning配置
六、Memory 机制(独立章节)
6.1 默认机制:文件注入
| 机制 | 做法 | 适用场景 |
|---|---|---|
| 文件注入(默认) | 把 MEMORY.md、USER.md 等直接塞进系统提示词的 Project Context | 个人设定、用户偏好、长期计划 |
| Semantic Memory(可选) | 向量索引 + 语义搜索,通过 openclaw memory search 检索 |
大量文档、知识库查询 |
文件注入的工作流程:
- 启动时:Gateway 检查
workspace/目录下的标准文件 - 注入:把文件内容塞进系统提示词的 "Project Context" 区域
- 限制:单文件默认最多 20,000 字符,超出会截断
- 生效:每次运行都重新读取,修改后立刻生效
6.2 Memory 文件的分层
text
workspace/
├── MEMORY.md ← 长期记忆(跨 Session 保留,手动维护)
├── memory/
│ └── 2026-04-27.md ← 当日日记(短期记录)
└── memory_consolidation/ ← 自动记忆整合
└── memory_consolidation.env
MEMORY.md:长期记忆,如"我是研二学生,投 EMNLP"memory/YYYY-MM-DD.md:当日日志,可手动或自动记录memory_consolidation:自动把短期日记整合进长期记忆
6.3 跨天连续感的实现路径
核心问题:过了 3 天继续聊,为什么还能记得上上个话题?
默认情况:模型不应该记得 3 天前的具体对话,因为旧 sessionId 的 JSONL 不在当前上下文中。
但实际记得,有三条路径:
- 路径 1:压缩前记忆刷新(自动)
当 session 接近 token 上限或即将被压缩/重置时,OpenClaw 会静默触发一轮"记忆刷新",提醒模型:"快把重要信息写到 MEMORY.md 或日记里" - 路径 2:每日日记的沉淀
memory/2026-04-24.md→ "今天验证了 idea #4,Rollout 拼接"
memory/2026-04-25.md→ "今天写了 OpenClaw 技术分析"
3 天后这些日记文件还在 workspace 里,重要内容会被注入到系统提示词。 - 路径 3:用户新消息自带线索
你说:"继续上次那个抖音调研"------模型可以:- 从
MEMORY.md里找到"抖音调研"的记录 - 或者调用
sessions_list/sessions_history工具主动查历史 session
- 从
6.4 Heartbeat 机制详解
Heartbeat(心跳)是 Gateway 的周期性轮询机制,默认每 30 分钟触发一次。它与 cron 定时任务不同,是一个轻量级的"定期唤醒"信号。
text
┌─────────────────────────────────────────────┐
│ Gateway 心跳调度器 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐│
│ │ Cron │ │ Heartbeat│ │ Message ││
│ │ 精确计时 │ │ 周期性轮询│ │ 消息触发 ││
│ │ 独立会话 │ │ 主会话 │ │ 主会话 ││
│ └─────────┘ └─────────┘ └─────────┘│
└─────────────────────────────────────────────┘
Heartbeat 的工作流程
text
Gateway 心跳计时器到期(默认 30min)
│
▼
┌─────────────────────────────────────────────┐
│ 1. 读取 HEARTBEAT.md │
│ - 如果文件存在,读取内容作为检查清单 │
│ - 如果文件为空或不存在,跳过 │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 2. 向主会话发送系统消息 │
│ "Read HEARTBEAT.md if it exists... │
│ If nothing needs attention, reply │
│ HEARTBEAT_OK." │
│ (这是一个特殊的系统级触发消息) │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 3. Agent 执行检查任务 │
│ - 检查邮件、日历、通知等 │
│ - 读取 HEARTBEAT.md 中的自定义清单 │
│ - 判断是否需要向用户报告 │
└─────────────────────────────────────────────┘
│
▼
有重要发现? ──是──→ 向用户发送主动消息
│否
▼
回复 HEARTBEAT_OK(Gateway 识别为心跳确认)
七、工具系统 (Tools)
7.1 核心工具矩阵
OpenClaw 提供了 20+ 内置工具,按功能分组:
| 工具组 | 工具名称 | 功能描述 |
|---|---|---|
| 文件系统 | read / write / edit |
文件读写、精确文本替换 |
apply_patch |
结构化多文件补丁应用 | |
| 运行时 | exec / process |
Shell 命令执行 / 后台进程管理 |
| Web | web_search / web_fetch |
网络搜索 / 网页内容提取 |
| 浏览器 | browser |
浏览器自动化(快照/点击/输入/截图) |
| 画布 | canvas |
Node Canvas 渲染控制 |
| 设备节点 | nodes |
远程设备发现与控制(相机/屏幕/通知) |
| 消息 | message |
跨渠道消息发送与管理 |
| 定时任务 | cron |
Cron 作业调度与管理 |
| 网关 | gateway |
Gateway 重启/配置更新 |
| 会话 | sessions_list / sessions_spawn / sessions_send |
会话管理 / 子 Agent 生成 |
| 图像 | image / pdf |
图像分析 / PDF 分析 |
7.2 工具策略级联 (Policy Cascade)
OpenClaw 实现了五级工具权限级联,这是其安全设计的核心。每一层都可以 allow 或 deny 工具,deny 永远优先。
五级层级详解
text
┌──────────────────────────────────────────────────────────────┐
│ 第1层:Global(全局) │
│ ├─ 定义位置:~/.openclaw/openclaw.json 的顶层 tools 字段 │
│ ├─ 作用范围:所有 Agent、所有 Session、所有 Provider │
│ ├─ 典型配置:deny 高危工具(如 gateway、cron) │
│ └─ 示例:tools: { deny: ["gateway", "cron"] } │
├──────────────────────────────────────────────────────────────┤
│ 第2层:Provider(模型供应商) │
│ ├─ 定义位置:tools.byProvider.<providerId> │
│ ├─ 作用范围:该 Provider 下的所有模型调用 │
│ ├─ 典型用途:对特定供应商降级工具集(如轻量模型不给 browser)│
│ └─ 示例:byProvider: { "google-gemini": { profile: "minimal" } } │
├──────────────────────────────────────────────────────────────┤
│ 第3层:Agent(智能体) │
│ ├─ 定义位置:agents.list[].tools 字段 │
│ ├─ 作用范围:该 Agent 的所有 Session │
│ ├─ 典型用途:不同 Agent 不同权限(如 support Agent 只有 messaging 工具)│
│ └─ 示例:{ id: "support", tools: { allow: ["message", "slack"] } } │
├──────────────────────────────────────────────────────────────┤
│ 第4层:Session(会话) │
│ ├─ 定义位置:运行时通过 /send on|off 或 sendPolicy 设置 │
│ ├─ 作用范围:单个 Session(单次对话) │
│ ├─ 典型用途:临时禁用某 session 的工具权限 │
│ └─ 示例:在当前对话发 /send off,禁止自动回复 │
├──────────────────────────────────────────────────────────────┤
│ 第5层:Sandbox(沙箱) │
│ ├─ 定义位置:sandbox 配置中的工具过滤 │
│ ├─ 作用范围:沙箱隔离内的工具执行 │
│ ├─ 典型用途:Docker 容器内限制可用命令 │
│ └─ 示例:sandbox 模式为 all 时,exec 只能在容器内运行 │
└──────────────────────────────────────────────────────────────┘
关键原则:
- deny 永远优先:如果任何一层 deny 了某工具,即使其他层 allow,该工具也不可用
- 越下层越严格:Agent 层可以比 Global 层更严格,但不能更宽松(deny 不可被覆盖)
- profile 预设:系统内置 coding、messaging、minimal、full 四种预设配置
工具组速记
| 速记 | 包含的工具 |
|---|---|
group:runtime |
exec + bash + process |
group:fs |
read + write + edit + apply_patch |
group:sessions |
所有会话相关工具(sessions_list、sessions_spawn 等) |
group:memory |
memory_search + memory_get |
group:web |
web_search + web_fetch |
完整配置示例(带详细注释)
json
{
// ========== 第1层:Global(全局工具策略)==========
// 这里配置对所有 Agent、所有会话生效的默认工具权限
"tools": {
// 基础模板:coding/messaging/minimal/full
// coding = 文件系统 + 运行时 + Web(适合开发)
// messaging = 消息发送 + 会话管理(适合客服)
// minimal = 仅 read + exec(最小权限)
// full = 所有工具(无限制)
"profile": "coding",
// 全局 allow:无论模板默认什么,这些工具一定可用
"allow": ["group:fs", "browser"],
// 全局 deny:无论其他层怎么配,这些工具永远禁用
// deny 优先级最高,不可被下层覆盖
"deny": ["gateway", "cron"],
// ========== 第2层:Provider 级别覆盖 ==========
// 针对特定 LLM Provider 的工具策略
"byProvider": {
// 对轻量模型降级工具集(防止它乱调用复杂工具)
"google-gemini": {
"profile": "minimal", // 只给 minimal 级别的工具
"deny": ["browser", "exec"] // 额外禁用浏览器和命令执行
}
}
},
// ========== 第3层:Agent 级别覆盖 ==========
// 每个 Agent 可以有自己的工具权限
"agents": {
"list": [
{
"id": "main",
"name": "Main"
// main Agent 继承全局配置,不做额外限制
},
{
"id": "support",
"name": "Support Bot",
// support Agent 只能使用 messaging 相关工具
"tools": {
"profile": "messaging", // 基础模板用 messaging
"allow": ["slack", "discord"], // 额外允许 Slack 和 Discord 工具
"deny": ["exec", "write", "edit"] // 禁止执行命令和写文件
}
}
]
}
}
八、多智能体架构 (Multi-Agent)
8.1 会话命名空间体系
OpenClaw 使用层次化的会话命名空间:
| 深度 | 会话 Key 格式 | 角色 | 能否 Spawn? |
|---|---|---|---|
| 0 | agent:<id>:main |
主 Agent | 永远可以 |
| 1 | agent:<id>:subagent:<uuid> |
子 Agent | 取决于 maxSpawnDepth |
| 2 | agent:<id>:subagent:<uuid>:subagent:<uuid> |
孙 Agent (叶节点) | 永远不可以 |
8.2 子 Agent (Sub-Agent) 机制
| 特性 | 说明 |
|---|---|
| 隔离运行 | 独立的会话、独立的上下文、可选的独立沙箱 |
| 后台执行 | 非阻塞,主会话立即返回 runId |
| 结果回传 | 完成后通过 announce 机制将结果推送回请求者会话 |
| 最大嵌套 | 默认 maxSpawnDepth: 1,最大可配置到 5 |
| 并发控制 | 独立的 subagent 队列道,默认并发 8 |
两种运行模式:
- mode: "run" (默认): 一次性任务,完成后
announce结果 - mode: "session" + thread: true: 持久会话绑定(Discord Thread)
Orchestrator 模式 (当 maxSpawnDepth >= 2):
text
Main Agent → Orchestrator Sub-Agent → Worker Sub-Sub-Agents
│ │
│←──── announce 结果 ────────┘
│
←──── announce 聚合结果 ────────────────────────
8.3 ACP 运行时 (外部 Agent 接入)
OpenClaw 支持通过 runtime: "acp" 接入外部 Agent 运行时:
- Codex (OpenAI)
- Claude Code (Anthropic)
- Gemini CLI (Google)
这允许在 OpenClaw 的编排框架内调用专业编码 Agent。
九、Plugin 与 Skill 扩展系统
9.1 Plugin 架构(四层模型)
OpenClaw 的 Plugin 系统分为 4 个层次:
- Manifest + Discovery (清单与发现)
- 读取
openclaw.plugin.json,无需执行代码即可验证配置
- 读取
- Enablement + Validation (启用与验证)
- 基于配置决定启用/禁用/阻断状态
- Runtime Loading (运行时加载)
- 通过
jiti在进程中加载 TS/JS 模块
- 通过
- Surface Consumption (能力消费)
- 核心读取注册表暴露工具/渠道/命令
Plugin 可注册的能力:
- Agent 工具 (
registerTool) - 消息渠道 (
registerChannel) - 模型 Provider (
registerProvider) - HTTP 路由 (
registerHttpRoute) - CLI 命令 (
registerCli) - 后台服务 (
registerService) - 生命周期钩子 (
registerHook/api.on) - 上下文引擎 (
registerContextEngine) - Skills (通过 manifest 声明)
9.2 Skill 和 plugin(AgentSkills 兼容)
Skill 是教 Agent 如何使用工具的指令包:
| 属性 | 说明 |
|---|---|
| 格式 | 目录 + SKILL.md (YAML frontmatter + Markdown 说明) |
| 加载优先级 | workspace/skills > ~/.openclaw/skills > bundled skills |
| 条件加载 | 支持基于 OS/二进制文件/环境变量/配置的门控 |
| ClawHub | 公开的 Skill 注册表 (https://clawhub.com) |
Skill 门控示例:
yaml
---
name: gemini
metadata:
{"openclaw": {"requires": {"bins": ["gemini"], "env": ["GEMINI_API_KEY"]}}}
---
为什么 OpenClaw 不直接用 MCP 做 Plugin?
MCP 太窄了。 MCP 只定义了"LLM 怎么调用外部工具"的协议,但 OpenClaw 的 Plugin 还要做:
- 注册飞书/微信渠道(MCP 不管渠道接入)
- 注册新的 LLM Provider(MCP 不管模型接入)
- 注册 CLI 命令(MCP 没有 CLI 概念)
- 注册后台服务(MCP 没有常驻服务)
- 注册 HTTP 路由(MCP 没有 Web 服务)
MCP 是"工具暴露协议",Plugin 是"系统扩展框架"。
OpenClaw 的 Plugin 可以理解为一个**"超级驱动框架"**------它自己定义了一套比 MCP 更完整的扩展机制,同时也能接入外部的 MCP Server 作为工具来源。
十、安全与沙箱设计
10.1 安全体系的分层
OpenClaw 的安全不是单一机制,而是五层独立但协同的防线:
text
┌─────────────────────────────────────────────┐
│ 第1层: 工具策略级联 (Tool Policy Cascade) │
│ 决定"Agent 能调用什么工具" │
│ 五级:Global → Provider → Agent → Session → Sandbox │
├─────────────────────────────────────────────┤
│ 第2层: 执行审批 (Exec Approvals) │
│ 决定"敏感操作是否需要用户确认" │
│ 如 exec 运行危险命令前弹出 /approve 提示 │
├─────────────────────────────────────────────┤
│ 第3层: 沙箱隔离 (Sandbox Isolation) │
│ 决定"代码在哪里执行" │
│ Docker 容器 / 主机 / 无沙箱 │
├─────────────────────────────────────────────┤
│ 第4层: 提升模式 (Elevated Mode) │
│ 决定"谁可以逃逸沙箱" │
│ 仅 allowlist 中的发送者可触发主机执行 │
├─────────────────────────────────────────────┤
│ 第5层: 渠道白名单 (Channel Allowlist) │
│ 决定"谁可以给 Agent 发消息" │
│ 限制哪些发送者可触发 Agent │
└─────────────────────────────────────────────┘
各层独立工作:
- 工具策略说"你可以用 exec",但沙箱说"你只能在 Docker 里执行"
- 沙箱说"你在 Docker 里",但审批说"rm -rf 这种命令要用户确认"
- 审批说"用户确认了",但提升模式说"只有 ou_xxx 可以执行主机级操作"
10.2 沙箱配置
| 配置项 | 选项 | 说明 |
|---|---|---|
| mode | off / non-main / all |
何时启用沙箱 |
| scope | session / agent / shared |
容器隔离粒度 |
| workspaceAccess | none / ro / rw |
工作空间访问权限 |
| docker.network | none (默认) / 自定义 |
网络隔离 |
| docker.binds | 自定义挂载 | 额外目录映射 |
默认安全设置:
- 沙箱容器无网络访问 (
network: "none") - 禁止挂载
docker.sock、/etc、/proc等敏感路径 - 浏览器沙箱使用独立 Docker 网络 + CIDR 白名单
- noVNC 访问使用短期 Token + URL Fragment 传参