Claude 程序化工具调用:完整技术实现深度解析
Claude 的工具调用(Tool Calling)机制是一个分层架构系统:API 层通过 JSON Schema 定义工具接口,消息协议层以 tool_use / tool_result 内容块实现请求-响应循环,推理层则通过注入系统提示词和(可选的)约束解码保证输出结构正确性。本文基于 Anthropic 官方文档、API 参考和 SDK 源码,系统梳理从 API 参数到内部实现的每一个技术细节。
API 层:工具定义、选择与响应结构
tools 参数的 JSON Schema 定义
每个工具通过 tools 顶层参数传入,其结构包含四个核心字段:name(必须匹配正则 ^[a-zA-Z0-9_-]{1,64}$)、description(纯文本描述,建议 3-4 句以上)、input_schema(JSON Schema 对象)和可选的 strict(布尔值,启用结构化输出)。
{
"name": "get_weather",
"description": "Get the current weather in a given location. Returns temperature, conditions, humidity.",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit, defaults to fahrenheit"
}
},
"required": ["location"]
}
}
input_schema 遵循标准 JSON Schema 规范,顶层 type 必须是 "object"。支持的属性类型包括 string、integer、number、boolean、array、object,以及 enum 约束。支持 required 数组、嵌套对象、数组类型和属性级 description。Beta 特性 input_examples 允许附加示例输入(需 advanced-tool-use-2025-11-20 头部),每个示例消耗约 20-200 tokens。
tool_choice 的四种模式
tool_choice 控制 Claude 是否以及如何选择工具,共有四种模式:
| 模式 | 行为 | 与 extended thinking 兼容性 |
|---|---|---|
{"type": "auto"} |
Claude 自主决定是否调用工具(默认值) | |
{"type": "any"} |
必须调用至少一个工具,但不指定具体工具 |
|
{"type": "tool", "name": "X"} |
强制调用指定工具 |
|
{"type": "none"} |
禁止调用任何工具 |
关键行为差异:当 tool_choice 为 any 或 tool 时,助手消息会被**预填充(prefill)**以强制工具调用,这意味着 Claude 不会在 tool_use 块之前输出任何自然语言文本,即使用户明确要求也不行。
并行工具调用通过 disable_parallel_tool_use 子参数控制:
{"type": "auto", "disable_parallel_tool_use": true}
设为 true 时,auto 模式最多调用一个工具,any/tool 模式恰好调用一个工具。
stop_reason 为 tool_use 时的完整响应
当 Claude 决定调用客户端工具时,响应的 stop_reason 为 "tool_use",content 数组包含一个或多个 tool_use 块(可能前置 text 块):
{
"id": "msg_01Aq9w938a90dw8q",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll check the weather for you."
},
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": {"location": "San Francisco, CA"}
}
],
"model": "claude-opus-4-6",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {"input_tokens": 472, "output_tokens": 89}
}
其他可能的 stop_reason 包括:"end_turn"(正常完成)、"max_tokens"(截断,若截断了不完整的 tool_use 块需增大 max_tokens 重试)、"pause_turn"(服务端工具循环达到迭代上限,默认 10 次)。
消息协议:tool_use 与 tool_result 的完整交互
tool_use 和 tool_result 的结构规范
tool_use 内容块包含三个字段:id(唯一标识符,格式如 toolu_01T1x1fJ34qAmk2tNTrN7Up6)、name(工具名)、input(符合 input_schema 的参数对象)。工具结果通过 user 消息中的 tool_result 块返回——这与 OpenAI 使用 tool 角色的设计不同,Claude 统一使用 user 角色:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "65°F, partly cloudy"
}
]
}
tool_result 的 content 支持三种形式:简单字符串、内容块数组(可包含 text、image、document 类型)、或空字符串/省略。错误结果通过 is_error: true 标记:
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "Error: Location 'Atlantis' not found in weather database.",
"is_error": true
}
消息顺序的严格约束
Messages API 是无状态的——每次请求必须发送完整对话历史。工具调用场景下有两条硬性约束:第一,tool_result 消息必须紧跟在包含对应 tool_use 的 assistant 消息之后,中间不能插入其他消息;第二,在包含 tool_result 的 user 消息中,tool_result 块必须排在所有 text 块之前,违反此顺序会触发 400 错误。完整的多轮对话历史如下:
"messages": [
{"role": "user", "content": "What's the weather in SF?"},
{"role": "assistant", "content": [
{"type": "text", "text": "I'll check that for you."},
{"type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather", "input": {"location": "San Francisco, CA"}}
]},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "65°F, partly cloudy"}
]}
]
并行工具调用的实现
Claude 默认可在单次响应中返回多个 tool_use 块。当发生并行调用时,所有 tool_result 必须在同一个 user 消息中返回:
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_01_weather", "content": "65°F"},
{"type": "tool_result", "tool_use_id": "toolu_02_time", "content": "3:45 PM PST"}
]
}
通过系统提示引导可提升并行调用率:在 system prompt 中加入类似 “For maximum efficiency, whenever you perform multiple independent operations, invoke all relevant tools simultaneously” 的指令。Claude 4 系列模型内置了改进的并行工具调用能力。
内部机制:决策过程、约束解码与 ID 生成
Claude 如何在 token 级别决定是否调用工具
当 API 请求包含 tools 参数时,Anthropic 会注入一段特殊的系统提示词到对话最前面,其模板结构为:
In this environment you have access to a set of tools you can use to answer the user's question.
{{ FORMATTING INSTRUCTIONS }}
String and scalar parameters should be specified as is, while lists and objects should use JSON format.
{{ TOOL DEFINITIONS IN JSON SCHEMA }}
{{ USER SYSTEM PROMPT }}
{{ TOOL CONFIGURATION }}
Claude 作为自回归模型逐 token 生成。在 tool_choice: "auto" 模式下,模型自主决定是否生成工具调用标记;在 any 或 tool 模式下,助手消息被预填充以确保直接进入工具调用序列。该注入系统提示会消耗额外 token——以 Claude 4 系列为例,auto/none 模式下基础开销约 346 tokens,any/tool 模式约 313 tokens(不含工具定义本身的 token 消耗)。
JSON 结构正确性:标准模式 vs 约束解码
标准模式(strict: false,默认):Claude 通过微调学习生成符合 schema 的 JSON,但不保证严格一致。可能出现类型不匹配(如字符串 "2" 代替整数 2)或遗漏必需字段。
严格模式(strict: true):启用约束解码(constrained decoding)——Anthropic 将 JSON Schema 编译为语法(grammar),在推理时约束 token 生成。关键特性包括:
- 编译后的语法缓存 24 小时(schema 结构变化使缓存失效,仅修改 name/description 不影响)
- 首次请求因语法编译有额外延迟
-
保证
input严格符合input_schema,name始终有效 - 支持 Claude Opus 4.6、Sonnet 4.6、Sonnet 4.5、Opus 4.5、Haiku 4.5
- 存在复杂度限制:可选参数数量、union 类型、嵌套层级均有上限
- 不支持
additionalProperties(除false外)和minimum/maximum等数值约束
{
"name": "get_weather",
"description": "...",
"input_schema": {"type": "object", "properties": {...}, "required": [...]},
"strict": true
}
tool_use ID 的生成规则
工具调用 ID 由服务端 API 生成(非模型自身生成),作为关联 tool_use 与 tool_result 的唯一标识。其格式遵循明确的前缀规则:
-
客户端工具:
toolu_01+ 随机字母数字字符串(约 20-26 字符),如toolu_01T1x1fJ34qAmk2tNTrN7Up6 -
服务端工具:
srvtoolu_前缀,如srvtoolu_014hJH82Qum7Td6UV8gDXThB -
Bedrock 变体:
toolu_bdrk_01前缀,如toolu_bdrk_01SnXQc6YVWD8Dom5jz7KhHy
01 可能是版本字节,后跟 base-62 随机分量。Anthropic 未公开详细生成算法。
Streaming 模式下的 event 流结构
启用 "stream": true 时,工具调用通过 SSE 事件流传输。完整事件序列为:
message_start
├── content_block_start (index 0, type: text)
│ ├── content_block_delta (text_delta) × N
│ └── content_block_stop
├── content_block_start (index 1, type: tool_use)
│ ├── content_block_delta (input_json_delta) × N
│ └── content_block_stop
├── message_delta (stop_reason: "tool_use")
└── message_stop
tool_use 块的 content_block_start 事件携带 id、name 和空 input: {}。后续的 content_block_delta 事件使用 input_json_delta 类型,partial_json 字段包含原始 JSON 片段:
delta: {"type":"input_json_delta","partial_json":""}
delta: {"type":"input_json_delta","partial_json":"{\"location\":"}
delta: {"type":"input_json_delta","partial_json":" \"San Francisco, CA\""}
delta: {"type":"input_json_delta","partial_json":", \"unit\": \"fahrenheit\"}"}
这些片段不是独立可解析的 JSON——必须拼接所有 partial_json 后在 content_block_stop 事件后统一解析。默认行为下,模型内部积累完一个完整键值对后才开始流式发送,因此事件之间可能有延迟。
细粒度工具流式传输(eager_input_streaming: true)已 GA,可消除缓冲延迟,直接流式输出参数——适用于长文本参数场景,但失去 JSON 完整性保证:
{
"name": "make_file",
"eager_input_streaming": true,
"input_schema": {...}
}
高级特性:Computer Use、服务端工具与 MCP
Computer Use 的三个特殊工具
Computer Use 提供三个 Anthropic 定义的无 schema 工具——无需提供 input_schema,模型内置了对应接口知识:
tools = [
{"type": "computer_20251124", "name": "computer",
"display_width_px": 1024, "display_height_px": 768, "display_number": 1},
{"type": "text_editor_20250728", "name": "str_replace_based_edit_tool"},
{"type": "bash_20250124", "name": "bash"}
]
Computer Tool(computer_20251124)支持 key、type、mouse_move、left_click、screenshot、scroll、zoom 等 17 种动作,需 beta 头部 computer-use-2025-11-24。Bash Tool(bash_20250124)接受 command 和 restart 参数,每次 API 调用增加 245 input tokens 开销,无需 beta 头部。Text Editor Tool(text_editor_20250728)支持 view、str_replace、create、insert 四种命令,无需 beta 头部。三者均为客户端执行——你的代码必须解释 tool_use 块并返回 tool_result。
服务端工具 vs 客户端工具的根本区别
| 维度 | 客户端工具 | 服务端工具 |
|---|---|---|
| 执行位置 | 你的服务器 | Anthropic 服务器 |
| stop_reason |
tool_use(需你处理) |
end_turn 或 pause_turn
|
| 结果处理 | 你返回 tool_result
|
结果自动并入响应 |
| 采样循环 | 单次请求-响应 | 服务端内部循环(最多 10 次迭代) |
| 内容块类型 |
tool_use / tool_result
|
server_tool_use / web_search_tool_result 等 |
服务端工具包括 web_search_20250305(支持 allowed_domains/blocked_domains/user_location 配置)、web_fetch_20250910(获取完整网页/PDF 内容)和 code_execution_20250825(沙盒化代码执行)。三者均已 GA,无需 beta 头部。
程序化工具调用(Programmatic Tool Calling) 是 code_execution 与客户端工具的组合形式——Claude 编写 Python 代码在沙盒中调用你定义的工具,通过 allowed_callers 字段授权:
{
"tools": [
{"type": "code_execution_20250825", "name": "code_execution"},
{
"name": "get_expenses",
"description": "...",
"input_schema": {...},
"allowed_callers": ["code_execution_20250825"]
}
]
}
这种模式将中间结果保留在代码执行沙盒中,不回传到 Claude 的上下文,内部测试显示平均减少 37% token 消耗。
MCP 与 API Tool Calling 的两条集成路径
路径一:客户端转换——将 MCP 工具定义转换为 Claude API 格式,关键操作是将 inputSchema 重命名为 input_schema:
async def get_claude_tools(mcp_session):
mcp_tools = await mcp_session.list_tools()
return [{
"name": tool.name,
"description": tool.description or "",
"input_schema": tool.inputSchema # 唯一需要重命名的字段
} for tool in mcp_tools.tools]
Claude 返回 tool_use 后,你调用 mcp_session.call_tool() 执行并返回 tool_result。
路径二:MCP Connector(服务端直连)——通过 mcp_servers 参数直接连接远程 MCP 服务器,Anthropic 处理连接管理、工具发现和错误处理:
{
"mcp_servers": [{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN"
}],
"tools": [{"type": "mcp_toolset", "mcp_server_name": "example-mcp"}]
}
需 beta 头部 mcp-client-2025-11-20,返回 mcp_tool_use 和 mcp_tool_result 内容块(区别于常规 tool_use)。仅访问 MCP 的 tools 端点,不支持 resources 和 prompts。
Extended thinking 模式下的工具调用
启用 extended thinking 后,只能使用 tool_choice: auto 或 none——使用 any 或 tool 会报错。核心行为变化是交错思考(Interleaved Thinking)——Claude 在工具调用之间产生 thinking 块:
[thinking] → [tool_use] → [thinking] → [tool_use] → [thinking] → [text]
Claude 4 系列中,Opus 4.6 在 adaptive thinking 模式下自动启用交错思考;其他 Claude 4 模型需 interleaved-thinking-2025-05-14 beta 头部。关键约束:多轮工具调用对话中,thinking 块必须原样保留并回传(包含签名),不得修改或省略。budget_tokens 在启用交错思考+工具时可以超过 max_tokens(因为总预算分散在多个 thinking 块中,上限为 200K 上下文窗口)。
工程实践:完整代码、token 开销与常见陷阱
Python 完整 Agentic Loop 实现
import anthropic, json
client = anthropic.Anthropic()
tools = [{
"name": "get_weather",
"description": "Get weather for a location. Returns temperature and conditions.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City and state"}
},
"required": ["location"]
}
}]
def process_tool_call(name, input_data):
if name == "get_weather":
return json.dumps({"temp": 72, "conditions": "sunny"})
raise ValueError(f"Unknown tool: {name}")
def run_agent(user_message, max_iterations=10):
messages = [{"role": "user", "content": user_message}]
for _ in range(max_iterations):
response = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=4096, tools=tools, messages=messages
)
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
try:
result = process_tool_call(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result
})
except Exception as e:
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(e),
"is_error": True
})
messages.append({"role": "user", "content": tool_results})
elif response.stop_reason == "end_turn":
return "".join(b.text for b in response.content if hasattr(b, "text"))
raise Exception("Max iterations reached")
SDK 也提供了简化的 Tool Runner(beta)——Python 使用 @beta_tool 装饰器,TypeScript 使用 betaZodTool 配合 Zod schema,自动处理循环、错误和状态管理。
Token 开销的量化分析
工具定义的 token 消耗来自多个层面。基础系统提示开销在提供至少一个工具时固定产生(Claude 4 系列约 346 tokens)。每个工具定义根据复杂度消耗约 50-500 tokens。一个实际案例:5 个 MCP 服务器、58 个工具定义约消耗 55,000 tokens——在对话开始前就占用了大量上下文窗口。为此 Anthropic 推出了 Tool Search Tool(defer_loading: true 标记的工具不预加载,按需发现,测试中减少 85% 工具定义 token)和上述程序化工具调用(减少 37% token)。
Prompt Caching 可显著降低重复工具定义的成本——缓存读取仅为基础价格的 0.1 倍。
五个最容易踩到的坑
第一,遗漏 tool_result——每个 tool_use 块都必须有对应的 tool_result,缺失会触发 invalid_request_error。这是 Claude Code 项目中报告最多的 bug。第二,tool_result 顺序错误——在 user 消息中,tool_result 块必须排在 text 块之前,否则 400 错误。第三,后续请求遗漏 tools 参数——如果消息历史中包含 tool_use/tool_result 块,每次请求都必须带上 tools 参数。第四,上下文窗口膨胀——50+ 工具的定义可轻松消耗 50K+ tokens,应使用 Tool Search Tool 管理大型工具库。第五,忽视并行调用处理——Claude 可能在单次响应中返回多个 tool_use 块,必须全部处理并一次性返回所有结果。
结论:架构思维与未来方向
Claude 的工具调用体系呈现出清晰的分层设计哲学:API 层提供声明式接口,消息协议层实现无状态的请求-响应循环,推理层通过系统提示注入和可选约束解码桥接自然语言与结构化输出。strict: true 的约束解码机制(grammar 编译 + 24 小时缓存)在保证可靠性的同时控制了延迟成本。服务端工具的引入将执行循环从客户端转移到 Anthropic 基础设施,而 MCP Connector 进一步将工具发现和连接管理标准化。程序化工具调用和 Tool Search Tool 代表了面向大规模 Agent 场景的优化方向——前者通过代码编排减少推理轮次和 token 消耗,后者通过延迟加载解决工具定义膨胀问题。理解这些机制的工程师能够在可靠性、延迟和成本之间做出精准权衡。