Claude Code 工具调用机制实战指南

2026-06-12阅读 0热度 0
Claude

内容导航

  • 1. 系统概述与核心流程
  • 2. 工具定义体系详解
  • 3. 工具集合的组装与过滤
  • 4. 工具 Schema 转换与 API 请求构建
  • 5. 模型使用工具的引导机制
  • 6. 工具调用的执行与编排
  • 7. 工具调用失败的兜底处理
  • 8. 完整链路图示

1. 系统概述与核心流程

Claude Code 工具调用机制的核心是一条完整动作链:定义工具 → 注册中心 → Schema 序列化 → 模型接收 → 自主决策 → 解析执行 → 结果回调。每个环节紧密耦合,确保模型能高效调用各类工具完成任务。

Claude Code 工具调用机制详解

整个流程五个关键点:

  1. 每次 API 请求时,所有工具(内置或 MCP 接入)均通过 tools 参数统一提交给模型。
  2. System Prompt 内置工具“操作手册”,明确场景与工具的对应关系。
  3. tool_choice 设置为 undefined,将调用决策权完全交予模型。
  4. 模型输出 tool_use block 后,客户端立即执行并封装结果为 tool_result 返回。
  5. 系统不实现自动重试。失败时,将错误信息打包为 is_error: truetool_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-327src/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 的 BetaToolUnion

4.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[].nametool.name工具唯一标识,模型通过此名调用
tools[].descriptiontool.prompt() 输出使用说明、注意事项与示例
tools[].input_schematool.inputSchema 转换的 JSON Schema参数结构、类型、必填项定义
tools[].defer_loadingToolSearch 功能true 表示需通过 ToolSearch 发现后才能调用
tool_choiceundefined模型自主决定是否调用(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-1172src/tools/ToolSearchTool/prompt.ts

工具数量达到一定规模时,部分工具标记 shouldDefer: true,其 Schema 不随每次请求发送。模型需通过 ToolSearch 发现后才能调用:

  1. 初始请求只包含非延迟工具 + ToolSearchTool
  2. 模型调用 ToolSearch(query: "select:toolName") 发现延迟工具
  3. 后续请求加载该工具的完整 Schema
  4. 模型至此才能正式调用该工具

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 调用时机分析

模型调用工具的决策取决于三个要素:

  1. API 参数 tool_choice: undefined:将决策权完全交付模型。
  2. 模型推理能力与对话上下文:根据用户请求和历史判断是否需要工具介入。
  3. System Prompt 指引:明确特定场景下工具优先级。

从架构层面拆解为两层:

  • 业务层面:模型输出 tool_use block 决定调用。
  • 执行层面:客户端流式接收 tool_use block 后立即触发执行(query.ts 处理逻辑)。

7. 工具调用失败的兜底处理

7.1 核心设计:不自动重试

Claude Code 对工具调用失败的处理原则明确:客户端绝不自动重试。失败信息封装为 is_error: truetool_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.tsTool 接口定义、buildTool 工厂函数、findToolByName
src/tools.ts工具组装、过滤、getTools / assembleToolPool
src/constants/tools.ts各类 agent 工具白名单/黑名单
src/constants/prompts.tsSystem Prompt 工具使用指引生成
src/utils/api.tstoolToAPISchema、Schema 转换
src/services/api/claude.tsAPI 请求构建、工具过滤(ToolSearch)
src/services/tools/toolOrchestration.ts工具编排(并行/串行执行)
src/services/tools/toolExecution.ts工具执行核心逻辑、错误处理
src/query.ts主查询循环、流式响应处理
src/tools/BashTool/prompt.tsBashTool 使用说明(prompt 示例)
免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策