乐于分享
好东西不私藏

OpenClaw 工具引擎架构全解析:AI Agent 的"双手"是怎么工作的?

OpenClaw 工具引擎架构全解析:AI Agent 的"双手"是怎么工作的?

大语言模型很会”想”,但它自己不能读文件、不能跑命令、不能上网。AI Agent 之所以能真正干活,靠的是一套工具引擎——它是 Agent 的”双手”,负责把模型的思考变成实际操作。

OpenClaw 的工具引擎不是一个单体模块,而是由 Agent Runtime 和 Gateway 两端协同执行:Runtime 负责调度工具调用,Gateway 负责管理浏览器实例、MCP Server 进程等重资源。两者通过 Session 共享状态。

这套引擎的设计相当精巧:三层工具体系、权限-审批-沙盒三重安全网、并行执行、自动容错。这篇文章从源码层面拆解它的每一个环节。

读完这篇你会了解:

  • • 一个工具调用从发起到返回的完整链路(7 步)
  • • 内置工具、Skills 插件、MCP Server 三层架构各自的定位和取舍
  • • exec / browser 这两个高危工具的安全设计细节
  • • 并行执行的策略和边界情况处理
  • • 生产环境下的容错哲学——为什么”把错误暴露给模型”比”隐藏错误”更好

一、一个工具调用的完整旅程

在深入细节之前,先看全局:当模型决定调用 exec ls -la 时,工具引擎到底做了什么?

模型调用: exec ls -la

[1] 查工具注册表 → exec 存在? ✓

[2] 查权限 → Agent 白名单有 exec? ✓

[3] 查审批 → ls -la 需要审批? ✗ (autoApprove)

[4] 查沙盒 → sandbox 模式? → 确定执行环境

[5] 执行 → spawn 子进程 → 拿到 stdout/stderr

[6] 格式化结果 → ToolResult → 返回给模型

[7] 记录审计日志

七步流水线,每一步都是一道安全闸门。注册表确认工具存在,权限确认 Agent 有资格用,审批确认操作被允许,沙盒决定在哪跑。任何一步失败,都会返回结构化错误给模型——模型可以据此换方案,而不是直接崩掉。


二、三层工具体系:从内置到生态

OpenClaw 的工具注册表(Tool Registry)分三层,越往上越灵活、越开放:

Tool Registry

Layer 3: MCP Server标准协议,动态发现,即插即用

mcp__filesystem

mcp__database

Layer 2: Skills 插件npm 包安装,配置挂载,社区生态

gmail.*

calendar.*

github.*

Layer 1: 内置工具 (Builtin)硬编码在 Agent Runtime 中,零配置可用

read

write

edit

exec

glob

browser

web

grep

Layer 1 — 内置工具:八大金刚,硬编码在 Agent Runtime 里,开箱即用。这是 Agent 最基础的能力——读写文件、跑命令、搜代码、上网、操作浏览器。

Layer 2 — Skills 插件:通过 npm 包安装的能力扩展。Gmail、Calendar、GitHub 这些第三方集成都走这层。社区可以贡献新 Skill。

Layer 3 — MCP Server:标准协议(Model Context Protocol),动态发现工具。任何实现了 MCP 协议的服务器都能即插即用,不需要改 Agent 代码。

这种分层设计的好处是:核心能力稳定可靠(Layer 1),扩展能力丰富灵活(Layer 2),外部集成无限开放(Layer 3)。


三、内置工具详解:八大金刚

3.1 工具清单与参数

先看完整的接口定义:

// read - 读取文件interface ReadParams {  path: string;          // 文件绝对路径  offset?: number;       // 起始行 (1-indexed, 负数从末尾)  limit?: number;        // 读取行数}// 返回: 行号标注的文件内容// write - 写入文件interface WriteParams {  path: string;          // 文件路径 (自动创建目录)  contents: string;      // 文件内容}// 原子写入: 写入 .tmp → rename (防止写一半崩溃)// edit - 精确替换interface EditParams {  path: string;  oldString: string;     // 要替换的文本 (必须在文件中唯一)  newString: string;  replaceAll?: boolean;  // 替换所有匹配 (默认 false)}// exec - 执行终端命令interface ExecParams {  command: string;       // shell 命令  cwd?: string;          // 工作目录 (默认: Agent Workspace)  timeout?: number;      // 超时 ms (默认: 60000)  env?: Record<string, string>;  // 额外环境变量}// 返回: { stdout, stderr, exitCode, timedOut }// glob - 文件搜索interface GlobParams {  pattern: string;       // glob 模式 (自动加 **/ 前缀)  cwd?: string;          // 搜索起点}// grep - 内容搜索 (ripgrep 后端)interface GrepParams {  pattern: string;       // 正则表达式  path?: string;         // 搜索路径  glob?: string;         // 文件过滤 "*.ts"  contextLines?: number; // 上下文行数}// browser - 浏览器自动化interface BrowserParams {  action: "navigate" | "click" | "type" | "screenshot"        | "extract" | "evaluate" | "wait" | "scroll";  url?: string;          // navigate 用  selector?: string;     // click/type 用  text?: string;         // type 用  expression?: string;   // evaluate 用}// web - 网页搜索interface WebParams {  query: string;         // 搜索关键词  maxResults?: number;   // 结果数量 (默认 5)}// 返回: 摘要 + URL 列表

几个值得注意的设计:

  • • write 是原子写入:先写 .tmp 文件,再 rename。这意味着不会出现”写了一半断电导致文件损坏”的情况。
  • • edit 要求唯一匹配oldString 必须在文件中唯一出现,否则报错。这避免了”改一处却改了十处”的事故。
  • • grep 底层是 ripgrep:所以搜索速度极快,万级文件目录也能在 1 秒内完成。

3.2 exec 工具:最危险也最强大

exec 是所有内置工具里权限最高的——它能执行任意 shell 命令。因此安全链条也最长:

匹配 deny 列表

匹配 requireApproval

匹配 autoApprove

sandbox=off

sandbox=non-main + 主会话

sandbox=non-main + 其他会话

sandbox=all

exec(‘npm test’, {cwd, timeout: 120000})

[1] 权限检查agent.tools.allow 包含 exec? ✓

[2] 审批检查 (按优先级)

拒绝执行

暂停 → 推送审批请求到用户用户批准 → 继续

[3] 沙盒决策

主机 shell 直接执行

沙盒执行

[4] 执行spawn(‘sh’, [‘-c’, command])maxBuffer: 10MB

[5] 超时?

SIGTERM → 5s → SIGKILLtimedOut: true

[6] 输出截断stdout/stderr > 100KB → 截断

[7] 返回{stdout, stderr, exitCode, timedOut, executionTimeMs}

审批检查的优先级很关键:deny > requireApproval > autoApprove。也就是说,黑名单永远优先于白名单。超时处理也很讲究——先发 SIGTERM 给进程一个优雅退出的机会,等 5 秒还没退就 SIGKILL 强杀。

3.3 browser 工具:Playwright 驱动的浏览器自动化

底层用的是 Playwright(Chromium headless),生命周期管理如下:

首次调用 browser

启动 Chromium (headless)

创建 BrowserContext (隔离环境)

创建 Page → 缓存到 Session

后续调用 (同一 Session)

复用已有 Page

Session 结束 / 5 分钟无使用

page.close() → context.close()释放 Chromium 资源

Gateway 关停

关闭所有 Chromium 实例

安全方面限制得很死:

限制项
说明
file://

 协议
禁止访问
127.0.0.1:18789
禁止访问(防自引用)
内网 IP
禁止访问(可配置放行)
页面导航超时
30s
JavaScript 执行超时
10s
截图尺寸限制
1920×1080

这些限制的核心思路是防止 Agent 通过浏览器攻击自身基础设施——比如访问 Gateway 的管理端口、读取本地文件等。


四、Skills 插件系统:npm 生态的力量

4.1 Skill 的目录结构

每个 Skill 就是一个标准 npm 包,结构清晰:

@openclaw/skill-gmail/├── package.json              标准 npm 包├── manifest.json             Skill 元数据├── src/│   ├── index.ts              入口 (导出 SkillFactory)│   ├── tools/│   │   ├── list.ts           gmail.list 实现│   │   ├── send.ts           gmail.send 实现│   │   ├── archive.ts        gmail.archive 实现│   │   └── search.ts         gmail.search 实现│   └── auth/│       └── google-oauth.ts   OAuth 2.0 流程└── README.md

4.2 Skill Manifest:工具的”身份证”

manifest.json 声明了这个 Skill 提供哪些工具、每个工具的输入 Schema、以及所需的认证方式:

{  "name": "gmail",  "version": "1.0.0",  "displayName": "Gmail Integration",  "description": "Read, send, and manage Gmail emails",  "author": "@openclaw",  "tools": [    {      "name": "gmail.list",      "description": "列出收件箱邮件。支持过滤标签、已读状态、日期范围。",      "inputSchema": {        "type": "object",        "properties": {          "query": { "type": "string", "description": "Gmail 搜索语法, 如 'is:unread from:boss'" },          "maxResults": { "type": "number", "default": 10 },          "labelIds": { "type": "array", "items": { "type": "string" } }        }      },      "auth": { "type": "oauth2", "provider": "google", "scopes": ["gmail.readonly"] }    },    {      "name": "gmail.send",      "description": "发送邮件。",      "inputSchema": {        "type": "object",        "required": ["to", "subject", "body"],        "properties": {          "to": { "type": "string" },          "subject": { "type": "string" },          "body": { "type": "string" },          "cc": { "type": "string" },          "attachments": { "type": "array" }        }      },      "auth": { "type": "oauth2", "provider": "google", "scopes": ["gmail.send"] }    }  ]}

注意 auth 字段:每个工具可以声明自己需要的 OAuth scope,安装时引导用户授权,运行时自动管理 Token 刷新。

4.3 Skill 的安装与运行时

安装流程——从 npm install 到热生效,无需重启:

$ npm install @openclaw/skill-gmail

安装 npm 包

读取 manifest.json

注册工具到 Tool Registry

需要 OAuth?

引导授权流程

写入 openclaw.json → skills 配置

热生效 (无需重启)

运行时执行——模型调用 gmail.list 时的完整链路:

有效

过期

无 Token

模型调用 gmail.list({query: ‘is:unread’})

Tool Router: ‘gmail.*’ → Skills Manager

Skills Manager: 加载 @openclaw/skill-gmail

Auth Manager:检查 OAuth Token

续用 Token

refresh_token 刷新

返回错误: ‘需要授权 Gmail’

执行: tools/list.ts → Gmail API

返回: 邮件列表 JSON

Skill 的代码接口也非常简洁:

interface SkillFactory {  name: string;  manifest: SkillManifest;  createTools(context: SkillContext): ToolImplementation[];}interface SkillContext {  auth: AuthManager;           // OAuth Token 管理  config: Record<string, any>; // Skill 配置  logger: Logger;  storage: SkillStorage;       // Skill 持久化存储}

一个 createTools 方法返回所有工具实现,SkillContext 提供了 Auth、Config、Storage 等基础设施。开发一个新 Skill 的心智负担很低。


五、MCP Server 集成:标准协议的威力

5.1 MCP 的完整生命周期

MCP(Model Context Protocol)是一个开放标准,让任何外部服务都能以统一方式为 Agent 提供工具。OpenClaw 对 MCP Server 的管理非常完善:

stdio

sse

MCP Server 崩溃

重启成功

Gateway 启动

[1] 读取 openclaw.jsonmcpServers 配置

[2] 逐个启动 MCP Server

spawn(command, args)通过 stdin/stdout 通信 (JSON-RPC)

连接 http://host:port/mcpServer-Sent Events

[3] 握手发送 initialize → 获取 ServerCapabilities

[4] 工具发现: tools/list获取所有工具 Schema

[5] 工具名添加前缀read_file → mcp__filesystem__read_file

[6] 注册到 Tool Registry → Agent 可用

运行中

标记工具为不可用后台指数退避重启

重新 tools/list → 恢复工具

Gateway 关停

发送 SIGTERM → 等 5s → SIGKILL

几个设计亮点:

  1. 1. 双传输协议:stdio(本地进程)和 SSE(远程服务)都支持。本地工具用 stdio 低延迟通信,远程服务用 SSE 走 HTTP。
  2. 2. 工具名前缀read_file 变成 mcp__filesystem__read_file,彻底避免不同 MCP Server 之间的命名冲突。
  3. 3. 崩溃自愈:MCP Server 挂了不会影响其他工具,后台指数退避重启,恢复后自动重新注册。

5.2 MCP 配置示例

{  "mcpServers": {    "filesystem": {      "command": "npx",      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"],      "env": {}    },    "database": {      "command": "npx",      "args": ["-y", "@openclaw/mcp-sqlite", "--db", "/path/to/db.sqlite"],      "env": {}    },    "custom-rag": {      "url": "http://localhost:8080/mcp",      "transport": "sse"    }  }}

配置非常直白:command + args 是 stdio 模式,url + transport 是 SSE 模式。

5.3 Skills vs MCP:什么时候用哪个?

有了 Skills 和 MCP 两种扩展机制,一个自然的问题是:做一个新集成,该选哪个?

对比维度
Skills 插件
MCP Server
开发语言
必须 TypeScript(npm 包)
任意语言(只要实现 MCP 协议)
安装方式 npm install

 + 配置
配置启动命令或 SSE URL
Auth 管理
内置 OAuth 管理(AuthManager)
自行处理(MCP 协议不涉及 Auth)
状态管理
有 SkillStorage 持久化
自行管理
适合场景
深度集成、需要 OAuth 的 SaaS 服务
已有服务的快速接入、非 Node.js 生态
生态壁垒
锁定 OpenClaw 平台
MCP 是开放标准,可跨平台复用

简单来说:如果你在做一个 Gmail/GitHub 级别的深度集成,选 Skills;如果你已有一个 Python 服务想快速暴露给 Agent,选 MCP。


六、Tool Router:工具分发的中枢大脑

前面三节讲的是”有哪些工具”,这一节开始讲”怎么调度它们”。Tool Router 是整个执行链路的入口——它接收模型的 tool_call,决定交给谁执行、是否需要审批、要不要进沙盒。

先看核心代码(约 50 行,逻辑非常清晰):

class ToolRouter {  async execute(toolCall: ToolCall, context: ExecutionContext): Promise<ToolResult> {    const { name, arguments: args } = toolCall;    // [1] 权限检查    if (!this.policy.isAllowed(name, context.agent)) {      return { toolCallId: toolCall.id, content: `Permission denied: ${name}`, isError: true };    }    // [2] 路由分发    let executor: ToolExecutor;    if (name.startsWith("mcp__")) {      executor = this.mcpManager.getExecutor(name);    } else if (this.builtinTools.has(name)) {      executor = this.builtinTools.get(name)!;    } else if (this.skillsManager.match(name)) {      executor = this.skillsManager.getExecutor(name);    } else {      return { toolCallId: toolCall.id, content: `Unknown tool: ${name}`, isError: true };    }    // [3] 审批检查 (仅 exec 类)    if (name === "exec") {      const approval = await this.approvals.check(args.command, context);      if (approval === "deny") {        return { toolCallId: toolCall.id, content: "Command denied by policy", isError: true };      }      if (approval === "require") {        const result = await this.approvals.requestApproval(args.command, context);        if (!result.approved) {          return { toolCallId: toolCall.id, content: `User ${result.action}: ${args.command}`, isError: true };        }      }    }    // [4] 沙盒决策    const sandboxed = this.sandbox.shouldSandbox(name, context);    // [5] 执行 + 超时    const result = await withTimeout(      sandboxed ? this.sandbox.execute(executor, args) : executor.execute(args),      context.agent.config.limits.toolTimeout    );    // [6] 审计记录    this.audit.logToolExecution({ toolCall, result, sandboxed, context });    return result;  }}

路由的判定顺序是:mcp__ 前缀 → 内置工具表 → Skills 模式匹配 → 未知工具报错。先做前缀检查是因为 MCP 工具天然带有 mcp__ 前缀(见第五节),字符串前缀匹配 O(1) 最快,可以第一时间分流;而内置工具数量固定(Map 查找),也优先于 Skills 的模式匹配。这个顺序本质上是按匹配成本从低到高排列

还有一个设计选择值得注意:审批检查只针对 exec(代码第 [3] 步)。为什么 write 不需要审批?因为 write 的破坏力有限——它只能写入 Agent Workspace 目录下的文件,而 exec 能执行任意 shell 命令(rm -rf /curl 外泄数据、安装恶意包……)。审批的粒度精确对准了风险最高的那个入口。


七、工具定义如何传递给模型

工具引擎面临一个容易被忽视的问题:模型怎么知道有哪些工具可用? 答案是——每次调用模型时,都要把所有工具的 Schema 塞进 tools 参数。这不是免费的,每个 Schema 都会消耗上下文窗口的 Token。

≤ toolTokenBudget

> toolTokenBudget (默认 5000)

[1] 收集所有已注册工具 Schemabuiltin: 8 个 · skills: 3 个 · mcp: 5 个总计: 16 个工具

[2] 按 Agent 白名单过滤allow=[*], deny=[]→ 16 个全部保留

[3] 适配模型格式

Anthropic{name, description, input_schema}

OpenAI{type:’function’, function:{name, description, parameters}}

[4] Token 预算控制16 工具 ≈ 2500 tokens

全部保留

按使用频率排序 → 保留最常用低频工具移除被移除工具返回:’Tool not available in this context’

Token 预算控制是个很务实的设计。算一笔账:16 个工具 ≈ 2500 tokens,占 Claude 200K 上下文的 1.25%,看起来不多。但如果 Skill + MCP 扩展到 50 个工具,就是 ~8000 tokens——而且这个成本是每轮对话都要付的固定税,一个 10 轮对话就是 80K tokens 花在了工具 Schema 上。

按使用频率裁剪是个聪明的策略:Agent 的工具使用有明显的幂律分布(read/write/exec/edit 四大天王占 80%+ 调用),低频工具被移除后 Agent 大概率不会用到。即使偶尔需要,模型收到 “Tool not available” 后也能调整策略。

另一个被低估的设计:Anthropic 和 OpenAI 的工具 Schema 格式不同。OpenClaw 在这一步做了适配,意味着同一套工具定义可以无缝对接不同的模型提供商——这对于需要 multi-model fallback 的生产环境至关重要。


八、并行工具执行:一次请求,多工具齐发

当模型在一次回复中返回多个 tool_call 时,OpenClaw 默认并行执行

[  tool_call("read", {"path": "a.txt"}),  tool_call("read", {"path": "b.txt"}),  tool_call("web", {"query": "latest news"})]

执行策略是 Promise.allSettled()(注意不是 Promise.all())——所有工具同时启动,互不阻塞。选 allSettled 而非 all 是关键决策:all 在任一工具失败时会立即终止所有工具并抛错,而 allSettled 保证即使某个工具失败,其他工具的结果也能正常返回。在 Agent 场景下,部分结果远好于没有结果

模型返回3 个 tool_call

read(‘a.txt’)

read(‘b.txt’)

web(‘news’)

result_a

result_b

result_c

合并结果返回模型

异常隔离是核心原则:单个工具失败只影响自己,返回错误信息,其他工具正常完成。每个工具有独立的 timeout 计时器。

边界情况处理(这些 edge case 的处理方式反映了设计哲学):

场景
处理
设计思路
包含需审批的 exec
不需审批的先执行;需审批的发送审批请求,等待回复后执行;全部完成后合并返回
不让审批阻塞其他无关工具
write 后接 read 同一文件
并行执行,read 可能读到旧内容;模型会在下一轮修正
刻意选择了”最终一致”而非”强一致”——要做依赖分析的成本远超偶尔多一轮对话的成本
并行数限制
maxParallelTools(默认 10),超过则队列化执行
防止 fork bomb 式的资源耗尽

九、工具结果格式化:给模型的”翻译层”

工具执行完的原始输出五花八门——可能是 200KB 的 npm test 日志、一张 Base64 截图、或者一个 JSON 对象。模型不需要(也处理不了)这些原始数据。ToolResult 就是这个翻译层:

interface ToolResult {  toolCallId: string;  content: string;           // 给模型看的文本  isError: boolean;  metadata: {    executionTimeMs: number;    sandbox: boolean;    approved?: boolean;    truncated?: boolean;     // 输出是否被截断  };}

截断策略防止大输出撑爆上下文窗口。注意每种工具的阈值不同——这不是随意设定的,而是反映了各类输出的信息密度:

  • • exec 输出 > 100KB → 截断。构建日志大量重复,截断损失小
  • • read 文件 > 200KB → 截断 + 建议使用 offset/limit。源码文件通常有结构,可以分段读取
  • • web 搜索结果 → 每条最多 500 字摘要。搜索引擎已经做了初步摘要
  • • browser 截图 → Base64(给视觉模型)或文字描述。视觉信息无法文本化截断,所以提供两种模式

传递给不同模型时的格式适配:

Anthropic: { type:"tool_result", tool_use_id, content, is_error }OpenAI:    { role:"tool", tool_call_id, content }

这个看似简单的格式差异,实际上是多模型 Agent 框架的一个常见痛点。OpenClaw 在 ToolResult 层统一抽象,在最后一步才做格式适配,避免了 Provider 特定逻辑污染整个工具链路。


十、自定义内置工具开发

理解了前面九节的执行链路,你会发现添加一个自定义工具只需要关心两件事:定义 Schema(告诉模型这个工具做什么、接受什么参数)和实现执行逻辑。路由、权限、审批、沙盒、结果格式化——这些全部由引擎处理。

import { ToolDefinition, ToolExecutor } from "../types/tool";export const myTool: ToolDefinition = {  name: "my_tool",  description: "简要说明这个工具做什么,模型根据此描述决定何时调用。",  inputSchema: {    type: "object",    required: ["param1"],    properties: {      param1: { type: "string", description: "参数说明" },      param2: { type: "number", description: "可选参数", default: 10 },    },  },};export const myToolExecutor: ToolExecutor = {  async execute(args: { param1: string; param2?: number }): Promise<string> {    const result = await doSomething(args.param1, args.param2 ?? 10);    return JSON.stringify(result);  },};// 注册:// src/tools/builtin/index.ts// registry.register(myTool, myToolExecutor);

两步:定义 Schema(ToolDefinition)+ 实现执行逻辑(ToolExecutor),然后在注册表里挂上。模型会根据 description 自动决定什么时候调用你的工具。

这里有个容易踩的坑:description 的质量直接决定了模型能否正确调用你的工具。写得太模糊,模型不知道什么时候该用;写得太具体,模型只在精确匹配时才用。好的 description 应该说明输入什么、做什么、输出什么,用一两句话。


十一、容错与监控:”把错误暴露给模型”

OpenClaw 的容错哲学可以用一句话概括:fail open to the model——出了问题,不要自己吞掉错误,而是把结构化的错误信息交给模型,让模型决定怎么处理。这比传统软件的”重试三次然后报错”要更适合 Agent 场景,因为模型有推理能力,能根据错误信息调整策略。

故障场景
处理
工具不存在
返回结构化错误给模型,模型换方案
权限不足
"Permission denied: {tool}"

 给模型
执行超时
SIGTERM → 5s → SIGKILL → timeout 错误
exec 退出码非零
返回 stderr 给模型(不标记为 isError),模型可能需要 stderr 信息来修正
MCP Server 挂
标记不可用 → 后台重连 → 告知模型
Skill OAuth 过期
refresh → 失败 → 请求用户重新授权
浏览器崩溃
重启 Chromium → 重新导航
磁盘满
write 失败 → 告知模型 → 建议清理
网络不通
web/browser 超时 → 告知模型

这张表里藏着一个最精妙的设计决策:exec 退出码非零时不标记为 isError

直觉上,命令执行失败 = 错误,应该标 isError: true。但 OpenClaw 刻意不这么做。原因是:npm test 失败时的 stderr 包含了具体哪个测试用例挂了、哪行代码有问题——这对模型来说不是”错误”,而是关键的上下文输入。如果标了 isError,某些模型可能会直接跳过 stderr 内容,反而失去了修复 bug 的线索。

这就是”fail open to the model”在实践中的体现:把原始信息原样交给模型,让它自己判断是”真错了”还是”有用的反馈”。

监控指标记录到 audit.jsonl + usage.jsonl

监控维度
指标
工具
调用次数、成功率、平均耗时
审批
通过率、拒绝率、超时率
MCP
连接状态、重启次数

十二、性能特征:实测数据

最后附上各环节的实测性能数据,供架构选型参考:

指标
实测值(参考)
Tool Router 分发 + 权限检查
< 2ms
read(100KB 文件)
5-20ms
write(10KB 文件,原子写)
10-30ms
edit(string replace)
5-15ms
exec(简单命令 ls)
50-200ms
exec(npm test)
5-120s
glob(10K 文件目录)
100-500ms
grep(10K 文件,ripgrep)
200-1000ms
browser navigate
1-10s
browser screenshot
200-500ms
web search
500-2000ms
Skill 执行(gmail.list)
500-2000ms
MCP 工具调用
50-500ms
审批等待
0-300s(用户)
工具定义序列化(16 工具) < 5ms
工具定义 Token 开销(16 工具) ~2500 tokens

几个值得关注的规律:

  1. 1. 路由层几乎免费:Tool Router 分发 + 权限检查 < 2ms,工具定义序列化 < 5ms。引擎的设计正确地把优化资源放在了工具执行本身,而非调度层。
  2. 2. I/O 密集型工具是真正的瓶颈:browser navigate(1-10s)、web search(0.5-2s)、Skill/MCP 远程调用(0.5-2s)——这些都是网络 I/O 主导的操作,恰好是并行执行收益最大的场景。三个 read 串行需要 15-60ms,并行只需要 5-20ms(取最慢的一个);如果换成三个 web search,串行可能要 6 秒,并行只需 2 秒,差距更加显著。
  3. 3. Token 税不可忽视:16 个工具的 Schema 就要 ~2500 tokens,这是每轮对话都要”交”的固定成本。工具越多,留给实际任务的上下文窗口就越小。

总结与思考

设计精华

回顾整个工具引擎,有四个设计原则贯穿始终:

  1. 1. 分层解耦:内置工具保证基本功、Skills 扩展能力边界、MCP 连接无限生态。三层之间通过 Tool Registry 统一抽象,上层不感知下层差异。
  2. 2. 安全纵深:权限 → 审批 → 沙盒,三道防线层层递进。且安全粒度精确——审批只卡 exec,不给其他工具增加不必要的摩擦。
  3. 3. Fail Open to the Model:不吞掉错误,不过度重试,把结构化错误暴露给模型。模型有推理能力,比硬编码的重试逻辑更能做出正确判断。
  4. 4. 性能务实:并行执行、Token 预算控制、按信息密度分级截断。每一处都在权衡”好一点”和”贵多少”。

局限性与开放问题

不过,这套设计也有一些值得思考的地方:

  • • 工具间依赖缺失:当前的并行执行不做依赖分析。write + read 同一文件的竞态条件虽然”模型下一轮可以修正”,但在长链路任务中,这种”最终一致”可能导致不必要的多轮对话开销。未来是否需要引入轻量级的 DAG 调度?
  • • 审批只覆盖 exec:随着 Skills 能力增强(比如 gmail.send 可以发邮件、github.merge 可以合并 PR),是否需要把审批机制泛化到高风险的 Skill 操作?目前这些都是”安装时授权,运行时放行”。
  • • Token 预算的冷启动问题:按使用频率裁剪工具需要历史数据。新 Agent / 新 Session 没有历史时,如何决定保留哪些工具?
  • • MCP Server 的安全边界:MCP 协议本身不包含认证和授权。一个恶意 MCP Server 可以注册与内置工具同名(加 mcp__ 前缀后不同名)的工具,但可以注册看起来像合法工具的误导性名称。这个信任模型是否足够?

与同类方案的对比

如果你用过 LangChain 的 Tool 抽象或 AutoGen 的 Function Calling,会发现 OpenClaw 的差异化在于:

  • • LangChain 的工具是纯函数式的,没有内置的权限/审批/沙盒链路——这些都需要开发者自行实现
  • • AutoGen 的 Function Calling 直接暴露给模型,缺少 MCP 这样的标准化扩展协议
  • • OpenClaw 在工具安全和生态扩展性上走得更远,代价是引入了更多概念(三层体系、审批策略、沙盒模式)

没有哪个方案是银弹。如果你的场景是受控环境下的简单工具调用,LangChain 的轻量方案就够了。如果你需要在生产环境运行、给不信任的用户暴露 Agent 能力,OpenClaw 的安全纵深才值得引入。


如果你正在构建自己的 AI Agent 框架,建议优先借鉴的是这三个模式:Tool Router 的分层路由(简单但高效)、exec 的审批-沙盒链路(安全设计的最小可行方案)、以及 “fail open to the model” 的容错哲学(比硬编码重试好得多)。

项目地址:github.com/openclaw/openclaw — 工具引擎相关代码主要在 src/tools/ 和 src/gateway/ 目录下。

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » OpenClaw 工具引擎架构全解析:AI Agent 的"双手"是怎么工作的?

猜你喜欢

  • 暂无文章
本站作品均采用知识共享署名-非商业性使用-相同方式共享 4.0进行许可,资源收集于网络仅供用于学习和交流,本站一切资源不代表本站立场,我们尊重软件和教程作者的版权,如有不妥请联系本站处理!

 沪ICP备2023009708号

© 2017-2026 夜雨聆风   | sitemap | 网站地图