# 第2章：工具系统 — 40+ 个工具作为模型的双手

> **定位**：本章分析 Claude Code 40+ 个工具的注册、分类与元数据设计——从 `Tool` 接口契约到工具管线的完整生命周期。前置依赖：第1章（三层架构）。适用场景：想了解 CC 工具注册、分类、元数据设计的读者。

## 为什么工具系统是 Claude Code 的核心

大语言模型的"思考"发生在文本空间，而软件工程的操作发生在文件系统、终端和网络中。**工具系统**是连接这两个世界的桥梁：它将模型的意图翻译为真实的副作用，再将副作用的结果翻译回模型可消费的文本。

Claude Code 的工具系统管理着 40+ 个内置工具和不限数量的 MCP 扩展工具。这些工具不是一个平铺的数组——它们经历了一条精密的管线：**定义 → 注册 → 过滤 → 调用 → 渲染**。每一步都有明确的契约。本章将从 `Tool.ts` 的接口定义开始，逐层拆解这条管线的设计决策。

---

## 2.1 `Tool` 接口契约

所有工具——无论是内置的 `BashTool` 还是通过 MCP 协议加载的第三方工具——都必须满足同一个 TypeScript 接口。这个接口定义在 `restored-src/src/Tool.ts:362-695`，是整个工具系统的基石。

### 核心字段一览

| 字段 | 类型 | 职责 | 必需 |
|------|------|------|------|
| `name` | `readonly string` | 工具的唯一标识符，用于权限匹配、分析埋点和 API 传输 | 是 |
| `description` | `(input, options) => Promise<string>` | 返回发送给模型的工具描述文本；可根据权限上下文动态调整 | 是 |
| `prompt` | `(options) => Promise<string>` | 返回工具的系统提示词（system prompt），详见第8章 | 是 |
| `inputSchema` | `z.ZodType` (Zod v4) | 用 Zod schema 定义工具的参数结构，自动转换为 JSON Schema 发送给 API | 是 |
| `call` | `(args, context, canUseTool, parentMessage, onProgress?) => Promise<ToolResult>` | 工具的核心执行逻辑 | 是 |
| `checkPermissions` | `(input, context) => Promise<PermissionResult>` | 工具级权限检查，在通用权限系统之后执行 | 是* |
| `validateInput` | `(input, context) => Promise<ValidationResult>` | 在权限检查之前验证输入的合法性 | 否 |
| `maxResultSizeChars` | `number` | 单工具结果的字符数上限，超出后持久化到磁盘 | 是 |
| `isConcurrencySafe` | `(input) => boolean` | 是否可以与其他工具并发执行 | 是* |
| `isReadOnly` | `(input) => boolean` | 是否为只读操作（不修改文件系统） | 是* |
| `isEnabled` | `() => boolean` | 当前环境下工具是否可用 | 是* |

> 标注 * 的字段由 `buildTool()` 提供默认值，工具定义时可省略。

几个值得深究的设计选择：

**`description` 是函数而非字符串。** 同一个工具在不同的权限模式下可能需要不同的描述。例如，当用户配置了 `alwaysDeny` 规则禁止某些子命令时，工具描述可以主动告知模型"不要尝试这些操作"，从而在提示层面就避免无用的工具调用。

**`inputSchema` 使用 Zod v4。** 这让工具参数可以在运行时进行严格校验，同时通过 `z.toJSONSchema()` 自动生成发送给 Anthropic API 的 JSON Schema。Zod 的 `z.strictObject()` 确保模型不会传入未定义的参数。

**`call` 接收 `canUseTool` 回调。** 这是一个极其重要的设计——工具执行过程中可能需要递归地检查子操作的权限。例如 `AgentTool` 在启动子 Agent 时需要检查子 Agent 是否有权使用特定工具。权限检查不是一次性的门禁，而是贯穿执行过程的持续验证。

### 渲染契约：三组方法

`Tool` 接口中定义了一组渲染方法，它们构成了工具在终端 UI 中的完整生命周期表现（详见 2.5 节）：

```
renderToolUseMessage          // 工具被调用时展示
renderToolUseProgressMessage  // 工具执行中展示进度
renderToolResultMessage       // 工具执行完成后展示结果
```

此外还有 `renderToolUseErrorMessage`、`renderToolUseRejectedMessage`（权限被拒）和 `renderGroupedToolUse`（并行工具的分组展示）等可选方法。

---

## 2.2 `buildTool()` 工厂函数与失败关闭默认值

每一个具体工具都不是直接导出一个满足 `Tool` 接口的对象，而是通过 `buildTool()` 工厂函数构建。这个函数定义在 `restored-src/src/Tool.ts:783-792`：

```typescript
export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {
  return {
    ...TOOL_DEFAULTS,
    userFacingName: () => def.name,
    ...def,
  } as BuiltTool<D>
}
```

运行时行为极简——就是一个对象展开（spread）。但它的类型层面设计（`BuiltTool<D>` 类型）精确地模拟了 `{ ...TOOL_DEFAULTS, ...def }` 的语义：如果工具定义提供了某个方法，使用工具定义的版本；否则使用默认值。

### 默认值与"失败关闭"哲学

`TOOL_DEFAULTS`（`restored-src/src/Tool.ts:757-769`）的设计遵循一个安全原则——**在不确定时假设最危险的情况**：

| 默认方法 | 默认值 | 设计意图 |
|---------|--------|---------|
| `isEnabled` | `() => true` | 除非明确禁用，工具默认可用 |
| `isConcurrencySafe` | `() => false` | **失败关闭**：假设不安全，禁止并发 |
| `isReadOnly` | `() => false` | **失败关闭**：假设会写入，需要权限 |
| `isDestructive` | `() => false` | 默认非破坏性 |
| `checkPermissions` | 返回 `{ behavior: 'allow' }` | 交给通用权限系统处理 |
| `toAutoClassifierInput` | `() => ''` | 默认不参与自动安全分类 |
| `userFacingName` | `() => def.name` | 使用工具名称 |

其中最重要的两个默认值是 `isConcurrencySafe: false` 和 `isReadOnly: false`。这意味着：一个新工具如果忘记声明这两个属性，系统会自动将其视为"可能修改文件系统且不能并发执行"——这是最保守、最安全的假设。只有当工具开发者主动声明 `isConcurrencySafe() { return true }` 和 `isReadOnly() { return true }` 时，系统才会放宽限制。

### 实际工具如何使用 `buildTool`

以 `GrepTool` 为例（`restored-src/src/tools/GrepTool/GrepTool.ts:160-194`）：

```typescript
export const GrepTool = buildTool({
  name: GREP_TOOL_NAME,
  searchHint: 'search file contents with regex (ripgrep)',
  maxResultSizeChars: 20_000,
  strict: true,
  // ...
  isConcurrencySafe() { return true },   // 搜索是安全的并发操作
  isReadOnly() { return true },           // 搜索不修改文件
  // ...
})
```

`GrepTool` 明确覆盖了两个默认值，因为搜索操作天然是只读且并发安全的。相比之下，`BashTool`（`restored-src/src/tools/BashTool/BashTool.tsx:434-441`）的并发安全性是有条件的：

```typescript
isConcurrencySafe(input) {
  return this.isReadOnly?.(input) ?? false;
},
isReadOnly(input) {
  const compoundCommandHasCd = commandHasAnyCd(input.command);
  const result = checkReadOnlyConstraints(input, compoundCommandHasCd);
  return result.behavior === 'allow';
},
```

`BashTool` 只有在被判定为只读命令时才允许并发——一个 `git status` 可以并发执行，但 `git push` 不行。这种**输入感知的并发控制**是 `buildTool` 的方法签名接收 `input` 参数的原因。

---

## 2.3 工具注册管线：`tools.ts`

`restored-src/src/tools.ts` 是工具池（Tool Pool）的组装中心。它回答一个核心问题：**在当前环境下，模型可以使用哪些工具？**

### 三级过滤

工具从定义到最终可用，经历三级过滤：

**第一级：编译期/启动期条件加载。** 大量工具通过 Feature Flag 进行条件加载（`restored-src/src/tools.ts:16-135`）：

```typescript
const SleepTool =
  feature('PROACTIVE') || feature('KAIROS')
    ? require('./tools/SleepTool/SleepTool.js').SleepTool
    : null

const cronTools = feature('AGENT_TRIGGERS')
  ? [
      require('./tools/ScheduleCronTool/CronCreateTool.js').CronCreateTool,
      require('./tools/ScheduleCronTool/CronDeleteTool.js').CronDeleteTool,
      require('./tools/ScheduleCronTool/CronListTool.js').CronListTool,
    ]
  : []
```

`feature()` 函数来自 `bun:bundle`，在打包时求值。这意味着未启用的工具**根本不会出现在最终的 JavaScript bundle 中**——这是一种比运行时 `if` 更彻底的死代码消除。

除了 Feature Flag，还有环境变量驱动的条件加载：

```typescript
const REPLTool =
  process.env.USER_TYPE === 'ant'
    ? require('./tools/REPLTool/REPLTool.js').REPLTool
    : null
```

`USER_TYPE === 'ant'` 标记 Anthropic 内部员工使用的特殊工具（如 `REPLTool`、`ConfigTool`、`TungstenTool`），这些工具在公开版本中不可用。

**第二级：`getAllBaseTools()` 组装基础工具池。** 这个函数（`restored-src/src/tools.ts:193-251`）将所有通过第一级过滤的工具收集到一个数组中。它是系统的"工具注册表"——所有可能存在的工具都在这里登记。当前版本包含约 40+ 个内置工具，根据 Feature Flag 的启用情况动态增减。

```typescript
export function getAllBaseTools(): Tools {
  return [
    AgentTool,
    TaskOutputTool,
    BashTool,
    ...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),
    FileReadTool,
    FileEditTool,
    FileWriteTool,
    // ... 省略 30+ 个工具
    ...(isToolSearchEnabledOptimistic() ? [ToolSearchTool] : []),
  ]
}
```

注意一个有趣的条件：`hasEmbeddedSearchTools()`。在 Anthropic 内部构建中，`bfs`（fast find）和 `ugrep` 被嵌入到 Bun 二进制文件中，此时 shell 里的 `find` 和 `grep` 已经被别名到这些快速工具，独立的 `GlobTool` 和 `GrepTool` 就变得多余了。

**第三级：`getTools()` 运行时过滤。** 这是最终的过滤层（`restored-src/src/tools.ts:271-327`），它执行三个操作：

1. **权限拒绝过滤**：通过 `filterToolsByDenyRules()` 移除被 `alwaysDeny` 规则覆盖的工具。如果用户配置了 `"Bash": "deny"`，`BashTool` 在发送给模型的工具列表中根本不会出现。
2. **REPL 模式隐藏**：当 REPL 模式启用时，`Bash`、`Read`、`Edit` 等基础工具被隐藏——它们通过 `REPLTool` 的 VM 上下文间接暴露。
3. **`isEnabled()` 最终检查**：每个工具的 `isEnabled()` 方法是最后一道开关。

### 简单模式与完整模式

`getTools()` 还支持一种"简单模式"（`CLAUDE_CODE_SIMPLE`），只暴露 `Bash`、`FileRead` 和 `FileEdit` 三个核心工具。这在一些集成场景下很有用——减少工具数量可以降低 token 消耗并减少模型的决策负担。

### MCP 工具的融合

最终的工具池由 `assembleToolPool()`（`restored-src/src/tools.ts:345-367`）完成组装：

```typescript
export function assembleToolPool(
  permissionContext: ToolPermissionContext,
  mcpTools: Tools,
): Tools {
  const builtInTools = getTools(permissionContext)
  const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext)
  const byName = (a: Tool, b: Tool) => a.name.localeCompare(b.name)
  return uniqBy(
    [...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
    'name',
  )
}
```

这里有两个关键设计：

1. **内置工具优先**：`uniqBy` 保留第一次出现的名称，内置工具排在前面，因此在名称冲突时内置工具胜出。
2. **按名称排序以稳定提示缓存**：内置工具和 MCP 工具各自排序后拼接（而非混合排序），确保内置工具作为"连续前缀"出现。这与 API 服务端的缓存断点设计协作——如果 MCP 工具穿插在内置工具中间，任何 MCP 工具的增减都会导致所有下游缓存键失效。详见第13章。

---

## 2.4 工具结果大小预算

当一个工具返回结果时，系统面临一个核心矛盾：模型需要看到完整的信息来做出正确决策，但上下文窗口是有限的。Claude Code 通过**两级预算**解决这个问题。

### 第一级：单工具结果上限 `maxResultSizeChars`

每个工具通过 `maxResultSizeChars` 字段声明自己的结果大小上限。超出此上限的结果会被持久化到磁盘，模型只看到一个预览（preview）加上磁盘文件路径。

以下是不同工具的 `maxResultSizeChars` 对比：

| 工具 | `maxResultSizeChars` | 说明 |
|------|---------------------|------|
| `McpAuthTool` | 10,000 | 认证结果，数据量小 |
| `GrepTool` | 20,000 | 搜索结果需要精简 |
| `BashTool` | 30,000 | Shell 输出可能较长 |
| `GlobTool` | 100,000 | 文件列表可能很多 |
| `AgentTool` | 100,000 | 子 Agent 结果 |
| `WebSearchTool` | 100,000 | 网页搜索结果 |
| `BriefTool` | 100,000 | 简要总结 |
| `FileReadTool` | **Infinity** | 永不持久化（见下文） |

`FileReadTool` 的 `maxResultSizeChars: Infinity` 是一个特殊设计——避免 Read → 持久化文件 → Read 的循环引用。系统还有一个全局上限 `DEFAULT_MAX_RESULT_SIZE_CHARS = 50,000`（`restored-src/src/constants/toolLimits.ts:13`），作为无论工具声明什么值都生效的硬顶。

### 第二级：单消息聚合上限

当模型在一个回合中并行调用多个工具时，所有工具结果会作为同一个 user message 的多个 `tool_result` 块发送。`MAX_TOOL_RESULTS_PER_MESSAGE_CHARS = 200,000`（`restored-src/src/constants/toolLimits.ts:49`）限制了单条消息的工具结果总大小，防止 N 个并行工具集体挤爆上下文窗口。

FileReadTool Infinity 设计理由、per-message 预算的持久化实现细节（包括 `ContentReplacementState` 决策持久性和 Infinity 豁免机制）详见第4章。

### 大小预算参数汇总

| 常量 | 值 | 定义位置 |
|------|---|---------|
| `DEFAULT_MAX_RESULT_SIZE_CHARS` | 50,000 字符 | `constants/toolLimits.ts:13` |
| `MAX_TOOL_RESULT_TOKENS` | 100,000 token | `constants/toolLimits.ts:22` |
| `MAX_TOOL_RESULT_BYTES` | 400,000 字节 | `constants/toolLimits.ts:33`（= 100K token x 4 bytes/token） |
| `MAX_TOOL_RESULTS_PER_MESSAGE_CHARS` | 200,000 字符 | `constants/toolLimits.ts:49` |
| `TOOL_SUMMARY_MAX_LENGTH` | 50 字符 | `constants/toolLimits.ts:57` |

---

## 2.5 三阶段渲染流程

工具在终端 UI 中的呈现不是一次性的，而是一个三阶段渐进过程。这三个阶段与工具执行的生命周期一一对应。

### 流程图

```mermaid
flowchart TD
    A["模型发出 tool_use 块<br/>（参数可能尚未流式完毕）"] --> B
    B["阶段 1: renderToolUseMessage<br/>工具被调用，显示名称和参数<br/>参数是 Partial&lt;Input&gt;（流式传输中）"]
    B -->|工具开始执行| C
    C["阶段 2: renderToolUseProgressMessage<br/>执行中，展示进度<br/>通过 onProgress 回调更新"]
    C -->|工具执行完成| D
    D["阶段 3: renderToolResultMessage<br/>完成，展示结果"]
```

### 阶段 1：`renderToolUseMessage` — 意图展示

当模型输出一个 `tool_use` 块时，这个方法立即被调用。注意其签名中的关键类型：

```typescript
renderToolUseMessage(
  input: Partial<z.infer<Input>>,  // 注意是 Partial!
  options: { theme: ThemeName; verbose: boolean; commands?: Command[] },
): React.ReactNode
```

`input` 是 `Partial` 的原因是：API 以流式方式返回工具参数的 JSON，在 JSON 解析完成之前只有部分字段可用。UI 必须在参数不完整时就能渲染——用户不应该看到空白屏幕。

以 `BashTool` 为例，即使 `command` 字段尚未完全接收，UI 已经可以显示 "Bash" 标签和已接收的部分命令文本。

### 阶段 2：`renderToolUseProgressMessage` — 过程可见性

这是一个**可选**方法。对于执行时间较长的工具（如 `BashTool`、`AgentTool`），进度反馈至关重要。`BashTool` 在 shell 命令执行超过 2 秒后开始显示进度（`PROGRESS_THRESHOLD_MS = 2000`，`restored-src/src/tools/BashTool/BashTool.tsx:55`）。

进度通过 `onProgress` 回调传递。每个工具的进度数据结构不同——`BashTool` 的 `BashProgress` 包含 stdout/stderr 片段，`AgentTool` 的 `AgentToolProgress` 包含子 Agent 的消息流。这些类型在 `restored-src/src/types/tools.ts` 中统一定义，通过 `ToolProgressData` 联合类型约束。

### 阶段 3：`renderToolResultMessage` — 结果呈现

这也是一个**可选**方法——省略时工具结果不在终端渲染（例如 `TodoWriteTool` 的结果通过专用面板展示，不出现在对话流中）。

`renderToolResultMessage` 接收 `style?: 'condensed'` 选项。在非 verbose 模式下，搜索类工具（`GrepTool`、`GlobTool`）显示精简摘要（如 "Found 42 files across 3 directories"），而在 verbose 模式下展示完整结果。工具可以通过 `isResultTruncated(output)` 方法告诉 UI 当前结果是否被截断，从而在全屏模式下启用"点击展开"交互。

### 分组渲染：`renderGroupedToolUse`

当模型在一个回合中并行调用多个同类型工具时（例如 5 个 `Grep` 搜索），逐个渲染会占据大量屏幕空间。`renderGroupedToolUse` 方法允许工具将多个并行调用合并为一个紧凑的分组视图——例如 "Searched 5 patterns, found 127 results across 34 files"。

这个方法只在**非 verbose 模式**下生效。verbose 模式下每个工具调用仍然在其原始位置独立渲染，确保调试时信息不丢失。

---

## 2.6 从具体工具看设计模式

### BashTool：最复杂的工具

`BashTool`（`restored-src/src/tools/BashTool/BashTool.tsx`）是整个工具系统中最复杂的单一工具，因为 shell 命令的语义空间是无限的。它需要：

- **解析命令结构**来判断是否只读（通过 `checkReadOnlyConstraints` 和 `parseForSecurity`）
- **感知管道和复合命令**（`ls && echo "---" && ls` 仍然是只读的）
- **条件性并发**：只有只读命令才能并发执行
- **进度追踪**：超过 2 秒的命令显示 stdout 流式输出
- **文件变更追踪**：通过 `fileHistoryTrackEdit` 和 `trackGitOperations` 记录 shell 命令导致的文件修改
- **沙箱执行**：在某些条件下通过 `SandboxManager` 隔离执行

`BashTool` 的 `maxResultSizeChars` 设为 30,000——比 `GrepTool` 的 20,000 宽松，因为 shell 输出通常包含更多结构化信息（编译错误、测试结果等），模型需要看到足够的上下文才能做出正确判断。

### GrepTool：并发安全的典范

`GrepTool` 的设计相对简洁。它无条件声明 `isConcurrencySafe: true` 和 `isReadOnly: true`，因为搜索操作永远不会修改文件系统。它的 `maxResultSizeChars` 设为 20,000——搜索结果超过这个长度说明模型的搜索范围太宽，持久化到磁盘并返回预览反而有助于模型调整策略。

### FileReadTool：`Infinity` 的哲学

`FileReadTool` 将 `maxResultSizeChars` 设为 `Infinity`，选择通过自身的 `maxTokens` 和 `maxSizeBytes` 限制来控制输出大小。这避免了前文提到的循环读取问题，也意味着 `FileReadTool` 的结果永远不会被替换为磁盘引用——模型总是能直接看到文件内容。

---

## 2.7 延迟加载与 ToolSearch

当工具数量超过一定阈值时（尤其是 MCP 工具大量接入后），将所有工具的完整 schema 发送给模型会消耗大量 token。Claude Code 通过**延迟加载**（Deferred Loading）机制解决这个问题。

标记了 `shouldDefer: true` 的工具在初始提示中只发送工具名称（`defer_loading: true`），不发送完整的参数 schema。模型需要先调用 `ToolSearchTool` 按关键词搜索并获取工具的完整定义后，才能调用这些延迟加载的工具。

每个工具的 `searchHint` 字段就是为此设计的——它提供 3-10 个词的能力描述，帮助 `ToolSearchTool` 进行关键词匹配。例如 `GrepTool` 的 `searchHint` 是 `'search file contents with regex (ripgrep)'`。

标记了 `alwaysLoad: true` 的工具则永远不会被延迟——它们的完整 schema 总是出现在初始提示中。这适用于模型在第一轮对话就必须能直接调用的核心工具。

---

## 2.8 模式提炼

从 Claude Code 的工具系统设计中，可以提炼出几个对 AI Agent 构建者普遍有价值的模式：

**模式 1：失败关闭的默认值。** `buildTool()` 的默认值假设最危险的情况（不可并发、非只读），工具开发者必须主动声明安全属性。这将安全从"选择加入"翻转为"选择退出"，大幅降低了遗漏导致的风险。

**模式 2：分层预算控制。** 单工具结果有上限，单消息也有聚合上限。两层控制互相补充——单工具上限防止单点失控，消息上限防止并行调用的集体爆炸。

**模式 3：输入感知的属性。** `isConcurrencySafe(input)` 和 `isReadOnly(input)` 接收工具输入，而非全局判断。同一个 `BashTool`，`ls` 和 `rm` 有完全不同的安全属性。这种细粒度的输入感知是实现精确权限控制的基础。详见第4章。

**模式 4：渐进渲染。** 三阶段渲染（意图 → 进度 → 结果）让用户在工具执行的每个阶段都有可见性。`Partial<Input>` 的设计确保即使在参数流式传输期间，UI 也不会空白。这对用户信任至关重要——用户需要知道 Agent 正在做什么，而不是盯着一个旋转的加载图标。

**模式 5：编译期消除 vs 运行时过滤。** Feature Flag 通过 `bun:bundle` 的 `feature()` 在编译期消除未启用的工具代码，而权限规则在运行时过滤工具列表。两种机制服务不同目的：前者减小 bundle 体积和攻击面，后者支持用户级配置。

---

## 用户能做什么

基于 Claude Code 工具系统的设计经验，以下是构建自己的 AI Agent 工具系统时可以采取的行动：

- **采用"失败关闭"默认值。** 在你的工具注册框架中，将 `isConcurrencySafe`、`isReadOnly` 等安全属性的默认值设为最保守的选项。让工具开发者主动声明安全属性，而非默认假设安全。
- **为每个工具设置结果大小上限。** 不要让工具返回无限大的结果。设置单工具上限（如 `maxResultSizeChars`）和单消息聚合上限，超出时持久化到磁盘并返回预览。
- **让工具描述成为函数而非静态字符串。** 如果你的工具在不同权限模式或上下文下有不同的行为限制，动态生成描述可以在提示层面引导模型避免无效调用。
- **实现三阶段渲染。** 为长时间运行的工具提供进度反馈（意图展示 → 执行进度 → 最终结果），让用户始终知道 Agent 在做什么。支持 `Partial<Input>` 在参数流式传输期间也能渲染。
- **使用条件加载减少工具集。** 通过 Feature Flag 或环境变量在编译期/启动期过滤不需要的工具，减少 token 消耗和模型决策负担。对于大量 MCP 工具场景，考虑延迟加载（deferred loading）机制。
- **工具排序要稳定。** 如果你使用 API 提示缓存，确保工具列表的顺序在请求之间保持稳定。将内置工具作为连续前缀，MCP 工具按名称排序追加，避免缓存键频繁失效。

---

## 小结

Claude Code 的工具系统是一个精心分层的架构：`Tool` 接口定义契约，`buildTool()` 提供安全默认值，`tools.ts` 的注册管线通过编译期和运行时两级过滤组装工具池，大小预算机制在单工具和单消息两个层面控制上下文消耗，三阶段渲染让工具执行过程对用户完全透明。

这套系统的设计哲学可以用一句话总结：**让正确的事情容易，让危险的事情困难。** `buildTool()` 的失败关闭默认值让"忘记声明安全属性"成为一个安全的错误；分层预算让"工具返回过多数据"成为一个可控的降级；条件加载让"添加实验性工具"成为一个零风险的操作。

工具的调用和编排——包括权限检查的完整流程、并发执行的调度策略、流式进度的传播机制——将在第4章详细展开。

---

### 版本演化说明

> 本章核心分析基于 v2.1.88 源码。截至 v2.1.92，本章涉及的工具注册与分类体系无重大结构性变化。具体信号变化见[附录 E](../appendix/e-version-evolution.md)。
