← Articles

基于 OpenClaw 项目的 AI Agent 技术分享

从 OpenClaw 的 Gateway、工具体系、Skills、MCP 与 Agent 执行流程,看个人 AI 助手系统的工程架构。

#OpenClaw#AI Agent#Gateway#Skills#MCP

目录

  1. OpenClaw 项目简介
  2. 项目架构概览
  3. AI Agents 能力相关概念介绍
    • 3.1 Agent / Rules / Tools / Skills / MCP 总览
    • 3.2 Rules/Spec — 公司规章制度与 SOP
    • 3.3 Tool Call — 员工操作软件的具体动作
    • 3.4 MCP
    • 3.5 Skills — 员工的简历与才艺
  4. AI Agent 主流架构模式
  5. OpenClaw 的 Agent 执行流程
    • 5.1 整体架构:增强型 ReAct + pi-mono 底座
    • 5.2 pi-mono 引擎:分层设计
    • 5.3 Agent 执行全流程:从消息到响应
    • 5.4 ReAct 工程增强
    • 5.5 记忆系统:让 Agent 拥有长期记忆
    • 5.6 架构选型思考
  6. 总结与展望

1. OpenClaw 项目简介

1.1 什么是 OpenClaw?

OpenClaw 是一个开源的、可自托管的个人 AI 助手系统,它的核心理念是:

  • 🏠 本地优先:在你自己的设备上运行,并启动网关服务处理所有渠道的消息请求
  • 📱 多渠道接入:支持 WhatsApp、Telegram、Slack、Discord、Signal、iMessage、hi 等多个消息平台
  • 🧠 智能代理:集成多个主流 AI 模型(Claude、GPT 等)
  • 🛠️ 工具生态:通过 Skills 和 Tools 本地执行各类工具命令,实现用户在电脑上的各类操作需求

1.2 与已有的助手有什么区别?

OpenClaw 和 Cowork/Manus 等 Agent 产品的核心区别有五点:

对比维度Manus/Claude Cowork 等通用 Agent 产品OpenClaw
交互入口以 CLI、Web、桌面 App 作为入口以 IM 作为核心入口,主要在手机上使用
交互方式用户→Agent 的单向交互(用户下发任务,Agent 完成任务)双向交互,Agent 可以根据 Cron、Heartbeat 机制主动执行任务和联系用户
Agent 生命周期按需启动7×24h 全天在线
Agent 运行环境分散的或一次性的持久的工作区和运行环境
自我进化能力固定,需要用户主动扩展可以自我进化,通过技能系统和插件动态增强能力

1.3 核心流程

image-4

Gateway:OpenClaw 的中央神经系统

在上面的核心流程图中,所有消息渠道和客户端都通过 Gateway 这个中心节点交互。Gateway 是 OpenClaw 最核心的基础设施组件,可以把它理解为公司的前台总机 + 门禁系统 + 广播站

  • 前台总机:所有来电(消息)都先到前台,前台决定转给谁
  • 门禁系统:验证来访者身份,控制谁能进入、能做什么
  • 广播站:有重要事件时,向全公司实时广播

它本质上是一个 WebSocket 服务器,默认监听 ws://127.0.0.1:18789,所有消息渠道和客户端都通过它与系统交互。

Gateway 的六大职责
职责公司隐喻说明
多渠道消息聚合前台接听所有电话线路统一接收来自 WhatsApp、Telegram、Slack 等渠道的消息
消息路由前台转接电话将消息智能路由到 Agent 进行处理
认证与权限控制门禁刷卡多层认证(Token、密码、设备身份)+ 细粒度授权
会话管理会议室预约系统创建/管理用户会话、跟踪聊天历史
事件广播公司广播站实时推送 Agent 事件给所有连接的客户端
资源协调行政总务启停渠道、调度定时任务、管理远程节点
消息路由全流程

以一条 WhatsApp 消息为例,看看它如何从用户手机到达 Agent,再返回给用户:

┌─ 用户在 WhatsApp 发送"帮我查看天气" ─────────────────────────────────┐
│                                                                     │
│  ① WhatsApp 渠道接收消息                                              │
│     Baileys SDK 拿到用户消息                                          │
│         │                                                           │
│         ▼                                                           │
│  ② 渠道调用 Gateway 方法                                              │
│     chat.send({                                                     │
│       sessionKey: "whatsapp:user123",                               │
│       message: "帮我查看天气"                                         │
│     })                                                              │
│         │                                                           │
│         ▼                                                           │
│  ③ Gateway 验证权限 + 获取/创建会话                                    │
│         │                                                           │
│         ▼                                                           │
│  ④ 触发 Agent Runner 执行                                            │
│     构建 System Prompt → 组装工具 → 启动 ReAct 循环                    │
│         │                                                           │
│         ▼                                                           │
│  ⑤ Agent 执行过程中,Gateway 实时广播事件                               │
│     → agent.started(Agent 开始工作)                                  │
│     → agent.event(思考中...)                                        │
│     → agent.event(调用 weather 工具)                                 │
│     → agent.completed(任务完成)                                      │
│     所有连接的客户端(CLI、Web UI、App)都能看到实时进度                   │
│         │                                                           │
│         ▼                                                           │
│  ⑥ WhatsApp 渠道接收 agent.completed 事件                              │
│     调用 send({ to: "user123", message: "今天晴天,25°C" })            │
│         │                                                           │
│         ▼                                                           │
│  ⑦ 用户在 WhatsApp 上看到回复                                         │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
Gateway 支持的方法一览

Gateway 暴露了丰富的 RPC 方法,按功能域分组:

功能域方法说明
Agentagent, agent.wait, agents.list, agents.create执行 Agent、管理 Agent 实例
聊天chat.send, chat.abort, chat.history, chat.inject发送消息、中止执行、查看历史
渠道channels.status, channels.logout渠道状态管理
会话sessions.list, sessions.patch, sessions.reset, sessions.delete会话 CRUD
节点node.list, node.invoke, node.pair.request远程节点管理
定时任务cron.list, cron.create, cron.delete定时任务管理
配置config.get, config.patch运行时配置管理
系统health, status健康检查和系统状态
HTTP 端点

除了核心的 WebSocket 协议,Gateway 同时暴露了若干 HTTP 端点:

端点用途
/WebSocket 连接升级入口
/control/Web 控制面板 UI
/v1/chat/completionsOpenAI 兼容 API(可以像调用 OpenAI 一样调用 OpenClaw)
/v1/responsesOpenResponses 标准 API
/hooks/接收外部 Webhook 回调
/canvas/Canvas 画布服务

其中 OpenAI 兼容 API 是一个非常巧妙的设计——它让任何支持 OpenAI API 格式的客户端都可以直接接入 OpenClaw 的 Gateway。

启动方式

Gateway 通过 CLI 命令启动:

openclaw gateway run [options]

# 常用选项
  --port <port>          # 端口,默认 18789
  --bind <mode>          # 绑定模式:loopback(仅本地)| lan(局域网)| tailnet(Tailscale)
  --auth <mode>          # 认证模式:token | password
  --token <token>        # 认证 Token
  --force                # 强制释放端口

启动后,Gateway 会依次完成:加载配置 → 创建 HTTP/HTTPS 服务器 → 启动 WebSocket 服务 → 初始化消息渠道 → 启动定时任务调度 → 启用 mDNS/Bonjour 局域网服务发现 → 进入运行循环。

一句话总结

Gateway 是 OpenClaw 的中央神经系统:它通过 WebSocket + JSON-RPC 协议,将多个消息渠道、多种客户端、AI Agent 引擎、定时任务和远程节点连接在一起,并在此过程中实施认证授权、会话管理和事件广播,是整个系统 7×24h 运行的核心枢纽。


2. 项目架构概览

2.1 目录结构

openclaw/
├── src/                      # 核心源码
│   ├── agents/              # Agent 运行时核心
│   │   ├── pi-tools.ts      # 工具注册与权限控制
│   │   ├── bash-tools.ts    # 命令执行工具
│   │   └── sandbox.js       # 沙箱隔离
│   ├── auto-reply/          # 自动回复与 Agent 执行
│   │   └── reply/
│   │       ├── agent-runner.ts          # Agent 执行主流程
│   │       ├── agent-runner-execution.ts # 执行逻辑
│   │       └── agent-runner-payloads.ts  # 响应构建
│   ├── channels/            # 各消息渠道集成
│   └── gateway/             # WebSocket Gateway
├── skills/                   # 技能定义目录
│   ├── github/              # GitHub 集成
│   ├── 1password/           # 1Password 集成
│   └── ...                  # 50+ 内置技能
├── docs/                     # 完整文档
├── apps/                     # 平台应用
│   ├── macos/               # macOS 菜单栏应用
│   ├── ios/                 # iOS 节点应用
│   └── android/             # Android 节点应用
└── AGENTS.md                # Agent 行为规范文档

2.2 技术栈

层级技术选型
运行时Node.js 22+ / TypeScript (ESM)
Agent 引擎@mariozechner/pi-mono
消息通道Baileys (WhatsApp), grammY (Telegram), discord.js, Slack Bolt
前端React (Web UI), SwiftUI (macOS/iOS), Kotlin (Android)
构建工具pnpm, Bun (开发), Vitest (测试)
协议WebSocket, JSON-RPC

3. AI Agents 能力相关概念介绍

前言:用”一家公司”来理解 AI Agent 系统

在深入技术细节之前,我们先建立一个直观的隐喻——把整个 AI Agent 系统想象成一家公司

技术概念公司隐喻一句话解释
Agent👤 员工公司里负责完成具体工作的人
Skills📋 员工的简历 / 才艺员工”会什么”——掌握的专业知识和操作技能
Tools🔧 公司内部设备公司内部维护的员工工具
Tool Call✋ 使用软件的具体动作打开 Excel → 选中 A1 → 输入数据 → 保存
Rules / Spec📖 公司规章制度 / SOP / JD员工必须遵守的规章、标准作业流程、岗位职责
MCP🔌 公司外部设备公司外部设备,黑盒模式完成部分任务

3.1 Agent / Rules / Tools / Skills / MCP 总览

              📖 Rules/Spec(公司规章制度 / SOP / JD)
              ════════════════════════════════════════
              AGENTS.md + Tool Policies + Runtime Permissions
              ─────────── 约束以下所有行为 ───────────


┌───────────────────────────────────────────────────────────────┐
│                                                               │
│    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐     │
│    │ 🔧 内置工具   │    │ 📋 Skills    │    │ 🔌 MCP      │     │
│    │ (Excel/打印)  │    │ (简历/才艺)   │    │ (USB外接)    │     │
│    └──────┬──────┘    └──────┬──────┘    └──────┬──────┘     │
│           │                  │                  │            │
│           └──────────────────┼──────────────────┘            │
│                              │                               │
│                   ❶ 工具清单注入(下发)                       │
│                    Tool Definitions /                        │
│                    Skill Descriptions /                      │
│                    MCP Tool Schemas                          │
│                   "今天你能用这些设备"                         │
│                              │                               │
│                              ▼                               │
│    ┌─────────────────────────────────────────────────────┐   │
│    │              👤 Agent(员工)的大脑                    │   │
│    │           AI 模型 (Claude/GPT/Gemini)                │   │
│    │                                                      │   │
│    │    ┌────────────┐ ┌────────────┐ ┌──────────┐       │   │
│    │    │System Prompt│ │Tool Schemas│ │ 用户消息  │       │   │
│    │    │ (员工手册)  │ │(设备说明书) │ │(客户需求) │       │   │
│    │    └────────────┘ └────────────┘ └──────────┘       │   │
│    │                                                      │   │
│    │    思考:"客户要查天气,我有 weather 工具"              │   │
│    │        → 输出 function_call 请求                     │   │
│    └─────────────────────────┬───────────────────────────┘   │
│                              │                               │
│                   ❷ 实际调用 (function_call)                  │
│                              │                               │
│                              ▼                               │
│    ┌─────────────────────────────────────────────────────┐   │
│    │           ✋ Tool Call 转换层("行政助理")             │   │
│    │      把指令翻译成不同品牌工具能理解的格式               │   │
│    │      • Anthropic → tool_use                         │   │
│    │      • OpenAI    → function                         │   │
│    │      • Gemini    → functionCall                     │   │
│    └──────────┬──────────────┬──────────────┬────────────┘   │
│               │              │              │                │
│               ▼              ▼              ▼                │
│          🔧 内置工具    📋 Skill 执行   🔌 MCP Server        │
│          (直接执行)    (子流程/脚本)   (JSON-RPC 调用)        │
│               │              │              │                │
│               └──────────────┼──────────────┘                │
│                              │                               │
│                              ▼                               │
│                       📤 Tool Result                         │
│                    (执行结果回传给 Agent,                     │
│                     进入下一轮思考循环)                       │
│                              │                               │
│                              └─────→ 回到 👤 Agent ↑         │
│                                                               │
└───────────────────────────────────────────────────────────────┘

三大”工具来源”对比

维度🔧 内置工具(公司自有设备)📋 Skills(员工简历/才艺)🔌 MCP(公司外部设备)
公司隐喻公司统一采购的电脑、打印机员工自带的专业知识和技能通过 标准协议即插即用的外设
本质代码中定义的函数System Prompt 超级提示词标准化工具通信协议
工作方式直接函数调用指导 Agent 组合已有工具通过 RPC 调用远程服务
执行位置本地进程内Agent 自身(本地进程内)MCP Server 侧(可本地/远程)
定义方式TypeScript 函数 + JSON SchemaSKILL.md Markdown 文件 + 代码MCP Server 声明
延迟低(手边直接用)低(脑子里的知识)中~高(跨进程/网络)
典型场景read/write/exec/browserGitHub 操作指导、1Password 使用数据库查询、第三方 API

3.2 Rules/Spec — 公司规章制度与 SOP

公司隐喻:任何公司要正常运转,首先需要规章制度(员工手册、行为准则)、SOP(标准作业程序)JD(职位描述)。新员工入职第一天,先通读《员工手册》,知道什么能做、什么不能做、做事的标准流程是什么。

在 OpenClaw 中,Agent(员工)的行为规范体系就是这套”公司制度”。

3.2.1 三层制度体系

就像一家成熟企业的管理制度分层一样,OpenClaw 的规范也分为三个层级:

┌─────────────────────────────────────────────────────────┐
│  Layer 1: AGENTS.md —— "员工手册"                        │
│  公司宪法级文件,所有员工入职必读                            │
│  (注入到 System Prompt,指导 Agent 理解项目规范)           │
└──────────────────────┬──────────────────────────────────┘


┌─────────────────────────────────────────────────────────┐
│  Layer 2: Tool Policies —— "部门权限制度"                  │
│  不同岗位/部门能使用哪些软件和设备                            │
│  (通过 JSON 配置控制工具权限和执行行为)                     │
└──────────────────────┬──────────────────────────────────┘


┌─────────────────────────────────────────────────────────┐
│  Layer 3: Runtime Permissions —— "现场审批单"               │
│  员工遇到敏感操作时,需要找主管当场签字审批                     │
│  (动态请求用户授权,细粒度控制敏感操作)                      │
└─────────────────────────────────────────────────────────┘

3.2.2 Layer 1: AGENTS.md —— “员工手册”

什么是 AGENTS.md?

AGENTS.md 是 OpenClaw 的 “公司员工手册”,它是一个特殊的 Markdown 文件,相当于公司的宪法级文件,包含了:

  • 📖 公司制度(代码仓库规范):公司的组织架构、部门划分、命名约定、工作流程
  • 🧑‍💻 岗位 SOP(开发者指引):如何构建、测试、发布——每项工作的标准操作步骤
  • 🤖 行为红线(Agent 行为规则):员工在操作代码时绝对不能触碰的底线

类比理解:

  • 新员工(Agent)上班第一天 → 人力资源部发放《员工手册》
  • Agent 初始化时 → 系统将 AGENTS.md 注入到 System Prompt(“把手册读进脑子里”)
  • 之后员工的所有行为都必须遵守手册规定

AGENTS.md 其实就是给 Agent 规定的 Rules。为什么需要 AGENTS.md?当前不同 AI Coding 工具有不同的 rules 读取规则,导致 Rules 碎片化,难以维护: image-1

所以 OpenAI 联合 Google、Cursor 等,推出了一个简单的新标准,用一个清晰、统一的文档,取代厂家特定的配置文件,为 AI 编程 Agent 提供一种可预测的方式来理解和操作软件项目。

示例:openclaw的AGENTS.md
# Repository Guidelines

- Repo: https://github.com/openclaw/openclaw
- GitHub issues/comments/PR comments: use literal multiline strings or `-F - <<'EOF'` (or $'...') for real newlines; never embed "\\n".

## Project Structure & Module Organization

- Source code: `src/` (CLI wiring in `src/cli`, commands in `src/commands`, web provider in `src/provider-web.ts`, infra in `src/infra`, media pipeline in `src/media`).
- Tests: colocated `*.test.ts`.
- Docs: `docs/` (images, queue, Pi config). Built output lives in `dist/`.
- Plugins/extensions: live under `extensions/*` (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them.
- Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `openclaw` in `devDependencies` or `peerDependencies` instead (runtime resolves `openclaw/plugin-sdk` via jiti alias).
- Installers served from `https://openclaw.ai/*`: live in the sibling repo `../openclaw.ai` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`).
- Messaging channels: always consider **all** built-in + extension channels when refactoring shared logic (routing, allowlists, pairing, command gating, onboarding, docs).
  - Core channel docs: `docs/channels/`
  - Core channel code: `src/telegram`, `src/discord`, `src/slack`, `src/signal`, `src/imessage`, `src/web` (WhatsApp web), `src/channels`, `src/routing`
  - Extensions (channel plugins): `extensions/*` (e.g. `extensions/msteams`, `extensions/matrix`, `extensions/zalo`, `extensions/zalouser`, `extensions/voice-call`)
- When adding channels/extensions/apps/docs, update `.github/labeler.yml` and create matching GitHub labels (use existing channel/extension label colors).

## Docs Linking (Mintlify)

- Docs are hosted on Mintlify (docs.openclaw.ai).
- Internal doc links in `docs/**/*.md`: root-relative, no `.md`/`.mdx` (example: `[Config](/configuration)`).
- When working with documentation, read the mintlify skill.
- Section cross-references: use anchors on root-relative paths (example: `[Hooks](/configuration#hooks)`).
- Doc headings and anchors: avoid em dashes and apostrophes in headings because they break Mintlify anchor links.
- When Peter asks for links, reply with full `https://docs.openclaw.ai/...` URLs (not root-relative).
- When you touch docs, end the reply with the `https://docs.openclaw.ai/...` URLs you referenced.
- README (GitHub): keep absolute docs URLs (`https://docs.openclaw.ai/...`) so links work on GitHub.
- Docs content must be generic: no personal device names/hostnames/paths; use placeholders like `user@gateway-host` and “gateway host”.

## Docs i18n (zh-CN)

- `docs/zh-CN/**` is generated; do not edit unless the user explicitly asks.
- Pipeline: update English docs → adjust glossary (`docs/.i18n/glossary.zh-CN.json`) → run `scripts/docs-i18n` → apply targeted fixes only if instructed.
- Translation memory: `docs/.i18n/zh-CN.tm.jsonl` (generated).
- See `docs/.i18n/README.md`.
- The pipeline can be slow/inefficient; if it’s dragging, ping @jospalmbier on Discord instead of hacking around it.

## exe.dev VM ops (general)

- Access: stable path is `ssh exe.dev` then `ssh vm-name` (assume SSH key already set).
- SSH flaky: use exe.dev web terminal or Shelley (web agent); keep a tmux session for long ops.
- Update: `sudo npm i -g openclaw@latest` (global install needs root on `/usr/lib/node_modules`).
- Config: use `openclaw config set ...`; ensure `gateway.mode=local` is set.
- Discord: store raw token only (no `DISCORD_BOT_TOKEN=` prefix).
- Restart: stop old gateway and run:
  `pkill -9 -f openclaw-gateway || true; nohup openclaw gateway run --bind loopback --port 18789 --force > /tmp/openclaw-gateway.log 2>&1 &`
- Verify: `openclaw channels status --probe`, `ss -ltnp | rg 18789`, `tail -n 120 /tmp/openclaw-gateway.log`.

## Build, Test, and Development Commands

- Runtime baseline: Node **22+** (keep Node + Bun paths working).
- Install deps: `pnpm install`
- Pre-commit hooks: `prek install` (runs same checks as CI)
- Also supported: `bun install` (keep `pnpm-lock.yaml` + Bun patching in sync when touching deps/patches).
- Prefer Bun for TypeScript execution (scripts, dev, tests): `bun <file.ts>` / `bunx <tool>`.
- Run CLI in dev: `pnpm openclaw ...` (bun) or `pnpm dev`.
- Node remains supported for running built output (`dist/*`) and production installs.
- Mac packaging (dev): `scripts/package-mac-app.sh` defaults to current arch. Release checklist: `docs/platforms/mac/release.md`.
- Type-check/build: `pnpm build`
- TypeScript checks: `pnpm tsgo`
- Lint/format: `pnpm check`
- Format check: `pnpm format` (oxfmt --check)
- Format fix: `pnpm format:fix` (oxfmt --write)
- Tests: `pnpm test` (vitest); coverage: `pnpm test:coverage`

## Coding Style & Naming Conventions

- Language: TypeScript (ESM). Prefer strict typing; avoid `any`.
- Formatting/linting via Oxlint and Oxfmt; run `pnpm check` before commits.
- Add brief code comments for tricky or non-obvious logic.
- Keep files concise; extract helpers instead of “V2” copies. Use existing patterns for CLI options and dependency injection via `createDefaultDeps`.
- Aim to keep files under ~700 LOC; guideline only (not a hard guardrail). Split/refactor when it improves clarity or testability.
- Naming: use **OpenClaw** for product/app/docs headings; use `openclaw` for CLI command, package/binary, paths, and config keys.

## Release Channels (Naming)

- stable: tagged releases only (e.g. `vYYYY.M.D`), npm dist-tag `latest`.
- beta: prerelease tags `vYYYY.M.D-beta.N`, npm dist-tag `beta` (may ship without macOS app).
- dev: moving head on `main` (no tag; git checkout main).

## Testing Guidelines

- Framework: Vitest with V8 coverage thresholds (70% lines/branches/functions/statements).
- Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`.
- Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic.
- Do not set test workers above 16; tried already.
- Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
- Full kit + what’s covered: `docs/testing.md`.
- Changelog: user-facing changes only; no internal/meta notes (version alignment, appcast reminders, release process).
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
- Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available.

## Commit & Pull Request Guidelines

**Full maintainer PR workflow (optional):** If you want the repo's end-to-end maintainer workflow (triage order, quality bar, rebase rules, commit/changelog conventions, co-contributor policy, and the `review-pr` > `prepare-pr` > `merge-pr` pipeline), see `.agents/skills/PR_WORKFLOW.md`. Maintainers may use other workflows; when a maintainer specifies a workflow, follow that. If no workflow is specified, default to PR_WORKFLOW.

- Create commits with `scripts/committer "<msg>" <file...>`; avoid manual `git add`/`git commit` so staging stays scoped.
- Follow concise, action-oriented commit messages (e.g., `CLI: add verbose flag to send`).
- Group related changes; avoid bundling unrelated refactors.
- Read this when submitting a PR: `docs/help/submitting-a-pr.md` ([Submitting a PR](https://docs.openclaw.ai/help/submitting-a-pr))
- Read this when submitting an issue: `docs/help/submitting-an-issue.md` ([Submitting an Issue](https://docs.openclaw.ai/help/submitting-an-issue))

## Shorthand Commands

- `sync`: if working tree is dirty, commit all changes (pick a sensible Conventional Commit message), then `git pull --rebase`; if rebase conflicts and cannot resolve, stop; otherwise `git push`.

## Security & Configuration Tips

- Web provider stores creds at `~/.openclaw/credentials/`; rerun `openclaw login` if logged out.
- Pi sessions live under `~/.openclaw/sessions/` by default; the base directory is not configurable.
- Environment variables: see `~/.profile`.
- Never commit or publish real phone numbers, videos, or live configuration values. Use obviously fake placeholders in docs, tests, and examples.
- Release flow: always read `docs/reference/RELEASING.md` and `docs/platforms/mac/release.md` before any release work; do not ask routine questions once those docs answer them.

## Troubleshooting

- Rebrand/migration issues or legacy config/service warnings: run `openclaw doctor` (see `docs/gateway/doctor.md`).

## Agent-Specific Notes

- Vocabulary: "makeup" = "mac app".
- Never edit `node_modules` (global/Homebrew/npm/git installs too). Updates overwrite. Skill notes go in `tools.md` or `AGENTS.md`.
- When adding a new `AGENTS.md` anywhere in the repo, also add a `CLAUDE.md` symlink pointing to it (example: `ln -s AGENTS.md CLAUDE.md`).
- Signal: "update fly" => `fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/openclaw && git pull --rebase origin main'"` then `fly machines restart e825232f34d058 -a flawd-bot`.
- When working on a GitHub Issue or PR, print the full URL at the end of the task.
- When answering questions, respond with high-confidence answers only: verify in code; do not guess.
- Never update the Carbon dependency.
- Any dependency with `pnpm.patchedDependencies` must use an exact version (no `^`/`~`).
- Patching dependencies (pnpm patches, overrides, or vendored changes) requires explicit approval; do not do this by default.
- CLI progress: use `src/cli/progress.ts` (`osc-progress` + `@clack/prompts` spinner); don’t hand-roll spinners/bars.
- Status output: keep tables + ANSI-safe wrapping (`src/terminal/table.ts`); `status --all` = read-only/pasteable, `status --deep` = probes.
- Gateway currently runs only as the menubar app; there is no separate LaunchAgent/helper label installed. Restart via the OpenClaw Mac app or `scripts/restart-mac.sh`; to verify/kill use `launchctl print gui/$UID | grep openclaw` rather than assuming a fixed label. **When debugging on macOS, start/stop the gateway via the app, not ad-hoc tmux sessions; kill any temporary tunnels before handoff.**
- macOS logs: use `./scripts/clawlog.sh` to query unified logs for the OpenClaw subsystem; it supports follow/tail/category filters and expects passwordless sudo for `/usr/bin/log`.
- If shared guardrails are available locally, review them; otherwise follow this repo's guidance.
- SwiftUI state management (iOS/macOS): prefer the `Observation` framework (`@Observable`, `@Bindable`) over `ObservableObject`/`@StateObject`; don’t introduce new `ObservableObject` unless required for compatibility, and migrate existing usages when touching related code.
- Connection providers: when adding a new connection, update every UI surface and docs (macOS app, web UI, mobile if applicable, onboarding/overview docs) and add matching status + configuration forms so provider lists and settings stay in sync.
- Version locations: `package.json` (CLI), `apps/android/app/build.gradle.kts` (versionName/versionCode), `apps/ios/Sources/Info.plist` + `apps/ios/Tests/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `apps/macos/Sources/OpenClaw/Resources/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `docs/install/updating.md` (pinned npm version), `docs/platforms/mac/release.md` (APP_VERSION/APP_BUILD examples), Peekaboo Xcode projects/Info.plists (MARKETING_VERSION/CURRENT_PROJECT_VERSION).
- "Bump version everywhere" means all version locations above **except** `appcast.xml` (only touch appcast when cutting a new macOS Sparkle release).
- **Restart apps:** “restart iOS/Android apps” means rebuild (recompile/install) and relaunch, not just kill/launch.
- **Device checks:** before testing, verify connected real devices (iOS/Android) before reaching for simulators/emulators.
- iOS Team ID lookup: `security find-identity -p codesigning -v` → use Apple Development (…) TEAMID. Fallback: `defaults read com.apple.dt.Xcode IDEProvisioningTeamIdentifiers`.
- A2UI bundle hash: `src/canvas-host/a2ui/.bundle.hash` is auto-generated; ignore unexpected changes, and only regenerate via `pnpm canvas:a2ui:bundle` (or `scripts/bundle-a2ui.sh`) when needed. Commit the hash as a separate commit.
- Release signing/notary keys are managed outside the repo; follow internal release docs.
- Notary auth env vars (`APP_STORE_CONNECT_ISSUER_ID`, `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_API_KEY_P8`) are expected in your environment (per internal release docs).
- **Multi-agent safety:** do **not** create/apply/drop `git stash` entries unless explicitly requested (this includes `git pull --rebase --autostash`). Assume other agents may be working; keep unrelated WIP untouched and avoid cross-cutting state changes.
- **Multi-agent safety:** when the user says "push", you may `git pull --rebase` to integrate latest changes (never discard other agents' work). When the user says "commit", scope to your changes only. When the user says "commit all", commit everything in grouped chunks.
- **Multi-agent safety:** do **not** create/remove/modify `git worktree` checkouts (or edit `.worktrees/*`) unless explicitly requested.
- **Multi-agent safety:** do **not** switch branches / check out a different branch unless explicitly requested.
- **Multi-agent safety:** running multiple agents is OK as long as each agent has its own session.
- **Multi-agent safety:** when you see unrecognized files, keep going; focus on your changes and commit only those.
- Lint/format churn:
  - If staged+unstaged diffs are formatting-only, auto-resolve without asking.
  - If commit/push already requested, auto-stage and include formatting-only follow-ups in the same commit (or a tiny follow-up commit if needed), no extra confirmation.
  - Only ask when changes are semantic (logic/data/behavior).
- Lobster seam: use the shared CLI palette in `src/terminal/palette.ts` (no hardcoded colors); apply palette to onboarding/config prompts and other TTY UI output as needed.
- **Multi-agent safety:** focus reports on your edits; avoid guard-rail disclaimers unless truly blocked; when multiple agents touch the same file, continue if safe; end with a brief “other files present” note only if relevant.
- Bug investigations: read source code of relevant npm dependencies and all related local code before concluding; aim for high-confidence root cause.
- Code style: add brief comments for tricky logic; keep files under ~500 LOC when feasible (split/refactor as needed).
- Tool schema guardrails (google-antigravity): avoid `Type.Union` in tool input schemas; no `anyOf`/`oneOf`/`allOf`. Use `stringEnum`/`optionalStringEnum` (Type.Unsafe enum) for string lists, and `Type.Optional(...)` instead of `... | null`. Keep top-level tool schema as `type: "object"` with `properties`.
- Tool schema guardrails: avoid raw `format` property names in tool schemas; some validators treat `format` as a reserved keyword and reject the schema.
- When asked to open a “session” file, open the Pi session logs under `~/.openclaw/agents/<agentId>/sessions/*.jsonl` (use the `agent=<id>` value in the Runtime line of the system prompt; newest unless a specific ID is given), not the default `sessions.json`. If logs are needed from another machine, SSH via Tailscale and read the same path there.
- Do not rebuild the macOS app over SSH; rebuilds must be run directly on the Mac.
- Never send streaming/partial replies to external messaging surfaces (WhatsApp, Telegram); only final replies should be delivered there. Streaming/tool events may still go to internal UIs/control channel.
- Voice wake forwarding tips:
  - Command template should stay `openclaw-mac agent --message "${text}" --thinking low`; `VoiceWakeForwarder` already shell-escapes `${text}`. Don’t add extra quotes.
  - launchd PATH is minimal; ensure the app’s launch agent PATH includes standard system paths plus your pnpm bin (typically `$HOME/Library/pnpm`) so `pnpm`/`openclaw` binaries resolve when invoked via `openclaw-mac`.
- For manual `openclaw message send` messages that include `!`, use the heredoc pattern noted below to avoid the Bash tool’s escaping.
- Release guardrails: do not change version numbers without operator’s explicit consent; always ask permission before running any npm publish/release step.

## NPM + 1Password (publish/verify)

- Use the 1password skill; all `op` commands must run inside a fresh tmux session.
- Sign in: `eval "$(op signin --account my.1password.com)"` (app unlocked + integration on).
- OTP: `op read 'op://Private/Npmjs/one-time password?attribute=otp'`.
- Publish: `npm publish --access public --otp="<otp>"` (run from the package dir).
- Verify without local npmrc side effects: `npm view <pkg> version --userconfig "$(mktemp)"`.
- Kill the tmux session after publish.
System Prompt 构建流程(“发放员工手册”)
// src/agents/pi-embedded-runner/run/workspace.ts
export function buildSystemPromptFiles(params: {
  agentDir?: string;
  workspaceDir?: string;
}): string[] {
  const files: string[] = [];
  
  // 1. 加载 AGENTS.md(员工手册 —— 公司规范)
  const agentsFile = findFileRecursive(workspaceDir, 'AGENTS.md');
  if (agentsFile) files.push(agentsFile);
  
  // 2. 加载 SOUL.md(员工个性 —— 岗位人设描述)
  const soulFile = findFileRecursive(agentDir, 'SOUL.md');
  if (soulFile) files.push(soulFile);
  
  // 3. 加载 TOOLS.md(工具使用手册 —— 设备操作指南)
  const toolsFile = findFileRecursive(agentDir, 'TOOLS.md');
  if (toolsFile) files.push(toolsFile);
  
  return files;
}

3.2.3 Layer 2: Tool Policies —— “部门权限制度”

公司隐喻:并非每个员工都能使用所有设备。前台不能操作车床,实习生不能登录财务系统。公司通过岗位权限矩阵来控制谁能用什么工具。

策略配置示例
// ~/.openclaw/openclaw.json(公司《岗位权限手册》)
{
  "tools": {
    "profile": "safe",        // 岗位类型:safe(基础岗)/ default / full(总监级)
    "allow": [
      "bash",                 // 允许使用的"设备"清单
      "read",
      "write",
      "sessions_*"            // 通配符:所有 sessions 相关的工具
    ],
    "deny": [
      "browser",              // 禁止使用的"设备"
      "gateway"
    ],
    "exec": {                 // 命令执行——"危险设备"的安全规程
      "security": "ask",      // ask = 每次使用需要主管审批 / deny = 完全禁止 / allow = 自由使用
      "safeBins": [           // 安全操作白名单(这些命令可以放心用)
        "git",
        "npm",
        "pnpm"
      ],
      "timeoutSec": 300
    }
  },
  "agents": {
    "default": {
      "sandbox": {
        "mode": "non-main",   // 非主会话在"隔离车间"中工作
        "docker": {
          "image": "node:22-alpine"
        }
      },
      "tools": {
        "allow": ["bash", "read", "sessions_*"],
        "deny": ["browser", "gateway"]
      }
    }
  }
}
权限检查流程(“门禁系统”)
// src/agents/pi-tools.policy.ts
export function filterToolsByPolicy(
  tools: AnyAgentTool[],
  policy: ToolPolicy
): AnyAgentTool[] {
  return tools.filter(tool => {
    const toolName = normalizeToolName(tool.name);
    
    // 1. 检查黑名单——"禁入区域"
    if (policy.deny?.some(pattern => matchPattern(toolName, pattern))) {
      return false;
    }
    
    // 2. 检查白名单——"授权设备"
    if (policy.allow) {
      return policy.allow.some(pattern => matchPattern(toolName, pattern));
    }
    
    // 3. 看岗位默认权限——"这个职位默认能用吗?"
    return getDefaultAllowed(policy.profile, toolName);
  });
}
三级权限继承(“集团 → 公司 → 部门”)
1. Global Policy(集团总规——全局策略)
   ~/.openclaw/openclaw.json

2. Agent-Level Policy(子公司规定——特定 Agent 策略)
   config.agents.default.tools

3. Session-Level Override(项目组临时授权——会话级别覆盖)
   运行时动态调整

3.2.4 Layer 3: Runtime Permissions —— “现场审批单”

公司隐喻:即使员工有权限使用某个设备,面对高风险操作(比如删除生产数据库、修改核心财务表),仍然需要现场找主管签字审批。审批选项:✅ 批准一次 / ✅ 永久授权 / ❌ 拒绝。

Agent 在执行敏感操作前会通过客户端交互请求用户授权(“提交审批单”)。

关键特性:

  • ✅ 细粒度权限控制 —— 精确到每一个操作
  • ✅ 用户交互式授权 —— 主管实时审批
  • ✅ 支持”始终允许”规则记忆 —— 一次授权、后续免审

3.3 Tool Call — 员工操作软件的具体动作

Tools:公司内部工具,白盒且Agent可直接调用。

Tool Call 的本质:Function Calling

Tool Call 本质上就是 AI 模型的 Function Calling 能力。不同模型提供商(软件品牌)的叫法不同,但核心机制完全一致——员工(模型)在思考过程中决定使用一个工具,并说出”我要怎么用、传什么参数”:

模型提供商(软件品牌)API 参数名模型响应类型底层协议
Anthropic (Claude)toolstool_useinput_schema (JSON Schema)
OpenAI (GPT)tools + type: "function"function_callparameters (JSON Schema)
Google (Gemini)functionDeclarationsfunctionCallparametersJsonSchema

OpenClaw 通过统一的 ToolToolCall 接口,屏蔽了这些差异(“不管用什么牌子的 Excel,操作方式统一”):

// pi-mono/packages/ai/src/types.ts — 统一操作接口

// 统一的工具定义("设备操作手册"——不分品牌)
interface Tool {
  name: string;
  description: string;
  parameters: TSchema;        // JSON Schema 格式
}

// 统一的工具调用("员工的一次操作动作")
interface ToolCall {
  type: "toolCall";
  id: string;                 // 这次操作的唯一编号
  name: string;               // 操作的是哪个工具
  arguments: Record<string, any>;  // 具体的操作参数
}

// 统一的停止原因
type StopReason = "stop" | "length" | "toolUse" | "error" | "aborted";

工具从定义到执行的完整链路(“从采购设备到员工使用”)

内置工具 / Skills 分发 / MCP 注册
(采购 / 自带技能 / USB 外接)


             AnyAgentTool[]           ← 统一工具接口(统一的设备规格)


          toToolDefinitions()         ← 适配器:包装 Hook、权限(贴上使用标签)


           ToolDefinition[]           ← 可执行的工具定义(可以上手用了)


            convertTools()            ← 翻译成不同品牌的操作格式

          ┌─────────┼─────────┐
          ▼         ▼         ▼
     Anthropic   OpenAI    Google
     tools[]     tools[]   functionDeclarations[]
          │         │         │
          └─────────┼─────────┘

              模型 API 请求(员工下达操作指令)


              模型返回 tool_use / function_call(员工说"我要读这个文件")


              执行工具 → 结果回传模型 → 继续推理(完成操作 → 看结果 → 继续工作)

工具调用生命周期(“员工操作一次工具的完整流程”)

每次工具调用(“员工每次使用一个设备”)都要经过完整的流程管理:

员工思考 → 决定使用某个工具(生成 Tool Call)


              ┌──────────────────────┐
              │  before_tool_call    │ ← 安保检查("你有权限用这个吗?")
              │  Hook 拦截           │    可阻止操作 / 修改参数
              └──────────┬───────────┘


              ┌──────────────────────┐
              │  权限校验             │ ← 刷门禁卡(Tool Policy 检查)
              │  (allow/deny/ask)    │    allow=通过 / deny=拒绝 / ask=找主管审批
              └──────────┬───────────┘


              ┌──────────────────────┐
              │  工具执行             │ ← 实际操作设备(沙箱/主机)
              │  tool.execute()      │
              └──────────┬───────────┘


              ┌──────────────────────┐
              │  after_tool_call     │ ← 操作日志(记录谁在什么时间用了什么)
              │  Hook 回调           │
              └──────────┬───────────┘


              操作结果 → 回传给员工大脑 → 继续工作

内置工具一览(“公司标配设备清单”)

OpenClaw 内置了丰富的工具集(“公司统一采购的标准设备”),通过 createOpenClawTools 函数统一注册:

// src/agents/openclaw-tools.ts
export function createOpenClawTools(options?: {...}): AnyAgentTool[] {
  const tools: AnyAgentTool[] = [
    createBrowserTool(),           // 🌐 浏览器(上网冲浪)
    createCanvasTool(),            // 🎨 画布(画图工具)
    createNodesTool(),             // 🖥️ 节点管理(服务器)
    createCronTool(),              // ⏰ 定时任务(闹钟/日程)
    createMessageTool(),           // 💬 消息发送(即时通讯)
    createTtsTool(),               // 🔊 文本转语音(语音合成器)
    createGatewayTool(),           // 🚪 网关通信(前台总机)
    createAgentsListTool(),        // 👥 员工花名册
    createSessionsListTool(),      // 📋 会议室列表
    createSessionsHistoryTool(),   // 📝 会议记录
    createSessionsSendTool(),      // ✉️ 会议消息发送
    createSessionsSpawnTool(),     // 🆕 预定新会议室
    createSessionStatusTool(),     // 📊 会议状态
    createWebSearchTool(),         // 🔍 搜索引擎
    createWebFetchTool(),          // 📥 网页下载
    createImageTool(),             // 🖼️ 图像分析
  ];
  return tools;
}

此外,还从 pi-coding-agent 引入了编码相关的工具(“专业级开发设备”):

// src/agents/pi-tools.ts
import {
  codingTools,          // 代码工具箱
  createEditTool,       // 文件编辑器
  createReadTool,       // 文件阅读器
  createWriteTool,      // 文件写入器
  readTool,
} from "@mariozechner/pi-coding-agent";

完整的工具分为以下几组(“设备分类”):

工具组(设备类别)包含工具用途
group:fs(文件柜)read, write, edit, apply_patch文件系统操作
group:runtime(生产线)exec, process命令执行与进程管理
group:web(互联网终端)web_search, web_fetch网络搜索与页面获取
group:sessions(会议系统)sessions_list, sessions_history, sessions_send, sessions_spawn, session_status会话管理
group:ui(多媒体设备)browser, canvas浏览器与画布
group:memory(档案室)memory_search, memory_get记忆检索
group:messaging(通讯设备)message消息发送
group:automation(自动化车间)cron, gateway定时任务与网关
group:nodes(分支机构)nodes节点管理

工具定义与适配(“设备安装与调试”)

每个工具通过统一的 AnyAgentTool 接口定义,再由 toToolDefinitions 适配器转换为模型可消费的 ToolDefinition 格式(“安装驱动程序”):

// src/agents/pi-tool-definition-adapter.ts
export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
  return tools.map((tool) => {
    const name = tool.name || "tool";
    return {
      name,
      label: tool.label ?? name,
      description: tool.description ?? "",
      parameters: tool.parameters,
      execute: async (...args): Promise<AgentToolResult<unknown>> => {
        const { toolCallId, params, onUpdate, signal } = splitToolExecuteArgs(args);
        try {
          // 1. 操作前安保检查(before hook)
          const hookOutcome = await runBeforeToolCallHook({
            toolName: name, params, toolCallId,
          });
          if (hookOutcome.blocked) {
            throw new Error(hookOutcome.reason);
          }
          
          // 2. 实际操作设备
          const result = await tool.execute(
            toolCallId, hookOutcome.params, signal, onUpdate
          );
          
          // 3. 操作后记录日志(after hook)
          await hookRunner.runAfterToolCall({
            toolName: name, params, result,
          });
          
          return result;
        } catch (err) {
          // 错误处理...
        }
      },
    };
  });
}

工具权限策略(“岗位级别与设备权限”)

OpenClaw 通过 Tool Profile 预设不同岗位(场景)的设备权限集合:

// src/agents/tool-policy.ts
export type ToolProfileId = "minimal" | "coding" | "messaging" | "full";

const TOOL_PROFILES: Record<ToolProfileId, ToolProfilePolicy> = {
  minimal:   { allow: ["session_status"] },                            // 实习生:只能看会议状态
  coding:    { allow: ["group:fs", "group:runtime", "group:sessions",  // 开发工程师:文件、命令、会话、记忆
                        "group:memory", "image"] },
  messaging: { allow: ["group:messaging", "sessions_list",             // 客服专员:消息和会话
                        "sessions_history", "sessions_send"] },
  full:      {},                                                       // CEO:不限制,全部权限
};

此外,还支持所有者专属工具——某些敏感设备(如公司保险柜)只有老板才能使用:

// src/agents/tool-policy.ts
export function applyOwnerOnlyToolPolicy(
  tools: AnyAgentTool[],
  senderIsOwner: boolean
) {
  return tools.map((tool) => {
    if (!isOwnerOnlyToolName(tool.name)) return tool;  // 不是敏感设备,跳过
    if (senderIsOwner) return tool;                     // 是老板,放行
    
    // 非老板:设备上锁,拒绝使用
    return {
      ...tool,
      execute: async () => {
        throw new Error("Tool restricted to owner senders.");
      },
    };
  });
}

Bash 工具安全机制(“危险设备操作规程”)

命令执行(exec 工具)是最敏感的工具之一——就像工厂里的高压电气设备,OpenClaw 对其实施了严格的安全校验:

// src/agents/bash-tools.exec.ts

// 危险操作黑名单("绝对禁止触碰的开关")
const DANGEROUS_HOST_ENV_VARS = new Set([
  "LD_PRELOAD", "LD_LIBRARY_PATH", "LD_AUDIT",
  "DYLD_INSERT_LIBRARIES", "DYLD_LIBRARY_PATH",
  "NODE_OPTIONS", "NODE_PATH",
  "PYTHONPATH", "PYTHONHOME",
  "BASH_ENV", "ENV", "IFS",
]);

function validateHostEnv(env: Record<string, string>): void {
  for (const key of Object.keys(env)) {
    // 1. 阻止已知危险变量("断路器跳闸")
    if (DANGEROUS_HOST_ENV_VARS.has(key.toUpperCase())) {
      throw new Error(
        `Security Violation: Environment variable '${key}' is forbidden.`
      );
    }
    // 2. 严格阻止 PATH 修改("不许私自改线路")
    if (key.toUpperCase() === "PATH") {
      throw new Error(
        "Security Violation: Custom 'PATH' variable is forbidden."
      );
    }
  }
}

Hook 拦截机制(“安保监控系统”)

工具调用前后都有 Hook 拦截点(“摄像头和门禁”),可以:

  • before_tool_call(操作前检查):阻止调用或修改参数
  • after_tool_call(操作后记录):记录日志、监控耗时
// src/agents/pi-tools.before-tool-call.ts
export async function runBeforeToolCallHook(args: {
  toolName: string;
  params: unknown;
}): Promise<HookOutcome> {
  const hookResult = await hookRunner.runBeforeToolCall(
    { toolName, params: normalizedParams }
  );

  // 安保系统可以拦截操作
  if (hookResult?.block) {
    return {
      blocked: true,
      reason: hookResult.blockReason || "Tool call blocked by hook",
    };
  }

  // 安保系统可以修改操作参数(比如自动加安全前缀)
  if (hookResult?.params) {
    return { blocked: false, params: { ...params, ...hookResult.params } };
  }

  return { blocked: false, params };
}

3.4 MCP

MCP:外部工具,公司不需要知道工具怎么工作的,只需要知道”它叫什么、怎么用、能返回什么”。

MCP 是什么?

MCP (Model Context Protocol)Anthropic 提出的一个开放标准协议,定义了 AI 模型与外部工具/数据源之间的通信方式

一句话理解:MCP 就是 AI 世界的 USB 接口 —— 不管什么设备(工具),只要实现了 MCP 协议,就能被任何支持 MCP 的 AI 系统即插即用。

MCP 的工作原理

┌──────────────────┐         JSON-RPC          ┌──────────────────┐
│                  │  ←── "你有什么功能?" ──→   │                  │
│   👤 员工         │  ──── "帮我扫描" ────→     │  🔌 外部工具      │
│  (MCP Client)    │  ←── 返回扫描结果 ────      │  (MCP Server)    │
│                  │                            │                  │
└──────────────────┘                            └──────────────────┘


                                              ┌──────────────────┐
                                              │  工具内部逻辑      │
                                              │ (对员工不可见)    │
                                              └──────────────────┘

通信过程分为三步——就像员工使用一台新 USB 外设:

  1. 插上设备,识别功能(发现工具):Client 连接 Server,Server 返回”我能干什么”(名称、描述、参数 Schema)
  2. 员工操作设备(调用工具):AI 模型推理时决定使用 → Client 发起 JSON-RPC 请求 → Server 执行
  3. 设备返回结果(返回结果):Server 把执行结果返回给 Client → 回传给模型继续推理

MCP Server 示例

以一个”内部数据查询”场景为例——就像公司买了一台能直接对接 ERP 系统的专业扫描仪:

// 一个查询内部订单系统的 MCP Server("ERP 专用扫描仪")
const orderMcpServer = {
  name: "internal-order-system",
  tools: [
    {
      name: "query_order",
      description: "根据订单号查询订单详情",
      inputSchema: {
        type: "object",
        properties: {
          orderId: { type: "string", description: "订单号" }
        },
        required: ["orderId"]
      },
      handler: async (params) => {
        // ✅ 这段逻辑只运行在设备内部(MCP Server 侧)
        // 员工看不到内部 API 地址、鉴权方式、数据库结构
        const data = await internalAPI.get(`/orders/${params.orderId}`, {
          headers: { Authorization: `Bearer ${INTERNAL_TOKEN}` }
        });
        // 只返回脱敏后的结果(设备只输出扫描件,不暴露内部电路)
        return { orderId: data.id, status: data.status, amount: data.amount };
      }
    }
  ]
};

MCP 的核心优势

1. 设备内部对员工不可见,保护核心机密

这是 MCP 最重要的优势。就像员工用一台扫描仪,只需要知道”按哪个按钮、放什么文件、出来什么结果”,完全不需要知道扫描仪内部的光学原理和芯片设计。Agent 端只知道”工具叫什么、怎么传参、返回什么”

员工视角:                             MCP外部逻辑(对员工不可见):
┌─────────────────────┐               ┌──────────────────────────────┐
│ 设备名: 订单查询器    │               │ 1. 连接内部 VPN              │
│ 操作: 输入订单号      │  ──按按钮──→  │ 2. 用服务账号鉴权              │
│ 结果: 订单信息       │               │ 3. 查询内部数据库              │
│                     │  ←─出结果──   │ 4. 数据脱敏处理                │
│ (员工只知道这些)    │               │ 5. 返回安全结果                │
└─────────────────────┘               └──────────────────────────────┘

对比让员工自己动手(Agent 通过 exec 执行 curl 调用 API),MCP的优势在于:

  • 内部 API 地址、Token、数据库连接串等公司核心机密不暴露给员工
  • 数据获取逻辑不会被泄漏
  • 可以在设备内部做数据脱敏和权限控制

2. 标准协议,一个工具所有公司通用 任何支持 (MCP)的AI 系统:Claude、Cursor、Windsurf、OpenClaw 等都可以接入。

MCP 的主要劣势

1. 采购和部署成本高

相比公司自有设备(写一个函数)或员工自带技能(写一份 Markdown),买一台 USB 外设需要:

步骤工作量
采购设备(开发 MCP Server)实现协议握手、工具注册、请求处理
安装维护(部署运维)独立进程/容器,需要考虑高可用、监控
故障排查(调试排查)跨进程通信,“线缆松了”还是”设备坏了”?链路更长
接入配置Client 端需要声明 Server 地址和连接方式

对于简单场景(比如读个文件、调个公开 API),用 MCP 属于”为了打印一页纸,专门买一台工业级打印机”。

2. 上下文膨胀问题

这是实际使用中最棘手的问题。每个MCP接上后,它的繁杂的全量功能手册都要放在员工桌上(注入到模型上下文中):

接 1 台外设,提供 5 个功能
→ 每个功能的说明书: ~300 tokens
→ 5 个功能 ≈ 占用 1500 tokens 桌面空间

接 10 台外设,每台 5 个功能
→ 50 本说明书 ≈ 占用 15000 tokens 桌面空间
→ 还没开始干活,桌子就已经被说明书堆满了!

桌面堆满带来的连锁反应:

  • 工作空间减少:留给真正工作材料的空间变少
  • 员工眼花缭乱:设备太多时,员工更容易选错工具或乱按按钮
  • 成本增加:每次工作都携带大量设备说明书,“桌面租金”上升

3. 操作参数全靠员工自己填

MCP 工具的参数完全由 AI 模型根据对话上下文”猜测生成”——就像让员工根据老板一句模糊的话,自己填一张复杂的表单:

老板: "帮我查一下上周的订单"

员工需要填写:{ "orderId": "???" }

                    上周的订单号是什么?员工并不知道!
                    可能会:
                    ① 瞎编一个订单号 ❌
                    ② 反问老板要订单号 ✅(但体验差)
                    ③ 先翻另一个系统查订单列表 ✅(但需要系统支持)

表单越复杂(嵌套 JSON、枚举值、格式约束),员工填错的概率越高。相比之下,Skills(员工的专业技能)可以指导员工自己组装正确的命令参数,可控性更强。

MCP vs 其他工具来源:何时选择 MCP?

场景推荐方案公司隐喻
涉及内部系统、敏感数据MCP机密设备要用”黑箱”外设,不能让员工看到内部
简单的文件/命令操作内置工具用桌上的鼠标键盘就行,何必外接设备
组合已有工具完成复杂任务Skills靠员工自身专业技能来编排,灵活轻量
工具数量多、参数简单谨慎使用 MCP ⚠️外设太多桌子放不下(上下文膨胀)
参数复杂、依赖上下文推理谨慎使用 MCP ⚠️表单太复杂员工容易填错

3.5 Skills — 员工的简历与才艺

公司招聘一位新员工时,最看重的是他的简历(Skills)——会 Python、精通数据库、懂金融业务逻辑……这些知识和技能存在员工的脑子里,不是一个外部设备,而是员工自身的能力。

当老板说”帮我分析一下这个 GitHub PR”,员工不需要一台专门的”PR 分析机”(MCP),他会根据脑子里的 GitHub 操作知识(Skills),自己组合已有的工具(终端、gh CLI)来完成任务。

什么是 Skills?

Skills 可以理解为一个超级进化版的提示词——它是员工简历上的一项专业技能。与 MCP(USB 外设)的区别是:当员工需要完成一个任务时,若有现成的外设(MCP),就直接用外设完成;而 Skills 则是员工脑子里的知识,指导员工自己动手完成任务。(两者并不冲突、也并非包含关系,在不同场景都有各自的适用点。)

每个 Skill 是一个独立的目录(“简历上的一个技能板块”),包含:

skills/github/
├── SKILL.md          # 技能说明(必需)—— "我会什么"
├── references/       # 参考资料(可选)—— "我的证书和作品集"
└── scripts/          # 辅助脚本(可选)—— "我自带的工具包"

SKILL.md 格式(主要)

---
name: weather
description: Get current weather and forecasts (no API key required).
homepage: https://wttr.in/:help
metadata: { "openclaw": { "emoji": "🌤️", "requires": { "bins": ["curl"] } } }
---

# Weather

Two free services, no API keys needed.

## wttr.in (primary)

Quick one-liner:

curl -s "wttr.in/London?format=3"
# Output: London: ⛅️ +8°C


Compact format:

curl -s "wttr.in/London?format=%l:+%c+%t+%h+%w"
# Output: London: ⛅️ +8°C 71% ↙5km/h


Full forecast:

curl -s "wttr.in/London?T"

Format codes: `%c` condition · `%t` temp · `%h` humidity · `%w` wind · `%l` location · `%m` moon

Tips:

- URL-encode spaces: `wttr.in/New+York`
- Airport codes: `wttr.in/JFK`
- Units: `?m` (metric) `?u` (USCS)
- Today only: `?1` · Current only: `?0`
- PNG: `curl -s "wttr.in/Berlin.png" -o /tmp/weather.png`

## Open-Meteo (fallback, JSON)

Free, no key, good for programmatic use:

curl -s "https://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.12&current_weather=true"

Find coordinates for a city, then query. Returns JSON with temp, windspeed, weathercode.

Docs: https://open-meteo.com/en/docs

Skills 加载机制

① 发现阶段 —— 六级优先级系统(“从各渠道收集简历”)

OpenClaw 从 6 个来源按优先级(低→高)扫描 skills 目录,同名 Skill 高优先级覆盖低优先级(通过 Map 合并实现):

优先级(低 → 高):

1. Extra Dirs           (最低优先级 — 外部顾问:配置的额外目录)
   config.skills.load.extraDirs

2. Bundled Skills       (内置技能 — 公司元老:随代码仓库发布)
   resolveBundledSkillsDir()

3. Managed Skills       (集团通用人才 — 所有 Agent 共享)
   ~/.openclaw/skills

4. Personal Agents      (个人才艺 — 用户全局自定义)
   ~/.agents/skills

5. Project Agents       (项目组专属 — 项目级自定义)
   <workspace>/.agents/skills

6. Workspace Skills     (最高优先级 — 部门专属人才:特定工作区专属)
   <workspace>/skills

此外,还会从已启用的插件 (Plugins) 中加载 skills 目录:

// src/agents/skills/plugin-skills.ts
// 遍历已启用的插件,查找其 skills/ 子目录
export function resolvePluginSkillDirs(): string[] { ... }
// src/agents/skills/workspace.ts — loadSkillEntries()
const merged = new Map<string, Skill>();

// 按优先级顺序合并(后面覆盖前面,同名技能以高优先级为准)
for (const skill of extraSkills)          merged.set(skill.name, skill);
for (const skill of bundledSkills)        merged.set(skill.name, skill);
for (const skill of managedSkills)        merged.set(skill.name, skill);
for (const skill of personalAgentsSkills) merged.set(skill.name, skill);
for (const skill of projectAgentsSkills)  merged.set(skill.name, skill);
for (const skill of workspaceSkills)      merged.set(skill.name, skill);
// → 同名 Skill,workspace 的版本"赢"
② 解析阶段 —— SKILL.md Frontmatter 解析(“阅读简历”)

每个 Skill 的 SKILL.md 包含 YAML frontmatter,解析后提取关键元数据:

// src/agents/skills/frontmatter.ts — OpenClaw 元数据结构
type OpenClawSkillMetadata = {
  always?: boolean;              // 始终包含此 Skill(VIP 员工,免审)
  skillKey?: string;             // 用于配置查找的键
  primaryEnv?: string;           // 主要环境变量名(如 GH_TOKEN)
  emoji?: string;                // UI 显示的 emoji
  homepage?: string;             // 文档链接
  os?: string[];                 // 支持的操作系统(如 ["darwin", "linux"])
  requires?: {
    bins?: string[];             // 必需的二进制文件(全部满足)
    anyBins?: string[];          // 至少需要一个二进制文件
    env?: string[];              // 必需的环境变量
    config?: string[];           // 必需的配置路径
  };
  install?: SkillInstallSpec[];  // 安装说明
};

// 调用策略
type SkillInvocationPolicy = {
  userInvocable: boolean;          // 用户可以通过 /skill 命令主动调用
  disableModelInvocation: boolean; // 模型不应主动调用此 Skill
};
③ 过滤/门控阶段 —— 多层能力评估(“岗前审查”)
// src/agents/skills/config.ts — shouldIncludeSkill()
// 像 HR 筛选简历一样,逐项检查候选 Skill 是否合格

function shouldIncludeSkill(skill: SkillEntry, context: GateContext): boolean {
  // 1️⃣ 配置开关("这个岗位还在招人吗?")
  if (skillConfig.enabled === false) return false;
  
  // 2️⃣ Bundled 白名单("只从指定猎头渠道接收简历")
  if (allowBundled && !allowBundled.includes(skill.name)) return false;
  
  // 3️⃣ 操作系统检查("你写精通 macOS,但我们公司用 Linux")
  if (meta.os && !meta.os.includes(platform)) return false;
  
  // 4️⃣ Always 标志("VIP 员工,免审直接通过")
  if (meta.always === true) return true;
  
  // 5️⃣ 必需二进制检查("你说会用 gh CLI,公司电脑装了吗?")
  if (meta.requires?.bins) {
    for (const bin of meta.requires.bins) {
      if (!which.sync(bin, { nothrow: true })) return false;
    }
  }
  
  // 6️⃣ 任一二进制检查("ffmpeg 或 ffprobe,至少要会用一个")
  if (meta.requires?.anyBins) {
    if (!meta.requires.anyBins.some(bin => which.sync(bin, { nothrow: true })))
      return false;
  }
  
  // 7️⃣ 环境变量检查("你说能操作 GitHub,GH_TOKEN 门禁卡有吗?")
  if (meta.requires?.env) {
    for (const envVar of meta.requires.env) {
      if (!process.env[envVar] && !getConfigValue(config, envVar))
        return false;
    }
  }
  
  // 8️⃣ 配置路径检查("这个岗位需要的资质证书齐全吗?")
  if (meta.requires?.config) {
    for (const path of meta.requires.config) {
      if (!getConfigValue(config, path)) return false;
    }
  }
  
  return true;  // ✅ 全部通过,录用!
}

除了上述通用过滤,还支持多级白名单过滤

┌──────────────────────────────────────────────────────────────┐
│  过滤层级                                                     │
│                                                              │
│  全局过滤 (shouldIncludeSkill)                                │
│     所有 Skill 必须通过的基础审查                               │
│         │                                                    │
│         ▼                                                    │
│  Agent 级别过滤                                               │
│     config.agents.list[].skills = ["weather", "github"]      │
│     → 只有白名单中的 Skill 对这个 Agent 生效                   │
│         │                                                    │
│         ▼                                                    │
│  Channel 级别过滤                                             │
│     config.discord.guilds[].channels[].skills = ["weather"]  │
│     → 不同消息渠道可以有不同的 Skill 集合                       │
│                                                              │
└──────────────────────────────────────────────────────────────┘
④ 快照阶段 —— SkillSnapshot 构建(“编制花名册”)

通过过滤的 Skills 被打包成一个快照 (SkillSnapshot),缓存在会话中,避免每次对话都重新扫描:

// src/agents/skills/types.ts
type SkillSnapshot = {
  prompt: string;                    // 格式化的 Skills 文本(注入 System Prompt 用)
  skills: Array<{
    name: string;
    primaryEnv?: string;
  }>;
  resolvedSkills?: Skill[];          // 完整的 Skill 对象列表
  version?: number;                  // 快照版本号(用于检测是否需要刷新)
};

快照通过版本号机制实现增量刷新:

// src/auto-reply/reply/session-updates.ts — ensureSkillSnapshot()
// 如果全局版本号大于当前快照版本号,说明有 Skill 变化,需要重新构建
const shouldRefreshSnapshot =
  snapshotVersion > 0 && (currentSnapshot?.version ?? 0) < snapshotVersion;
⑤ 注入阶段 —— 嵌入 System Prompt(“发放工牌”)
// src/agents/system-prompt.ts — buildSkillsSection()
// 将 Skills 信息注入到 System Prompt 中

// 注入的内容格式如下:
`
## Skills (mandatory)
Before replying: scan <available_skills> <description> entries.
- If exactly one skill clearly applies: read its SKILL.md at <location>
  with \`read\`, then follow it.
- If multiple could apply: choose the most specific one, then read/follow it.
- If none clearly apply: do not read any SKILL.md.
Constraints: never read more than one skill up front; only read after selecting.

<available_skills>
  weather — Get current weather and forecasts (no API key required).
  github — GitHub operations (PR, issues, code review).
  ...
</available_skills>
`

注意这里的一个精巧设计:Skill 的完整内容(SKILL.md body)并不会直接注入 System Prompt。只有 Skill 的名称和描述(摘要级信息)被注入。Agent 在需要使用某个 Skill 时,才会通过 read 工具主动读取完整的 SKILL.md 内容。这样做的好处是大幅减少 System Prompt 的 Token 占用——50+ 个 Skill 只占很少的上下文空间。

⑥ 热更新 —— 文件监视(“持续招聘”)
// src/agents/skills/refresh.ts — ensureSkillsWatcher()
// 使用 chokidar 实时监控 Skill 目录变化

// 监视的目录:
//   <workspace>/skills
//   ~/.openclaw/skills
//   配置的额外目录
//   插件的 skills 目录

// 忽略的路径模式:
//   .git, node_modules, dist, __pycache__, .venv 等

// 当检测到 SKILL.md 文件变化时:
//   → 250ms 防抖(可通过 config.skills.load.watchDebounceMs 自定义)
//   → 递增全局快照版本号
//   → 下次 Agent 执行时自动重新构建快照

4. AI Agent 主流架构模式

在了解了 Agent 系统的核心组件(Rules、Tools、MCP、Skills)之后,本节聚焦于Agent 本身的架构设计——即 Agent 是如何思考和行动的

不同的架构模式决定了 Agent 在面对一个复杂任务时,是”想一步做一步”,还是”先规划再执行”,还是”多人协作分工完成”。

4.1 一个核心问题:Agent 如何完成任务?

给 Agent 一个任务,比如”帮我查看这个项目的最新 PR,分析代码变更,然后写一个 review”——Agent 面临的核心问题是:

  • 何时思考? 每一步都思考,还是一开始规划好?
  • 何时行动? 思考完立刻做,还是攒一批再做?
  • 出错怎么办? 根据反馈纠正,还是重新规划?

不同的回答方式,催生了不同的 Agent 架构模式。

4.2 主流架构模式总览

┌──────────────────────────────────────────────────────────────────────┐
│                    AI Agent 架构模式光谱                               │
│                                                                      │
│  简单 ─────────────────────────────────────────────────────── 复杂    │
│                                                                      │
│  ┌──────────┐  ┌──────────┐  ┌──────────────┐  ┌──────────────────┐  │ 
│  │ 简单推理  │  │  ReAct   │  │Plan-and-     │  │  Multi-Agent     │  │
│  │ Chain    │  │  循环    │   │ Execute     │  │  协作系统          │  │
│  │          │  │          │  │              │  │                  │  │
│  │ 无工具    │  │ 推理→行动 │  │ 先规划→再执行  │   │ 多角色分工协作     │  │
│  │ 直接回答  │  │ →观察循环 │  │              │   │                  │  │
│  └──────────┘  └──────────┘  └──────────────┘  └──────────────────┘  │
│                     ▲                                                │
│                     │                                                │
│              OpenClaw 采用此架构                                      │
└──────────────────────────────────────────────────────────────────────┘
架构模式核心思想典型代表适用场景
简单推理 Chain输入→LLM→输出,无工具调用基础 ChatBot问答、翻译、摘要
ReAct推理→行动→观察,循环往复OpenClaw、Claude Code、Cursor Agent需要与外部环境交互的复杂任务
Plan-and-Execute先制定计划,再逐步执行AutoGPT、BabyAGI(早期)多步骤、可分解的长任务
Multi-Agent多个 Agent 角色分工协作CrewAI、MetaGPT、Manus大型复杂项目、模拟团队协作

4.3 ReAct 架构:推理-行动-观察循环

ReAct(Reasoning + Acting) 是当前最主流、最成熟的 Agent 架构模式,也是 OpenClaw 所采用的核心架构。

核心理念

ReAct 的灵感来源于人类的工作方式——我们不会先把所有计划想好再动手,而是边想边做

人类工作方式:
  "这个 Bug 看起来像是数据库连接问题..."(思考)
  → 打开日志文件看一下(行动)
  → 果然有连接超时的错误(观察)
  → "可能是连接池配置不对..."(思考)
  → 检查数据库配置文件(行动)
  → 发现 maxConnections 设置太小(观察)
  → "改成 50 应该够了"(思考)
  → 修改配置并重启(行动)
  → 服务恢复正常(观察)
  → 完成!

执行流程

┌──────────────────────────────────────────────┐
│              ReAct 循环                       │
│                                              │
│   用户输入                                    │
│      │                                       │
│      ▼                                       │
│   ┌──────────┐                               │
│   │ 🧠 推理   │ ← LLM 分析当前状态             │
│   │ (Reason) │   "我需要做什么?用什么工具?"    │
│   └────┬─────┘                               │
│        │                                     │
│        ├── 决定调用工具 ──┐                    │
│        │                 ▼                   │
│        │          ┌──────────┐               │
│        │          │ 🔧 行动   │ ← 执行工具     │
│        │          │ (Act)    │               │
│        │          └────┬─────┘               │
│        │               │                    │
│        │               ▼                    │
│        │          ┌──────────┐               │
│        │          │ 👁️ 观察   │ ← 获取结果     │
│        │          │(Observe) │               │
│        │          └────┬─────┘               │
│        │               │                    │
│        │               └─── 回到推理 ──→ 🧠   │
│        │                                     │
│        └── 决定直接回复 ──→ 输出最终响应         │
│                                              │
└──────────────────────────────────────────────┘

优势与局限

维度说明
自适应每一步都根据最新观察调整策略,不会被错误的初始计划困住
鲁棒性单步出错可以立即纠正,不需要推翻整个计划
简单高效实现简洁,不需要额外的规划模块
⚠️ 缺乏全局视野”只见树木不见森林”,可能在局部打转
⚠️ 效率依赖模型模型推理能力不足时,可能做出低效的工具调用

4.4 Plan-and-Execute 架构:先规划再执行

核心理念

与 ReAct 的”边想边做”不同,Plan-and-Execute 主张先制定完整计划,再逐步执行——就像一个项目经理先写需求文档和排期,再分配任务给开发者。

执行流程

┌─────────────────────────────────────────────────────┐
│           Plan-and-Execute 架构                      │
│                                                     │
│   用户输入                                           │
│      │                                              │
│      ▼                                              │
│   ┌───────────────────────────┐                     │
│   │ 📋 规划阶段 (Planner)      │ ← 独立的 LLM 调用   │
│   │                           │                     │
│   │ "分析任务 → 拆解子任务      │                     │
│   │  → 确定执行顺序"           │                     │
│   │                           │                     │
│   │ 输出: Step 1 → Step 2     │                     │
│   │       → Step 3 → ...      │                     │
│   └───────────┬───────────────┘                     │
│               │                                     │
│               ▼                                     │
│   ┌───────────────────────────┐                     │
│   │ ⚙️ 执行阶段 (Executor)     │ ← 逐步执行计划      │
│   │                           │                     │
│   │ for step in plan:         │                     │
│   │   result = execute(step)  │                     │
│   │   if failed:              │                     │
│   │     replan()  ←── 执行失败可触发重新规划           │
│   └───────────┬───────────────┘                     │
│               │                                     │
│               ▼                                     │
│          输出最终结果                                  │
└─────────────────────────────────────────────────────┘

优势与局限

维度说明
全局视野在执行前就有全局规划,避免”只见树木不见森林”
可追踪计划可以展示给用户,让用户知道 Agent 打算做什么
适合长任务步骤多的任务不容易跑偏
⚠️ 规划可能不准初始计划基于有限信息,可能需要反复重新规划
⚠️ 灵活性差遇到意外情况时调整成本高

4.5 Multi-Agent 架构:多角色分工协作

核心理念

不再依靠一个”全能员工”,而是让多个专业 Agent 各司其职,像一个团队一样协作完成任务。

典型架构

┌─────────────────────────────────────────────────────┐
│            Multi-Agent 协作系统                       │
│                                                     │
│   用户输入                                           │
│      │                                              │
│      ▼                                              │
│   ┌───────────────────────┐                         │
│   │ 🎯 协调者 (Orchestrator)│ ← 分配任务、汇总结果    │
│   └───────────┬───────────┘                         │
│               │                                     │
│       ┌───────┼───────┐                             │
│       ▼       ▼       ▼                             │
│   ┌──────┐┌──────┐┌──────┐                          │
│   │🧑‍💻 编码││🧪 测试││📝 文档│  ← 各自独立的 Agent    │
│   │Agent ││Agent ││Agent │                          │
│   └──┬───┘└──┬───┘└──┬───┘                          │
│      │       │       │                              │
│      └───────┼───────┘                              │
│              ▼                                      │
│        汇总 → 最终输出                                │
└─────────────────────────────────────────────────────┘

优势与局限

维度说明
专业化每个 Agent 专注于一个领域,可以用不同模型和提示词
并行执行多个 Agent 可以同时工作,提高效率
可扩展新增一种能力只需添加一个新 Agent
⚠️ 复杂度高Agent 之间的通信和协调是难题
⚠️ 成本高多个 Agent 意味着多倍的 LLM 调用
⚠️ 上下文丢失Agent 之间传递信息可能丢失重要上下文

4.6 三种架构的选型对比

维度ReActPlan-and-ExecuteMulti-Agent
实现复杂度⭐ 低⭐⭐ 中⭐⭐⭐ 高
任务适应性灵活,自适应适合结构化长任务适合大型复杂项目
LLM 调用次数中等较多(规划+执行)最多(多 Agent)
出错恢复逐步纠正需要重新规划依赖协调者处理
可解释性中等高(计划可展示)高(分工可见)
适合的模型强推理模型通用模型可混用不同模型
典型产品Claude Code, CursorAutoGPT (早期)CrewAI, MetaGPT, Manus

💡 实际趋势:当前主流 AI Coding Agent(Claude Code、Cursor Agent、Windsurf 等)几乎都采用 ReAct 架构,因为它在灵活性和实现简洁性之间取得了最佳平衡。Plan-and-Execute 和 Multi-Agent 更多出现在研究项目和特定领域应用中,ClaudeCode / Codex 在近期都推出了subagents模式,通过子Agent提高复杂任务的执行速度和准确率。


5. OpenClaw 的 Agent 执行流程

理解了主流架构模式之后,本章将完整剖析 OpenClaw 的 Agent 执行流程——从底层引擎(pi-mono)如何驱动 ReAct 循环,到记忆系统如何让 Agent 拥有”长期记忆”,全方位展示一个工程化 Agent 系统的内部运作。

一个关键认知:Agent 应用的核心早就不只是”写 prompt / 选架构模式”了,而是一个可迭代、可测试、可部署的工程系统。

5.1 整体架构:增强型 ReAct + pi-mono 底座

OpenClaw 的 Agent 引擎(基于 pi-mono)采用的是 增强型 ReAct 循环架构。就像 Web 开发需要 Express/Next.js 一样,Agent 开发也需要一个成熟的运行时引擎——OpenClaw 选择的底座就是 pi-mono

一句话理解:pi-mono 是 Agent 引擎(底盘 + 发动机),OpenClaw 是基于它造出来的整车(加上外壳、座椅、导航)

┌──────────────────────────────────────────────────────────────┐
│              OpenClaw 增强型 ReAct 架构                        │
│                                                              │
│   ┌─────────────────────────────────────────────────────┐    │
│   │              标准 ReAct 循环(pi-agent-core)         │    │
│   │                                                     │    │
│   │     推理 (Reason) → 行动 (Act) → 观察 (Observe)      │    │
│   │            ↑                           │             │    │
│   │            └───────────────────────────┘             │    │
│   └─────────────────────────────────────────────────────┘    │
│                           +                                  │
│   ┌─────────────────────────────────────────────────────┐    │
│   │              工程增强(OpenClaw 应用层)               │    │
│   │                                                     │    │
│   │  ① 双层消息队列(转向 + 后续)                         │    │
│   │  ② 可调节的推理深度(Thinking Level)                  │    │
│   │  ③ 记忆系统(长期记忆 + 上下文压缩)                   │    │
│   │  ④ 子 Agent 派生(Subagent Spawn)                    │    │
│   └─────────────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────────────┘

5.2 pi-mono 引擎:分层设计

pi-mono 是一个开源的 TypeScript Agent 引擎 monorepo(@mariozechner/pi-mono),它为 Agent 应用提供了从 LLM 调用到 UI 渲染的完整基础设施。如果 Agent 的架构模式(ReAct)是”怎么想”,那 pi-mono 就是”怎么跑”

分层架构

┌─────────────────────────────────────────────────────────┐
│                    应用层                                 │
│          OpenClaw / MOM (Slack Bot) / CLI                │
└──────────────────────────┬──────────────────────────────┘
                           │ 使用
┌──────────────────────────┼──────────────────────────────┐
│              pi-coding-agent(编码 Agent 层)              │
│     内置编码工具 + 会话管理 + 扩展系统 + 技能系统           │
└──────────────────────────┬──────────────────────────────┘
                           │ 使用
┌──────────────────────────┼──────────────────────────────┐
│              pi-agent-core(Agent 核心层)                 │
│         有状态 Agent + ReAct 循环 + 事件系统              │
└──────────────────────────┬──────────────────────────────┘
                           │ 使用
┌──────────────────────────┼──────────────────────────────┐
│                    pi-ai(LLM 统一接口层)                 │
│     多提供商适配 + 流式处理 + 工具定义 + 成本计算           │
└─────────────────────────────────────────────────────────┘
  • pi-ai:20+ LLM 提供商开箱即用,统一消息/事件类型,支持流式、Token 成本计算、跨提供商兼容
  • pi-agent-core:有状态 Agent 类,实现 ReAct 循环,提供 prompt()steer()followUp() 等方法,完善的事件系统
  • pi-coding-agent:内置编码工具(read/write/edit/bash/find/grep),会话持久化/分支/压缩,扩展系统支持注册自定义命令和工具

职责分工一览

层级OpenClaw 做什么pi-mono 做什么
LLM 调用配置选用哪个模型/提供商,管理多 API Key 轮转处理 API 调用、流式、成本计算
System Prompt组装 AGENTS.md + Skills + Runtime Info 等提供 prompt 注入接口
工具系统注册 OpenClaw 特色工具 + MCP + 权限策略提供基础编码工具 + 工具执行框架
Agent 循环配置 ReAct 参数(思考级别等)执行 ReAct 循环、事件分发
会话管理管理多渠道多用户会话、格式校验提供持久化、分支、压缩能力
记忆系统记忆索引、混合搜索、Memory Flush提供上下文压缩 API
错误恢复上下文压缩、Profile 轮转、思考降级提供压缩 API + 事件通知

5.3 Agent 执行全流程:从消息到响应

当一条用户消息到达时,OpenClaw 做了什么才让 Agent “跑起来”的? AgentRunner 的核心工作就是**“组装”**——把 pi-mono 提供的各层零件,加上 OpenClaw 自己的特色部件,拼成一个完整的、可执行的 Agent:

┌─ 用户消息到达 ─────────────────────────────────────────────────────────┐
│                                                                        │
│  ① 选模型 ──────────────────────────────────────────────────────────── │
│  │  使用 pi-ai 的 ModelRegistry 解析 provider + modelId               │
│  │  → 得到 Model 对象(上下文窗口、token 成本、支持的输入类型等)        │
│  │  → 如果 API Key 限流,自动轮转到备用认证 profile                    │
│  │                                                                     │
│  ② 构建 System Prompt ─────────────────────────────────────────────── │
│  │  收集多个来源,拼接成一份完整的系统提示:                              │
│  │  AGENTS.md + SOUL.md + Skills 列表 + Runtime Info                   │
│  │  + Memory 指导 + 工具摘要 + Owner 身份                               │
│  │                                                                     │
│  ③ 组装工具集 ──────────────────────────────────────────────────────── │
│  │  三类工具合并:pi-coding-agent 基础工具                              │
│  │  + OpenClaw 特色工具 (message/memory/canvas/exec)                   │
│  │  + 外部 MCP 工具                                                    │
│  │                                                                     │
│  ④ 应用权限策略 ────────────────────────────────────────────────────── │
│  │  按优先级层叠:Profile → 全局 → Agent → Channel → Sandbox           │
│  │                                                                     │
│  ⑤ 创建 Agent Session ─────────────────────────────────────────────── │
│  │  调用 pi-coding-agent 的 createAgentSession()                       │
│  │  → 加载/创建会话历史 + 设置 Thinking Level + 注入系统提示            │
│  │                                                                     │
│  ⑥ 订阅事件 + 启动 ReAct 循环 ─────────────────────────────────────── │
│  │  订阅 text_delta / tool_use / tool_result / thinking / compaction   │
│  │  → session.prompt(userMessage) 启动 Agent                           │
│  │                                                                     │
│  ⑦ 错误恢复(贯穿全程)────────────────────────────────────────────── │
│     上下文溢出 → 自动压缩      API 限流 → 轮转认证 profile              │
│     格式冲突 → 重置会话         思考级别不支持 → 自动降级                │
│                                                                        │
└────────────────────────────────────────────────────────────────────────┘

核心循环:双层循环结构

Agent 的核心循环实现在 pi-mono/packages/agent/src/agent-loop.ts 中,是一个双层循环结构

用户输入


╔══════════════════════════════════════════════════════════╗
║  外层循环                                                ║
║  ┌─────────────────────────────────────────────────┐    ║
║  │    ╔════════════════════════════════════════╗    │    ║
║  │    ║  内层循环 (ReAct)                       ║    │    ║
║  │    ║  ┌───────────────────────────────┐     ║    │    ║
║  │    ║  │  ① 检查转向消息 → 注入上下文    │     ║    │    ║
║  │    ║  │          │                    │     ║    │    ║
║  │    ║  │          ▼                    │     ║    │    ║
║  │    ║  │  ② 流式调用 LLM               │     ║    │    ║
║  │    ║  │     ┌────┴─────┐              │     ║    │    ║
║  │    ║  │     ▼          ▼              │     ║    │    ║
║  │    ║  │  有工具调用  仅文本输出         │     ║    │    ║
║  │    ║  │     │          │              │     ║    │    ║
║  │    ║  │     ▼          ▼              │     ║    │    ║
║  │    ║  │  ③ 执行工具  退出内层循环 ──→──┤     ║    │    ║
║  │    ║  │     │                         │     ║    │    ║
║  │    ║  │     └── 继续循环(带工具结果)──┘     ║    │    ║
║  │    ║  └───────────────────────────────┘     ║    │    ║
║  │    ╚════════════════════════════════════════╝    │    ║
║  │                     │                           │    ║
║  │                     ▼                           │    ║
║  │         ④ 检查后续消息 → 有则继续循环,无则结束    │    ║
║  └─────────────────────────────────────────────────┘    ║
╚══════════════════════════════════════════════════════════╝

关键设计决策:决策完全由 LLM 驱动(无显式规划)、工具顺序执行(确保确定性)、流式输出(实时推送给用户)。

完整执行流程图

将所有组件串联起来,OpenClaw 处理一条用户消息的完整流程如下: image-3


5.4 ReAct 工程增强

增强 ①:双层消息队列

标准 ReAct 循环中,一旦 Agent 开始执行就无法被中断。OpenClaw 引入了两级消息队列

转向消息 (Steering)后续消息 (Follow-up)
作用”紧急打断""下一个任务”
时机立即中断当前执行,注入到下一次 LLM 调用前等待 Agent 完全停止后才处理,触发新一轮外层循环
场景”停下来,先做另一件事""顺便也看下那个文件”

增强 ②:可调节的推理深度(Thinking Level)

推理深度选项:off → minimal → low → medium → high → xhigh

  off     : 不启用深度思考,速度最快、成本最低
  minimal : 简单任务,轻度推理
  medium  : 一般复杂度任务
  high    : 复杂推理任务
  xhigh   : 最深度思考,适合最复杂的分析任务

让同一个 ReAct 框架可以灵活地在”快速响应”和”深度推理”之间切换。

增强 ③:子 Agent 派生(Subagent)

通过 sessions_spawn 工具派生子 Agent,实现轻量级的多 Agent 协作:

父 Agent(主会话)

  ├─→ sessions_spawn ──→ 子 Agent(独立会话 + 独立工具集 + 独立 ReAct 循环)
  │                               │
  │   ←─── 返回结果 ──────────────┘

父 Agent 拿到结果,继续工作

与 Multi-Agent 架构的区别:没有全局协调者、没有 Agent 间直接通信、各自独立的 ReAct 循环。

增强 ④:记忆系统

这是 OpenClaw 最具特色的增强,详见下节。


5.5 记忆系统:让 Agent 拥有长期记忆

对于一个 7×24h 在线的个人 AI 助手来说,记忆是至关重要的能力。然而,LLM 天生有一个致命限制——上下文窗口有限。OpenClaw 通过一套多层次的记忆系统解决了这个问题。

记忆系统全景

┌───────────────────────────────────────────────────────────────────┐
│                     OpenClaw 记忆系统                               │
│                                                                   │
│  ┌────────────────────────────────────────────────────────────┐   │
│  │  ① 长期记忆存储                                             │   │
│  │     MEMORY.md + memory/*.md 文件                            │   │
│  │     → Agent 主动写入重要信息                                 │   │
│  │     → 日期分组: memory/2026-02-27.md                        │   │
│  └────────────────────┬───────────────────────────────────────┘   │
│                        │                                          │
│                        ▼                                          │
│  ┌────────────────────────────────────────────────────────────┐   │
│  │  ② 记忆索引与搜索(MemoryIndexManager)                     │   │
│  │     • 文件分块 (400 tokens/块, 80 tokens 重叠)              │   │
│  │     • 向量嵌入 (OpenAI/Gemini/Voyage/本地)                  │   │
│  │     • 混合搜索 (向量 70% + 关键词 30%)                      │   │
│  │     • SQLite 存储 (sqlite-vec 向量 + FTS5 全文)             │   │
│  └────────────────────┬───────────────────────────────────────┘   │
│                        │                                          │
│                        ▼                                          │
│  ┌────────────────────────────────────────────────────────────┐   │
│  │  ③ Agent 记忆工具                                           │   │
│  │     memory_search — 语义搜索记忆文件                         │   │
│  │     memory_get    — 读取记忆文件片段                         │   │
│  └────────────────────┬───────────────────────────────────────┘   │
│                        │                                          │
│                        ▼                                          │
│  ┌────────────────────────────────────────────────────────────┐   │
│  │  ④ 上下文生命周期管理                                       │   │
│  │     Memory Flush — 压缩前保存重要信息到记忆文件               │   │
│  │     Compaction   — 自动压缩对话历史,生成摘要                 │   │
│  │     会话持久化    — JSONL 格式持久化会话记录                   │   │
│  └────────────────────────────────────────────────────────────┘   │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

5.5.1 长期记忆存储

Agent 的长期记忆以 Markdown 文件的形式存储在工作区中:

~/.openclaw/
├── MEMORY.md              # 主记忆文件(全局重要信息)
└── memory/
    ├── 2026-02-25.md      # 日期分组的记忆
    ├── 2026-02-26.md      # 每天一个文件
    └── 2026-02-27.md      # Agent 自动追加写入

System Prompt 中会注入记忆使用指导,要求 Agent 在回答关于过往工作、决策、日期、偏好等问题前,先执行 memory_search 搜索记忆文件。

5.5.2 记忆索引与混合搜索

MemoryIndexManager 是记忆系统的核心,对记忆文件建立索引,支持高效的语义搜索:

记忆文件(MEMORY.md + memory/*.md + 会话记录)


  分块 (Chunking)
  → 将文件切分为 400 token 的块,80 token 重叠


  嵌入 (Embedding)
  → 使用嵌入模型将文本块转化为向量
  → 支持: OpenAI / Gemini / Voyage / 本地模型
  → 内置嵌入缓存,避免重复调用 API


  索引存储 (SQLite)
  → chunks 表 (文本块元数据)
  → chunks_vec (sqlite-vec 向量索引)
  → chunks_fts (FTS5 全文索引)


  混合搜索 (Hybrid Search)
  → 向量搜索 (余弦相似度) → 权重 0.7
  → 关键词搜索 (BM25)     → 权重 0.3
  → 合并排序 → 返回 Top-K 结果(默认 6 条,最低分 0.35)

Agent 通过两个工具与记忆系统交互:

  • memory_search:语义搜索记忆文件(传入查询文本、最多结果数、最低相似度分数)
  • memory_get:读取记忆文件指定范围的片段

5.5.3 上下文生命周期管理

这是记忆系统中最精巧的部分——解决 ReAct 循环中上下文不断膨胀的问题:

正常对话(上下文逐渐增长)

       │  上下文使用量达到 ~80% 阈值

┌──────────────────────────────────────────────────────────┐
│  阶段 1: Memory Flush(记忆刷写)                         │
│                                                          │
│  在压缩前,先运行一个专用的 Memory Flush Agent:           │
│  "Pre-compaction memory flush.                           │
│   Store durable memories now                             │
│   (use memory/YYYY-MM-DD.md).                            │
│   APPEND new content only, do not overwrite."            │
│                                                          │
│  → Agent 将对话中的重要信息(决策、偏好、待办等)           │
│    写入 memory/2026-02-27.md                              │
│  → 确保重要信息不会在压缩中丢失                            │
└──────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────┐
│  阶段 2: Compaction(上下文压缩)                          │
│                                                          │
│  由 pi-coding-agent 的 compact() 函数执行:              │
│  1. 计算当前上下文 Token 数                               │
│  2. 使用 LLM 为旧对话生成摘要                             │
│  3. 保留最近的 ~20,000 tokens 消息                        │
│  4. 用摘要替换更早的消息                                  │
│                                                          │
│  压缩前:  [系统提示][100轮对话][最新消息]  → 95% 满        │
│  压缩后:  [系统提示][摘要][最近20轮对话]   → 40% 满        │
└──────────────────────┬───────────────────────────────────┘


┌──────────────────────────────────────────────────────────┐
│  阶段 3: 继续对话                                         │
│                                                          │
│  Agent 继续工作,上下文被"瘦身"                            │
│  重要信息已通过 Memory Flush 持久化到文件                  │
│  如需回忆旧信息 → 使用 memory_search 检索                  │
│  → 让 Agent 可以 7×24h 持续在线而不会上下文溢出            │
└──────────────────────────────────────────────────────────┘

5.5.4 索引同步与热更新

记忆索引通过多种机制保持最新:

触发机制说明
文件监视 (chokidar)监视 MEMORY.md / memory/ 目录变化,1500ms 防抖,标记索引为脏
会话增量跟踪跟踪每个会话文件的字节增量,达到阈值(100KB 或 50 条消息)时触发同步
搜索前同步每次 memory_search 前检查是否有脏数据,有则先同步再搜索
会话开始同步每次新会话开始时触发一次完整同步

5.6 架构选型思考

为什么选择 ReAct?

考量因素ReAct 如何满足
7×24h 在线循环轻量、无状态开销,适合长期驻留运行
多样化任务不限定任务类型,从简单问答到复杂编码都能应对
实时交互每一步推理都可以立即输出,不需要等待完整规划
成本可控不需要额外的规划 LLM 调用
容错能力单步出错可以立即纠正

为什么需要 Agent 底座(pi-mono)?

工程问题pi-mono 怎么解决
多模型支持统一接口 + 20+ 提供商开箱即用
Agent 循环成熟的 ReAct 循环 + 事件系统
会话持久化完整的会话管理(持久化/分支/压缩)
Token 管理自动 Token 估算 + 智能压缩
可扩展性扩展系统(钩子 + 命令 + UI)

“底座思维”的核心价值:把精力集中在应用真正独特的地方——对于 OpenClaw 来说,就是多渠道消息接入、7×24h 在线、Skills 生态、记忆系统和自动化能力。


6. 总结与展望

6.1 全文回顾

本文从四个层次解析了 OpenClaw 的 AI Agent 技术架构:

第一层:Agent 的"配套设施"是什么?
  └─ Rules(规章制度)、Tools(工具)、MCP(标准接口)、Skills(技能)

第二层:Agent 有哪些架构模式?
  └─ ReAct(推理-行动-观察循环)
  └─ Plan-and-Execute(先规划再执行)
  └─ Multi-Agent(多角色协作)

第三层:OpenClaw 的 Agent 执行流程
  └─ pi-mono 底座:分层引擎(pi-ai → pi-agent-core → pi-coding-agent)
  └─ 增强型 ReAct:双层消息队列 + 可调推理深度 + 子 Agent 派生
  └─ 记忆系统:混合搜索 + Memory Flush + 上下文压缩
     让 Agent 在有限上下文中拥有"长期记忆",支撑 7×24h 持续运行

6.2 学习资源


感谢阅读! 如有问题,欢迎交流讨论。


最后更新: 2026-02-27 贡献者: 张雨 (zhangyu421@xiaohongshu.com)