文字生成之外
前几篇我们反复说一句话:语言模型只有上下文窗口。它能输出的东西只有文字——字符串。
但一个真正有用的 AI 助手不能只说话,它必须能做事:打开浏览器查一个网页,读一个本地文件,执行一段 Shell 命令,给你的手机发一条通知,在固定时间触发一个提醒。这些动作没有一个是靠"输出文字"完成的。
工具(Tool)是这个鸿沟上的桥梁。模型输出一段结构化的 JSON,声明它想调用哪个函数、参数是什么;运行时框架解析这段 JSON,在真实环境里执行对应的动作,把结果作为新的上下文塞回去。从模型的角度看,工具是它的手和眼;从系统的角度看,工具是"语言生成能力"与"真实世界副作用"之间唯一合法的接口。
OpenClaw 的工具系统是整个平台里规模最大、分层最复杂的子系统之一。
组装的起点:createOpenClawCodingTools()
每次 Agent 执行开始,系统都会调用 src/agents/pi-tools.ts 里的 createOpenClawCodingTools() 函数,构建出这次执行可用的完整工具列表,然后把这个列表连同模型调用一起发给 API。
这个函数接受一个极其丰富的配置对象,涵盖 Agent 身份、模型 Provider 类型、沙箱上下文、文件系统策略、消息路由信息、以及各种策略覆盖。它的执行结果是一个 AnyAgentTool[] 数组,包含所有这次执行中模型可以调用的函数定义。
工具来自两个来源,按顺序合并。
第一个来源是 @mariozechner/pi-coding-agent 提供的 codingTools,这是 Pi 框架的核心文件系统和执行工具集。OpenClaw 在合并时不是简单地追加,而是替换其中几个工具,用自己的版本覆盖 Pi 的默认实现——主要是 exec(增加了沙箱隔离支持)、read 和 write(增加了工作区路径约束)。
第二个来源是 src/agents/openclaw-tools.ts 里的 createOpenClawTools(),这里是 OpenClaw 专属工具的创建入口。
有两条 Provider 特定的门控规则值得单独说明。apply_patch 工具只在 OpenAI 系列 Provider 下被启用,非 OpenAI Provider 的模型调用时这个工具不出现在列表里。当使用 Anthropic OAuth 传输层时,工具名称会在发送给 API 之前被重映射为 Claude Code 风格的名称,让 Anthropic 的服务端能识别这是一个兼容 Claude Code 协议的客户端。
Pi 核心工具:文件系统与进程执行
Pi 框架提供的核心工具覆盖了开发者最基础的操作能力。
read 工具读取文件内容,支持按行范围读取大文件,防止一次读取超大文件撑爆上下文。write 工具写入文件,会在写入前创建必要的父目录。edit 工具对文件做字符串精确替换,比先读后写更高效。apply_patch 工具应用 unified diff 格式的补丁,适合跨多个文件的批量修改。
exec 工具执行 Shell 命令,是工具体系里权限最高的一个,也是沙箱机制重点保护的对象。它的配置来自三个层次的合并:options.exec 内联覆盖、agents.list[id].tools.exec 的 per-agent 配置、以及全局默认值。可配置项包括允许的 Shell 解释器、命令超时时间、是否允许网络访问、以及是否需要明确的用户审批。
process 工具管理后台进程——启动一个长期运行的进程(比如一个 Dev 服务器),检查其输出,向其发送输入,在需要时终止它。这让 Agent 能够真正地"启动然后监控",而不是只能执行一次性命令。
当 tools.fs.workspaceOnly 为 true 时,read、write、edit、apply_patch 这四个工具会被包裹上路径约束 wrapper,任何试图访问 workspaceRoot 之外路径的调用都会被直接拒绝,连文件是否存在都不透露给模型。这是防止 Agent 误操作系统文件的基础屏障。
OpenClaw 专属工具:十大能力群
createOpenClawTools() 创建的专属工具涵盖十个能力群,每一个都对应 OpenClaw 区别于普通编码 Agent 的核心价值。
browser——浏览器自动化。通过 CDP(Chrome DevTools Protocol)控制一个专属的 Chromium 实例,支持导航、截图、点击、表单填写、JavaScript 执行、Cookie 和 LocalStorage 的读写。与普通的 web_fetch 不同,browser 工具操作的是一个真实渲染了 JavaScript 的浏览器,能处理那些需要动态渲染的页面。每个 Agent 可以有多个 browser profile(命名规则:小写字母数字加连字符,最多 64 个字符),不同 profile 的 Cookie 和 Session 完全隔离,支持同时操作多个独立的浏览器身份。
canvas——实时 UI 渲染。在连接到 Gateway 的原生 App(macOS、iOS、Android)上渲染一个实时更新的可视化界面。Agent 可以用 HTML、SVG 或结构化数据描述一个界面,canvas 工具把它推送到你的设备上显示。这让 Agent 能够突破纯文字的限制,真正展示图表、表格、仪表盘、交互表单。
web_search 和 web_fetch。web_search 需要 Brave API Key,对查询结果做 15 分钟的缓存。web_fetch 抓取 URL 内容并转换为 Markdown,同样带缓存,最大字符数由 tools.web.fetch.maxCharsCap 硬性限制(默认 50000),防止单次抓取的内容把整个上下文窗口都占满。两个工具通过 tools.web.search.enabled 和 tools.web.fetch.enabled 分别控制,也可以配置 Firecrawl 作为反反爬虫的备用抓取后端。
nodes——设备节点控制。向配对的 iOS、Android、macOS 设备节点发送通知、截图、摄像头拍照、执行命令。这让 Agent 真正突破单台机器的边界,可以在你的手机上弹通知,可以截取你的电脑屏幕,可以在另一台机器上执行命令。每个 node 操作都有对应的用户同意机制:摄像头和屏幕录制需要 App 在前台,通知需要已授权的通知权限。
cron——定时任务。创建、修改、删除基于 cron 表达式的定时任务。Agent 可以在对话中自己创建定时任务——"每天早上 8 点检查一次邮件里有没有紧急事项"——不需要你手动编辑配置文件。
sessions——子 Agent 管理。这是多 Agent 协作体系的入口工具,支持以下动作:spawn(创建一个子 Agent Session)、list(列出可见的 Session)、history(读取某个 Session 的历史记录)、send(向另一个 Session 发送消息)。关于子 Agent 的完整体系,将在第十五篇详细展开。
message——跨通道发消息。向指定通道(Telegram、WhatsApp、Discord 等)的指定用户或群组主动发送消息,不依赖"回复当前对话"的路径。这让 Agent 能够真正主动触达——定时任务完成后在 WhatsApp 给你发一条汇报,异常发生时在 Telegram 发一条告警。
memory——记忆系统操作。memory_get(读取特定记忆文件)、memory_search(语义检索记忆)、memory_set(写入记忆)。这是第十一篇的主题,这里不展开。
image——图像分析。调用配置的图像模型(agents.defaults.imageModel)分析图片内容。只有在配置了图像模型、或者主模型能自动配对图像能力时,这个工具才出现在工具列表里。
tts——语音合成。把文字转换为语音,投递到支持语音的通道(macOS 原生 App、配对的移动设备)。在 messageProvider=voice 的执行上下文里,这个工具不出现,避免语音回复被双重处理。
三层策略管道:谁能用哪个工具
工具能否被调用,由三层策略按顺序决定,定义在 src/agents/tool-policy.ts 里。
第一层:全局工具策略(tools.allow / tools.deny)。这是配置文件里的顶层控制,对所有 Agent 生效。deny 永远优先于 allow——如果一个工具同时出现在 allow 和 deny 列表里,deny 生效。被 deny 的工具不只是"执行时报错",而是根本不出现在发给模型的工具列表里,模型看不到它,自然不会尝试调用它。
第二层:per-agent 策略覆盖(agents.list[].tools)。每个 Agent 可以在全局策略的基础上叠加自己的 allow/deny 规则。叠加是增量的,不是替换的——per-agent 的 deny 在全局 allow 基础上追加限制,per-agent 的 allow 只能在全局 allow 范围内扩展(无法绕过全局 deny)。
第三层:沙箱工具策略(sandbox.tools)。当前 Session 处于沙箱模式时,在前两层之上再叠加一层沙箱特定的限制。默认沙箱策略允许 bash、process、read、write、edit、sessions_list、sessions_history、sessions_send、sessions_spawn,拒绝 browser、canvas、nodes、cron、discord、gateway。这个默认值体现了"沙箱环境只做文件和进程操作,不做对外交互"的最小权限原则。
为了简化配置,策略列表里支持 group 快捷方式:group:fs(所有文件系统工具)、group:runtime(exec 和 process)、group:sessions(所有 sessions 相关工具)、group:memory(所有记忆工具)、group:web(web_search 和 web_fetch)。
还有一个 tools.byProvider 配置,允许针对特定的 Provider 或模型施加额外的工具限制,不改变全局默认值。比如你可以配置"当使用某个不可信的第三方模型时,只允许 read 和 web_search,不允许 exec"。
两层 Wrapper:日志与防循环
每个工具在被放入最终列表之前,会经过两层 wrapper 包裹。
第一层是 wrapToolWithBeforeToolCallHook(),位于 src/agents/pi-tools.before-tool-call.ts。这个 hook 在工具实际执行前触发,负责:向 Gateway 广播"模型正在调用这个工具"的事件(让 Web UI 和 TUI 能实时显示工具调用进度);如果工具配置了 requiresApproval: true,在这里暂停执行,向用户请求确认,等待 approve 或 deny 响应;记录工具调用历史,为下一层的循环检测提供数据。
第二层是循环检测器,通过 tools.loopDetection 配置启用(默认关闭)。它跟踪最近 N 次工具调用的历史,检测三种无进展循环模式:genericRepeat(相同工具、相同参数的反复调用)、knownPollNoProgress(轮询类工具的重复调用但输出不变)、pingPong(A→B→A→B 的交替调用且无实质进展)。触发警告阈值时,向模型注入一条提示;触发严重阈值时,向用户发出警告;触发全局断路器阈值时,直接中止当前 Agent 执行,防止无限循环消耗 API 额度。
Docker 沙箱:工具执行的隔离容器
exec、read、write、edit 在沙箱模式下不在 Gateway 所在的宿主机上执行,而是在一个 Docker 容器里执行。这是工具系统里最重要的安全机制。
沙箱模式由 agents.defaults.sandbox.mode 控制,支持三个值。off 表示所有工具都在宿主机上运行,适合只有你自己使用的单用户部署。non-main 表示只有非主 Session(群组、频道、定时任务、子 Agent)的工具在沙箱里运行,主 Session 仍然在宿主机上运行,这是官方推荐的平衡配置。all 表示所有 Session 的工具都在沙箱里运行,最安全但配置最复杂。
容器的生命周期由 sandbox.scope 控制:session 模式每个 Session 有自己独立的容器,Session 结束时容器销毁;agent 模式同一个 Agent 的所有非主 Session 共享一个容器,跨 Session 复用;shared 模式所有 Agent 的非主 Session 共享同一个容器,资源占用最低。
容器里的文件系统访问通过 workspaceAccess 控制:none 表示容器看不到宿主机的工作区;ro 表示只读挂载工作区;rw 表示读写挂载。docker.binds 数组可以额外挂载宿主机的任意目录:
{agents: {defaults: {sandbox: {docker: {binds: ["/home/user/source:/source:ro"],},},},},}
浏览器沙箱使用独立的 Docker 镜像和独立的 Docker 网络(openclaw-sandbox-browser),与工具执行容器完全隔离。沙箱里的 Chromium 默认禁用 GPU 加速和 3D API,需要 WebGL 时通过环境变量 OPENCLAW_BROWSER_DISABLE_GRAPHICS_FLAGS=0 关闭这些限制。
工具展示层:emoji 和参数摘要
工具系统里还有一个容易被忽略的细节:src/agents/tool-display.json。这个 JSON 文件把每个工具名称映射到三样东西:一个 emoji(比如 exec 对应 🔨,browser 对应 🌐,read 对应 📖)、一个面向用户的展示标题、以及要从参数里提取哪个字段作为详细标签(比如 exec 提取 command 参数,read 提取 path 参数)。
Web UI 和 TUI 用这个映射文件展示实时的工具调用进度,让用户看到的不是{"type":"tool_call","name":"exec","params":{"command":"ls -la"}} 这样的机器输出,而是一行有温度的展示:🔨 执行命令 ls -la。文件里有一个 fallback 条目,处理所有没有显式映射的工具,确保插件工具也能在 UI 里得到基本的展示。
小结
OpenClaw 的工具系统是整个平台的执行引擎。Pi 核心工具提供文件系统和进程执行能力;十个 OpenClaw 专属工具把浏览器、设备节点、定时任务、跨通道消息、多 Agent 协作全部纳入 Agent 的能力边界;三层策略管道确保最小权限原则在全局、per-agent、沙箱三个维度都得到执行;Docker 沙箱把高风险工具的爆炸半径限制在容器边界内;两层 wrapper 提供日志、审批、循环防护。
这套系统的设计哲学和安全架构有很多值得深挖的地方,第十七篇会从攻击者视角全面审视它的安全边界。
下一篇,我们进入记忆系统——Agent 是如何在对话结束、进程重启、设备更换之后,仍然记得你的。
源码参考:src/agents/pi-tools.ts · src/agents/openclaw-tools.ts · src/agents/tool-policy.ts · src/agents/sandbox.ts · src/agents/tool-display.json · src/agents/bash-tools.ts基于 commit bf6ec64f 版本
夜雨聆风