Claude Code 工具调用机制实战指南
内容导航
- 1. 系统概述与核心流程
- 2. 工具定义体系详解
- 3. 工具集合的组装与过滤
- 4. 工具 Schema 转换与 API 请求构建
- 5. 模型使用工具的引导机制
- 6. 工具调用的执行与编排
- 7. 工具调用失败的兜底处理
- 8. 完整链路图示
1. 系统概述与核心流程
Claude Code 工具调用机制的核心是一条完整动作链:定义工具 → 注册中心 → Schema 序列化 → 模型接收 → 自主决策 → 解析执行 → 结果回调。每个环节紧密耦合,确保模型能高效调用各类工具完成任务。
整个流程五个关键点:
- 每次 API 请求时,所有工具(内置或 MCP 接入)均通过
tools参数统一提交给模型。 - System Prompt 内置工具“操作手册”,明确场景与工具的对应关系。
tool_choice设置为undefined,将调用决策权完全交予模型。- 模型输出
tool_useblock 后,客户端立即执行并封装结果为tool_result返回。 - 系统不实现自动重试。失败时,将错误信息打包为
is_error: true的tool_result,由模型自行决策补错逻辑。
2. 工具定义体系详解
2.1 Tool 接口定义
文件位置:src/Tool.ts:366-695
每个工具必须实现 Tool 接口。核心字段与方法结构如下:
type Tool name: string // 工具唯一标识
aliases?: string[] // 向后兼容别名
searchHint?: string // 搜索提示关键词
// 核心方法
call(args, context, canUseTool, ...): Promise>
prompt(options): Promise // 返回完整使用说明(作为 API description)
description(input, options): Promise // 概要描述
// Schema
readonly inputSchema: Input // Zod 参数结构定义
readonly inputJSONSchema?: ToolInputJSONSchema // MCP 工具提供的 JSON Schema
// 执行控制
isConcurrencySafe(input): boolean // 是否支持并行执行
isReadOnly(input): boolean // 是否只读操作
isEnabled(): boolean // 当前启用状态
// 安全与校验
validateInput?(input, context): Promise
checkPermissions(input, context): Promise
// 结果处理
maxResultSizeChars: number // 结果长度上限
mapToolResultToToolResultBlockParam(content, toolUseID): ToolResultBlockParam
renderToolResultMessage?(...): React.ReactNode
renderToolUseMessage?(...): React.ReactNode
// 延迟加载
readonly shouldDefer?: boolean // 是否延迟加载(ToolSearch 相关)
readonly alwaysLoad?: boolean // 是否始终加载
readonly strict?: boolean // 严格模式
} 2.2 buildTool 工厂函数
文件位置:src/Tool.ts:757-792
使用 buildTool() 快速创建工具实例,自动填充默认配置:
const TOOL_DEFAULTS = {
isEnabled: () => true,
isConcurrencySafe: () => false, // 默认串行执行
isReadOnly: () => false,
isDestructive: () => false,
checkPermissions: () => ({ behavior: 'allow', updatedInput: input }),
toAutoClassifierInput: () => '',
userFacingName: () => name,
}2.3 工具的 prompt() 方法——使用说明
每个工具的 prompt() 方法返回详细的使用说明书,该说明会被注入 API 的 description 字段。模型对工具的正确理解完全依赖这份文档。
以 BashTool 为例,其 prompt(位于 src/tools/BashTool/prompt.ts)涵盖:
- 工具基本能力描述
- 适用场景边界(有专用工具时优先使用专用工具)
- 命令编写规范与超时限制
- Git 操作安全协议
- Sandbox 配置与覆盖规则
- 核心逻辑:Bash 作为万能后备工具,但遇到 Read/Edit/Write 等专用工具时,必须优先使用它们。
BashTool 是 Claude Code 中调用频率最高的工具之一,其自管理逻辑值得深入阅读源码。
3. 工具集合的组装与过滤
3.1 内置工具注册入口
文件位置:src/tools.ts:193-251
注册函数结构如下:
export function getAllBaseTools(): Tools {
return [
AgentTool,
TaskOutputTool,
BashTool,
...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),
ExitPlanModeV2Tool,
FileReadTool,
FileEditTool,
FileWriteTool,
NotebookEditTool,
WebFetchTool,
TodoWriteTool,
WebSearchTool,
TaskStopTool,
AskUserQuestionTool,
SkillTool,
EnterPlanModeTool,
BriefTool,
ListMcpResourcesTool,
ReadMcpResourceTool,
// 条件性添加的工具(通过 feature flag / process.env 控制)
]
}部分工具为条件性注册:
ConfigTool—— 仅USER_TYPE === 'ant'时生效EnterWorktreeTool / ExitWorktreeTool—— worktree 模式启用时TaskCreateTool / TaskGetTool / TaskUpdateTool / TaskListTool—— todo v2 启用时ToolSearchTool—— ToolSearch 功能启用时
3.2 工具过滤与最终集合
文件位置:src/tools.ts:271-327 和 src/tools.ts:345-367
组装流程分为三步:
getTools(permissionContext)
→ 根据 mode(simple/normal)筛选
→ 根据 deny rules 过滤
→ 根据 isEnabled() 过滤
→ 返回最终的内置工具列表assembleToolPool(permissionContext, mcpTools)
→ getTools() + MCP 工具
→ uniqBy name(内置优先)
→ 按名称排序(稳定 prompt cache)
→ 返回完整的工具池
3.3 工具禁用列表
文件位置:src/constants/tools.ts
不同 agent 类型使用的工具集有严格定义:
ALL_AGENT_DISALLOWED_TOOLS // 普通 sub-agent 禁用
ASYNC_AGENT_ALLOWED_TOOLS // 异步 agent 可用
COORDINATOR_MODE_ALLOWED_TOOLS // 协调模式可用4. 工具 Schema 转换与 API 请求构建
4.1 toolToAPISchema——转为 API 格式
文件位置:src/utils/api.ts:119-266
该函数负责将内部工具定义映射为 API 标准格式:
async function toolToAPISchema(tool, options): Promise {
// 1. 获取 JSON Schema(Zod → JSON Schema 或直接使用 inputJSONSchema)
let input_schema = tool.inputJSONSchema || zodToJsonSchema(tool.inputSchema)
// 2. 过滤 swarm 字段(非 EAP 用户不可见)
if (!isAgentSwarmsEnabled()) {
input_schema = filterSwarmFieldsFromSchema(tool.name, input_schema)
}
// 3. 构建基础 schema
const schema = {
name: tool.name,
description: await tool.prompt({...}), // 使用说明注入点
input_schema,
strict: tool.strict ? true : undefined,
eager_input_streaming: true, // 细粒度 tool streaming
}
// 4. 可选:defer_loading(ToolSearch 功能)
if (options.deferLoading) {
schema.defer_loading = true // 模型调用前需先发现此工具
}
// 5. 可选:cache_control(prompt caching)
if (options.cacheControl) {
schema.cache_control = options.cacheControl
}
return schema
} Schema 转换路径清晰明了:
Zod Schema(z.object({...}))
→ zodToJsonSchema()
→ JSON Schema({ type: "object", properties: {...} })
→ Anthropic API 的 BetaToolUnion4.2 API 请求的构建
文件位置:src/services/api/claude.ts:1100-1396
工具过滤阶段专注于 ToolSearch:
// 判断是否启用 ToolSearch
let useToolSearch = await isToolSearchEnabled(...)
// 启用时仅发送非延迟工具 + 已发现延迟工具
if (useToolSearch) {
const discoveredToolNames = extractDiscoveredToolNames(messages)
filteredTools = tools.filter(tool => {
if (!deferredToolNames.has(tool.name)) return true
if (toolMatchesName(tool, TOOL_SEARCH_TOOL_NAME)) return true
return discoveredToolNames.has(tool.name)
})
} else {
// 未启用 ToolSearch,直接移除 ToolSearchTool
filteredTools = tools.filter(t => !toolMatchesName(t, TOOL_SEARCH_TOOL_NAME))
}构建所有工具的 Schema:
const toolSchemas = await Promise.all(
filteredTools.map(tool =>
toolToAPISchema(tool, {
getToolPermissionContext: ...,
tools,
agents: ...,
model: options.model,
deferLoading: willDefer(tool), // 是否延迟加载
}),
),
)
const allTools = [...toolSchemas, ...extraToolSchemas]最终 API 请求参数核心结构(claude.ts:1699-1728):
return {
model: normalizeModelStringForAPI(options.model),
messages: addCacheBreakpoints(messagesForAPI, ...),
system, // System Prompt
tools: allTools, // 所有工具定义数组
tool_choice: options.toolChoice, // undefined → auto(模型自主决定)
betas: betasParams,
max_tokens: maxOutputTokens,
thinking,
temperature,
}4.3 模型接收的工具参数
模型可见的工具信息由三部分组成:
| 参数 | 来源 | 作用 |
|---|---|---|
tools[].name | tool.name | 工具唯一标识,模型通过此名调用 |
tools[].description | tool.prompt() 输出 | 使用说明、注意事项与示例 |
tools[].input_schema | tool.inputSchema 转换的 JSON Schema | 参数结构、类型、必填项定义 |
tools[].defer_loading | ToolSearch 功能 | true 表示需通过 ToolSearch 发现后才能调用 |
tool_choice | undefined | 模型自主决定是否调用(auto 模式) |
| system prompt 中"Using your tools"部分 | prompts.ts | 场景与工具对应关系指引 |
5. 模型使用工具的引导机制
5.1 System Prompt 中的工具使用指引
文件位置:src/constants/prompts.ts:269-314
getUsingYourToolsSection() 生成的核心规则:
# Using your tools
- 有专用工具时,禁止使用 Bash
- 读文件用 FileReadTool,禁止使用 cat/head/tail
- 编辑文件用 FileEditTool,禁止使用 sed/awk
- 创建文件用 FileWriteTool
- 搜索文件用 GlobTool,禁止使用 find/ls
- 内容搜索用 GrepTool,禁止使用 grep/rg
- 使用 TaskCreate 管理任务
- 独立工具调用支持并行执行5.2 工具被拒绝后的行为指引
- 用户拒绝工具调用后,切勿原封不动重试
- 分析拒绝原因,调整策略
- 无法判断原因时,使用 AskUserQuestionTool 询问用户5.3 工具独有的使用指引
每个工具的 prompt() 方法提供针对性说明。BashTool 示例:
- 执行命令前优先评估更合适的专用工具
- Git 操作安全规范
- 超时管控机制
- Sandbox 限制及绕过方法
模型结合实际场景,综合 system prompt 通用指引与各工具独有规则,做出最优选择。
5.4 ToolSearch:延迟加载机制
文件位置:src/services/api/claude.ts:1120-1172 和 src/tools/ToolSearchTool/prompt.ts
工具数量达到一定规模时,部分工具标记 shouldDefer: true,其 Schema 不随每次请求发送。模型需通过 ToolSearch 发现后才能调用:
- 初始请求只包含非延迟工具 + ToolSearchTool
- 模型调用
ToolSearch(query: "select:toolName")发现延迟工具 - 后续请求加载该工具的完整 Schema
- 模型至此才能正式调用该工具
6. 工具调用的执行与编排
6.1 主循环
文件位置:src/query.ts
消息循环流程:
用户消息
→ 构建 API 请求(含 tools 参数)
→ 流式响应接收
→ 收集 tool_use blocks(query.ts:829-835)
→ 触发工具执行(query.ts:1382)
→ 工具结果以 tool_result 形式返回
→ 继续下一轮循环或结束关键代码段(query.ts:829-835):
const msgToolUseBlocks = message.message.content.filter(
content => content.type === 'tool_use',
) as ToolUseBlock[]if (msgToolUseBlocks.length > 0) {
toolUseBlocks.push(...msgToolUseBlocks)
needsFollowUp = true
}
6.2 工具编排
文件位置:src/services/tools/toolOrchestration.ts:19-82
编排核心逻辑:
async function* runTools(toolUseMessages, assistantMessages, canUseTool, toolUseContext) {
// 1. 按并发安全性分区
for (const { isConcurrencySafe, blocks } of partitionToolCalls(toolUseMessages)) {
if (isConcurrencySafe) {
// 读操作 → 并行执行(最大并发数 MAX_TOOL_USE_CONCURRENCY=10)
yield* runToolsConcurrently(blocks, ...)
} else {
// 写操作 → 串行执行
yield* runToolsSerially(blocks, ...)
}
}
}6.3 单个工具执行
文件位置:src/services/tools/toolExecution.ts:337-490
单工具执行路径:
runToolUse(toolUse, assistantMessage, canUseTool, toolUseContext)
│
├─ 1. findToolByName() 查找工具实例
│ └─ 未找到 → 返回 "No such tool available" 错误
│
├─ 2. 检查取消状态(abortController)
│ └─ 已取消 → 返回取消消息
│
└─ 3. checkPermissionsAndCallTool()
├─ a. parsedInput = tool.inputSchema.safeParse(input)
│ └─ 校验失败 → 返回 InputValidationError
│
├─ b. tool.validateInput(input, context)
│ └─ 校验失败 → 返回校验错误
│
├─ c. runPreToolUseHooks() 执行前置钩子
│
├─ d. checkPermissions() → 权限检查
│ └─ 被拒绝 → 返回权限错误
│
├─ e. tool.call(input, context, ...) 真正执行工具
│ └─ 成功 → mapToolResultToToolResultBlockParam()
│
├─ f. runPostToolUseHooks() 执行后置钩子
│
└─ g. 返回 tool_result + 可能附加消息6.4 调用时机分析
模型调用工具的决策取决于三个要素:
- API 参数
tool_choice: undefined:将决策权完全交付模型。 - 模型推理能力与对话上下文:根据用户请求和历史判断是否需要工具介入。
- System Prompt 指引:明确特定场景下工具优先级。
从架构层面拆解为两层:
- 业务层面:模型输出
tool_useblock 决定调用。 - 执行层面:客户端流式接收
tool_useblock 后立即触发执行(query.ts 处理逻辑)。
7. 工具调用失败的兜底处理
7.1 核心设计:不自动重试
Claude Code 对工具调用失败的处理原则明确:客户端绝不自动重试。失败信息封装为 is_error: true 的 tool_result 返回给模型,由模型自行决策下一步动作。
7.2 各场景失败处理
场景 1:模型调用不存在的工具
文件位置:src/services/tools/toolExecution.ts:368-410
if (!tool) {
yield {
message: createUserMessage({
content: [{
type: 'tool_result',
content: `Error: No such tool available: ${toolName} `,
is_error: true,
tool_use_id: toolUse.id,
}],
}),
}
return // 返回模型,由模型决定
}场景 2:参数校验失败(Zod)
文件位置:src/services/tools/toolExecution.ts:615-679
const parsedInput = tool.inputSchema.safeParse(input)if (!parsedInput.success) {
let errorContent = formatZodValidationError(tool.name, parsedInput.error)
// 对延迟工具且未发现状态,附加提示使用 ToolSearch 加载
const schemaHint = buildSchemaNotSentHint(tool, messages, tools)
if (schemaHint) errorContent += schemaHint
// 返回 InputValidationError
}
场景 3:自定义校验失败
文件位置:src/services/tools/toolExecution.ts:682-732
const isValidCall = await tool.validateInput?.(parsedInput.data, toolUseContext)if (isValidCall?.result === false) {
// 返回工具自定义校验错误
}
场景 4:执行时抛出异常
文件位置:src/services/tools/toolExecution.ts:469-489
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error) yield {
message: createUserMessage({
content: [{
type: 'tool_result',
content: `Error calling tool (${tool.name}): ${errorMessage} `,
is_error: true,
tool_use_id: toolUse.id,
}],
}),
}
}
场景 5:权限被拒绝
文件位置:src/services/tools/toolExecution.ts:995-1103
当 permissionDecision.behavior !== 'allow' 时返回权限拒绝错误。auto 模式下,classifier 拒绝调用会触发 PermissionDenied hooks。若 hook 返回 retry: true,结果附带提示让模型重新尝试。
7.3 System Prompt 中的失败处理指引
文件位置:src/constants/prompts.ts
- 用户拒绝工具调用时,不要重复相同调用。
分析拒绝原因并调整策略。
- 无法确定原因时,使用 AskUserQuestionTool 询问用户。7.4 重试策略总结
| 失败类型 | 客户端自动重试 | 模型收到后行为 |
|---|---|---|
| 工具不存在 | ❌ | 模型自行决定(换工具或报告错误) |
| 参数校验失败 | ❌ | 模型自行决定(修正参数重试) |
| 执行异常 | ❌ | 模型自行决定(换方法或报告错误) |
| 权限被拒绝 | ❌ | 模型不应重试相同调用 |
| API 网络错误 | ✅(withRetry) | claude.ts API 级别重试,与工具调用无关 |
| 流错误(413等) | ✅(reactive compact) | 上下文压缩后重试,API 级别 |
8. 完整链路图示
┌─────────────────────────────────────────────────────────────────┐
│ 1. 工具定义(Tool.ts) │
│ 每个工具实现 Tool 接口:name, prompt(), inputSchema, call() │
└──────────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 2. 工具注册(tools.ts) │
│ getAllBaseTools() + MCP 工具 → assembleToolPool() │
│ 按 deny rules / isEnabled() / mode 过滤 │
└──────────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 3. Schema 转换(api.ts) │
│ Zod → JSON Schema │
│ tool.prompt() → description 字段 │
│ 添加 defer_loading(可选) │
└──────────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 4. API 请求构建(claude.ts:1699) │
│ POST /v1/messages │
│ { tools: allTools, tool_choice: undefined, ... } │
└──────────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 5. 模型决策 │
│ 解析 tools[].description → 理解工具功能 │
│ 解析 tools[].input_schema → 理解参数结构 │
│ 结合 system prompt 使用指引 │
│ 自主决定是否调用、选择工具 │
│ 输出 tool_use block │
└──────────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 6. 流式接收(query.ts) │
│ 收集 tool_use blocks │
│ stream → assistantMessages │
└──────────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 7. 工具编排(toolOrchestration.ts) │
│ partitionToolCalls() → 读并行 / 写串行 │
│ runToolsConcurrently / runToolsSerially │
└──────────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 8. 工具执行(toolExecution.ts) │
│ findToolByName → 输入校验 → 权限检查 → 执行 → hooks │
│ 失败返回 is_error: true 的 tool_result │
└──────────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 9. 结果回调 │
│ tool_result → 作为 user message 加入消息历史 │
│ API 再次请求(含新消息)→ 模型处理结果 → 继续或结束 │
└─────────────────────────────────────────────────────────────────┘关键文件索引
| 文件 | 功能 |
|---|---|
src/Tool.ts | Tool 接口定义、buildTool 工厂函数、findToolByName |
src/tools.ts | 工具组装、过滤、getTools / assembleToolPool |
src/constants/tools.ts | 各类 agent 工具白名单/黑名单 |
src/constants/prompts.ts | System Prompt 工具使用指引生成 |
src/utils/api.ts | toolToAPISchema、Schema 转换 |
src/services/api/claude.ts | API 请求构建、工具过滤(ToolSearch) |
src/services/tools/toolOrchestration.ts | 工具编排(并行/串行执行) |
src/services/tools/toolExecution.ts | 工具执行核心逻辑、错误处理 |
src/query.ts | 主查询循环、流式响应处理 |
src/tools/BashTool/prompt.ts | BashTool 使用说明(prompt 示例) |
