# 工具即能力：AI Agent 的工具系统设计哲学


<!-- more -->


上一篇文章我们聊了 Think-Act-Observe 循环——AI 编程助手的骨架。骨架本身不会干活，它需要肌肉。在 Claude Code 里，肌肉就是**工具**。

没有工具，循环里 Think 和 Observe 之间缺了 Act，退化回普通的问答。有了工具，模型才能触碰真实世界：读你的代码、改你的配置、跑你的测试。

但工具系统的设计远比"给模型一堆函数调用"复杂。Claude Code 源码里藏着几个关键设计决策，每一个都指向同一个问题：

**怎么让 AI 知道该用什么工具、怎么用、什么时候用？**

答案是：**描述比实现重要。**

## 工具的三层结构

Claude Code 里的每个工具都有三层：

**第一层：定义（Definition）**——工具的名字、描述、参数 schema。这一层不执行任何操作，它只回答"我是什么、我能干什么、你需要给我什么"。

**第二层：实现（Implementation）**——工具实际做什么。读文件就调 `fs.readFile`，执行命令就调 `child_process`。这是工程师写的代码。

**第三层：权限（Permission）**——工具的风险等级。读文件是低风险的，直接执行；删文件是高风险的，需要用户确认。

这三层里，**模型只能看到第一层**。它不知道 `read_file` 内部调的是 `fs.readFile` 还是某种缓存机制，它只知道"这个工具能读取文件内容，需要传入 path 参数"。

这意味着工具的定义层是模型和真实世界之间的**唯一接口**。接口设计得好不好，直接决定了模型能不能正确使用工具。

## 工具描述：被低估的 Prompt Engineering

大多数人在设计 AI 工具时，精力都放在实现上——功能对不对、性能好不好、边界条件有没有覆盖。这没错，但 Claude Code 的工具系统揭示了一个容易被忽略的事实：

**工具描述（description）是一段微型 prompt。**

它会被注入到 system prompt 里，模型靠它理解"这个工具是干什么的、什么时候该用、参数是什么意思"。一段好的工具描述和一段烂的描述，差距可能是一个工具被频繁使用 vs. 从未被调用。

看 Claude Code 里 grep_search 工具的描述：

> Search file contents using regular expressions (powered by ripgrep). Returns matching lines with file paths and line numbers.

这段描述回答了三个问题：
- **干什么**：搜索文件内容
- **用什么**：正则表达式
- **返回什么**：匹配行、文件路径、行号

再看一个反例。假设描述只写 "Search files"——模型知道你要搜索，但不知道是搜索文件名还是文件内容，不知道支持什么语法，不知道返回什么格式。这种描述下，模型要么不敢用（信息不足），要么乱用（猜错了）。

**工具描述的质量 = 模型使用工具的准确率。** 这不是 Prompt Engineering 的分支，这就是 Prompt Engineering。

## 工具的分类逻辑

Claude Code 的工具不是随意堆砌的。源码里的工具可以按**认知维度**分成几类：

**感知类（Perception）**——让模型"看到"项目状态。`read_file`、`glob`（文件匹配）、`grep`（内容搜索）、`ls`（目录列表）。这类工具只读不改，是模型理解上下文的主要手段。

**操作类（Action）**——让模型"改变"项目状态。`write_file`、`edit`（精确替换）、`bash`（执行命令）。这类工具有副作用，是权限系统的重点管控对象。

**外部类（External）**——让模型"触达"项目之外的世界。`web_search`、`web_fetch`。这类工具引入了不可控的外部信息源，风险和收益都更高。

**元认知类（Meta）**——让模型"了解自己"。`mcp_status`、`cost`。这类工具帮助模型监控自身状态和资源配置。

这种分类不是源码里的显式结构，但它在 system prompt 和权限系统里被隐式地体现出来。感知类工具通常无需确认即可调用，操作类工具需要分级确认，外部类工具有独立的开关。

分类的本质是**风险分级**。不是所有工具调用都同等危险，权限系统需要知道"这次调用该不该问用户"。

## 渐进式披露：上下文爆炸的解法

工具系统面临一个根本矛盾：

- 工具越多，AI 能力越强。
- 工具越多，工具描述占用的上下文越多。
- 上下文被工具描述占满，留给实际对话的空间越少。
- 空间越少，模型表现越差（所谓"lost in the middle"效应）。

Claude Code 的解法是**渐进式披露（Progressive Disclosure）**：

不是把所有工具的定义一次性塞进 system prompt，而是在对话过程中**按需加载**。模型一开始只看到核心工具的描述（read、write、edit、bash），当它需要更专业的能力时，再通过某种机制"发现"并加载额外工具。

这个思路在源码里体现为 deferred tools（延迟工具）机制——某些工具的定义默认不注入 system prompt，只在特定条件下被激活。

渐进式披露的本质和操作系统里的**懒加载**一样：不用不着急加载，用了再加载也不迟。区别是操作系统的懒加载优化的是内存，AI Agent 的懒加载优化的是**注意力**。模型的注意力是有限的上下文窗口，每一 token 都应该花在刀刃上。

## 工具调用的同步性

Claude Code 的工具调用是**同步阻塞**的——模型请求调用一个工具，循环暂停，等工具执行完毕，结果返回，循环继续。

这个选择值得咀嚼。异步并行显然更快：如果模型需要读三个文件，并行读比串行读快三倍。但 Claude Code 选择了串行。

原因是**认知一致性**。模型在 Think 阶段基于当前已知的信息做出判断，它请求调用工具 A 是因为它认为"我现在最需要知道 X"。如果三个工具并行执行、结果同时返回，模型看到的是一堆它没按顺序请求的信息——它可能已经基于工具 A 的结果做了判断，工具 B 的结果却推翻了那个判断。

串行调用慢，但它保证了模型的思考过程是**线性可追踪的**。每一步都有明确的前因后果，出了问题容易回溯。

当然，Claude Code 也在某些场景下支持并行工具调用——当模型明确判断多个工具调用之间没有依赖关系时。但默认策略是串行，这是**安全优先**的设计选择。

## 工具系统的哲学

Claude Code 的工具系统背后有一条贯穿始终的设计哲学：

**AI Agent 的能力不由模型决定，由工具决定。**

同样的 GPT-4o 或 Claude Sonnet，给它不同的工具集，它就是不同的 Agent。只有 read/write 工具，它是文本编辑器。加上 bash，它是运维助手。加上 web_search，它是研究员。加上 MCP 协议连接外部服务，它的边界几乎无限。

模型是引擎，工具是变速箱和轮胎。引擎再好，没有合适的传动系统也跑不起来。

这也是为什么 Claude Code 的源码里，工具系统的代码量和 agent 循环的代码量几乎一样多。不是因为工具实现有多复杂——大多数工具几行到几十行就搞定了——而是因为**工具的定义、调度、权限、错误处理、结果格式化**这套基础设施需要精心设计。

## 延伸阅读：BYF 的 Kaos 抽象与 TaskEntry 判别联合

在 [BYF](https://github.com/ByronFinn/byf) 项目里，工具系统不仅做了 Claude Code 做的事，还增加了一层抽象——**Kaos（执行环境抽象）**。

BYF 的 `Kaos` 接口将"执行"与"位置"解耦：一个操作（读文件、跑命令）可以在本地执行，也可以通过 SSH 在远程执行，调用方不需要知道区别。代码调 `readText()`、`exec()` 时，底层可能走 `LocalKaos`（本地文件系统）或未来的 `SSHKaos`（远程），`AsyncLocalStorage` 自动绑定到当前上下文。这是对 Claude Code 工具系统中"权限分类"的进一步深化——不仅分读/写/执行，还分**在哪执行**。

BYF 还做过一个有意思的设计债清理。它的后台任务系统原本用一个 `ManagedProcess` 数据结构同时管理两种任务：真实 OS 进程和 JS Promise 子代理。为了让 Promise 任务适配，代码中有一处 `as unknown as KaosProcess` 的类型强制转换——BYF 源码中唯一一处 `as unknown`。通过引入 **`TaskEntry` 判别联合**（`ProcessTaskEntry | PromiseTaskEntry`），BYF 彻底消除了这个类型逃生口，让每种任务有了自己的形态，编译器强制你在访问 `entry.proc` 前做 `kind` 守卫。

这个决策和 Claude Code 的"工具三层结构"是同一个思路：**类型系统本身就是工具系统的一部分。** 你用类型编码工具之间的差异，让错误在编译时被捕获，而不是在运行时被模型发现。

## 下一篇

工具描述是注入到 system prompt 里的。但一个生产级 AI Agent 的 system prompt 不只是工具描述的堆砌——它是一个几百甚至上千行的精密工程作品，分层、占位符、延迟加载、动态注入……下一篇文章我们要拆的就是这个：**800 行 system prompt 背后的设计决策**。

---

> 本系列基于 [Claude Code 官方源码](https://github.com/anthropics/claude-code)项目进行架构分析，聚焦设计思想而非代码实现。

