程序化工具调用


程序化工具调用允许 Claude 编写代码,在代码执行容器中以编程方式调用你的工具,而不是每次工具调用都需要通过模型进行往返。这通过允许 Claude 在数据到达模型的上下文窗口之前对其进行过滤或处理,减少了多工具工作流的延迟并降低了 token 消耗。在 BrowseCompDeepSearchQA 等 agentic 搜索基准测试中(这些测试评估多步网络研究和复杂信息检索能力),在基础搜索工具之上添加程序化工具调用是完全释放 agent 性能的关键因素。

在实际工作流中,这种差异会快速累积。考虑检查 20 名员工的预算合规情况:传统方法需要 20 次单独的模型往返,在此过程中将数千条费用明细拉入上下文。使用程序化工具调用,单个脚本运行所有 20 次查询,过滤结果,并仅返回超出限额的员工,将 Claude 需要推理的内容从数百千字节缩减到寥寥数行。

Tip

有关程序化工具调用所解决的推理和上下文成本的更深入分析,请参阅高级工具使用

Note

此功能需要启用代码执行工具。

Note

此功能不符合零数据保留 (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 决定使用该工具时:

  1. Claude 编写 Python 代码将该工具作为函数调用,可能包含多次工具调用和前后处理逻辑
  2. Claude 通过代码执行在沙箱容器中运行此代码
  3. 当工具函数被调用时,代码执行暂停,API 返回一个 tool_use
  4. 你提供工具结果,代码执行继续(中间结果不会加载到 Claude 的上下文窗口中)
  5. 所有代码执行完成后,Claude 接收最终输出并继续处理任务

这种方法特别适用于:

  • 大数据处理: 在工具结果到达 Claude 上下文之前进行过滤或聚合
  • 多步工作流: 通过串行或循环调用工具(无需在工具调用之间采样 Claude)来节省 token 和延迟
  • 条件逻辑: 基于中间工具结果做出决策
Note

自定义工具会被转换为异步 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"] - 既可直接调用也可从代码执行调用
Tip

为每个工具选择 ["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 以在请求之间保持状态
Warning

当工具被程序化调用且容器正在等待你的工具结果时,你必须在容器过期前响应。监控 expires_at 字段。如果容器过期,Claude 可能会将工具调用视为超时并重试。

示例工作流

以下是完整的程序化工具调用流程:

步骤 1:初始请求

发送一个包含代码执行和允许程序化调用的工具的请求。要启用程序化调用,请在工具定义中添加 allowed_callers 字段。

Note

在工具描述中提供工具输出格式的详细描述。如果你指定工具返回 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 不支持程序化调用

工具限制

以下工具不能被程序化调用:

消息格式限制

响应程序化工具调用时,有严格的格式要求:

仅工具结果响应: 如果有待处理的程序化工具调用在等待结果,你的响应消息必须包含 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 倍。

用量和定价

程序化工具调用使用与代码执行相同的定价。有关详情,请参阅代码执行定价

Note

程序化工具调用的 token 计数:程序化调用的工具结果不计入你的输入/输出 token 用量。只有最终的代码执行结果和 Claude 的响应才计入。

最佳实践

工具设计

  • 提供详细的输出描述: 由于 Claude 在代码中反序列化工具结果,请清晰记录格式(JSON 结构、字段类型等)
  • 返回结构化数据: JSON 或其他易于解析的格式最适合程序化处理
  • 保持响应简洁: 仅返回必要的数据以最小化处理开销

何时使用程序化调用

良好用例:

  • 处理只需要聚合或摘要的大型数据集
  • 具有 3 次以上依赖工具调用的多步工作流
  • 需要对工具结果进行过滤、排序或转换的操作
  • 中间数据不应影响 Claude 推理的任务
  • 跨多个项目的并行操作(例如检查 50 个端点)

不太理想的用例:

  • 具有简单响应的单次工具调用
  • 需要即时用户反馈的工具
  • 代码执行开销会超过收益的非常快速的操作

性能优化

  • 发出多个相关请求时重用容器以保持状态
  • 尽可能在单次代码执行中批量处理类似操作

故障排除

常见问题

"Tool not allowed" 错误

  • 验证你的工具定义包含 "allowed_callers": ["code_execution_20260120"]

容器过期

  • 确保在容器闲置超时前响应工具调用(4.5 分钟不活动;最长 30 天)
  • 监控响应中的 expires_at 字段
  • 考虑实现更快的工具执行

工具结果未正确解析

  • 确保你的工具返回 Claude 可以反序列化的字符串数据
  • 在工具描述中提供清晰的输出格式文档

调试技巧

  1. 记录所有工具调用和结果以跟踪流程
  2. 检查 caller 字段以确认程序化调用
  3. 监控容器 ID 以确保正确重用
  4. 在启用程序化调用之前独立测试工具

为什么程序化工具调用有效

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 和数据保留

相关功能