工具执行完整流程
从 API 响应中提取 tool_use 到执行完毕返回结果的端到端链路
职责概述
工具执行是 Claude Code 的核心能力通道。当模型返回 tool_use block 后,系统需要完成工具匹配、参数校验、权限检查、Hook 前置/后置处理、并发控制、结果处理等一系列步骤。本页完整追踪这一端到端流程,涵盖正常路径、错误路径、重试逻辑和并发安全机制。
架构设计
工具执行涉及五个层级,自上而下协作完成一次调用:
核心数据流
以下是工具执行的完整流程,包含决策点和错误路径:
tool_use ContentBlock。query.ts 主循环提取所有 tool_use blocks。findToolByName(tools, name) 在注册表中查找。支持别名匹配(tool.aliases),未找到则返回错误消息。tool.isConcurrencySafe(input)。并发不安全的工具(如 FileEdit)在 plan 模式下串行执行;安全的工具(如 Read、Glob)可并行执行。canUseTool(tool, input, context) 检查 ToolPermissionContext。结果为 allow → 继续;deny → 返回错误;ask → 弹出用户确认对话框。command(子进程)、http(HTTP POST)、prompt(LLM 单次调用)、agent(多轮 LLM)。exit_code=2 阻止执行。tool.call(args, context, canUseTool, parentMessage, onProgress)。执行过程中通过 ToolCallProgress 回调报告进度。支持 AbortSignal 取消。maxResultSizeChars 限制检查。超大结果持久化到磁盘(diskOutput),仅向模型返回摘要预览。正常结果直接作为 tool_result 返回。错误与重试路径
{"retry": true} 让模型重试。关键类型与接口
Tool 核心接口
// Tool.ts:362 — 工具类型定义
export type Tool<
Input extends AnyObject = AnyObject,
Output = unknown,
P extends ToolProgressData = ToolProgressData,
> = {
readonly name: string
aliases?: string[]
call(
args: z.infer<Input>,
context: ToolUseContext,
canUseTool: CanUseToolFn,
parentMessage: AssistantMessage,
onProgress?: ToolCallProgress<P>,
): Promise<ToolResult<Output>>
isConcurrencySafe(input: z.infer<Input>): boolean
isReadOnly(input: z.infer<Input>): boolean
isDestructive?(input: z.infer<Input>): boolean
maxResultSizeChars: number
interruptBehavior?(): 'cancel' | 'block'
}
工具匹配函数
// Tool.ts:348-360 — 工具查找
export function toolMatchesName(
tool: Tool, name: string,
): boolean {
return tool.name === name ||
tool.aliases?.includes(name) === true
}
export function findToolByName(
tools: Tools, name: string,
): Tool | undefined {
return tools.find(t => toolMatchesName(t, name))
}
ToolUseContext 执行上下文
// Tool.ts:158-300 — 工具执行上下文
export type ToolUseContext = {
options: {
tools: Tools
mainLoopModel: string
isNonInteractiveSession: boolean
thinkingConfig: ThinkingConfig
mcpClients: Map<string, MCPServerConnection>
// ...更多选项
}
agentId?: string
abortController: AbortController
setAppState: (updater: (prev: AppState) => AppState) => void
getAppState: () => AppState
readFileState: FileStateCache
contentReplacementState?: ContentReplacementState
setResponseLength: (updater: (prev: number) => number) => void
// ...更多字段
}
Hook 结果类型
// hooks.ts — Hook 执行结果
export type HookResult = {
hook: HookCommand
outcome: 'success' | 'blocking' | 'cancelled'
| 'non_blocking_error'
message?: AttachmentMessage
blockingError?: { blockingError: string; command: string }
preventContinuation?: boolean
stopReason?: string
}
Hook 事件元数据(部分)
// hookEvents.ts:27-99 — Hook 事件定义
PreToolUse: {
summary: 'Before tool execution',
matcherMetadata: {
fieldToMatch: 'tool_name',
values: toolNames, // 动态填充所有注册工具名
},
},
PostToolUse: {
summary: 'After tool execution',
matcherMetadata: { fieldToMatch: 'tool_name', values: toolNames },
},
PostToolUseFailure: {
summary: 'After tool execution fails',
matcherMetadata: { fieldToMatch: 'tool_name', values: toolNames },
},
PermissionDenied: {
summary: 'After auto mode classifier denies a tool call',
// 可返回 {"retry": true} 让模型重试
},
PermissionRequest: {
summary: 'When a permission dialog is displayed',
// 可返回 decision: allow/deny 覆盖用户选择
}
设计模式与亮点
1. 四种 Hook 执行策略
Hook 系统支持四种执行模式,每种适用于不同场景:
- command — 子进程执行,支持超时和流式输出
- http — HTTP POST,支持 SSRF 防护、环境变量插值、sandbox 代理
- prompt — 单轮 LLM 调用(Haiku 模型),返回结构化 JSON
- agent — 多轮 LLM 查询,支持工具调用,最多 50 轮
// 四种 Hook 类型
type HookCommand =
| { type: 'command'; command: string; timeout?: number }
| { type: 'http'; url: string; headers?: Record<string, string> }
| { type: 'prompt'; prompt: string; model?: string }
| { type: 'agent'; prompt: string; model?: string; timeout?: number }
2. 异步 Hook 注册表
AsyncHookRegistry 管理长时间运行的 Hook 进程。Hook 可返回 {"async": true} 进入异步模式,系统每 500ms 轮询 stdout 检查 JSON 响应。最多缓存 100 个待处理事件。
3. 结果持久化策略
工具输出超过 maxResultSizeChars 时自动持久化到磁盘,模型仅收到预览摘要。Read 工具设为 Infinity 避免循环(Read 文件 → 持久化 → 又要 Read 持久化文件)。
4. 安全分层防护
HTTP Hook 执行经过多层安全检查:
- URL 白名单(
allowedHttpHookUrls通配符匹配) - 环境变量白名单(防止密钥泄露)
- SSRF 防护(
ssrfGuardedLookup屏蔽私有 IP) - Header 注入防护(剥离 CR/LF/NUL 字节)
- Sandbox 代理强制域名白名单
5. 工具并发控制
系统根据 isConcurrencySafe() 将工具分为两类:
- 安全 — Read, Glob, Grep, WebSearch 可并行
- 不安全 — FileEdit, FileWrite, Bash 串行执行
这防止了文件写入冲突,同时最大化只读操作的吞吐量。
6. 工具延迟加载
通过 shouldDefer 标记,工具 schema 可延迟发送。模型需要先调用 ToolSearch 工具获取完整 schema,减少首次 prompt 的 token 消耗。alwaysLoad 标记确保关键工具始终可见。
开发者实践指南
如何新增一个工具
- 在
tools/下创建目录,如tools/MyTool/MyTool.ts - 使用
buildTool()工厂函数定义工具,自动填充默认实现 - 实现
call()、description()、inputSchema三个必填字段 - 在
tools.ts中注册到工具列表 - 如需自定义权限,实现
isReadOnly()和isConcurrencySafe()
// buildTool 工厂函数 — 自动填充默认值
export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {
return { ...TOOL_DEFAULTS, ...def }
}
// 默认值
const TOOL_DEFAULTS = {
isConcurrencySafe: () => false, // 默认不安全
isReadOnly: () => false, // 默认非只读
isEnabled: () => true, // 默认启用
maxResultSizeChars: 50000, // 50K 字符上限
}
Hook 调试技巧
- 所有 Hook 执行日志写入
logForDebugging,开启 verbose 模式可查看 - PreToolUse exit_code=2 是标准"阻止"语义,不会被当作错误
- Agent Hook 最多 50 轮,超时默认 60 秒,均可通过
timeout字段调整
架构师决策指南
设计权衡
Hook 同步 vs 异步:PreToolUse Hook 默认同步执行,会阻塞工具调用。对于耗时操作(如网络请求),可返回 {"async": true} 异步处理,但需要轮询机制增加复杂度。
结果截断策略:maxResultSizeChars 默认 50K,平衡了上下文窗口利用率和信息完整性。Read 设为 Infinity 是因为其自身已有行数限制,持久化会导致循环读取。
权限模型选择:PermissionRequest Hook 允许覆盖用户选择,适合企业场景的自动化审批,但也带来安全风险。需要配合 allowedHttpHookUrls 白名单使用。
扩展性考量
- 工具注册表是静态数组,运行时不可动态添加(除了 MCP 工具)
- Hook 按 matcher 分组查找,
sortMatchersByPriority确保高优先级 Hook 先执行 - 并发工具数无硬性上限,受 API 并行 tool_use 数量限制
- Hook 执行超时独立于工具执行超时,两者通过
createCombinedAbortSignal组合
性能关键路径
- 工具匹配:O(n) 线性扫描,n 为工具数量(~40),可接受
- Hook 查找:memoized 按 toolNames 排序拼接缓存,避免重复计算
- 结果持久化:异步写入,不阻塞主循环
代码索引
| 文件 | 行数 | 说明 |
|---|---|---|
Tool.ts | ~793 | Tool 接口定义、findToolByName、buildTool 工厂 |
tools.ts | ~200 | 工具注册表、ALL_AGENT_DISALLOWED_TOOLS |
query.ts | ~800 | 主查询循环、tool_use 分发 |
QueryEngine.ts | ~300 | 查询引擎、compact、contextCollapse |
utils/hooks/hookEvents.ts | ~400 | Hook 事件元数据、分组查找 |
utils/hooks/execHttpHook.ts | ~243 | HTTP Hook 执行、SSRF 防护 |
utils/hooks/execAgentHook.ts | ~340 | Agent Hook 多轮执行 |
utils/hooks/execPromptHook.ts | ~212 | Prompt Hook 单轮 LLM 调用 |
utils/hooks/AsyncHookRegistry.ts | ~310 | 异步 Hook 注册表、轮询检查 |
utils/hooks/hooksSettings.ts | ~200 | Hook 配置解析、优先级排序 |
utils/permissions/permissions.ts | ~300 | 权限判定、hasPermissionsToUseTool |
utils/toolResultStorage.ts | ~150 | 工具结果持久化、预览生成 |
utils/task/diskOutput.ts | ~100 | 磁盘输出管理、evictTaskOutput |