本教程通过五个同心环(ring)构建一个日历管理智能体。每一环都是一个完整、可运行的程序,并且只在前一环的基础上增加一个概念。完成后,您将先手动编写“agentic loop”(智能体循环),然后用“tool runner”(工具运行器)这一 SDK 抽象来替代它。
示例工具是 create_calendar_event。它的 schema 使用了嵌套对象、数组和可选字段,因此您将看到 Claude 如何处理真实的输入结构,而不仅仅是单个扁平字符串。
注意
每一环都可以独立运行。将任意一环复制到一个新文件中,无需前面各环的代码即可运行。
第 1 环:单个工具,单轮对话
最小的工具使用程序:一个工具、一条用户消息、一次工具调用、一个结果。代码带有大量注释,便于您将每一行对应到工具使用生命周期。
请求在用户消息之外还发送一个 tools 数组。当 Claude 判断需要调用工具时,响应会返回 stop_reason: "tool_use" 以及一个 tool_use 内容块,其中包含工具名称、唯一的 id 和结构化的 input。您的代码运行该工具,然后在一个 tool_result 块中将结果发回,该块的 tool_use_id 与调用中的 id 相匹配。
cURL
此工作流需要的逻辑超出了单个 shell 命令所能实现的范围。请使用 Claude SDK 运行它。
CLI
此工作流需要的逻辑超出了单个 shell 命令所能实现的范围。请使用 Claude SDK 运行它。
Python
# 第 1 环:单个工具,单轮对话。
import json
import anthropic
# 创建客户端。它会从环境变量中读取 ANTHROPIC_API_KEY。
client = anthropic.Anthropic()
# 定义一个工具。input_schema 是一个 JSON Schema 对象,用于描述
# Claude 调用此工具时应传递的参数。此 schema
# 包含嵌套对象(recurrence)、数组(attendees)和可选
# 字段,这比扁平的字符串参数更接近真实世界的工具。
tools = [
{
"name": "create_calendar_event",
"description": "Create a calendar event with attendees and optional recurrence.",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start": {"type": "string", "format": "date-time"},
"end": {"type": "string", "format": "date-time"},
"attendees": {
"type": "array",
"items": {"type": "string", "format": "email"},
},
"recurrence": {
"type": "object",
"properties": {
"frequency": {"enum": ["daily", "weekly", "monthly"]},
"count": {"type": "integer", "minimum": 1},
},
},
},
"required": ["title", "start", "end"],
},
}
]
# 将用户的请求连同工具定义一起发送。Claude 会根据
# 请求和工具描述来决定是否调用该工具。
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=tools,
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=[
{
"role": "user",
"content": "Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am.",
}
],
)
# 当 Claude 调用工具时,响应的 stop_reason 为 "tool_use",
# 并且 content 数组中会包含一个 tool_use 块以及可能的文本。
print(f"stop_reason: {response.stop_reason}")
# 查找 tool_use 块。响应可能在 tool_use 块之前包含文本块,
# 因此应扫描 content 数组,而不是假定其位置。
tool_use = next(block for block in response.content if block.type == "tool_use")
print(f"Tool: {tool_use.name}")
print(f"Input: {tool_use.input}")
# 运行工具。在真实系统中,这里会调用您的日历 API。
# 此处将结果硬编码,以使示例保持独立完整。
result = {"event_id": "evt_123", "status": "created"}
# 将结果发回。tool_result 块放在 user 消息中,
# 其 tool_use_id 必须与之前 tool_use 块的 ID 匹配。
# 同时包含 assistant 之前的响应,以便 Claude 获得完整历史。
followup = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=tools,
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=[
{
"role": "user",
"content": "Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am.",
},
{"role": "assistant", "content": response.content},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": json.dumps(result),
}
],
},
],
)
# 获得工具结果后,Claude 会生成最终的自然语言
# 回答,且 stop_reason 变为 "end_turn"。
print(f"stop_reason: {followup.stop_reason}")
final_text = next(block for block in followup.content if block.type == "text")
print(final_text.text)
TypeScript
// 第 1 环:单个工具,单轮对话。
import Anthropic from "@anthropic-ai/sdk";
// 创建客户端。它会从环境变量中读取 ANTHROPIC_API_KEY。
const client = new Anthropic();
// 定义一个工具。input_schema 是一个 JSON Schema 对象,用于描述
// Claude 调用此工具时应传递的参数。此 schema
// 包含嵌套对象(recurrence)、数组(attendees)和可选
// 字段,这比扁平的字符串参数更接近真实世界的工具。
const tools: Anthropic.Tool[] = [
{
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
input_schema: {
type: "object",
properties: {
title: { type: "string" },
start: { type: "string", format: "date-time" },
end: { type: "string", format: "date-time" },
attendees: {
type: "array",
items: { type: "string", format: "email" }
},
recurrence: {
type: "object",
properties: {
frequency: { enum: ["daily", "weekly", "monthly"] },
count: { type: "integer", minimum: 1 }
}
}
},
required: ["title", "start", "end"]
}
}
];
// 将用户请求与工具定义一起发送。Claude 会根据
// 请求和工具描述来决定是否调用该工具。
const response = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
tool_choice: { type: "auto", disable_parallel_tool_use: true },
messages: [
{
role: "user",
content:
"Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am."
}
]
});
// 当 Claude 调用工具时,响应的 stop_reason 为 "tool_use",
// 并且 content 数组中会包含一个 tool_use 块以及可能的文本。
console.log(`stop_reason: ${response.stop_reason}`);
// 查找 tool_use 块。响应中可能在 tool_use 块之前包含文本块,
// 因此应扫描 content 数组,而不是假定其位置。
const toolUse = response.content.find((block) => block.type === "tool_use")!;
console.log(`Tool: ${toolUse.name}`);
console.log(`Input: ${JSON.stringify(toolUse.input)}`);
// 运行工具。在真实系统中,这里会调用您的日历 API。
// 此处将结果硬编码,以使示例保持独立完整。
const result = { event_id: "evt_123", status: "created" };
// 将结果发回。tool_result 块放在 user 消息中,
// 其 tool_use_id 必须与之前 tool_use 块的 ID 匹配。
// 同时包含助手之前的响应,以便 Claude 获得完整历史。
const followup = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
tool_choice: { type: "auto", disable_parallel_tool_use: true },
messages: [
{
role: "user",
content:
"Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am."
},
{ role: "assistant", content: response.content },
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: toolUse.id,
content: JSON.stringify(result)
}
]
}
]
});
// 获得工具结果后,Claude 会生成最终的自然语言
// 回答,stop_reason 变为 "end_turn"。
console.log(`stop_reason: ${followup.stop_reason}`);
for (const block of followup.content) {
if (block.type === "text") {
console.log(block.text);
}
}
C#
// 第 1 环:单个工具,单轮对话。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Anthropic;
using Anthropic.Models.Messages;
// 创建客户端。它会从环境变量中读取 ANTHROPIC_API_KEY。
AnthropicClient client = new();
// 定义一个工具。输入模式是一个 JSON Schema 对象,用于描述
// Claude 调用此工具时应传递的参数。此模式
// 包含嵌套对象(recurrence)、数组(attendees)和可选
// 字段,这比扁平的字符串参数更接近真实世界的工具。
List<ToolUnion> tools =
[
new ToolUnion(new Tool()
{
Name = "create_calendar_event",
Description = "Create a calendar event with attendees and optional recurrence.",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["title"] = JsonSerializer.SerializeToElement(new { type = "string" }),
["start"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date-time" }),
["end"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date-time" }),
["attendees"] = JsonSerializer.SerializeToElement(new
{
type = "array",
items = new { type = "string", format = "email" },
}),
["recurrence"] = JsonSerializer.SerializeToElement(new
{
type = "object",
properties = new
{
frequency = new { @enum = new[] { "daily", "weekly", "monthly" } },
count = new { type = "integer", minimum = 1 },
},
}),
},
Required = ["title", "start", "end"],
},
}),
];
// 要求每轮最多调用一次工具,以便后续的单轮流程
// 保持可预测。
var toolChoice = new ToolChoice(new ToolChoiceAuto { DisableParallelToolUse = true });
const string userPrompt =
"Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am.";
// 将用户请求与工具定义一起发送。Claude 会根据
// 请求和工具描述来决定是否调用该工具。
var response = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = tools,
ToolChoice = toolChoice,
Messages = [new UserMessageParam { Content = userPrompt }],
});
// 当 Claude 调用工具时,响应的 stop_reason 为 "tool_use",
// 并且 content 数组中会包含一个 tool_use 块以及任何文本。
Console.WriteLine($"stop_reason: {response.StopReason?.Raw()}");
// 查找 tool_use 块。响应中可能在 tool_use 块之前包含文本块,
// 因此应扫描 content 数组,而不是假定其位置。
ToolUseBlock? toolUse = null;
foreach (var block in response.Content)
{
if (block.TryPickToolUse(out var picked))
{
toolUse = picked;
break;
}
}
Console.WriteLine($"Tool: {toolUse!.Name}");
Console.WriteLine($"Input: {JsonSerializer.Serialize(toolUse.Input)}");
// 运行工具。在真实系统中,这里会调用您的日历 API。
// 此处将结果硬编码,以使示例保持独立完整。
var result = """{"event_id": "evt_123", "status": "created"}""";
// 将结果发回。tool_result 块放在 user 消息中,
// 其 tool_use_id 必须与之前 tool_use 块的 ID 匹配。
// 同时包含助手之前的响应,以便 Claude 获得完整历史。
List<ContentBlockParam> toolResults =
[
new ContentBlockParam(new ToolResultBlockParam()
{
ToolUseID = toolUse.ID,
Content = result,
}),
];
var followup = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = tools,
ToolChoice = toolChoice,
Messages =
[
new UserMessageParam { Content = userPrompt },
new AssistantMessageParam { Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList() },
new UserMessageParam { Content = toolResults },
],
});
// 获得工具结果后,Claude 会生成最终的自然语言
// 回答,stop_reason 变为 "end_turn"。
Console.WriteLine($"stop_reason: {followup.StopReason?.Raw()}");
foreach (var block in followup.Content)
{
if (block.TryPickText(out var text))
{
Console.WriteLine(text.Text);
}
}
Go
// 第 1 环:单个工具,单轮对话。
package main
import (
"context"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go/v2"
)
func main() {
// 创建客户端。它会从环境变量中读取 ANTHROPIC_API_KEY。
client := anthropic.NewClient()
ctx := context.Background()
// 定义一个工具。输入模式是一个 JSON Schema 对象,用于描述
// Claude 调用此工具时应传递的参数。此模式
// 包含嵌套对象(recurrence)、数组(attendees)和可选
// 字段,这比扁平的字符串参数更接近真实世界的工具。
tools := []anthropic.ToolUnionParam{
{OfTool: &anthropic.ToolParam{
Name: "create_calendar_event",
Description: anthropic.String("Create a calendar event with attendees and optional recurrence."),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"title": map[string]any{"type": "string"},
"start": map[string]any{"type": "string", "format": "date-time"},
"end": map[string]any{"type": "string", "format": "date-time"},
"attendees": map[string]any{
"type": "array",
"items": map[string]any{"type": "string", "format": "email"},
},
"recurrence": map[string]any{
"type": "object",
"properties": map[string]any{
"frequency": map[string]any{"enum": []string{"daily", "weekly", "monthly"}},
"count": map[string]any{"type": "integer", "minimum": 1},
},
},
},
Required: []string{"title", "start", "end"},
},
}},
}
// 要求每轮最多调用一次工具,以便后续的单轮流程
// 保持可预测。
toolChoice := anthropic.ToolChoiceUnionParam{
OfAuto: &anthropic.ToolChoiceAutoParam{DisableParallelToolUse: anthropic.Bool(true)},
}
userMessage := anthropic.NewUserMessage(anthropic.NewTextBlock(
"Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am.",
))
// 将用户请求与工具定义一起发送。Claude 会根据
// 请求和工具描述来决定是否调用该工具。
response, err := client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Tools: tools,
ToolChoice: toolChoice,
Messages: []anthropic.MessageParam{userMessage},
})
if err != nil {
log.Fatal(err)
}
// 当 Claude 调用工具时,响应的 stop_reason 为 "tool_use",
// 并且 content 数组中会包含一个 tool_use 块以及任何文本。
fmt.Printf("stop_reason: %s\n", response.StopReason)
// 查找 tool_use 块。响应中可能在 tool_use 块之前包含文本块,
// 因此应扫描 content 数组,而不是假定其位置。
var toolUse anthropic.ContentBlockUnion
for _, block := range response.Content {
if block.Type == "tool_use" {
toolUse = block
break
}
}
fmt.Printf("Tool: %s\n", toolUse.Name)
fmt.Printf("Input: %s\n", string(toolUse.Input))
// 运行工具。在真实系统中,这里会调用您的日历 API。
// 此处将结果硬编码,以使示例保持独立完整。
result := `{"event_id": "evt_123", "status": "created"}`
// 将结果发回。tool_result 块放在 user 消息中,
// 其 tool_use_id 必须与之前 tool_use 块的 ID 匹配。
// 同时包含助手之前的响应,以便 Claude 获得完整历史记录。
var assistantContent []anthropic.ContentBlockParamUnion
for _, block := range response.Content {
assistantContent = append(assistantContent, block.ToParam())
}
followup, err := client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Tools: tools,
ToolChoice: toolChoice,
Messages: []anthropic.MessageParam{
userMessage,
anthropic.NewAssistantMessage(assistantContent...),
anthropic.NewUserMessage(anthropic.NewToolResultBlock(toolUse.ID, result, false)),
},
})
if err != nil {
log.Fatal(err)
}
// 获得工具结果后,Claude 会生成最终的自然语言
// 回答,stop_reason 变为 "end_turn"。
fmt.Printf("stop_reason: %s\n", followup.StopReason)
for _, block := range followup.Content {
if block.Type == "text" {
fmt.Println(block.Text)
}
}
}
Java
// 第 1 环:单个工具,单轮对话。
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.JsonValue;
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.Tool;
import com.anthropic.models.messages.Tool.InputSchema;
import com.anthropic.models.messages.ToolChoiceAuto;
import com.anthropic.models.messages.ToolResultBlockParam;
import com.anthropic.models.messages.ToolUseBlock;
import java.util.List;
import java.util.Map;
void main() {
// 创建客户端。它会从环境变量中读取 ANTHROPIC_API_KEY。
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// 定义一个工具。输入模式是一个 JSON Schema 对象,用于描述
// Claude 调用此工具时应传递的参数。此模式
// 包含嵌套对象(recurrence)、数组(attendees)和可选
// 字段,这比扁平的字符串参数更接近真实世界的工具。
Tool calendarTool = Tool.builder()
.name("create_calendar_event")
.description("Create a calendar event with attendees and optional recurrence.")
.inputSchema(InputSchema.builder()
.properties(JsonValue.from(Map.of(
"title", Map.of("type", "string"),
"start", Map.of("type", "string", "format", "date-time"),
"end", Map.of("type", "string", "format", "date-time"),
"attendees", Map.of(
"type", "array",
"items", Map.of("type", "string", "format", "email")
),
"recurrence", Map.of(
"type", "object",
"properties", Map.of(
"frequency", Map.of("enum", List.of("daily", "weekly", "monthly")),
"count", Map.of("type", "integer", "minimum", 1)
)
)
)))
.required(List.of("title", "start", "end"))
.build())
.build();
// 要求每轮最多调用一次工具,以便后续的单轮流程
// 保持可预测。
ToolChoiceAuto toolChoice = ToolChoiceAuto.builder()
.disableParallelToolUse(true)
.build();
String userPrompt =
"Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am.";
// 将用户请求与工具定义一起发送。Claude 会根据
// 请求和工具描述来决定是否调用该工具。
Message response = client.messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addTool(calendarTool)
.toolChoice(toolChoice)
.addUserMessage(userPrompt)
.build());
// 当 Claude 调用工具时,响应的 stop_reason 为 "tool_use",
// 并且 content 数组中会包含一个 tool_use 块以及任何文本。
IO.println("stop_reason: " + response.stopReason().orElse(null));
// 查找 tool_use 块。响应可能在 tool_use 块之前包含文本块,
// 因此应扫描 content 数组,而不是假定其位置。
ToolUseBlock toolUse = response.content().stream()
.flatMap(block -> block.toolUse().stream())
.findFirst()
.orElseThrow();
IO.println("Tool: " + toolUse.name());
IO.println("Input: " + toolUse._input());
// 运行工具。在实际系统中,这里会调用您的日历 API。
// 此处将结果硬编码,以使示例保持独立完整。
String result = "{\"event_id\": \"evt_123\", \"status\": \"created\"}";
// 将结果发回。tool_result 块放在 user 消息中,
// 其 tool_use_id 必须与之前 tool_use 块中的 ID 匹配。
// 同时包含助手之前的响应,以便 Claude 获得完整历史记录。
Message followup = client.messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addTool(calendarTool)
.toolChoice(toolChoice)
.addUserMessage(userPrompt)
.addMessage(response)
.addUserMessageOfContentBlockParams(List.of(ContentBlockParam.ofToolResult(
ToolResultBlockParam.builder()
.toolUseId(toolUse.id())
.content(result)
.build())))
.build());
// 获得工具结果后,Claude 会生成最终的自然语言
// 回答,stop_reason 变为 "end_turn"。
IO.println("stop_reason: " + followup.stopReason().orElse(null));
followup.content().stream()
.flatMap(block -> block.text().stream())
.forEach(textBlock -> IO.println(textBlock.text()));
}
PHP
<?php
// 第 1 环:单个工具,单轮对话。
use Anthropic\Client;
use Anthropic\Messages\ToolChoiceAuto;
// 创建客户端。它会从环境变量中读取 ANTHROPIC_API_KEY。
$client = new Client();
// 定义一个工具。input_schema 是一个 JSON Schema 对象,用于描述
// Claude 调用此工具时应传递的参数。此 schema
// 包含嵌套对象(recurrence)、数组(attendees)和可选
// 字段,相比扁平的字符串参数更接近真实世界的工具。
$tools = [
[
'name' => 'create_calendar_event',
'description' => 'Create a calendar event with attendees and optional recurrence.',
'input_schema' => [
'type' => 'object',
'properties' => [
'title' => ['type' => 'string'],
'start' => ['type' => 'string', 'format' => 'date-time'],
'end' => ['type' => 'string', 'format' => 'date-time'],
'attendees' => [
'type' => 'array',
'items' => ['type' => 'string', 'format' => 'email'],
],
'recurrence' => [
'type' => 'object',
'properties' => [
'frequency' => ['enum' => ['daily', 'weekly', 'monthly']],
'count' => ['type' => 'integer', 'minimum' => 1],
],
],
],
'required' => ['title', 'start', 'end'],
],
],
];
$userMessage = [
'role' => 'user',
'content' => 'Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am.',
];
// 要求每轮最多调用一次工具,以便后续的单轮流程
// 保持可预测。
$toolChoice = ToolChoiceAuto::with(disableParallelToolUse: true);
// 将用户请求与工具定义一起发送。Claude 会根据
// 请求和工具描述来决定是否调用该工具。
$response = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 1024,
tools: $tools,
toolChoice: $toolChoice,
messages: [$userMessage],
);
// 当 Claude 调用工具时,响应的 stop_reason 为 "tool_use",
// 且 content 数组中会包含一个 tool_use 块以及可能的文本。
printf("stop_reason: %s\n", $response->stopReason);
// 查找 tool_use 块。响应中可能在 tool_use 块之前包含文本块,
// 因此应扫描 content 数组,而不是假定其位置。
$toolUse = null;
foreach ($response->content as $block) {
if ($block->type === 'tool_use') {
$toolUse = $block;
break;
}
}
printf("Tool: %s\n", $toolUse->name);
printf("Input: %s\n", json_encode($toolUse->input));
// 运行工具。在真实系统中,这里会调用您的日历 API。
// 此处将结果硬编码,以使示例保持独立完整。
$result = ['event_id' => 'evt_123', 'status' => 'created'];
// 将结果发回。tool_result 块放在 user 消息中,
// 其 tool_use_id 必须与之前 tool_use 块的 ID 匹配。
// 同时包含助手之前的响应,以便 Claude 获得完整历史。
$followup = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 1024,
tools: $tools,
toolChoice: $toolChoice,
messages: [
$userMessage,
['role' => 'assistant', 'content' => $response->content],
[
'role' => 'user',
'content' => [
[
'type' => 'tool_result',
'tool_use_id' => $toolUse->id,
'content' => json_encode($result),
],
],
],
],
);
// 获得工具结果后,Claude 会生成最终的自然语言
// 回答,stop_reason 变为 "end_turn"。
printf("stop_reason: %s\n", $followup->stopReason);
foreach ($followup->content as $block) {
if ($block->type === 'text') {
echo $block->text, "\n";
}
}
Ruby
# 第 1 环:单个工具,单轮对话。
require "anthropic"
# 创建客户端。它会从环境变量中读取 ANTHROPIC_API_KEY。
client = Anthropic::Client.new
# 定义一个工具。input_schema 是一个 JSON Schema 对象,用于描述
# Claude 调用此工具时应传递的参数。此 schema
# 包含嵌套对象(recurrence)、数组(attendees)和可选
# 字段,这比扁平的字符串参数更接近真实世界的工具。
tools = [
{
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
input_schema: {
type: "object",
properties: {
title: {type: "string"},
start: {type: "string", format: "date-time"},
end: {type: "string", format: "date-time"},
attendees: {
type: "array",
items: {type: "string", format: "email"}
},
recurrence: {
type: "object",
properties: {
frequency: {enum: ["daily", "weekly", "monthly"]},
count: {type: "integer", minimum: 1}
}
}
},
required: ["title", "start", "end"]
}
}
]
user_message = {
role: "user",
content: "Schedule a 30-minute sync with alice@example.com and bob@example.com on Monday, March 30, 2026 at 10am."
}
# 要求每轮最多调用一次工具,以便后续的单轮流程
# 保持可预测。
tool_choice = {type: "auto", disable_parallel_tool_use: true}
# 将用户的请求连同工具定义一起发送。Claude 会根据
# 请求和工具描述来决定是否调用该工具。
response = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: tools,
tool_choice: tool_choice,
messages: [user_message]
)
# 当 Claude 调用工具时,响应的 stop_reason 为 "tool_use",
# 并且 content 数组中会包含一个 tool_use 块以及可能的文本。
puts "stop_reason: #{response.stop_reason}"
# 查找 tool_use 块。响应可能在 tool_use 块之前包含文本块,
# 因此应扫描 content 数组,而不是假定其位置。
tool_use = response.content.find { |block| block.type == :tool_use }
puts "Tool: #{tool_use.name}"
puts "Input: #{tool_use.input}"
# 运行工具。在真实系统中,这里会调用您的日历 API。
# 此处将结果硬编码,以使示例保持独立完整。
result = {event_id: "evt_123", status: "created"}
# 将结果发回。tool_result 块放在 user 消息中,
# 其 tool_use_id 必须与之前 tool_use 块的 ID 匹配。
# 同时包含 assistant 之前的响应,以便 Claude 获得完整历史。
followup = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: tools,
tool_choice: tool_choice,
messages: [
user_message,
{role: "assistant", content: response.content},
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: tool_use.id,
content: JSON.generate(result)
}
]
}
]
)
# 获得工具结果后,Claude 会生成最终的自然语言
# 回答,且 stop_reason 变为 "end_turn"。
puts "stop_reason: #{followup.stop_reason}"
followup.content.each do |block|
puts block.text if block.type == :text
end
预期结果
Output
stop_reason: tool_use
Tool: create_calendar_event
Input: {'title': 'Sync', 'start': '2026-03-30T10:00:00', 'end': '2026-03-30T10:30:00', 'attendees': ['alice@example.com', 'bob@example.com']}
stop_reason: end_turn
I've scheduled your 30-minute sync with Alice and Bob for Monday, March 30 at 10am.
第一个 stop_reason 是 tool_use,因为 Claude 正在等待日历结果。在您发送结果之后,第二个 stop_reason 是 end_turn,其内容是面向用户的自然语言。
第 2 环:智能体循环
第 1 环假设 Claude 恰好只调用一次工具。实际任务通常需要多次调用:Claude 可能会创建一个事件,读取确认信息,然后再创建另一个。解决办法是使用一个 while 循环,持续运行工具并将结果反馈回去,直到 stop_reason 不再是 "tool_use"。
另一个变化是对话历史。不要在每次请求时重新构建 messages 数组,而是维护一个持续更新的列表并向其追加内容。这样每次请求都会包含完整的先前上下文。
cURL
此工作流需要的逻辑超出了单个 shell 命令所能实现的范围。请使用 Claude SDK 运行它。
CLI
此工作流需要的逻辑超出了单个 shell 命令所能实现的范围。请使用 Claude SDK 运行它。
Python
# 第 2 环:agentic loop(智能体循环)。
import json
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "create_calendar_event",
"description": "Create a calendar event with attendees and optional recurrence.",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start": {"type": "string", "format": "date-time"},
"end": {"type": "string", "format": "date-time"},
"attendees": {
"type": "array",
"items": {"type": "string", "format": "email"},
},
"recurrence": {
"type": "object",
"properties": {
"frequency": {"enum": ["daily", "weekly", "monthly"]},
"count": {"type": "integer", "minimum": 1},
},
},
},
"required": ["title", "start", "end"],
},
}
]
def run_tool(name, tool_input):
if name == "create_calendar_event":
return {
"event_id": "evt_123",
"status": "created",
"title": tool_input["title"],
}
return {"error": f"Unknown tool: {name}"}
# 将完整的对话历史保存在列表中,使每一轮都能看到之前的上下文。
messages = [
{
"role": "user",
"content": "Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: alice@example.com, bob@example.com, carol@example.com.",
}
]
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=tools,
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=messages,
)
# 循环直到 Claude 不再请求工具。每次迭代都会运行所请求的
# 工具,将结果追加到历史记录中,并请求 Claude 继续。
while response.stop_reason == "tool_use":
tool_use = next(block for block in response.content if block.type == "tool_use")
result = run_tool(tool_use.name, tool_use.input)
messages.append({"role": "assistant", "content": response.content})
messages.append(
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": json.dumps(result),
}
],
}
)
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=tools,
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=messages,
)
final_text = next(block for block in response.content if block.type == "text")
print(final_text.text)
TypeScript
// 第 2 环:智能体循环(agentic loop)。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [
{
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
input_schema: {
type: "object",
properties: {
title: { type: "string" },
start: { type: "string", format: "date-time" },
end: { type: "string", format: "date-time" },
attendees: {
type: "array",
items: { type: "string", format: "email" }
},
recurrence: {
type: "object",
properties: {
frequency: { enum: ["daily", "weekly", "monthly"] },
count: { type: "integer", minimum: 1 }
}
}
},
required: ["title", "start", "end"]
}
}
];
function runTool(name: string, input: Record<string, unknown>) {
if (name === "create_calendar_event") {
return { event_id: "evt_123", status: "created", title: input.title };
}
return { error: `Unknown tool: ${name}` };
}
// 保留完整的对话历史,使每一轮都能看到之前的上下文。
const messages: Anthropic.MessageParam[] = [
{
role: "user",
content:
"Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: alice@example.com, bob@example.com, carol@example.com."
}
];
let response = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
tool_choice: { type: "auto", disable_parallel_tool_use: true },
messages
});
// 循环直到 Claude 不再请求工具。每次迭代都会运行所请求的
// 工具,将结果追加到历史记录中,并请 Claude 继续。
while (response.stop_reason === "tool_use") {
const toolUse = response.content.find((block) => block.type === "tool_use")!;
const result = runTool(toolUse.name, toolUse.input as Record<string, unknown>);
messages.push({ role: "assistant", content: response.content });
messages.push({
role: "user",
content: [
{
type: "tool_result",
tool_use_id: toolUse.id,
content: JSON.stringify(result)
}
]
});
response = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
tool_choice: { type: "auto", disable_parallel_tool_use: true },
messages
});
}
for (const block of response.content) {
if (block.type === "text") {
console.log(block.text);
}
}
C#
// 第 2 环:agentic loop(智能体循环)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
List<ToolUnion> tools =
[
new ToolUnion(new Tool()
{
Name = "create_calendar_event",
Description = "Create a calendar event with attendees and optional recurrence.",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["title"] = JsonSerializer.SerializeToElement(new { type = "string" }),
["start"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date-time" }),
["end"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date-time" }),
["attendees"] = JsonSerializer.SerializeToElement(new
{
type = "array",
items = new { type = "string", format = "email" },
}),
["recurrence"] = JsonSerializer.SerializeToElement(new
{
type = "object",
properties = new
{
frequency = new { @enum = new[] { "daily", "weekly", "monthly" } },
count = new { type = "integer", minimum = 1 },
},
}),
},
Required = ["title", "start", "end"],
},
}),
];
// 运行所请求的工具,并以字符串形式返回其结果。
string RunTool(ToolUseBlock toolUse)
{
if (toolUse.Name == "create_calendar_event")
{
var title = toolUse.Input.TryGetValue("title", out var t) ? t.GetString() : "";
return JsonSerializer.Serialize(new { event_id = "evt_123", status = "created", title });
}
return JsonSerializer.Serialize(new { error = $"Unknown tool: {toolUse.Name}" });
}
var toolChoice = new ToolChoice(new ToolChoiceAuto { DisableParallelToolUse = true });
// 将完整的对话历史保存在列表中,使每一轮都能看到先前的上下文。
List<MessageParam> messages =
[
new UserMessageParam
{
Content = "Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: alice@example.com, bob@example.com, carol@example.com.",
},
];
var response = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = tools,
ToolChoice = toolChoice,
Messages = messages,
});
// 循环执行,直到 Claude 不再请求工具。每次迭代都会运行所请求的
// 工具,将结果追加到历史记录中,并请求 Claude 继续。
while (response.StopReason == StopReason.ToolUse)
{
ToolUseBlock? toolUse = null;
foreach (var block in response.Content)
{
if (block.TryPickToolUse(out var picked))
{
toolUse = picked;
break;
}
}
var result = RunTool(toolUse!);
messages.Add(new AssistantMessageParam
{
Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList(),
});
messages.Add(new UserMessageParam
{
Content = new MessageContentParam(
[
new ContentBlockParam(new ToolResultBlockParam() { ToolUseID = toolUse!.ID, Content = result }),
]),
});
response = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = tools,
ToolChoice = toolChoice,
Messages = messages,
});
}
foreach (var block in response.Content)
{
if (block.TryPickText(out var text))
{
Console.WriteLine(text.Text);
}
}
Go
// 第 2 环:智能体循环(agentic loop)。
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go/v2"
)
func runTool(name string, input map[string]any) string {
if name == "create_calendar_event" {
title, _ := input["title"].(string)
return fmt.Sprintf(`{"event_id": "evt_123", "status": "created", "title": %q}`, title)
}
return fmt.Sprintf(`{"error": "Unknown tool: %s"}`, name)
}
func main() {
client := anthropic.NewClient()
ctx := context.Background()
tools := []anthropic.ToolUnionParam{
{OfTool: &anthropic.ToolParam{
Name: "create_calendar_event",
Description: anthropic.String("Create a calendar event with attendees and optional recurrence."),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"title": map[string]any{"type": "string"},
"start": map[string]any{"type": "string", "format": "date-time"},
"end": map[string]any{"type": "string", "format": "date-time"},
"attendees": map[string]any{
"type": "array",
"items": map[string]any{"type": "string", "format": "email"},
},
"recurrence": map[string]any{
"type": "object",
"properties": map[string]any{
"frequency": map[string]any{"enum": []string{"daily", "weekly", "monthly"}},
"count": map[string]any{"type": "integer", "minimum": 1},
},
},
},
Required: []string{"title", "start", "end"},
},
}},
}
toolChoice := anthropic.ToolChoiceUnionParam{
OfAuto: &anthropic.ToolChoiceAutoParam{DisableParallelToolUse: anthropic.Bool(true)},
}
// 将完整的对话历史保存在一个 slice 中,使每一轮都能看到先前的上下文。
messages := []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock(
"Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: alice@example.com, bob@example.com, carol@example.com.",
)),
}
response, err := client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Tools: tools,
ToolChoice: toolChoice,
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
// 循环直到 Claude 不再请求工具。每次迭代都会运行所请求的
// 工具,将结果追加到历史记录中,并请求 Claude 继续。
for response.StopReason == "tool_use" {
var toolUse anthropic.ContentBlockUnion
for _, block := range response.Content {
if block.Type == "tool_use" {
toolUse = block
break
}
}
var input map[string]any
if err := json.Unmarshal(toolUse.Input, &input); err != nil {
log.Fatal(err)
}
result := runTool(toolUse.Name, input)
var assistantContent []anthropic.ContentBlockParamUnion
for _, block := range response.Content {
assistantContent = append(assistantContent, block.ToParam())
}
messages = append(messages, anthropic.NewAssistantMessage(assistantContent...))
messages = append(messages, anthropic.NewUserMessage(
anthropic.NewToolResultBlock(toolUse.ID, result, false),
))
response, err = client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Tools: tools,
ToolChoice: toolChoice,
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
}
for _, block := range response.Content {
if block.Type == "text" {
fmt.Println(block.Text)
}
}
}
Java
// 第 2 环:agentic loop(智能体循环)。
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.ContentBlockParam;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.MessageParam;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.StopReason;
import com.anthropic.models.messages.Tool;
import com.anthropic.models.messages.Tool.InputSchema;
import com.anthropic.models.messages.ToolChoiceAuto;
import com.anthropic.models.messages.ToolResultBlockParam;
import com.anthropic.models.messages.ToolUseBlock;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
String runTool(ToolUseBlock toolUse) {
// 原始工具输入是一个 JSON 对象;请将其作为 map 读取其中的字段。
Map<String, JsonValue> input = (Map<String, JsonValue>) toolUse._input().asObject().get();
if (toolUse.name().equals("create_calendar_event")) {
String title = input.containsKey("title") ? input.get("title").asStringOrThrow() : "";
return "{\"event_id\": \"evt_123\", \"status\": \"created\", \"title\": \"" + title + "\"}";
}
return "{\"error\": \"Unknown tool: " + toolUse.name() + "\"}";
}
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
Tool calendarTool = Tool.builder()
.name("create_calendar_event")
.description("Create a calendar event with attendees and optional recurrence.")
.inputSchema(InputSchema.builder()
.properties(JsonValue.from(Map.of(
"title", Map.of("type", "string"),
"start", Map.of("type", "string", "format", "date-time"),
"end", Map.of("type", "string", "format", "date-time"),
"attendees", Map.of(
"type", "array",
"items", Map.of("type", "string", "format", "email")
),
"recurrence", Map.of(
"type", "object",
"properties", Map.of(
"frequency", Map.of("enum", List.of("daily", "weekly", "monthly")),
"count", Map.of("type", "integer", "minimum", 1)
)
)
)))
.required(List.of("title", "start", "end"))
.build())
.build();
ToolChoiceAuto toolChoice = ToolChoiceAuto.builder()
.disableParallelToolUse(true)
.build();
// 将完整的对话历史保存在列表中,使每一轮都能看到先前的上下文。
List<MessageParam> messages = new ArrayList<>();
messages.add(MessageParam.ofUser(
"Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: alice@example.com, bob@example.com, carol@example.com."));
Message response = client.messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addTool(calendarTool)
.toolChoice(toolChoice)
.messages(messages)
.build());
// 循环直到 Claude 不再请求工具。每次迭代都会运行所请求的
// 工具,将结果追加到历史记录中,并请求 Claude 继续。
while (response.stopReason().isPresent()
&& response.stopReason().get().equals(StopReason.TOOL_USE)) {
ToolUseBlock toolUse = response.content().stream()
.flatMap(block -> block.toolUse().stream())
.findFirst()
.orElseThrow();
String result = runTool(toolUse);
messages.add(response.toParam());
messages.add(MessageParam.ofUserOfContentBlockParams(List.of(ContentBlockParam.ofToolResult(
ToolResultBlockParam.builder()
.toolUseId(toolUse.id())
.content(result)
.build()))));
response = client.messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addTool(calendarTool)
.toolChoice(toolChoice)
.messages(messages)
.build());
}
response.content().stream()
.flatMap(block -> block.text().stream())
.forEach(textBlock -> IO.println(textBlock.text()));
}
PHP
<?php
// 第 2 环:agentic loop(智能体循环)。
use Anthropic\Client;
use Anthropic\Messages\ToolChoiceAuto;
$client = new Client();
$tools = [
[
'name' => 'create_calendar_event',
'description' => 'Create a calendar event with attendees and optional recurrence.',
'input_schema' => [
'type' => 'object',
'properties' => [
'title' => ['type' => 'string'],
'start' => ['type' => 'string', 'format' => 'date-time'],
'end' => ['type' => 'string', 'format' => 'date-time'],
'attendees' => [
'type' => 'array',
'items' => ['type' => 'string', 'format' => 'email'],
],
'recurrence' => [
'type' => 'object',
'properties' => [
'frequency' => ['enum' => ['daily', 'weekly', 'monthly']],
'count' => ['type' => 'integer', 'minimum' => 1],
],
],
],
'required' => ['title', 'start', 'end'],
],
],
];
function runTool(string $name, array $input): string
{
if ($name === 'create_calendar_event') {
return json_encode([
'event_id' => 'evt_123',
'status' => 'created',
'title' => $input['title'],
]);
}
return json_encode(['error' => "Unknown tool: {$name}"]);
}
$toolChoice = ToolChoiceAuto::with(disableParallelToolUse: true);
// 将完整的对话历史保存在数组中,使每一轮都能看到先前的上下文。
$messages = [
[
'role' => 'user',
'content' => 'Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: alice@example.com, bob@example.com, carol@example.com.',
],
];
$response = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 1024,
tools: $tools,
toolChoice: $toolChoice,
messages: $messages,
);
// 循环直到 Claude 不再请求工具。每次迭代都会运行所请求的
// 工具,将结果追加到历史记录中,并请求 Claude 继续。
while ($response->stopReason === 'tool_use') {
$toolUse = null;
foreach ($response->content as $block) {
if ($block->type === 'tool_use') {
$toolUse = $block;
break;
}
}
$result = runTool($toolUse->name, $toolUse->input);
$messages[] = ['role' => 'assistant', 'content' => $response->content];
$messages[] = [
'role' => 'user',
'content' => [
[
'type' => 'tool_result',
'tool_use_id' => $toolUse->id,
'content' => $result,
],
],
];
$response = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 1024,
tools: $tools,
toolChoice: $toolChoice,
messages: $messages,
);
}
foreach ($response->content as $block) {
if ($block->type === 'text') {
echo $block->text, "\n";
}
}
Ruby
# 第 2 环:agentic loop(智能体循环)。
require "anthropic"
client = Anthropic::Client.new
tools = [
{
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
input_schema: {
type: "object",
properties: {
title: {type: "string"},
start: {type: "string", format: "date-time"},
end: {type: "string", format: "date-time"},
attendees: {
type: "array",
items: {type: "string", format: "email"}
},
recurrence: {
type: "object",
properties: {
frequency: {enum: ["daily", "weekly", "monthly"]},
count: {type: "integer", minimum: 1}
}
}
},
required: ["title", "start", "end"]
}
}
]
def run_tool(name, input)
case name
when "create_calendar_event"
JSON.generate({event_id: "evt_123", status: "created", title: input[:title]})
else
JSON.generate({error: "Unknown tool: #{name}"})
end
end
tool_choice = {type: "auto", disable_parallel_tool_use: true}
# 将完整的对话历史保存在数组中,使每一轮都能看到先前的上下文。
messages = [
{
role: "user",
content: "Schedule a weekly team standup every Monday at 9am for the next 4 weeks, starting Monday, March 30, 2026. Invite the whole team: alice@example.com, bob@example.com, carol@example.com."
}
]
response = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: tools,
tool_choice: tool_choice,
messages: messages
)
# 循环直到 Claude 不再请求工具。每次迭代都会运行所请求的
# 工具,将结果追加到历史记录中,并请求 Claude 继续。
while response.stop_reason == :tool_use
tool_use = response.content.find { |block| block.type == :tool_use }
result = run_tool(tool_use.name, tool_use.input)
messages << {role: "assistant", content: response.content}
messages << {
role: "user",
content: [
{
type: "tool_result",
tool_use_id: tool_use.id,
content: result
}
]
}
response = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: tools,
tool_choice: tool_choice,
messages: messages
)
end
response.content.each do |block|
puts block.text if block.type == :text
end
预期结果
Output
I've set up your weekly team standup for the next 4 Mondays at 9am with Alice, Bob, and Carol invited.
循环可能运行一次或多次,取决于 Claude 如何拆解任务。您的代码不再需要事先知道。
第 3 环:多个工具,并行调用
智能体很少只有一种能力。添加第二个工具 list_calendar_events,这样 Claude 就可以在创建新事件之前检查现有日程。
当 Claude 需要进行多个相互独立的工具调用时,它可能会在单个响应中返回多个 tool_use 块。您的循环需要处理所有这些块,并在一条用户消息中将所有结果一起发回。请遍历 response.content 中的每一个 tool_use 块,而不仅仅是第一个。
cURL
此工作流需要的逻辑超出了单个 shell 命令所能实现的范围。请使用 Claude SDK 运行它。
CLI
此工作流需要的逻辑超出了单个 shell 命令所能实现的范围。请使用 Claude SDK 运行它。
Python
# 第 3 环:多个工具,并行调用。
import json
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "create_calendar_event",
"description": "Create a calendar event with attendees and optional recurrence.",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start": {"type": "string", "format": "date-time"},
"end": {"type": "string", "format": "date-time"},
"attendees": {
"type": "array",
"items": {"type": "string", "format": "email"},
},
"recurrence": {
"type": "object",
"properties": {
"frequency": {"enum": ["daily", "weekly", "monthly"]},
"count": {"type": "integer", "minimum": 1},
},
},
},
"required": ["title", "start", "end"],
},
},
{
"name": "list_calendar_events",
"description": "List all calendar events on a given date.",
"input_schema": {
"type": "object",
"properties": {
"date": {"type": "string", "format": "date"},
},
"required": ["date"],
},
},
]
def run_tool(name, tool_input):
if name == "create_calendar_event":
return {
"event_id": "evt_123",
"status": "created",
"title": tool_input["title"],
}
if name == "list_calendar_events":
return {
"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]
}
return {"error": f"Unknown tool: {name}"}
messages = [
{
"role": "user",
"content": "Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.",
}
]
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
while response.stop_reason == "tool_use":
# 单个响应可以包含多个 tool_use 块。请处理所有
# 这些块,并在一条用户消息中一并返回所有结果。
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = run_tool(block.name, block.input)
tool_results.append(
{
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result),
}
)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
final_text = next(block for block in response.content if block.type == "text")
print(final_text.text)
TypeScript
// 第 3 环:多个工具,并行调用。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [
{
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
input_schema: {
type: "object",
properties: {
title: { type: "string" },
start: { type: "string", format: "date-time" },
end: { type: "string", format: "date-time" },
attendees: {
type: "array",
items: { type: "string", format: "email" }
},
recurrence: {
type: "object",
properties: {
frequency: { enum: ["daily", "weekly", "monthly"] },
count: { type: "integer", minimum: 1 }
}
}
},
required: ["title", "start", "end"]
}
},
{
name: "list_calendar_events",
description: "List all calendar events on a given date.",
input_schema: {
type: "object",
properties: {
date: { type: "string", format: "date" }
},
required: ["date"]
}
}
];
function runTool(name: string, input: Record<string, unknown>) {
if (name === "create_calendar_event") {
return { event_id: "evt_123", status: "created", title: input.title };
}
if (name === "list_calendar_events") {
return {
events: [{ title: "Existing meeting", start: "14:00", end: "15:00" }]
};
}
return { error: `Unknown tool: ${name}` };
}
const messages: Anthropic.MessageParam[] = [
{
role: "user",
content:
"Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts."
}
];
let response = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
messages
});
while (response.stop_reason === "tool_use") {
// 单个响应可以包含多个 tool_use 块。请处理所有
// 这些块,并在一条用户消息中一并返回所有结果。
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type === "tool_use") {
const result = runTool(block.name, block.input as Record<string, unknown>);
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(result)
});
}
}
messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });
response = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
messages
});
}
for (const block of response.content) {
if (block.type === "text") {
console.log(block.text);
}
}
C#
// 第 3 环:多个工具,并行调用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
List<ToolUnion> tools =
[
new ToolUnion(new Tool()
{
Name = "create_calendar_event",
Description = "Create a calendar event with attendees and optional recurrence.",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["title"] = JsonSerializer.SerializeToElement(new { type = "string" }),
["start"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date-time" }),
["end"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date-time" }),
["attendees"] = JsonSerializer.SerializeToElement(new
{
type = "array",
items = new { type = "string", format = "email" },
}),
["recurrence"] = JsonSerializer.SerializeToElement(new
{
type = "object",
properties = new
{
frequency = new { @enum = new[] { "daily", "weekly", "monthly" } },
count = new { type = "integer", minimum = 1 },
},
}),
},
Required = ["title", "start", "end"],
},
}),
new ToolUnion(new Tool()
{
Name = "list_calendar_events",
Description = "List all calendar events on a given date.",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["date"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date" }),
},
Required = ["date"],
},
}),
];
string RunTool(ToolUseBlock toolUse)
{
if (toolUse.Name == "create_calendar_event")
{
var title = toolUse.Input.TryGetValue("title", out var t) ? t.GetString() : "";
return JsonSerializer.Serialize(new { event_id = "evt_123", status = "created", title });
}
if (toolUse.Name == "list_calendar_events")
{
return """{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}""";
}
return JsonSerializer.Serialize(new { error = $"Unknown tool: {toolUse.Name}" });
}
List<MessageParam> messages =
[
new UserMessageParam
{
Content = "Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.",
},
];
var response = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = tools,
Messages = messages,
});
while (response.StopReason == StopReason.ToolUse)
{
// 单个响应可以包含多个 tool_use 块。请处理所有
// 这些块,并在一条用户消息中一并返回所有结果。
List<ContentBlockParam> toolResults = [];
foreach (var block in response.Content)
{
if (block.TryPickToolUse(out var toolUse))
{
toolResults.Add(new ContentBlockParam(new ToolResultBlockParam()
{
ToolUseID = toolUse.ID,
Content = RunTool(toolUse),
}));
}
}
messages.Add(new AssistantMessageParam
{
Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList(),
});
messages.Add(new UserMessageParam { Content = toolResults });
response = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = tools,
Messages = messages,
});
}
foreach (var block in response.Content)
{
if (block.TryPickText(out var text))
{
Console.WriteLine(text.Text);
}
}
Go
// 第 3 环:多个工具,并行调用。
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go/v2"
)
func runTool(name string, input map[string]any) string {
if name == "create_calendar_event" {
title, _ := input["title"].(string)
return fmt.Sprintf(`{"event_id": "evt_123", "status": "created", "title": %q}`, title)
}
if name == "list_calendar_events" {
return `{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}`
}
return fmt.Sprintf(`{"error": "Unknown tool: %s"}`, name)
}
func main() {
client := anthropic.NewClient()
ctx := context.Background()
tools := []anthropic.ToolUnionParam{
{OfTool: &anthropic.ToolParam{
Name: "create_calendar_event",
Description: anthropic.String("Create a calendar event with attendees and optional recurrence."),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"title": map[string]any{"type": "string"},
"start": map[string]any{"type": "string", "format": "date-time"},
"end": map[string]any{"type": "string", "format": "date-time"},
"attendees": map[string]any{
"type": "array",
"items": map[string]any{"type": "string", "format": "email"},
},
"recurrence": map[string]any{
"type": "object",
"properties": map[string]any{
"frequency": map[string]any{"enum": []string{"daily", "weekly", "monthly"}},
"count": map[string]any{"type": "integer", "minimum": 1},
},
},
},
Required: []string{"title", "start", "end"},
},
}},
{OfTool: &anthropic.ToolParam{
Name: "list_calendar_events",
Description: anthropic.String("List all calendar events on a given date."),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"date": map[string]any{"type": "string", "format": "date"},
},
Required: []string{"date"},
},
}},
}
messages := []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock(
"Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.",
)),
}
response, err := client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Tools: tools,
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
for response.StopReason == "tool_use" {
// 单个响应可以包含多个 tool_use 块。请处理所有
// 这些块,并在一条用户消息中一并返回所有结果。
var toolResults []anthropic.ContentBlockParamUnion
for _, block := range response.Content {
if block.Type == "tool_use" {
var input map[string]any
if err := json.Unmarshal(block.Input, &input); err != nil {
log.Fatal(err)
}
result := runTool(block.Name, input)
toolResults = append(toolResults, anthropic.NewToolResultBlock(block.ID, result, false))
}
}
var assistantContent []anthropic.ContentBlockParamUnion
for _, block := range response.Content {
assistantContent = append(assistantContent, block.ToParam())
}
messages = append(messages, anthropic.NewAssistantMessage(assistantContent...))
messages = append(messages, anthropic.NewUserMessage(toolResults...))
response, err = client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Tools: tools,
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
}
for _, block := range response.Content {
if block.Type == "text" {
fmt.Println(block.Text)
}
}
}
Java
// 第 3 环:多个工具,并行调用。
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.ContentBlock;
import com.anthropic.models.messages.ContentBlockParam;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.MessageParam;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.StopReason;
import com.anthropic.models.messages.Tool;
import com.anthropic.models.messages.Tool.InputSchema;
import com.anthropic.models.messages.ToolResultBlockParam;
import com.anthropic.models.messages.ToolUseBlock;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
String runTool(ToolUseBlock toolUse) {
// 原始工具输入是一个 JSON 对象;以 map 形式从中读取字段。
Map<String, JsonValue> input = (Map<String, JsonValue>) toolUse._input().asObject().get();
if (toolUse.name().equals("create_calendar_event")) {
String title = input.containsKey("title") ? input.get("title").asStringOrThrow() : "";
return "{\"event_id\": \"evt_123\", \"status\": \"created\", \"title\": \"" + title + "\"}";
}
if (toolUse.name().equals("list_calendar_events")) {
return "{\"events\": [{\"title\": \"Existing meeting\", \"start\": \"14:00\", \"end\": \"15:00\"}]}";
}
return "{\"error\": \"Unknown tool: " + toolUse.name() + "\"}";
}
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
Tool calendarTool = Tool.builder()
.name("create_calendar_event")
.description("Create a calendar event with attendees and optional recurrence.")
.inputSchema(InputSchema.builder()
.properties(JsonValue.from(Map.of(
"title", Map.of("type", "string"),
"start", Map.of("type", "string", "format", "date-time"),
"end", Map.of("type", "string", "format", "date-time"),
"attendees", Map.of(
"type", "array",
"items", Map.of("type", "string", "format", "email")
),
"recurrence", Map.of(
"type", "object",
"properties", Map.of(
"frequency", Map.of("enum", List.of("daily", "weekly", "monthly")),
"count", Map.of("type", "integer", "minimum", 1)
)
)
)))
.required(List.of("title", "start", "end"))
.build())
.build();
Tool listTool = Tool.builder()
.name("list_calendar_events")
.description("List all calendar events on a given date.")
.inputSchema(InputSchema.builder()
.properties(JsonValue.from(Map.of(
"date", Map.of("type", "string", "format", "date")
)))
.required(List.of("date"))
.build())
.build();
List<MessageParam> messages = new ArrayList<>();
messages.add(MessageParam.ofUser(
"Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts."));
Message response = client.messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addTool(calendarTool)
.addTool(listTool)
.messages(messages)
.build());
while (response.stopReason().isPresent()
&& response.stopReason().get().equals(StopReason.TOOL_USE)) {
// 单个响应可能包含多个 tool_use 块。请处理所有
// 这些块,并在一条 user 消息中一并返回所有结果。
List<ContentBlockParam> toolResults = new ArrayList<>();
for (ContentBlock block : response.content()) {
if (block.toolUse().isPresent()) {
ToolUseBlock toolUse = block.toolUse().get();
toolResults.add(ContentBlockParam.ofToolResult(
ToolResultBlockParam.builder()
.toolUseId(toolUse.id())
.content(runTool(toolUse))
.build()));
}
}
messages.add(response.toParam());
messages.add(MessageParam.ofUserOfContentBlockParams(toolResults));
response = client.messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addTool(calendarTool)
.addTool(listTool)
.messages(messages)
.build());
}
response.content().stream()
.flatMap(block -> block.text().stream())
.forEach(textBlock -> IO.println(textBlock.text()));
}
PHP
<?php
// 第 3 环:多个工具,并行调用。
use Anthropic\Client;
$client = new Client();
$tools = [
[
'name' => 'create_calendar_event',
'description' => 'Create a calendar event with attendees and optional recurrence.',
'input_schema' => [
'type' => 'object',
'properties' => [
'title' => ['type' => 'string'],
'start' => ['type' => 'string', 'format' => 'date-time'],
'end' => ['type' => 'string', 'format' => 'date-time'],
'attendees' => [
'type' => 'array',
'items' => ['type' => 'string', 'format' => 'email'],
],
'recurrence' => [
'type' => 'object',
'properties' => [
'frequency' => ['enum' => ['daily', 'weekly', 'monthly']],
'count' => ['type' => 'integer', 'minimum' => 1],
],
],
],
'required' => ['title', 'start', 'end'],
],
],
[
'name' => 'list_calendar_events',
'description' => 'List all calendar events on a given date.',
'input_schema' => [
'type' => 'object',
'properties' => [
'date' => ['type' => 'string', 'format' => 'date'],
],
'required' => ['date'],
],
],
];
function runTool(string $name, array $input): string
{
if ($name === 'create_calendar_event') {
return json_encode([
'event_id' => 'evt_123',
'status' => 'created',
'title' => $input['title'],
]);
}
if ($name === 'list_calendar_events') {
return json_encode([
'events' => [['title' => 'Existing meeting', 'start' => '14:00', 'end' => '15:00']],
]);
}
return json_encode(['error' => "Unknown tool: {$name}"]);
}
$messages = [
[
'role' => 'user',
'content' => 'Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.',
],
];
$response = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 1024,
tools: $tools,
messages: $messages,
);
while ($response->stopReason === 'tool_use') {
// 单个响应可以包含多个 tool_use 块。请处理所有
// 这些块,并在一条用户消息中一并返回所有结果。
$toolResults = [];
foreach ($response->content as $block) {
if ($block->type === 'tool_use') {
$toolResults[] = [
'type' => 'tool_result',
'tool_use_id' => $block->id,
'content' => runTool($block->name, $block->input),
];
}
}
$messages[] = ['role' => 'assistant', 'content' => $response->content];
$messages[] = ['role' => 'user', 'content' => $toolResults];
$response = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 1024,
tools: $tools,
messages: $messages,
);
}
foreach ($response->content as $block) {
if ($block->type === 'text') {
echo $block->text, "\n";
}
}
Ruby
# 第 3 环:多个工具,并行调用。
require "anthropic"
client = Anthropic::Client.new
tools = [
{
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
input_schema: {
type: "object",
properties: {
title: {type: "string"},
start: {type: "string", format: "date-time"},
end: {type: "string", format: "date-time"},
attendees: {
type: "array",
items: {type: "string", format: "email"}
},
recurrence: {
type: "object",
properties: {
frequency: {enum: ["daily", "weekly", "monthly"]},
count: {type: "integer", minimum: 1}
}
}
},
required: ["title", "start", "end"]
}
},
{
name: "list_calendar_events",
description: "List all calendar events on a given date.",
input_schema: {
type: "object",
properties: {
date: {type: "string", format: "date"}
},
required: ["date"]
}
}
]
def run_tool(name, input)
case name
when "create_calendar_event"
JSON.generate({event_id: "evt_123", status: "created", title: input[:title]})
when "list_calendar_events"
JSON.generate({events: [{title: "Existing meeting", start: "14:00", end: "15:00"}]})
else
JSON.generate({error: "Unknown tool: #{name}"})
end
end
messages = [
{
role: "user",
content: "Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts."
}
]
response = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: tools,
messages: messages
)
while response.stop_reason == :tool_use
# 单个响应可以包含多个 tool_use 块。请处理所有
# 这些块,并在一条用户消息中一并返回所有结果。
tool_results = response.content.select { |block| block.type == :tool_use }.map do |tool_use|
{
type: "tool_result",
tool_use_id: tool_use.id,
content: run_tool(tool_use.name, tool_use.input)
}
end
messages << {role: "assistant", content: response.content}
messages << {role: "user", content: tool_results}
response = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: tools,
messages: messages
)
end
response.content.each do |block|
puts block.text if block.type == :text
end
预期结果
Output
I checked your calendar for Monday, March 30 and found an existing meeting from 2pm to 3pm. I've scheduled the planning session for 10am to 11am to avoid the conflict.
有关并发执行和顺序保证的更多信息,请参阅并行工具使用。
第 4 环:错误处理
工具会失败。日历 API 可能会拒绝参与者过多的事件,或者日期格式可能有误。当工具抛出错误时,请使用 is_error: true 将错误消息发回,而不是让程序崩溃。Claude 会读取错误信息,并可以使用修正后的输入重试、向用户请求澄清,或解释该限制。
cURL
此工作流需要的逻辑超出了单个 shell 命令所能实现的范围。请使用 Claude SDK 运行它。
CLI
此工作流需要的逻辑超出了单个 shell 命令所能实现的范围。请使用 Claude SDK 运行它。
Python
# 第 4 环:错误处理。
import json
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "create_calendar_event",
"description": "Create a calendar event with attendees and optional recurrence.",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start": {"type": "string", "format": "date-time"},
"end": {"type": "string", "format": "date-time"},
"attendees": {
"type": "array",
"items": {"type": "string", "format": "email"},
},
"recurrence": {
"type": "object",
"properties": {
"frequency": {"enum": ["daily", "weekly", "monthly"]},
"count": {"type": "integer", "minimum": 1},
},
},
},
"required": ["title", "start", "end"],
},
},
{
"name": "list_calendar_events",
"description": "List all calendar events on a given date.",
"input_schema": {
"type": "object",
"properties": {
"date": {"type": "string", "format": "date"},
},
"required": ["date"],
},
},
]
def run_tool(name, tool_input):
if name == "create_calendar_event":
if "attendees" in tool_input and len(tool_input["attendees"]) > 10:
raise ValueError("Too many attendees (max 10)")
return {
"event_id": "evt_123",
"status": "created",
"title": tool_input["title"],
}
if name == "list_calendar_events":
return {
"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]
}
raise ValueError(f"Unknown tool: {name}")
messages = [
{
"role": "user",
"content": "Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: "
+ ", ".join(f"user{i}@example.com" for i in range(15)),
}
]
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
while response.stop_reason == "tool_use":
tool_results = []
for block in response.content:
if block.type == "tool_use":
try:
result = run_tool(block.name, block.input)
tool_results.append(
{
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result),
}
)
except Exception as exc:
# 发出失败信号,以便 Claude 重试或请求澄清。
tool_results.append(
{
"type": "tool_result",
"tool_use_id": block.id,
"content": str(exc),
"is_error": True,
}
)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
final_text = next(block for block in response.content if block.type == "text")
print(final_text.text)
TypeScript
// 第 4 环:错误处理。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const tools: Anthropic.Tool[] = [
{
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
input_schema: {
type: "object",
properties: {
title: { type: "string" },
start: { type: "string", format: "date-time" },
end: { type: "string", format: "date-time" },
attendees: {
type: "array",
items: { type: "string", format: "email" }
},
recurrence: {
type: "object",
properties: {
frequency: { enum: ["daily", "weekly", "monthly"] },
count: { type: "integer", minimum: 1 }
}
}
},
required: ["title", "start", "end"]
}
},
{
name: "list_calendar_events",
description: "List all calendar events on a given date.",
input_schema: {
type: "object",
properties: {
date: { type: "string", format: "date" }
},
required: ["date"]
}
}
];
function runTool(name: string, input: Record<string, unknown>) {
if (name === "create_calendar_event") {
const attendees = input.attendees as string[] | undefined;
if (attendees && attendees.length > 10) {
throw new Error("Too many attendees (max 10)");
}
return { event_id: "evt_123", status: "created", title: input.title };
}
if (name === "list_calendar_events") {
return {
events: [{ title: "Existing meeting", start: "14:00", end: "15:00" }]
};
}
throw new Error(`Unknown tool: ${name}`);
}
const emails = Array.from({ length: 15 }, (_, i) => `user${i}@example.com`).join(", ");
const messages: Anthropic.MessageParam[] = [
{
role: "user",
content: `Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: ${emails}`
}
];
let response = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
messages
});
while (response.stop_reason === "tool_use") {
const toolResults: Anthropic.ToolResultBlockParam[] = [];
for (const block of response.content) {
if (block.type === "tool_use") {
try {
const result = runTool(block.name, block.input as Record<string, unknown>);
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(result)
});
} catch (err) {
// 发出失败信号,以便 Claude 重试或请求澄清。
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: String(err),
is_error: true
});
}
}
}
messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });
response = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
tools,
messages
});
}
for (const block of response.content) {
if (block.type === "text") {
console.log(block.text);
}
}
C#
// 第 4 环:错误处理。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
List<ToolUnion> tools =
[
new ToolUnion(new Tool()
{
Name = "create_calendar_event",
Description = "Create a calendar event with attendees and optional recurrence.",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["title"] = JsonSerializer.SerializeToElement(new { type = "string" }),
["start"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date-time" }),
["end"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date-time" }),
["attendees"] = JsonSerializer.SerializeToElement(new
{
type = "array",
items = new { type = "string", format = "email" },
}),
["recurrence"] = JsonSerializer.SerializeToElement(new
{
type = "object",
properties = new
{
frequency = new { @enum = new[] { "daily", "weekly", "monthly" } },
count = new { type = "integer", minimum = 1 },
},
}),
},
Required = ["title", "start", "end"],
},
}),
new ToolUnion(new Tool()
{
Name = "list_calendar_events",
Description = "List all calendar events on a given date.",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["date"] = JsonSerializer.SerializeToElement(new { type = "string", format = "date" }),
},
Required = ["date"],
},
}),
];
string RunTool(ToolUseBlock toolUse)
{
if (toolUse.Name == "create_calendar_event")
{
if (toolUse.Input.TryGetValue("attendees", out var attendees) && attendees.GetArrayLength() > 10)
{
throw new InvalidOperationException("Too many attendees (max 10)");
}
var title = toolUse.Input.TryGetValue("title", out var t) ? t.GetString() : "";
return JsonSerializer.Serialize(new { event_id = "evt_123", status = "created", title });
}
if (toolUse.Name == "list_calendar_events")
{
return """{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}""";
}
throw new InvalidOperationException($"Unknown tool: {toolUse.Name}");
}
// 构造一个超出该工具参会人数上限的请求,以触发错误处理路径。
var emails = string.Join(", ", Enumerable.Range(0, 15).Select(i => $"user{i}@example.com"));
List<MessageParam> messages =
[
new UserMessageParam { Content = $"Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: {emails}" },
];
var response = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = tools,
Messages = messages,
});
while (response.StopReason == StopReason.ToolUse)
{
List<ContentBlockParam> toolResults = [];
foreach (var block in response.Content)
{
if (block.TryPickToolUse(out var toolUse))
{
ToolResultBlockParam toolResult;
try
{
toolResult = new ToolResultBlockParam() { ToolUseID = toolUse.ID, Content = RunTool(toolUse) };
}
catch (Exception e)
{
// 发出失败信号,以便 Claude 重试或请求澄清。
toolResult = new ToolResultBlockParam()
{
ToolUseID = toolUse.ID,
Content = e.Message,
IsError = true,
};
}
toolResults.Add(new ContentBlockParam(toolResult));
}
}
messages.Add(new AssistantMessageParam
{
Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList(),
});
messages.Add(new UserMessageParam { Content = toolResults });
response = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Tools = tools,
Messages = messages,
});
}
foreach (var block in response.Content)
{
if (block.TryPickText(out var text))
{
Console.WriteLine(text.Text);
}
}
Go
// 第 4 环:错误处理。
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"github.com/anthropics/anthropic-sdk-go/v2"
)
func runTool(name string, input map[string]any) (string, error) {
if name == "create_calendar_event" {
if attendees, ok := input["attendees"].([]any); ok && len(attendees) > 10 {
return "", fmt.Errorf("too many attendees (max 10)")
}
title, _ := input["title"].(string)
return fmt.Sprintf(`{"event_id": "evt_123", "status": "created", "title": %q}`, title), nil
}
if name == "list_calendar_events" {
return `{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}`, nil
}
return "", fmt.Errorf("unknown tool: %s", name)
}
func main() {
client := anthropic.NewClient()
ctx := context.Background()
tools := []anthropic.ToolUnionParam{
{OfTool: &anthropic.ToolParam{
Name: "create_calendar_event",
Description: anthropic.String("Create a calendar event with attendees and optional recurrence."),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"title": map[string]any{"type": "string"},
"start": map[string]any{"type": "string", "format": "date-time"},
"end": map[string]any{"type": "string", "format": "date-time"},
"attendees": map[string]any{
"type": "array",
"items": map[string]any{"type": "string", "format": "email"},
},
"recurrence": map[string]any{
"type": "object",
"properties": map[string]any{
"frequency": map[string]any{"enum": []string{"daily", "weekly", "monthly"}},
"count": map[string]any{"type": "integer", "minimum": 1},
},
},
},
Required: []string{"title", "start", "end"},
},
}},
{OfTool: &anthropic.ToolParam{
Name: "list_calendar_events",
Description: anthropic.String("List all calendar events on a given date."),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"date": map[string]any{"type": "string", "format": "date"},
},
Required: []string{"date"},
},
}},
}
// 构造一个超出该工具参会人数上限的请求,以触发错误处理路径。
emails := make([]string, 15)
for i := range emails {
emails[i] = fmt.Sprintf("user%d@example.com", i)
}
messages := []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock(
"Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: " + strings.Join(emails, ", "),
)),
}
response, err := client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Tools: tools,
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
for response.StopReason == "tool_use" {
var toolResults []anthropic.ContentBlockParamUnion
for _, block := range response.Content {
if block.Type == "tool_use" {
var input map[string]any
if err := json.Unmarshal(block.Input, &input); err != nil {
log.Fatal(err)
}
result, toolErr := runTool(block.Name, input)
if toolErr != nil {
// 发出失败信号,以便 Claude 重试或请求澄清。
toolResults = append(toolResults, anthropic.NewToolResultBlock(block.ID, toolErr.Error(), true))
} else {
toolResults = append(toolResults, anthropic.NewToolResultBlock(block.ID, result, false))
}
}
}
var assistantContent []anthropic.ContentBlockParamUnion
for _, block := range response.Content {
assistantContent = append(assistantContent, block.ToParam())
}
messages = append(messages, anthropic.NewAssistantMessage(assistantContent...))
messages = append(messages, anthropic.NewUserMessage(toolResults...))
response, err = client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Tools: tools,
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
}
for _, block := range response.Content {
if block.Type == "text" {
fmt.Println(block.Text)
}
}
}
Java
// 第 4 环:错误处理。
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.ContentBlock;
import com.anthropic.models.messages.ContentBlockParam;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.MessageParam;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.StopReason;
import com.anthropic.models.messages.Tool;
import com.anthropic.models.messages.Tool.InputSchema;
import com.anthropic.models.messages.ToolResultBlockParam;
import com.anthropic.models.messages.ToolUseBlock;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
String runTool(ToolUseBlock toolUse) {
// 原始工具输入是一个 JSON 对象;请将其作为 map 读取字段。
Map<String, JsonValue> input = (Map<String, JsonValue>) toolUse._input().asObject().get();
if (toolUse.name().equals("create_calendar_event")) {
int attendeeCount = input.containsKey("attendees")
? ((List<?>) input.get("attendees").asArray().get()).size()
: 0;
if (attendeeCount > 10) {
throw new IllegalArgumentException("Too many attendees (max 10)");
}
String title = input.containsKey("title") ? input.get("title").asStringOrThrow() : "";
return "{\"event_id\": \"evt_123\", \"status\": \"created\", \"title\": \"" + title + "\"}";
}
if (toolUse.name().equals("list_calendar_events")) {
return "{\"events\": [{\"title\": \"Existing meeting\", \"start\": \"14:00\", \"end\": \"15:00\"}]}";
}
throw new IllegalArgumentException("Unknown tool: " + toolUse.name());
}
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
Tool calendarTool = Tool.builder()
.name("create_calendar_event")
.description("Create a calendar event with attendees and optional recurrence.")
.inputSchema(InputSchema.builder()
.properties(JsonValue.from(Map.of(
"title", Map.of("type", "string"),
"start", Map.of("type", "string", "format", "date-time"),
"end", Map.of("type", "string", "format", "date-time"),
"attendees", Map.of(
"type", "array",
"items", Map.of("type", "string", "format", "email")
),
"recurrence", Map.of(
"type", "object",
"properties", Map.of(
"frequency", Map.of("enum", List.of("daily", "weekly", "monthly")),
"count", Map.of("type", "integer", "minimum", 1)
)
)
)))
.required(List.of("title", "start", "end"))
.build())
.build();
Tool listTool = Tool.builder()
.name("list_calendar_events")
.description("List all calendar events on a given date.")
.inputSchema(InputSchema.builder()
.properties(JsonValue.from(Map.of(
"date", Map.of("type", "string", "format", "date")
)))
.required(List.of("date"))
.build())
.build();
// 构造一个超出该工具参会者上限的请求,以触发错误路径。
String emails = IntStream.range(0, 15)
.mapToObj(i -> "user" + i + "@example.com")
.collect(Collectors.joining(", "));
List<MessageParam> messages = new ArrayList<>();
messages.add(MessageParam.ofUser(
"Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: " + emails));
Message response = client.messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addTool(calendarTool)
.addTool(listTool)
.messages(messages)
.build());
while (response.stopReason().isPresent()
&& response.stopReason().get().equals(StopReason.TOOL_USE)) {
List<ContentBlockParam> toolResults = new ArrayList<>();
for (ContentBlock block : response.content()) {
if (block.toolUse().isPresent()) {
ToolUseBlock toolUse = block.toolUse().get();
ToolResultBlockParam.Builder resultBuilder = ToolResultBlockParam.builder()
.toolUseId(toolUse.id());
try {
resultBuilder.content(runTool(toolUse));
} catch (Exception e) {
// 发出失败信号,以便 Claude 重试或请求澄清。
resultBuilder.content(e.getMessage()).isError(true);
}
toolResults.add(ContentBlockParam.ofToolResult(resultBuilder.build()));
}
}
messages.add(response.toParam());
messages.add(MessageParam.ofUserOfContentBlockParams(toolResults));
response = client.messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addTool(calendarTool)
.addTool(listTool)
.messages(messages)
.build());
}
response.content().stream()
.flatMap(block -> block.text().stream())
.forEach(textBlock -> IO.println(textBlock.text()));
}
PHP
<?php
// 第 4 环:错误处理。
use Anthropic\Client;
$client = new Client();
$tools = [
[
'name' => 'create_calendar_event',
'description' => 'Create a calendar event with attendees and optional recurrence.',
'input_schema' => [
'type' => 'object',
'properties' => [
'title' => ['type' => 'string'],
'start' => ['type' => 'string', 'format' => 'date-time'],
'end' => ['type' => 'string', 'format' => 'date-time'],
'attendees' => [
'type' => 'array',
'items' => ['type' => 'string', 'format' => 'email'],
],
'recurrence' => [
'type' => 'object',
'properties' => [
'frequency' => ['enum' => ['daily', 'weekly', 'monthly']],
'count' => ['type' => 'integer', 'minimum' => 1],
],
],
],
'required' => ['title', 'start', 'end'],
],
],
[
'name' => 'list_calendar_events',
'description' => 'List all calendar events on a given date.',
'input_schema' => [
'type' => 'object',
'properties' => [
'date' => ['type' => 'string', 'format' => 'date'],
],
'required' => ['date'],
],
],
];
function runTool(string $name, array $input): string
{
if ($name === 'create_calendar_event') {
if (count($input['attendees'] ?? []) > 10) {
throw new \InvalidArgumentException('Too many attendees (max 10)');
}
return json_encode([
'event_id' => 'evt_123',
'status' => 'created',
'title' => $input['title'],
]);
}
if ($name === 'list_calendar_events') {
return json_encode([
'events' => [['title' => 'Existing meeting', 'start' => '14:00', 'end' => '15:00']],
]);
}
throw new \InvalidArgumentException("Unknown tool: {$name}");
}
// 构造一个超出该工具参会人数上限的请求,以触发错误处理路径。
$emails = array_map(fn (int $i): string => "user{$i}@example.com", range(0, 14));
$messages = [
[
'role' => 'user',
'content' => 'Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: ' . implode(', ', $emails),
],
];
$response = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 1024,
tools: $tools,
messages: $messages,
);
while ($response->stopReason === 'tool_use') {
$toolResults = [];
foreach ($response->content as $block) {
if ($block->type === 'tool_use') {
try {
$toolResults[] = [
'type' => 'tool_result',
'tool_use_id' => $block->id,
'content' => runTool($block->name, $block->input),
];
} catch (\Exception $e) {
// 发出失败信号,以便 Claude 重试或请求澄清。
$toolResults[] = [
'type' => 'tool_result',
'tool_use_id' => $block->id,
'content' => $e->getMessage(),
'is_error' => true,
];
}
}
}
$messages[] = ['role' => 'assistant', 'content' => $response->content];
$messages[] = ['role' => 'user', 'content' => $toolResults];
$response = $client->messages->create(
model: 'claude-opus-5-5',
maxTokens: 1024,
tools: $tools,
messages: $messages,
);
}
foreach ($response->content as $block) {
if ($block->type === 'text') {
echo $block->text, "\n";
}
}
Ruby
# 第 4 环:错误处理。
require "anthropic"
client = Anthropic::Client.new
tools = [
{
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
input_schema: {
type: "object",
properties: {
title: {type: "string"},
start: {type: "string", format: "date-time"},
end: {type: "string", format: "date-time"},
attendees: {
type: "array",
items: {type: "string", format: "email"}
},
recurrence: {
type: "object",
properties: {
frequency: {enum: ["daily", "weekly", "monthly"]},
count: {type: "integer", minimum: 1}
}
}
},
required: ["title", "start", "end"]
}
},
{
name: "list_calendar_events",
description: "List all calendar events on a given date.",
input_schema: {
type: "object",
properties: {
date: {type: "string", format: "date"}
},
required: ["date"]
}
}
]
def run_tool(name, input)
case name
when "create_calendar_event"
attendees = input[:attendees]
raise ArgumentError, "Too many attendees (max 10)" if attendees && attendees.length > 10
JSON.generate({event_id: "evt_123", status: "created", title: input[:title]})
when "list_calendar_events"
JSON.generate({events: [{title: "Existing meeting", start: "14:00", end: "15:00"}]})
else
raise ArgumentError, "Unknown tool: #{name}"
end
end
# 构造一个超出该工具参会人数上限的请求,以触发错误路径。
emails = (0...15).map { |i| "user#{i}@example.com" }
messages = [
{
role: "user",
content: "Schedule a one-hour all-hands on Monday, March 30, 2026 at 10am with everyone: #{emails.join(", ")}"
}
]
response = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: tools,
messages: messages
)
while response.stop_reason == :tool_use
tool_results = response.content.select { |block| block.type == :tool_use }.map do |tool_use|
begin
{
type: "tool_result",
tool_use_id: tool_use.id,
content: run_tool(tool_use.name, tool_use.input)
}
rescue => e
# 发出失败信号,以便 Claude 重试或请求澄清。
{
type: "tool_result",
tool_use_id: tool_use.id,
content: e.message,
is_error: true
}
end
end
messages << {role: "assistant", content: response.content}
messages << {role: "user", content: tool_results}
response = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: tools,
messages: messages
)
end
response.content.each do |block|
puts block.text if block.type == :text
end
预期结果
Output
I tried to schedule the all-hands but the calendar only allows 10 attendees per event. I can split this into two sessions, or you can let me know which 10 people to prioritize.
is_error 标志是与成功结果之间的唯一区别。Claude 会接收该标志和错误文本,并作出相应的回应。有关完整的错误处理参考,请参阅处理工具调用。
第 5 环:工具运行器 SDK 抽象
第 2 环到第 4 环都手动编写了相同的循环:调用 API、检查 stop_reason、运行工具、追加结果、重复。工具运行器会替您完成这些工作。将每个工具定义为一个函数,把列表传给 client.beta.messages.tool_runner() (typescript: client.beta.messages.toolRunner(); java: client.beta().messages().toolRunner(); php: $client->beta->messages->toolRunner(); csharp: client.Beta.Messages.ToolRunner(); go: client.Beta.Messages.NewToolRunner()),并在循环完成后获取最终消息。错误包装、结果格式化和对话管理都在内部处理。
SDK 提供了一个辅助工具,可以将普通函数转换为可运行的工具,并根据函数签名推导出输入 schema;以下示例展示了惯用写法。
cURL
工具运行器是 Claude SDK 中的一个辅助工具。原始 HTTP 请求没有对应的等效功能。
CLI
工具运行器是 Claude SDK 中的一个辅助工具。ant CLI 没有对应的等效功能。
Python
# Ring 5:tool runner SDK 抽象层。
import json
import anthropic
from anthropic import beta_tool
client = anthropic.Anthropic()
@beta_tool
def create_calendar_event(
title: str,
start: str,
end: str,
attendees: list[str] | None = None,
recurrence: dict | None = None,
) -> str:
"""Create a calendar event with attendees and optional recurrence.
Args:
title: Event title.
start: Start time in ISO 8601 format.
end: End time in ISO 8601 format.
attendees: Email addresses to invite.
recurrence: Dict with 'frequency' (daily, weekly, monthly) and 'count'.
"""
if attendees and len(attendees) > 10:
raise ValueError("Too many attendees (max 10)")
return json.dumps({"event_id": "evt_123", "status": "created", "title": title})
@beta_tool
def list_calendar_events(date: str) -> str:
"""List all calendar events on a given date.
Args:
date: Date in YYYY-MM-DD format.
"""
return json.dumps(
{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}
)
final_message = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
tools=[create_calendar_event, list_calendar_events],
messages=[
{
"role": "user",
"content": "Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.",
}
],
).until_done()
for block in final_message.content:
if block.type == "text":
print(block.text)
TypeScript
// Ring 5:tool runner SDK 抽象层。
import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";
const client = new Anthropic();
const createCalendarEvent = betaZodTool({
name: "create_calendar_event",
description: "Create a calendar event with attendees and optional recurrence.",
inputSchema: z.object({
title: z.string(),
start: z.string().datetime(),
end: z.string().datetime(),
attendees: z.array(z.string().email()).optional(),
recurrence: z
.object({
frequency: z.enum(["daily", "weekly", "monthly"]),
count: z.number().int().min(1)
})
.optional()
}),
run: async (input) => {
if (input.attendees && input.attendees.length > 10) {
throw new Error("Too many attendees (max 10)");
}
return JSON.stringify({
event_id: "evt_123",
status: "created",
title: input.title
});
}
});
const listCalendarEvents = betaZodTool({
name: "list_calendar_events",
description: "List all calendar events on a given date.",
inputSchema: z.object({
date: z.string().date()
}),
run: async () => {
return JSON.stringify({
events: [{ title: "Existing meeting", start: "14:00", end: "15:00" }]
});
}
});
const finalMessage = await client.beta.messages.toolRunner({
model: "claude-opus-5-5",
max_tokens: 1024,
tools: [createCalendarEvent, listCalendarEvents],
messages: [
{
role: "user",
content:
"Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts."
}
]
});
for (const block of finalMessage.content) {
if (block.type === "text") {
console.log(block.text);
}
}
C#
// Ring 5:tool runner SDK 抽象层。
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Anthropic;
using Anthropic.Helpers.Beta;
using Anthropic.Models.Beta.Messages;
using MessageCreateParams = Anthropic.Models.Beta.Messages.MessageCreateParams;
using InputSchema = Anthropic.Models.Beta.Messages.InputSchema;
using Model = Anthropic.Models.Messages.Model;
AnthropicClient client = new();
// 将每个工具定义为可运行工具:定义中包含 JSON Schema,
// 而 Run 回调包含具体实现。抛出异常时,
// 该消息会作为设置了 is_error 的工具结果发回给 Claude。
var createCalendarEvent = new BetaRunnableTool
{
Name = "create_calendar_event",
Definition = new BetaTool
{
Name = "create_calendar_event",
Description = "Create a calendar event with attendees and optional recurrence.",
InputSchema = new InputSchema
{
Properties = new Dictionary<string, JsonElement>
{
["title"] = JsonSerializer.SerializeToElement(new { type = "string", description = "Event title" }),
["start"] = JsonSerializer.SerializeToElement(new { type = "string", description = "Start time in ISO 8601 format" }),
["end"] = JsonSerializer.SerializeToElement(new { type = "string", description = "End time in ISO 8601 format" }),
["attendees"] = JsonSerializer.SerializeToElement(new
{
type = "array",
items = new { type = "string" },
description = "Email addresses to invite",
}),
["recurrence"] = JsonSerializer.SerializeToElement(new
{
type = "object",
properties = new
{
frequency = new { @enum = new[] { "daily", "weekly", "monthly" } },
count = new { type = "integer", minimum = 1 },
},
}),
},
Required = ["title", "start", "end"],
},
},
Run = (toolUse, _) =>
{
if (toolUse.Input.TryGetValue("attendees", out var attendees) && attendees.GetArrayLength() > 10)
{
throw new InvalidOperationException("Too many attendees (max 10)");
}
var title = toolUse.Input.TryGetValue("title", out var t) ? t.GetString() : "";
return Task.FromResult<BetaToolResultBlockParamContent>(
JsonSerializer.Serialize(new { event_id = "evt_123", status = "created", title })
);
},
};
var listCalendarEvents = new BetaRunnableTool
{
Name = "list_calendar_events",
Definition = new BetaTool
{
Name = "list_calendar_events",
Description = "List all calendar events on a given date.",
InputSchema = new InputSchema
{
Properties = new Dictionary<string, JsonElement>
{
["date"] = JsonSerializer.SerializeToElement(new { type = "string", description = "Date in YYYY-MM-DD format" }),
},
Required = ["date"],
},
},
Run = (toolUse, _) => Task.FromResult<BetaToolResultBlockParamContent>(
"""{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}"""
),
};
// runner 会调用 API、运行所请求的工具并回传结果,
// 直到 Claude 给出最终答案。
var runner = client.Beta.Messages.ToolRunner(
new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new BetaUserMessageParam
{
Content = "Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.",
},
],
},
[createCalendarEvent, listCalendarEvents]
);
BetaMessage? finalMessage = null;
await foreach (var message in runner)
{
finalMessage = message;
}
foreach (var block in finalMessage!.Content)
{
if (block.TryPickText(out var text))
{
Console.WriteLine(text.Text);
}
}
Go
// Ring 5:tool runner SDK 抽象层。
package main
import (
"context"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go/v2"
"github.com/anthropics/anthropic-sdk-go/v2/toolrunner"
)
// 输入结构体定义了每个工具的 schema。tool runner 会根据
// 结构体字段及其 jsonschema 标签生成 JSON Schema。
type RecurrenceInput struct {
Frequency string `json:"frequency,omitempty" jsonschema:"enum=daily,enum=weekly,enum=monthly,description=How often the event repeats"`
Count int `json:"count,omitempty" jsonschema:"description=Number of occurrences"`
}
type CreateCalendarEventInput struct {
Title string `json:"title" jsonschema:"required,description=Event title"`
Start string `json:"start" jsonschema:"required,description=Start time in ISO 8601 format"`
End string `json:"end" jsonschema:"required,description=End time in ISO 8601 format"`
Attendees []string `json:"attendees,omitempty" jsonschema:"description=Email addresses to invite"`
Recurrence *RecurrenceInput `json:"recurrence,omitempty"`
}
type ListCalendarEventsInput struct {
Date string `json:"date" jsonschema:"required,description=Date in YYYY-MM-DD format"`
}
func main() {
client := anthropic.NewClient()
ctx := context.Background()
// 将每个工具定义为一个处理函数。返回错误时,会将
// 该消息作为设置了 is_error 的工具结果发回给 Claude。
createCalendarEvent, err := toolrunner.NewBetaToolFromJSONSchema(
"create_calendar_event",
"Create a calendar event with attendees and optional recurrence.",
func(ctx context.Context, input CreateCalendarEventInput) (anthropic.BetaToolResultBlockParamContentUnion, error) {
if len(input.Attendees) > 10 {
return anthropic.BetaToolResultBlockParamContentUnion{}, fmt.Errorf("too many attendees (max 10)")
}
return anthropic.BetaToolResultBlockParamContentUnion{
OfText: &anthropic.BetaTextBlockParam{
Text: fmt.Sprintf(`{"event_id": "evt_123", "status": "created", "title": %q}`, input.Title),
},
}, nil
},
)
if err != nil {
log.Fatal(err)
}
listCalendarEvents, err := toolrunner.NewBetaToolFromJSONSchema(
"list_calendar_events",
"List all calendar events on a given date.",
func(ctx context.Context, input ListCalendarEventsInput) (anthropic.BetaToolResultBlockParamContentUnion, error) {
return anthropic.BetaToolResultBlockParamContentUnion{
OfText: &anthropic.BetaTextBlockParam{
Text: `{"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}`,
},
}, nil
},
)
if err != nil {
log.Fatal(err)
}
// runner 会调用 API、运行所请求的工具并将结果回传,
// 直到 Claude 给出最终答案。
runner := client.Beta.Messages.NewToolRunner(
[]anthropic.BetaTool{createCalendarEvent, listCalendarEvents},
anthropic.BetaToolRunnerParams{
BetaMessageNewParams: anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(
"Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.",
)),
},
},
},
)
var finalMessage *anthropic.BetaMessage
for message, err := range runner.All(ctx) {
if err != nil {
log.Fatal(err)
}
finalMessage = message
}
for _, block := range finalMessage.Content {
if block.Type == "text" {
fmt.Println(block.Text)
}
}
}
Java
// Ring 5:tool runner SDK 抽象层。
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.helpers.BetaToolRunner;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import java.util.List;
import java.util.function.Supplier;
// 将每个工具定义为一个类:字段描述输入 schema,
// get() 方法包含具体实现。抛出异常会将
// 消息作为设置了 is_error 的工具结果发回给 Claude。
@JsonClassDescription("Create a calendar event with attendees.")
static class CreateCalendarEvent implements Supplier<String> {
@JsonPropertyDescription("Event title")
public String title;
@JsonPropertyDescription("Start time in ISO 8601 format")
public String start;
@JsonPropertyDescription("End time in ISO 8601 format")
public String end;
@JsonPropertyDescription("Email addresses to invite")
public List<String> attendees;
@Override
public String get() {
if (attendees != null && attendees.size() > 10) {
throw new IllegalArgumentException("Too many attendees (max 10)");
}
return "{\"event_id\": \"evt_123\", \"status\": \"created\", \"title\": \"" + title + "\"}";
}
}
@JsonClassDescription("List all calendar events on a given date.")
static class ListCalendarEvents implements Supplier<String> {
@JsonPropertyDescription("Date in YYYY-MM-DD format")
public String date;
@Override
public String get() {
return "{\"events\": [{\"title\": \"Existing meeting\", \"start\": \"14:00\", \"end\": \"15:00\"}]}";
}
}
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// runner 会调用 API、运行所请求的工具并回传结果,
// 直到 Claude 给出最终答案。
BetaToolRunner runner = client.beta()
.messages()
.toolRunner(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addBeta("structured-outputs-2025-11-13")
.addUserMessage("Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.")
.addTool(CreateCalendarEvent.class)
.addTool(ListCalendarEvents.class)
.build());
BetaMessage finalMessage = null;
for (BetaMessage message : runner) {
finalMessage = message;
}
finalMessage.content().stream()
.flatMap(block -> block.text().stream())
.forEach(textBlock -> IO.println(textBlock.text()));
}
PHP
<?php
// Ring 5:tool runner SDK 抽象层。
use Anthropic\Client;
use Anthropic\Lib\Tools\BetaRunnableTool;
use Anthropic\Messages\Model;
$client = new Client();
// 将每个工具定义为可运行工具:定义中包含 JSON Schema,
// run 闭包则包含具体实现。抛出异常时,会将该
// 消息作为设置了 is_error 的工具结果发回给 Claude。
$createCalendarEvent = new BetaRunnableTool(
definition: [
'name' => 'create_calendar_event',
'description' => 'Create a calendar event with attendees and optional recurrence.',
'input_schema' => [
'type' => 'object',
'properties' => [
'title' => ['type' => 'string', 'description' => 'Event title'],
'start' => ['type' => 'string', 'description' => 'Start time in ISO 8601 format'],
'end' => ['type' => 'string', 'description' => 'End time in ISO 8601 format'],
'attendees' => [
'type' => 'array',
'items' => ['type' => 'string'],
'description' => 'Email addresses to invite',
],
'recurrence' => [
'type' => 'object',
'properties' => [
'frequency' => ['enum' => ['daily', 'weekly', 'monthly']],
'count' => ['type' => 'integer', 'minimum' => 1],
],
],
],
'required' => ['title', 'start', 'end'],
],
],
run: function (array $input): string {
if (count($input['attendees'] ?? []) > 10) {
throw new \InvalidArgumentException('Too many attendees (max 10)');
}
return json_encode([
'event_id' => 'evt_123',
'status' => 'created',
'title' => $input['title'],
]);
},
);
$listCalendarEvents = new BetaRunnableTool(
definition: [
'name' => 'list_calendar_events',
'description' => 'List all calendar events on a given date.',
'input_schema' => [
'type' => 'object',
'properties' => [
'date' => ['type' => 'string', 'description' => 'Date in YYYY-MM-DD format'],
],
'required' => ['date'],
],
],
run: fn (array $input): string => json_encode([
'events' => [['title' => 'Existing meeting', 'start' => '14:00', 'end' => '15:00']],
]),
);
// runner 会调用 API、运行所请求的工具并回传结果,
// 直到 Claude 给出最终答案。
$runner = $client->beta->messages->toolRunner(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => 'Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts.',
],
],
model: Model::CLAUDE_OPUS_5_5,
tools: [$createCalendarEvent, $listCalendarEvents],
);
$finalMessage = null;
foreach ($runner as $message) {
$finalMessage = $message;
}
foreach ($finalMessage->content as $block) {
if ($block->type === 'text') {
echo $block->text, "\n";
}
}
Ruby
# Ring 5:tool runner SDK 抽象层。
require "anthropic"
client = Anthropic::Client.new
# 将每个工具定义为一个类:类型化的输入模型描述其 schema,
# call 方法包含具体实现。抛出错误时,错误消息会
# 作为设置了 is_error 的工具结果发回给 Claude。
class RecurrenceInput < Anthropic::BaseModel
optional :frequency, Anthropic::InputSchema::EnumOf["daily", "weekly", "monthly"],
doc: "How often the event repeats"
optional :count, Integer, doc: "Number of occurrences"
end
class CreateCalendarEventInput < Anthropic::BaseModel
required :title, String, doc: "Event title"
required :start, String, doc: "Start time in ISO 8601 format"
required :end, String, doc: "End time in ISO 8601 format"
optional :attendees, Anthropic::InputSchema::ArrayOf[String], doc: "Email addresses to invite"
optional :recurrence, RecurrenceInput, doc: "Optional recurrence rule"
end
class CreateCalendarEvent < Anthropic::BaseTool
doc "Create a calendar event with attendees and optional recurrence."
input_schema CreateCalendarEventInput
def call(input)
raise ArgumentError, "Too many attendees (max 10)" if input.attendees && input.attendees.length > 10
JSON.generate({event_id: "evt_123", status: "created", title: input.title})
end
end
class ListCalendarEventsInput < Anthropic::BaseModel
required :date, String, doc: "Date in YYYY-MM-DD format"
end
class ListCalendarEvents < Anthropic::BaseTool
doc "List all calendar events on a given date."
input_schema ListCalendarEventsInput
def call(input)
JSON.generate({events: [{title: "Existing meeting", start: "14:00", end: "15:00"}]})
end
end
# runner 会调用 API、运行所请求的工具并将结果回传,
# 直到 Claude 给出最终答案。
runner = client.beta.messages.tool_runner(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
tools: [CreateCalendarEvent.new, ListCalendarEvents.new],
messages: [
{
role: "user",
content: "Check what I have on Monday, March 30, 2026, then schedule a one-hour planning session that day that avoids any conflicts."
}
]
)
final_message = nil
runner.each_message { |message| final_message = message }
final_message.content.each do |block|
puts block.text if block.type == :text
end
预期结果
Output
I checked your calendar for Monday, March 30 and found an existing meeting from 2pm to 3pm. I've scheduled the planning session for 10am to 11am to avoid the conflict.
输出与第 3 环完全相同。区别在于代码:行数大约减少一半,没有手动循环,并且 schema 与实现放在一起。
您构建了什么
您从一个硬编码的单次工具调用开始,最终构建出一个具备生产形态的智能体,它能够处理多个工具、并行调用和错误,然后又将所有这些简化为工具运行器。在此过程中,您了解了工具使用协议的每个组成部分:tool_use 块、tool_result 块、tool_use_id 匹配、stop_reason 检查以及 is_error 信号。