程序化工具调用
程序化工具调用允许 Claude 编写代码,在代码执行容器中以编程方式调用你的工具,而不是每次工具调用都需要通过模型进行往返。这通过允许 Claude 在数据到达模型的上下文窗口之前对其进行过滤或处理,减少了多工具工作流的延迟并降低了 token 消耗。在 BrowseComp 和 DeepSearchQA 等 agentic 搜索基准测试中(这些测试评估多步网络研究和复杂信息检索能力),在基础搜索工具之上添加程序化工具调用是完全释放 agent 性能的关键因素。
在实际工作流中,这种差异会快速累积。考虑检查 20 名员工的预算合规情况:传统方法需要 20 次单独的模型往返,在此过程中将数千条费用明细拉入上下文。使用程序化工具调用,单个脚本运行所有 20 次查询,过滤结果,并仅返回超出限额的员工,将 Claude 需要推理的内容从数百千字节缩减到寥寥数行。
有关程序化工具调用所解决的推理和上下文成本的更深入分析,请参阅高级工具使用。
此功能需要启用代码执行工具。
此功能不符合零数据保留 (ZDR) 资格。数据按照该功能的标准保留策略进行保留。
模型兼容性
程序化工具调用需要 code_execution_20260120,以下模型支持该版本:
| 模型 |
|---|
Claude Opus 4.7 (claude-opus-4-7) |
Claude Opus 4.6 (claude-opus-4-6) |
Claude Sonnet 4.6 (claude-sonnet-4-6) |
Claude Opus 4.5 (claude-opus-4-5-20251101) |
Claude Sonnet 4.5 (claude-sonnet-4-5-20250929) |
有关完整的代码执行工具版本矩阵,请参阅代码执行工具模型兼容性表。程序化工具调用在 Claude API、AWS 上的 Claude 平台和 Microsoft Foundry 上可用。目前在 Amazon Bedrock 或 Vertex AI 上不可用。
快速开始
这是一个简单示例,Claude 以编程方式多次查询数据库并聚合结果:
curl https://api.anthropic.com/v1/messages \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
--data '{
"model": "claude-opus-4-7",
"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_20260120",
"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_20260120"]
}
]
}'
ant messages create <<'YAML'
model: claude-opus-4-7
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_20260120
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_20260120
YAML
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-7",
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_20260120", "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_20260120"],
},
],
)
print(response)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
async function main() {
const response = await client.messages.create({
model: "claude-opus-4-7",
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_20260120",
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" as const,
properties: {
sql: {
type: "string",
description: "SQL query to execute"
}
},
required: ["sql"]
},
allowed_callers: ["code_execution_20260120"]
}
]
});
console.log(response);
}
main().catch(console.error);
using Anthropic;
using Anthropic.Models.Messages;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus4_7,
MaxTokens = 4096,
Messages = [
new() {
Role = Role.User,
Content = "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
}
],
Tools = [
new CodeExecutionTool20260120(),
new ToolUnion(new Tool()
{
Name = "query_database",
Description = "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["sql"] = JsonSerializer.SerializeToElement(new { type = "string", description = "SQL query to execute" }),
},
Required = ["sql"],
},
AllowedCallers = ["code_execution_20260120"]
}),
]
};
var message = await client.Messages.Create(parameters);
Console.WriteLine(message);
}
}
package main
import (
"context"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go"
)
func main() {
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus4_7,
MaxTokens: 4096,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue")),
},
Tools: []anthropic.ToolUnionParam{
{OfCodeExecutionTool20260120: &anthropic.CodeExecutionTool20260120Param{}},
{OfTool: &anthropic.ToolParam{
Name: "query_database",
Description: anthropic.String("Execute a SQL query against the sales database. Returns a list of rows as JSON objects."),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"sql": map[string]any{
"type": "string",
"description": "SQL query to execute",
},
},
Required: []string{"sql"},
},
AllowedCallers: []string{"code_execution_20260120"},
}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Tool;
import com.anthropic.models.messages.Tool.InputSchema;
import com.anthropic.models.messages.CodeExecutionTool20260120;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model("claude-opus-4-7")
.maxTokens(4096L)
.addUserMessage("Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue")
.addTool(CodeExecutionTool20260120.builder().build())
.addTool(Tool.builder()
.name("query_database")
.description("Execute a SQL query against the sales database. Returns a list of rows as JSON objects.")
.inputSchema(InputSchema.builder()
.properties(JsonValue.from(Map.of(
"sql", Map.of(
"type", "string",
"description", "SQL query to execute"
)
)))
.putAdditionalProperty("required", JsonValue.from(List.of("sql")))
.build())
.allowedCallers(List.of(Tool.AllowedCaller.of("code_execution_20260120")))
.build())
.build();
Message response = client.messages().create(params);
System.out.println(response);
}
}
<?php
use Anthropic\Client;
$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));
$message = $client->messages->create(
maxTokens: 4096,
messages: [
['role' => 'user', 'content' => 'Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue'],
],
model: 'claude-opus-4-7',
tools: [
[
'type' => 'code_execution_20260120',
'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_20260120'],
],
],
);
echo $message;
require "anthropic"
client = Anthropic::Client.new
message = client.messages.create(
model: "claude-opus-4-7",
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_20260120",
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_20260120"]
}
]
)
puts message
程序化工具调用的工作原理
当你将工具配置为可从代码执行中调用,并且 Claude 决定使用该工具时:
- Claude 编写 Python 代码将该工具作为函数调用,可能包含多次工具调用和前后处理逻辑
- Claude 通过代码执行在沙箱容器中运行此代码
- 当工具函数被调用时,代码执行暂停,API 返回一个
tool_use块 - 你提供工具结果,代码执行继续(中间结果不会加载到 Claude 的上下文窗口中)
- 所有代码执行完成后,Claude 接收最终输出并继续处理任务
这种方法特别适用于:
- 大数据处理: 在工具结果到达 Claude 上下文之前进行过滤或聚合
- 多步工作流: 通过串行或循环调用工具(无需在工具调用之间采样 Claude)来节省 token 和延迟
- 条件逻辑: 基于中间工具结果做出决策
自定义工具会被转换为异步 Python 函数以支持并行工具调用。当 Claude 编写代码调用你的工具时,它使用 await(例如 result = await query_database("<sql>")),并自动包含适当的异步包装函数。
为清晰起见,本文档中的代码示例省略了异步包装。
核心概念
allowed_callers 字段
allowed_callers 字段指定哪些上下文可以调用该工具:
{
"name": "query_database",
"description": "Execute a SQL query against the database",
"input_schema": {
// ...
},
"allowed_callers": ["code_execution_20260120"]
}
可能的值:
["direct"]- 只有 Claude 可以直接调用此工具(省略时的默认值)["code_execution_20260120"]- 仅可从代码执行内部调用["direct", "code_execution_20260120"]- 既可直接调用也可从代码执行调用
为每个工具选择 ["direct"] 或 ["code_execution_20260120"],而不是同时启用两者,因为这为 Claude 如何最好地使用该工具提供了更清晰的指导。
响应中的 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_20260120",
"tool_id": "srvtoolu_abc123"
}
}
tool_id 引用发起程序化调用的代码执行工具。
容器生命周期
程序化工具调用使用与代码执行相同的容器:
- 容器创建: 除非你重用现有容器,否则每个请求都会创建一个新容器
- 过期: 容器最长生命周期为 30 天,闲置 4.5 分钟后会被清理
- 容器 ID: 在响应的
container字段中返回 - 重用: 传递容器 ID 以在请求之间保持状态
当工具被程序化调用且容器正在等待你的工具结果时,你必须在容器过期前响应。监控 expires_at 字段。如果容器过期,Claude 可能会将工具调用视为超时并重试。
示例工作流
以下是完整的程序化工具调用流程:
步骤 1:初始请求
发送一个包含代码执行和允许程序化调用的工具的请求。要启用程序化调用,请在工具定义中添加 allowed_callers 字段。
在工具描述中提供工具输出格式的详细描述。如果你指定工具返回 JSON,Claude 会尝试在代码中反序列化和处理结果。你提供的输出 schema 细节越多,Claude 就能越好地以编程方式处理响应。
请求格式与快速开始示例相同:在工具列表中包含 code_execution,为你希望 Claude 从代码中调用的任何工具添加 allowed_callers: ["code_execution_20260120"],然后发送用户消息。此工作流的其余步骤使用用户消息 "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"。
步骤 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_20260120",
"tool_id": "srvtoolu_abc123"
}
}
],
"container": {
"id": "container_xyz789",
"expires_at": "2026-01-20T14:30:00Z"
},
"stop_reason": "tool_use"
}
步骤 3:提供工具结果
包含完整的对话历史和你的工具结果:
ant messages create <<'YAML'
model: claude-opus-4-7
max_tokens: 4096
container: container_xyz789
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_20260120
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: [...]
YAML
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=4096,
container="container_xyz789", # 重用容器
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_20260120",
"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=[...],
)
print(response)
const response = await client.messages.create({
model: "claude-opus-4-7",
max_tokens: 4096,
container: "container_xyz789", // 重用容器
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_20260120",
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: [
/* ... */
]
});
console.log(response);
using System;
using System.Threading.Tasks;
using Anthropic;
using Anthropic.Models.Messages;
class Program
{
static async Task Main(string[] args)
{
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus4_7,
MaxTokens = 4096,
Container = "container_xyz789",
Messages =
[
new()
{
Role = Role.User,
Content = "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"
},
new()
{
Role = Role.Assistant,
Content = new ContentBlock[]
{
new TextBlock { Text = "I'll query the purchase history and analyze the results." },
new ServerToolUseBlock
{
Id = "srvtoolu_abc123",
Name = "code_execution",
Input = new { code = "..." }
},
new ToolUseBlock
{
Id = "toolu_def456",
Name = "query_database",
Input = new { sql = "<sql>" },
Caller = new ToolCaller
{
Type = "code_execution_20260120",
ToolId = "srvtoolu_abc123"
}
}
}
},
new()
{
Role = Role.User,
Content = new ContentBlockParam[]
{
new ToolResultBlockParam
{
ToolUseID = "toolu_def456",
Content = "[{\"customer_id\": \"C1\", \"revenue\": 45000}, {\"customer_id\": \"C2\", \"revenue\": 38000}, ...]"
}
}
}
],
Tools = []
};
var message = await client.Messages.Create(parameters);
Console.WriteLine(message);
}
}
package main
import (
"context"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go"
)
func main() {
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus4_7,
MaxTokens: 4096,
Container: anthropic.MessageNewParamsContainerUnion{
OfString: anthropic.String("container_xyz789"),
},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Query customer purchase history from the last quarter and identify our top 5 customers by revenue")),
{
Role: anthropic.MessageParamRoleAssistant,
Content: []anthropic.ContentBlockParamUnion{
anthropic.NewTextBlock("I'll query the purchase history and analyze the results."),
{OfServerToolUse: &anthropic.ServerToolUseBlockParam{
ID: "srvtoolu_abc123",
Name: anthropic.ServerToolUseBlockParamNameCodeExecution,
Input: map[string]any{"code": "..."},
}},
{OfToolUse: &anthropic.ToolUseBlockParam{
ID: "toolu_def456",
Name: "query_database",
Input: map[string]any{"sql": "<sql>"},
Caller: anthropic.ServerToolUseBlockParamCallerUnion{
OfCodeExecution20260120: &anthropic.ServerToolCaller20260120Param{
ToolID: "srvtoolu_abc123",
},
},
}},
},
},
{
Role: anthropic.MessageParamRoleUser,
Content: []anthropic.ContentBlockParamUnion{
{OfToolResult: &anthropic.ToolResultBlockParam{
ToolUseID: "toolu_def456",
Content: []anthropic.ToolResultBlockParamContentUnion{
{OfText: &anthropic.TextBlockParam{
Text: `[{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2", "revenue": 38000}, ...]`,
}},
},
}},
},
},
},
Tools: []anthropic.ToolUnionParam{
{OfCodeExecutionTool20260120: &anthropic.CodeExecutionTool20260120Param{}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.CodeExecutionTool20260120;
import com.anthropic.models.messages.ContentBlockParam;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.ServerToolUseBlockParam;
import com.anthropic.models.messages.TextBlockParam;
import com.anthropic.models.messages.ToolResultBlockParam;
import com.anthropic.models.messages.ToolUseBlockParam;
import java.util.List;
import java.util.Map;
public class ContainerReuse {
public static void main(String[] args) {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_4_7)
.maxTokens(4096L)
.container("container_xyz789")
.addUserMessage("Query customer purchase history from the last quarter and identify our top 5 customers by revenue")
.addAssistantMessageOfBlockParams(List.of(
ContentBlockParam.ofText(
TextBlockParam.builder()
.text("I'll query the purchase history and analyze the results.")
.build()),
ContentBlockParam.ofServerToolUse(
ServerToolUseBlockParam.builder()
.id("srvtoolu_abc123")
.name("code_execution")
.input(JsonValue.from(Map.of("code", "...")))
.build()),
ContentBlockParam.ofToolUse(
ToolUseBlockParam.builder()
.id("toolu_def456")
.name("query_database")
.input(JsonValue.from(Map.of("sql", "<sql>")))
.codeExecution20260120Caller("srvtoolu_abc123")
.build())
))
.addUserMessageOfBlockParams(List.of(
ContentBlockParam.ofToolResult(
ToolResultBlockParam.builder()
.toolUseId("toolu_def456")
.content("[{\"customer_id\": \"C1\", \"revenue\": 45000}, {\"customer_id\": \"C2\", \"revenue\": 38000}, ...]")
.build())
))
.addTool(CodeExecutionTool20260120.builder().build())
.build();
Message response = client.messages().create(params);
System.out.println(response);
}
}
<?php
use Anthropic\Client;
$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));
$message = $client->messages->create(
maxTokens: 4096,
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_20260120',
'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}, ...]',
],
],
],
],
model: 'claude-opus-4-7',
container: 'container_xyz789',
tools: [],
);
echo $message;
require "anthropic"
client = Anthropic::Client.new
message = client.messages.create(
model: "claude-opus-4-7",
max_tokens: 4096,
container: "container_xyz789",
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_20260120",
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: [
{ type: "code_execution_20260120", name: "code_execution" }
]
)
puts message
步骤 4:下一个工具调用或完成
代码执行继续并处理结果。如果需要额外的工具调用,请重复步骤 3 直到所有工具调用都得到满足。
步骤 5:最终响应
代码执行完成后,Claude 提供最终响应:
{
"content": [
{
"type": "code_execution_tool_result",
"tool_use_id": "srvtoolu_abc123",
"content": {
"type": "code_execution_result",
"stdout": "Top 5 customers: [{'customer_id': 'C1', 'revenue': 45000}, {'customer_id': 'C2', 'revenue': 38000}, {'customer_id': 'C5', 'revenue': 32000}, {'customer_id': 'C8', 'revenue': 28500}, {'customer_id': 'C3', 'revenue': 24000}]",
"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 def query_database(sql):
return [{"revenue": 100}]
async def _claude_code():
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)
# 以编程方式处理结果
top_region = max(results.items(), key=lambda x: x[1])
print(f"Top region: {top_region[0]} with ${top_region[1]:,} in revenue")
_ = _claude_code
这种模式:
- 将模型往返从 N 次(每个区域一次)减少到 1 次
- 在返回 Claude 之前以编程方式处理大型结果集
- 通过仅返回聚合结论而非原始数据来节省 token
提前终止
Claude 可以在满足成功条件后立即停止处理:
async def check_health(ep):
return "healthy"
async def _claude_code():
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 # 提前停止,不检查剩余端点
_ = _claude_code
条件工具选择
async def get_file_info(p):
return {"size": 500}
async def read_full_file(p):
return "content"
async def read_file_summary(p):
return "summary"
path = "/tmp/example.txt"
async def _claude_code():
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)
_ = _claude_code
数据过滤
async def fetch_logs(sid):
return ["INFO: ok", "ERROR: failed"]
server_id = "srv-01"
async def _claude_code():
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:]: # 仅返回最后 10 个错误
print(error)
_ = _claude_code
响应格式
程序化工具调用
当代码执行调用工具时:
{
"type": "tool_use",
"id": "toolu_abc123",
"name": "query_database",
"input": { "sql": "<sql>" },
"caller": {
"type": "code_execution_20260120",
"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 | 工具输入与 schema 不匹配 | 验证工具的 input_schema |
tool_not_allowed | 工具不允许请求的调用者类型 | 检查 allowed_callers 是否包含正确的上下文 |
工具调用期间容器过期
如果你的工具响应时间过长,代码执行会收到 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字段 - 为工具执行实现超时
- 考虑将长操作分解为较小的块
工具执行错误
如果你的工具返回错误:
{
"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不支持程序化调用
工具限制
以下工具不能被程序化调用:
- 由 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?" }
]
}
有效 - 响应程序化工具调用时仅包含工具结果:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01",
"content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}]"
}
]
}
此限制仅适用于响应程序化(代码执行)工具调用。对于常规客户端工具调用,你可以在工具结果之后包含文本内容。
速率限制
程序化工具调用与常规工具调用适用相同的速率限制。代码执行中的每次工具调用都计为一次单独的调用。
使用前验证工具结果
实现将被程序化调用的用户定义工具时:
- 工具结果以字符串形式返回: 它们可以包含任何内容,包括可能被执行环境处理的代码片段或可执行命令。
- 验证外部工具结果: 如果你的工具返回来自外部源的数据或接受用户输入,请注意如果输出将被解释或执行为代码,则存在代码注入风险。
Token 效率
程序化工具调用可以显著减少 token 消耗:
- 程序化调用的工具结果不会添加到 Claude 的上下文中 - 只有最终的代码输出会
- 中间处理在代码中进行 - 过滤、聚合等不消耗模型 token
- 一次代码执行中的多次工具调用 - 与单独的模型回合相比减少了开销
例如,直接调用 10 个工具使用的 token 约为以编程方式调用它们并返回摘要的 10 倍。
用量和定价
程序化工具调用使用与代码执行相同的定价。有关详情,请参阅代码执行定价。
程序化工具调用的 token 计数:程序化调用的工具结果不计入你的输入/输出 token 用量。只有最终的代码执行结果和 Claude 的响应才计入。
最佳实践
工具设计
- 提供详细的输出描述: 由于 Claude 在代码中反序列化工具结果,请清晰记录格式(JSON 结构、字段类型等)
- 返回结构化数据: JSON 或其他易于解析的格式最适合程序化处理
- 保持响应简洁: 仅返回必要的数据以最小化处理开销
何时使用程序化调用
良好用例:
- 处理只需要聚合或摘要的大型数据集
- 具有 3 次以上依赖工具调用的多步工作流
- 需要对工具结果进行过滤、排序或转换的操作
- 中间数据不应影响 Claude 推理的任务
- 跨多个项目的并行操作(例如检查 50 个端点)
不太理想的用例:
- 具有简单响应的单次工具调用
- 需要即时用户反馈的工具
- 代码执行开销会超过收益的非常快速的操作
性能优化
- 发出多个相关请求时重用容器以保持状态
- 尽可能在单次代码执行中批量处理类似操作
故障排除
常见问题
"Tool not allowed" 错误
- 验证你的工具定义包含
"allowed_callers": ["code_execution_20260120"]
容器过期
- 确保在容器闲置超时前响应工具调用(4.5 分钟不活动;最长 30 天)
- 监控响应中的
expires_at字段 - 考虑实现更快的工具执行
工具结果未正确解析
- 确保你的工具返回 Claude 可以反序列化的字符串数据
- 在工具描述中提供清晰的输出格式文档
调试技巧
- 记录所有工具调用和结果以跟踪流程
- 检查
caller字段以确认程序化调用 - 监控容器 ID 以确保正确重用
- 在启用程序化调用之前独立测试工具
为什么程序化工具调用有效
Claude 的训练包含大量代码接触,使其能够有效地推理和链接函数调用。当工具作为代码执行环境中的可调用函数呈现时,Claude 可以利用这一优势:
- 自然地推理工具组合: 像编写任何 Python 代码一样自然地链接操作和处理依赖关系
- 高效处理大型结果: 过滤大型工具输出、仅提取相关数据,或在将摘要返回上下文窗口之前将中间结果写入文件
- 显著减少延迟: 消除了在多步工作流中每次工具调用之间重新采样 Claude 的开销
这种方法使传统工具使用不切实际的工作流成为可能(例如处理超过 100 万 token 的文件),允许 Claude 以编程方式处理数据而不是将所有内容加载到对话上下文中。
替代实现
程序化工具调用是一种可推广的模式,也可以在你自己的基础设施上实现。以下是两种方法的比较:
客户端直接执行
为 Claude 提供代码执行工具并描述该环境中可用的函数。当 Claude 使用代码调用工具时,你的应用程序在定义这些函数的位置本地执行。
优势:
- 实现简单,只需最少的重新架构
- 完全控制环境和指令
劣势:
- 在沙箱外执行不受信任的代码
- 工具调用可能成为代码注入的载体
适用场景: 你的应用程序可以安全执行任意代码,你想要简单的解决方案,且 Anthropic 的托管服务不适合你的需求。
自托管沙箱执行
从 Claude 的角度来看是相同的方法,但代码在具有安全限制的沙箱容器中运行(例如没有网络出口)。如果你的工具需要外部资源,你需要一个在沙箱外执行工具调用的协议。
优势:
- 在你自己的基础设施上实现安全的程序化工具调用
- 完全控制执行环境
劣势:
- 构建和维护复杂
- 需要同时管理基础设施和进程间通信
适用场景: 安全性至关重要,且 Anthropic 的托管解决方案不适合你的需求。
Anthropic 托管执行
Anthropic 的程序化工具调用是沙箱执行的托管版本,具有为 Claude 调优的 Python 环境。Anthropic 处理容器管理、代码执行和安全的工具调用通信。
优势:
- 默认安全可靠
- 只需最少配置即可启用
- 为 Claude 优化的环境和指令
如果你使用 Claude API、AWS 上的 Claude 平台或 Microsoft Foundry,请考虑使用 Anthropic 的托管解决方案。
数据保留
程序化工具调用建立在代码执行基础设施之上,使用相同的沙箱容器。容器数据(包括执行产物和输出)最长保留 30 天。
有关所有功能的 ZDR 资格,请参阅 API 和数据保留。