Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions docs/09-工具系统设计.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# 第 9 篇:工具系统设计 — buildTool() 的抽象之美

> 本篇是《深入 Claude Code 源码》系列的第 9 篇。我们将深入工具系统的核心设计:从 `Tool` 接口的核心方法,到 `buildTool()` 的 builder 模式,到 `tools.ts` 的注册表架构,再到 ToolSearch 的延迟加载机制,揭示一个生产级 AI Agent 如何管理 40+ 个内置工具和无限数量的 MCP 工具。
> 本篇是《深入 Claude Code 源码》系列的第 9 篇。我们将深入工具系统的核心设计:从 `Tool` 接口的核心方法,到 `buildTool()` 的 builder 模式,到 `tools.ts` 的注册表架构,再到 ToolSearch 的延迟加载机制,揭示一个生产级 AI Agent 如何管理一整族内置工具(具体清单与分类见 [附录 A](./appendix/A.md))和无限数量的 MCP 工具。

## 为什么工具系统是 Agent 的灵魂?

一个 AI Agent 与普通 chatbot 的本质区别在于:**Agent 能执行动作**。当模型决定读取一个文件、运行一条 Shell 命令、或搜索代码时,它依赖的就是工具系统。

Claude Code 拥有 40+ 个内置工具(BashTool、FileReadTool、GlobTool……)和通过 MCP 协议接入的无限数量外部工具。管理这样规模的工具集面临几个核心挑战:
Claude Code 内置工具的真实集合不是一个固定数字,而是「`tools/` 下作为 family 的顶层目录」「`tools.ts` 默认 register 的运行期 leaf」「受 `feature(...)` / 环境变量条件装载的 feature-gated」三列模型 —— 完整清单见 [附录 A · 工具速查表](./appendix/A.md)。再叠加 MCP 协议接入的、动态规模的外部工具,管理这样规模的工具集面临几个核心挑战:

1. **接口一致性**:每个工具需要统一的调用、验证、权限检查、UI 渲染协议
2. **安全默认**:工具涉及文件系统和 Shell 操作,默认行为必须 fail-closed
Expand Down Expand Up @@ -432,6 +432,13 @@ const getTeamCreateTool = () =>
1. **函数包装** `require()` —— 延迟执行,避免模块加载时的循环
2. **`as typeof import(...)`** —— 保留完整类型信息,不丢失类型安全

### 3.5 跨工具复用的薄基座:`tools/utils.ts` 与 `tools/shared/`

注册表之外,`tools/` 目录还有两处不属于任何单一工具、但被多个工具共用的基座:

- **`tools/utils.ts`**(40 行 · `tools/utils.ts:12-24`)只暴露两个函数 `tagMessagesWithToolUseID` 与 `getToolUseIDFromParentMessage`。前者遍历传入的消息数组,仅对 `m.type === 'user'` 的条目附上 `sourceToolUseID`,让 "X is running" 这类瞬态 user 消息在工具结束后自动消失(attachment 与 system 类型原样透传,不被打标);后者用来从 assistant 消息里反查特定工具的 `tool_use.id`。两件事都不属于哪个具体工具,所以从工具实现里拎出来落在这里。
- **`tools/shared/`** 当前只有两个文件:`gitOperationTracking.ts`(277 行)与 `spawnMultiAgent.ts`。`detectGitOperation`(`tools/shared/gitOperationTracking.ts:135-186`)是一个纯解析函数:它扫描 BashTool / PowerShellTool 跑过的命令字符串与 stdout/stderr,识别 `git commit` / `git push` / `git merge` / `git rebase` / `gh pr create|edit|merge|comment|close|ready` 等动作,把命中结果组装成 `{ commit, push, branch, pr }` 对象返回,本身不发任何事件、也不动计数器。真正的事件发射与 OTLP counter 更新发生在另一个函数 `trackGitOperations`(`gitOperationTracking.ts:189-277`):它在命令成功(`exitCode === 0`)时根据正则命中分别 `logEvent('tengu_git_operation', ...)` 并对 `getCommitCounter()` / `getPrCounter()` 做 `add(1)`;额外的,`gh pr create` 成功还会动态 import `linkSessionToPR` 把当前 session 与新 PR 绑定,`glab mr create` 与 `curl POST <pulls|pull-requests|merge[-_]requests>` 端点的 PR 创建也被识别为 `pr_create` 并计数。这套识别因为操作的是裸命令字符串,对 Bash 与 PowerShell 是同一份正则(`gitOperationTracking.ts:1-9` 注释明示该设计意图),所以放在 `shared/` 而不是任一 shell 自己的目录里。

---

## 四、工具执行编排:并发安全分区
Expand Down Expand Up @@ -698,7 +705,9 @@ tools/
│ ├── UI.tsx
│ ├── limits.ts # 读取限制配置
│ └── imageProcessor.ts
└── ...(40+ 个工具目录)
├── shared/ # 跨工具复用的子模块(gitOperationTracking、spawnMultiAgent)
├── utils.ts # 工具间共享的 tagMessagesWithToolUseID / getToolUseIDFromParentMessage
└── ...(其余各工具目录见附录 A)
```

这种组织方式的核心原则:
Expand Down
Loading