阶段 5 | 工具调用系统¶
目标:理解 Claude Code 的"工具"抽象 —— LLM 怎么"调用工具"、前端怎么渲染、用户怎么授权、结果怎么回到对话流。 时长:1~2 天 前端类比:表单 + 异步 action + 权限 modal —— 把"用户提交 → 异步执行 → 结果回填"完整循环做成可视化系统。
5.1 全景:43 个工具¶
src/tools/ 共 43 个工具子目录
├── AgentTool/ 启动子 agent
├── AskUserQuestionTool/ 多选题询问
├── BashTool/ 跑 shell 命令(16 个文件)
├── BriefTool/ 简短摘要模式
├── ConfigTool/ 改运行时配置
├── EnterPlanModeTool / EnterWorktreeTool
├── ExitPlanModeTool / ExitWorktreeTool
├── FileEditTool/ 编辑文件(7 个文件)
├── FileReadTool / FileWriteTool
├── GlobTool / GrepTool 文件查找 + 内容搜索
├── LSPTool LSP 集成
├── ListMcpResourcesTool / McpAuthTool / MCPTool / ReadMcpResourceTool (MCP)
├── NotebookEditTool Jupyter 笔记本
├── PowerShellTool Windows 专用
├── RemoteTriggerTool 远程触发(Ant-only)
├── REPLTool REPL 内部工具(Ant-only)
├── ScheduleCronTool/ 定时任务(3 个:CronCreate/Delete/List)
├── SendMessageTool swarm 队友消息
├── SkillTool 加载 skill
├── SleepTool PROACTIVE/KAIROS 模式
├── SyntheticOutputTool 合成输出
├── TaskCreateTool / TaskGetTool / TaskListTool / TaskOutputTool / TaskStopTool / TaskUpdateTool
├── TeamCreateTool / TeamDeleteTool swarm 队伍管理
├── TodoWriteTool todo 列表
├── ToolSearchTool 工具检索
├── WebFetchTool / WebSearchTool 网络
├── shared/ 工具间共享代码
├── testing/ 测试用
└── utils.ts 通用工具
每个工具都是独立子目录,结构高度一致:
<BashTool>/
├── BashTool.tsx 工具实现(核心)
├── UI.tsx 渲染组件(用户看到的)
├── prompt.ts 工具描述(喂给 LLM 的)
├── utils.ts 工具私有工具函数
├── <special> (如 bashSecurity.ts、pathValidation.ts...)
└── 常量 / types / 等
5.2 核心抽象:src/Tool.ts¶
792 行 —— 整个工具系统的基础 导出:14 个类型 + 4 个工具函数 + 1 个工厂函数
5.2.1 关键类型¶
// src/Tool.ts:362
export type Tool<Name extends string = string, Input = any> = {
name: Name
description: string // 给 LLM 看的工具描述
inputSchema: AnyObject // zod schema,校验输入
// ... 渲染、执行、权限等字段
}
// src/Tool.ts:701
export type Tools = readonly Tool[]
// src/Tool.ts:158
export type ToolUseContext = {
// 工具执行时的上下文(abortController、state、permissions...)
}
// src/Tool.ts:123
export type ToolPermissionContext = DeepImmutable<{
// 当前权限规则(allow/deny/ask)
}>
// src/Tool.ts:321
export type ToolResult<T> = {
content: T
is_error?: boolean
}
// src/Tool.ts:95
export type ValidationResult = {
result: true | { errorMessage: string }
}
5.2.2 工厂函数 buildTool¶
// src/Tool.ts:783
export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {
// ToolDef → BuiltTool 转换器
// 补全默认值、注入 ctx 类型、绑定元数据
}
前端类比:和 React 的 forwardRef / memo 是同种"工厂 + 增强"模式。
5.2.3 工具查询¶
// src/Tool.ts:348
export function toolMatchesName(tool: Tool, name: string): boolean
export function findToolByName(tools: Tools, name: string): Tool | undefined
前端类比:Redux 的 selectTodoById、React Router 的 matchPath。
5.3 工具注册表:src/tools.ts¶
389 行 —— 43 个工具的"中央集线器"
5.3.1 注册模式¶
// src/tools.ts 头部
import { AgentTool } from './tools/AgentTool/AgentTool.js'
import { SkillTool } from './tools/SkillTool/SkillTool.js'
import { BashTool } from './tools/BashTool/BashTool.js'
import { FileEditTool } from './tools/FileEditTool/FileEditTool.js'
// ... 43 个静态 import
// DCE 控制
const REPLTool = process.env.USER_TYPE === 'ant'
? require('./tools/REPLTool/REPLTool.js').REPLTool
: null
const SleepTool = feature('PROACTIVE') || feature('KAIROS')
? require('./tools/SleepTool/SleepTool.js').SleepTool
: null
const cronTools = feature('AGENT_TRIGGERS')
? [CronCreateTool, CronDeleteTool, CronListTool]
: []
5.3.2 预设(preset)¶
// src/tools.ts:161
export const TOOL_PRESETS = ['default'] as const
export type ToolPreset = (typeof TOOL_PRESETS)[number]
export function parseToolPreset(preset: string): ToolPreset | null { ... }
export function getToolsForDefaultPreset(): string[] { ... }
export function getAllBaseTools(): Tools { ... }
关键洞察:用户可以选择预设('default'),未来可能加 'minimal'、'web-only' 等。这是 Web 项目的"feature flag"思想在工具粒度的应用。
5.3.3 权限过滤¶
// src/tools.ts:262
export function filterToolsByDenyRules<T extends Tools>(tools: T, rules: ...): T
// src/tools.ts:271
export const getTools = (permissionContext: ToolPermissionContext): Tools => {
// 1. 拿所有基础工具
// 2. 跑权限 deny rules 过滤
// 3. 按用户/项目配置加/减
// 4. 返回 readonly Tools
}
前端类比:和 React Router 的"基于权限的路由表"是同种模式。
5.4 工具全生命周期¶
LLM 返回 tool_use block
↓
query.ts 解析 tool_use,findToolByName(tools, name)
↓
ToolUseContext 构造(abortController / getAppState / ...)
↓
Permission check: shouldRunInSandbox? / needsUserApproval?
↓
[需要用户批准] → PermissionRequest UI → 用户点 Yes/No
↓
[拒绝] → 构造 denial ToolResult,注入回 messages
[批准] → 继续
↓
tool.call(input, ctx) → 异步执行
↓
执行中可能发出 ToolProgress("BashTool 正在跑 5 秒")
↓
执行完毕 → ToolResult
↓
[如果需要] UI 渲染 tool_result(如 FileEditToolResultMessage)
↓
toolResult 注入回 messages,进入下一轮 LLM 调用
5.5 深读:FileEditTool/¶
完整路径:
src/tools/FileEditTool/7 个文件:constants.ts,FileEditTool.ts,prompt.ts,types.ts,UI.tsx,utils.ts
5.5.1 目录结构 = 完整工具定义¶
constants.ts 工具相关的常量(默认行数限制、文件后缀白名单...)
FileEditTool.ts 工具实现(execute / validate / 渲染)
prompt.ts 喂给 LLM 的工具描述("Use this tool to edit files...")
types.ts 工具私有类型
UI.tsx 渲染组件(用户看到的样子 + 用户交互)
utils.ts 工具私有工具函数
💡 这就是"工具"的完整定义:实现 + 描述 + UI + 校验 + 常量。
前端类比:一个 React 表单组件 =index.tsx (UI) + validation.ts + types.ts + constants.ts。
5.5.2 FileEditTool.ts 头部揭示的依赖¶
观察 FileEditTool.ts 头部 import:
| 导入 | 作用 |
|---|---|
analytics/growthbook.js |
特性开关(编辑行为是否要 LSP 联动) |
services/diagnosticTracking.ts |
LSP 诊断上报 |
services/lsp/manager.js |
LSP 服务器管理 |
skills/loadSkillsDir.js |
编辑后激活 conditional skills |
Tool.js 的 buildTool + ToolUseContext |
工具基类 |
utils/diff.js 的 countLinesChanged |
计算 diff 行数 |
utils/file.js 的读/写文件原语 |
实际 IO |
utils/fileHistory.js |
文件版本历史(撤销/回看) |
utils/gitDiff.js 的 fetchSingleFileGitDiff |
取 git 历史 diff |
utils/permissions/filesystem.js |
文件写权限检查 |
utils/permissions/PermissionResult.js |
权限决策类型 |
关键洞察:一个"编辑文件"的工具,背后串联了 20+ 业务模块 —— analytics、LSP、skills、diff、权限、文件历史。这是工具系统的"杠杆点" —— 加一个工具就能复用全部基础设施。
5.5.3 UI.tsx —— 用户看到的工具调用¶
// 推测结构(基于其他 UI 组件模式)
function FileEditToolUI({ input, result, onApprove, onDeny }) {
return (
<Box flexDirection="column">
<FileEditToolDiff input={input} dim={!result} />
<KeyboardShortcutHint keys="y" label="Approve" />
<KeyboardShortcutHint keys="n" label="Deny" />
</Box>
);
}
前端类比:和 shadcn 的"确认对话框"+"diff 视图"是同种 UX 模式。
5.6 深读:BashTool/¶
完整路径:
src/tools/BashTool/16 个文件 —— 因为 bash 涉及 安全敏感,所以拆得最细
5.6.1 安全子模块¶
| 文件 | 职责 |
|---|---|
BashTool.tsx |
主实现 |
UI.tsx |
渲染(含命令高亮、警告) |
prompt.ts |
LLM 描述(含安全约束) |
bashCommandHelpers.ts |
命令解析、tokenize |
bashPermissions.ts |
bash 权限规则(glob 模式) |
bashSecurity.ts |
安全检查(危险命令检测) |
commandSemantics.ts |
命令语义分析(cd / export / 副作用) |
commentLabel.ts |
命令注释提取 |
destructiveCommandWarning.ts |
rm -rf / 等破坏性命令警告 |
modeValidation.ts |
BashMode 校验 |
pathValidation.ts |
路径校验(防越权) |
readOnlyValidation.ts |
只读模式校验 |
sedEditParser.ts |
sed 命令解析 |
sedValidation.ts |
sed 安全性校验 |
shouldUseSandbox.ts |
是否走沙箱 |
关键洞察 1:BashTool 的复杂度是 Claude Code 安全模型的体现。
关键洞察 2:"安全检查"和"工具实现"分离 —— bashSecurity.ts 是纯函数,好测试、好审计。
关键洞察 3:沙箱策略(shouldUseSandbox.ts)作为独立模块 —— 未来切换到 macOS Seatbelt / Linux bubblewrap 都不影响 BashTool 主实现。
💡 学习价值:BashTool 的目录结构 = 一个安全敏感型工具的"完整设计模式"。其他工具(FileEditTool、FileWriteTool)会借鉴这个模式。
5.7 工具权限系统¶
核心路径:
src/utils/permissions/
5.7.1 三种模式¶
| 模式 | 行为 | 适用 |
|---|---|---|
default |
每次危险操作前询问 | 默认 |
acceptEdits |
文件编辑类自动通过 | 编辑文件频繁时 |
bypassPermissions |
全部自动通过 | 受信任环境 |
plan |
工具不执行,只生成 plan | Plan Mode |
5.7.2 权限决策流¶
Tool 想执行
↓
utils/permissions/PermissionResult.ts: matchingRuleForInput
↓
匹配到规则:
- allow → 直接执行
- deny → 拒绝,注入 denial ToolResult
- ask → 弹 PermissionRequest UI
↓
[用户决策] → 更新 DenialTrackingState(避免重复问)
↓
执行
关键文件:
- src/utils/permissions/PermissionMode.ts —— 模式定义
- src/utils/permissions/PermissionResult.ts —— 决策类型
- src/utils/permissions/denialTracking.ts —— 拒绝追踪(避免重复问)
- src/utils/permissions/shellRuleMatching.ts —— 通配符匹配
5.8 工具进度反馈¶
类型:
src/Tool.ts:307的ToolProgress<P>场景:BashTool 跑 30 秒、FileEditTool 改大文件、AgentTool 启动子 agent 跑几分钟
// src/Tool.ts 推测
export type ToolProgress<P extends ToolProgressData> = {
toolUseID: string
data: P // 进度数据
}
// src/Tool.ts 推测
export type ToolProgressData =
| BashProgress // "Running for 5s, 3 lines of stdout"
| MCPProgress // "MCP server X responding"
| REPLToolProgress
| TaskOutputProgress
| AgentToolProgress
| WebSearchProgress
| SkillToolProgress
前端类比:和 WebSocket 的"服务端推送中间状态"是同种模式。
5.9 子 Agent 工具:AgentTool/¶
特殊工具:它不是"做一件事",而是启动一个完整的子 agent
// src/tools/AgentTool/ 推测导出
export const AgentTool = buildTool({
name: 'Agent',
description: 'Launch a new agent...',
inputSchema: z.object({
prompt: z.string(),
subagent_type: z.string(),
}),
async *call(input, context) {
// 启动子进程跑 agent
yield { type: 'progress', data: { ... } }
// 收集输出
yield { type: 'result', data: { ... } }
}
})
关键洞察:Tool 是异步生成器(async *call)——yield 多次发送进度,最后 yield result。这是个比 Promise 更强大的模式:
- 多次 yield(进度 + 中间结果 + 最终结果)
- 调用方可以在任意 yield 点 cancel
- 自然支持流式输出
前端类比:和 React Suspense 的 "throw promise" 是同种"lazy evaluation"思路。
5.10 工具与 LLM 通信¶
完整路径:
src/services/api/claude.ts+src/services/api/messages.ts
5.10.1 工具描述喂给 LLM¶
prompt.ts 里的内容会被拼到 system prompt 里:
You have access to the following tools:
- Bash: Run shell commands...
- FileEdit: Edit files by providing old_string + new_string...
- Agent: Launch a subagent...
...
Claude API 用 tools 字段(zod schema)让 LLM 输出 tool_use block。
5.10.2 工具结果回传¶
// 工具结果转成 Anthropic SDK 格式
const toolResultBlock: ToolResultBlockParam = {
type: 'tool_result',
tool_use_id: toolUse.id,
content: result.content, // string or array
is_error: result.is_error,
}
5.11 关键洞察¶
5.11.1 工具即"业务领域"¶
43 个工具 = 43 个业务能力。每个工具目录是业务能力的封装:实现 + 描述 + UI + 校验 + 权限。
前端类比:微前端(micro-frontend)每个子应用是个完整业务域。Claude Code 工具就是 TUI 的"微前端"。
5.11.2 安全敏感工具"拆得最细"¶
BashTool 16 个文件是因为它涉及安全模型。bashSecurity.ts / pathValidation.ts / destructiveCommandWarning.ts 各自独立。
这是"高安全代码"的组织范式 —— 把安全相关代码提到顶层模块,方便审计和测试。
5.11.3 工具的"异步生成器"是精髓¶
async *call() + yield 多次让 Claude Code 能:
- 显示进度
- 中途取消
- 流式输出结果
- 区分中间状态 vs 最终结果
比 Promise 强大。React 的 React Server Components 也是同样思想(streaming + 多次 yield)。
5.11.4 prompt.ts 是 LLM 工具描述的事实标准¶
每个工具独立写自己的 prompt,意味着: - LLM 拿到的工具描述经过专门优化("Use this tool when X, not Y") - 改一个工具的描述不会影响其他 - A/B 测试工具描述 容易("换 prompt,看 LLM 选不选")
5.12 阅读清单¶
- ✅
src/Tool.ts(792 行)—— 重点读 buildTool、Tool、ToolUseContext、findToolByName - ✅
src/tools.ts(389 行)—— 重点读 getTools、filterToolsByDenyRules、TOOL_PRESETS - ✅
src/tools/FileEditTool/FileEditTool.ts(看头部 import + buildTool 入口)—— 工具定义模板 - 🔍
src/tools/BashTool/bashSecurity.ts—— 安全检查实现 - 🔍
src/tools/BashTool/UI.tsx—— bash 命令的渲染(含危险警告) - 📌
src/tools/AgentTool/AgentTool.tsx(Ant-only?)—— 异步生成器模式 - 📌
src/utils/permissions/PermissionResult.ts+shellRuleMatching.ts—— 权限规则 - 📌
src/utils/permissions/denialTracking.ts—— 拒绝追踪
5.13 练习任务¶
- 手写 buildTool 的 TypeScript 类型 —— 支持 name / description / inputSchema(zod) / call(input, ctx) / UI / validate 字段
- 设计一个新工具
GitCommitTool:调用方式git commit -m "...",UI 渲染 commit message,要求用户输入 commit message 字符串。画出目录结构 - 分析 BashTool 的安全检查链 —— 从
BashTool.tsx的 call() 入口跟到bashSecurity.ts哪些函数被调,画出流程图 - 思考:如果让你把"工具结果"做 Web 版(前端调 LLM API 拿 tool_use,本地执行 tool,结果回传),你会用什么抽象?为什么 Claude Code 的
async *call + yield模式比 Promise 好?
5.14 下一步¶
进入 阶段 6:Agent 循环 + 流式 API —— query.ts、QueryEngine.ts 是怎么把"LLM 流式响应 → 工具调用 → 结果回传 → 下一轮"做成死循环的。