程序化工具调用
程序化工具调用允许Claude在代码执行容器中编写代码来以编程方式调用您的工具,而不是要求为每次工具调用进行往返。这减少了多工具工作流的延迟,并通过允许Claude在数据到达模型上下文窗口之前过滤或处理数据来降低令牌消耗。
模型兼容性
程序化工具调用在以下模型上可用:
| 模型 | 工具版本 |
|---|---|
Claude Opus 4.5 (claude-opus-4-5-20251101) | code_execution_20250825 |
Claude Sonnet 4.5 (claude-sonnet-4-5-20250929) | code_execution_20250825 |
程序化工具调用可通过Claude API和Microsoft Foundry获得。
快速开始
这是一个简单的示例,其中Claude以编程方式多次查询数据库并聚合结果:
curl https://api.anthropic.com/v1/messages \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "anthropic-beta: advanced-tool-use-2025-11-20" \
--header "content-type: application/json" \
--data '{
"model": "claude-sonnet-4-5",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
}
],
"tools": [
{
"type": "code_execution_20250825",
"name": "code_execution"
},
{
"name": "query_database",
"description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
"input_schema": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "SQL query to execute"
}
},
"required": ["sql"]
},
"allowed_callers": ["code_execution_20250825"]
}
]
}'程序化工具调用的工作原理
当您配置一个工具可从代码执行调用,并且Claude决定使用该工具时:
- Claude编写Python代码来调用该工具作为函数,可能包括多个工具调用和预/后处理逻辑
- Claude通过代码执行在沙箱容器中运行此代码
- 当调用工具函数时,代码执行暂停,API返回一个
tool_use块 - 您提供工具结果,代码执行继续(中间结果不会加载到Claude的上下文窗口中)
- 一旦所有代码执行完成,Claude接收最终输出并继续处理任务
这种方法特别适用于:
- 大数据处理:在工具结果到达Claude上下文之前过滤或聚合工具结果
- 多步工作流:通过在工具调用之间不采样Claude来串行或循环调用工具,从而节省令牌和延迟
- 条件逻辑:基于中间工具结果做出决策
自定义工具被转换为异步Python函数以支持并行工具调用。当Claude编写调用您的工具的代码时,它使用await(例如,result = await query_database("<sql>"))并自动包含适当的异步包装函数。
为了清晰起见,本文档中的代码示例中省略了异步包装。
核心概念
allowed_callers字段
allowed_callers字段allowed_callers字段指定哪些上下文可以调用工具:
{
"name": "query_database",
"description": "Execute a SQL query against the database",
"input_schema": {...},
"allowed_callers": ["code_execution_20250825"]
}可能的值:
["direct"]- 仅Claude可以直接调用此工具(如果省略则为默认值)["code_execution_20250825"]- 仅可从代码执行中调用["direct", "code_execution_20250825"]- 可直接调用和从代码执行调用
我们建议为每个工具选择["direct"]或["code_execution_20250825"],而不是同时启用两者,因为这为Claude提供了更清晰的指导,说明如何最好地使用该工具。
响应中的caller字段
caller字段每个工具使用块都包含一个caller字段,指示它是如何被调用的:
直接调用(传统工具使用):
{
"type": "tool_use",
"id": "toolu_abc123",
"name": "query_database",
"input": {"sql": "<sql>"},
"caller": {"type": "direct"}
}程序化调用:
{
"type": "tool_use",
"id": "toolu_xyz789",
"name": "query_database",
"input": {"sql": "<sql>"},
"caller": {
"type": "code_execution_20250825",
"tool_id": "srvtoolu_abc123"
}
}tool_id引用进行程序化调用的代码执行工具。
容器生命周期
程序化工具调用使用与代码执行相同的容器:
- 容器创建:为每个会话创建一个新容器,除非您重用现有容器
- 过期:容器在大约4.5分钟的不活动后过期(可能会更改)
- 容器ID:通过
container字段在响应中返回 - 重用:传递容器ID以在请求之间维护状态
当工具以编程方式调用且容器等待您的工具结果时,您必须在容器过期之前响应。监视expires_at字段。如果容器过期,Claude可能会将工具调用视为超时并重试它。
示例工作流
以下是完整的程序化工具调用流程的工作方式:
步骤1:初始请求
发送包含代码执行和允许程序化调用的工具的请求。要启用程序化调用,请将allowed_callers字段添加到您的工具定义中。
在工具描述中提供工具输出格式的详细描述。如果您指定工具返回JSON,Claude将尝试反序列化并在代码中处理结果。您提供的关于输出架构的详细信息越多,Claude就能更好地以编程方式处理响应。
response = client.beta.messages.create(
model="claude-sonnet-4-5",
betas=["advanced-tool-use-2025-11-20"],
max_tokens=4096,
messages=[{
"role": "user",
"content": "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"
}],
tools=[
{
"type": "code_execution_20250825",
"name": "code_execution"
},
{
"name": "query_database",
"description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
"input_schema": {...},
"allowed_callers": ["code_execution_20250825"]
}
]
)步骤2:API响应与工具调用
Claude编写调用您的工具的代码。API暂停并返回:
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll query the purchase history and analyze the results."
},
{
"type": "server_tool_use",
"id": "srvtoolu_abc123",
"name": "code_execution",
"input": {
"code": "results = await query_database('<sql>')\ntop_customers = sorted(results, key=lambda x: x['revenue'], reverse=True)[:5]\nprint(f'Top 5 customers: {top_customers}')"
}
},
{
"type": "tool_use",
"id": "toolu_def456",
"name": "query_database",
"input": {"sql": "<sql>"},
"caller": {
"type": "code_execution_20250825",
"tool_id": "srvtoolu_abc123"
}
}
],
"container": {
"id": "container_xyz789",
"expires_at": "2025-01-15T14:30:00Z"
},
"stop_reason": "tool_use"
}步骤3:提供工具结果
包含完整的对话历史加上您的工具结果:
response = client.beta.messages.create(
model="claude-sonnet-4-5",
betas=["advanced-tool-use-2025-11-20"],
max_tokens=4096,
container="container_xyz789", # Reuse the container
messages=[
{"role": "user", "content": "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"},
{
"role": "assistant",
"content": [
{"type": "text", "text": "I'll query the purchase history and analyze the results."},
{
"type": "server_tool_use",
"id": "srvtoolu_abc123",
"name": "code_execution",
"input": {"code": "..."}
},
{
"type": "tool_use",
"id": "toolu_def456",
"name": "query_database",
"input": {"sql": "<sql>"},
"caller": {
"type": "code_execution_20250825",
"tool_id": "srvtoolu_abc123"
}
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_def456",
"content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}, {\"customer_id\": \"C2\", \"revenue\": 38000}, ...]"
}
]
}
],
tools=[...]
)步骤4:下一个工具调用或完成
代码执行继续并处理结果。如果需要额外的工具调用,重复步骤3直到所有工具调用都得到满足。
步骤5:最终响应
一旦代码执行完成,Claude提供最终响应:
{
"content": [
{
"type": "code_execution_tool_result",
"tool_use_id": "srvtoolu_abc123",
"content": {
"type": "code_execution_result",
"stdout": "Top 5 customers by revenue:\n1. Customer C1: $45,000\n2. Customer C2: $38,000\n3. Customer C5: $32,000\n4. Customer C8: $28,500\n5. Customer C3: $24,000",
"stderr": "",
"return_code": 0,
"content": []
}
},
{
"type": "text",
"text": "I've analyzed the purchase history from last quarter. Your top 5 customers generated $167,500 in total revenue, with Customer C1 leading at $45,000."
}
],
"stop_reason": "end_turn"
}高级模式
使用循环进行批处理
Claude可以编写代码来高效处理多个项目:
# async wrapper omitted for clarity
regions = ["West", "East", "Central", "North", "South"]
results = {}
for region in regions:
data = await query_database(f"<sql for {region}>")
results[region] = sum(row["revenue"] for row in data)
# Process results programmatically
top_region = max(results.items(), key=lambda x: x[1])
print(f"Top region: {top_region[0]} with ${top_region[1]:,} in revenue")此模式:
- 将模型往返次数从N(每个区域一次)减少到1
- 在返回Claude之前以编程方式处理大型结果集
- 通过仅返回聚合结论而不是原始数据来节省令牌
早期终止
Claude可以在满足成功标准后立即停止处理:
# async wrapper omitted for clarity
endpoints = ["us-east", "eu-west", "apac"]
for endpoint in endpoints:
status = await check_health(endpoint)
if status == "healthy":
print(f"Found healthy endpoint: {endpoint}")
break # Stop early, don't check remaining条件工具选择
# async wrapper omitted for clarity
file_info = await get_file_info(path)
if file_info["size"] < 10000:
content = await read_full_file(path)
else:
content = await read_file_summary(path)
print(content)数据过滤
# async wrapper omitted for clarity
logs = await fetch_logs(server_id)
errors = [log for log in logs if "ERROR" in log]
print(f"Found {len(errors)} errors")
for error in errors[-10:]: # Only return last 10 errors
print(error)响应格式
程序化工具调用
当代码执行调用工具时:
{
"type": "tool_use",
"id": "toolu_abc123",
"name": "query_database",
"input": {"sql": "<sql>"},
"caller": {
"type": "code_execution_20250825",
"tool_id": "srvtoolu_xyz789"
}
}工具结果处理
您的工具结果被传递回运行中的代码:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_abc123",
"content": "[{\"customer_id\": \"C1\", \"revenue\": 45000, \"orders\": 23}, {\"customer_id\": \"C2\", \"revenue\": 38000, \"orders\": 18}, ...]"
}
]
}代码执行完成
当所有工具调用都得到满足且代码完成时:
{
"type": "code_execution_tool_result",
"tool_use_id": "srvtoolu_xyz789",
"content": {
"type": "code_execution_result",
"stdout": "Analysis complete. Top 5 customers identified from 847 total records.",
"stderr": "",
"return_code": 0,
"content": []
}
}错误处理
常见错误
| 错误 | 描述 | 解决方案 |
|---|---|---|
invalid_tool_input | 工具输入与架构不匹配 | 验证您的工具的input_schema |
tool_not_allowed | 工具不允许请求的调用者类型 | 检查allowed_callers包含正确的上下文 |
missing_beta_header | 未提供PTC测试版标头 | 将两个测试版标头添加到您的请求中 |
工具调用期间容器过期
如果您的工具响应时间过长,代码执行将收到TimeoutError。Claude在stderr中看到这个并通常会重试:
{
"type": "code_execution_tool_result",
"tool_use_id": "srvtoolu_abc123",
"content": {
"type": "code_execution_result",
"stdout": "",
"stderr": "TimeoutError: Calling tool ['query_database'] timed out.",
"return_code": 0,
"content": []
}
}要防止超时:
- 监视响应中的
expires_at字段 - 为您的工具执行实现超时
- 考虑将长操作分解为较小的块
工具执行错误
如果您的工具返回错误:
# Provide error information in the tool result
{
"type": "tool_result",
"tool_use_id": "toolu_abc123",
"content": "Error: Query timeout - table lock exceeded 30 seconds"
}Claude的代码将收到此错误并可以适当地处理它。
约束和限制
功能不兼容性
- 结构化输出:具有
strict: true的工具不支持程序化调用 - 工具选择:您不能通过
tool_choice强制程序化调用特定工具 - 并行工具使用:
disable_parallel_tool_use: true不支持程序化调用
工具限制
以下工具目前无法以编程方式调用,但可能在未来版本中添加支持:
- Web搜索
- Web获取
- 由MCP连接器提供的工具
消息格式限制
在响应程序化工具调用时,有严格的格式要求:
仅工具结果响应:如果有待处理的程序化工具调用等待结果,您的响应消息必须仅包含tool_result块。您不能包含任何文本内容,即使在工具结果之后也不行。
// ❌ 无效 - 在响应程序化工具调用时不能包含文本
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_01", "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}]"},
{"type": "text", "text": "What should I do next?"} // This will cause an error
]
}
// ✅ 有效 - 在响应程序化工具调用时仅工具结果
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_01", "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}]"}
]
}此限制仅适用于响应程序化(代码执行)工具调用。对于常规客户端工具调用,您可以在工具结果后包含文本内容。
速率限制
程序化工具调用受与常规工具调用相同的速率限制。来自代码执行的每个工具调用都计为单独的调用。
在使用前验证工具结果
在实现将以编程方式调用的自定义工具时:
- 工具结果作为字符串返回:它们可以包含任何内容,包括代码片段或可执行命令,这些可能由执行环境处理。
- 验证外部工具结果:如果您的工具返回来自外部源的数据或接受用户输入,请注意如果输出将被解释或作为代码执行,代码注入风险。
令牌效率
程序化工具调用可以显著降低令牌消耗:
- 来自程序化调用的工具结果不会添加到Claude的上下文中 - 仅最终代码输出会
- 中间处理在代码中进行 - 过滤、聚合等不消耗模型令牌
- 一个代码执行中的多个工具调用 - 与单独的模型轮次相比减少开销
例如,直接调用10个工具使用的令牌数约为以编程方式调用它们并返回摘要的10倍。
使用和定价
程序化工具调用使用与代码执行相同的定价。有关详细信息,请参阅代码执行定价。
程序化工具调用的令牌计数:来自程序化调用的工具结果不计入您的输入/输出令牌使用。仅最终代码执行结果和Claude的响应计数。
最佳实践
工具设计
- 提供详细的输出描述:由于Claude在代码中反序列化工具结果,请清楚地记录格式(JSON结构、字段类型等)
- 返回结构化数据:JSON或其他易于解析的格式最适合程序化处理
- 保持响应简洁:仅返回必要的数据以最小化处理开销
何时使用程序化调用
好的用例:
- 处理大型数据集,其中您只需要聚合或摘要
- 具有3个或更多依赖工具调用的多步工作流
- 需要过滤、排序或转换工具结果的操作
- 中间数据不应影响Claude推理的任务
- 跨许多项目的并行操作(例如,检查50个端点)
不太理想的用例:
- 具有简单响应的单个工具调用
- 需要立即用户反馈的工具
- 非常快速的操作,其中代码执行开销会超过收益
性能优化
- 重用容器在进行多个相关请求时以维护状态
- 在单个代码执行中批处理类似操作(如果可能)
故障排除
常见问题
"工具不允许"错误
- 验证您的工具定义包含
"allowed_callers": ["code_execution_20250825"] - 检查您使用的是正确的测试版标头
容器过期
- 确保您在容器的生命周期内响应工具调用(约4.5分钟)
- 监视响应中的
expires_at字段 - 考虑实现更快的工具执行
测试版标头问题
- 您需要标头:
"advanced-tool-use-2025-11-20"
工具结果未正确解析
- 确保您的工具返回Claude可以反序列化的字符串数据
- 在您的工具描述中提供清晰的输出格式文档
调试提示
- 记录所有工具调用和结果以跟踪流程
- 检查
caller字段以确认程序化调用 - 监视容器ID以确保正确重用
- 在启用程序化调用之前独立测试工具
为什么程序化工具调用有效
Claude的训练包括对代码的广泛接触,使其能够有效地推理和链接函数调用。当工具在代码执行环境中作为可调用函数呈现时,Claude可以利用这一优势来:
- 自然地推理工具组合:链接操作并处理依赖关系,就像编写任何Python代码一样自然
- 高效处理大型结果:过滤大型工具输出、仅提取相关数据或在返回摘要到上下文窗口之前将中间结果写入文件
- 显著降低延迟:消除在多步工作流中每个工具调用之间重新采样Claude的开销
这种方法支持传统工具使用不切实际的工作流——例如处理超过1M令牌的文件——通过允许Claude以编程方式处理数据而不是将所有内容加载到对话上下文中。
替代实现
程序化工具调用是一个可以在Anthropic的托管代码执行之外实现的通用模式。以下是方法概述:
客户端直接执行
为Claude提供代码执行工具并描述该环境中可用的功能。当Claude使用代码调用工具时,您的应用程序在定义这些函数的本地执行它。
优点:
- 简单实现,最少重新架构
- 完全控制环境和指令
缺点:
- 在沙箱外执行不受信任的代码
- 工具调用可能是代码注入的向量
**使用时机:**您的应用程序可以安全地执行任意代码,您想要一个简单的解决方案,并且Anthropic的托管产品不适合您的需求。
自管理沙箱执行
从Claude的角度来看相同的方法,但代码在具有安全限制的沙箱容器中运行(例如,无网络出口)。如果您的工具需要外部资源,您需要一个协议来在沙箱外执行工具调用。
优点:
- 在您自己的基础设施上进行安全的程序化工具调用
- 完全控制执行环境
缺点:
- 复杂的构建和维护
- 需要管理基础设施和进程间通信
**使用时机:**安全至关重要,Anthropic的托管解决方案不适合您的要求。
Anthropic托管执行
Anthropic的程序化工具调用是沙箱执行的托管版本,具有为Claude调整的固定Python环境。Anthropic处理容器管理、代码执行和安全工具调用通信。
优点:
- 默认安全和安全
- 最少配置即可轻松启用
- 为Claude优化的环境和指令
如果您使用Claude API,我们建议使用Anthropic的托管解决方案。