默认情况下,Claude 可能会在单个响应中调用多个工具。本页介绍如何运行这些调用、如何格式化消息历史以保持并行性正常工作,以及在需要时如何禁用“parallel tool use”(并行工具使用)。有关单次调用流程,请参阅处理工具调用。

执行语义

当 Claude 调用工具时,响应的 stop_reason 为 tool_use,并且可以在单个助手轮次中包含多个 tool_use 块。如何运行这些调用由您决定。API 不规定执行顺序:您可以并发运行这些调用(Promise.all、asyncio.gather),按它们出现的顺序依次运行,或者采用任何适合您工具的组合方式。

请根据您的工具的功能选择策略。独立的只读操作通常可以安全地并行运行,以降低“latency”(延迟)。具有副作用、共享状态或顺序要求的工具可能更适合依次运行。

无论您使用哪种策略,都要为每个 tool_use 块返回一个 tool_result,并将它们全部放在下一条用户消息中。使用 tool_use_id 将每个结果与其调用匹配,并将每个 tool_result 块放在该消息中任何文本内容之前。有关完整的格式规则,请参阅处理工具调用。如果您选择不运行某个特定调用(例如,因为您依次运行了该批次而较早的调用失败了),仍然要为其返回一个带有 is_error: true 和简短说明的 tool_result。

{
  "type": "tool_result",
  "tool_use_id": "toolu_02",
  "is_error": true,
  "content": "Not executed: the preceding write_file call failed."
}

计算机使用工具和浏览器使用工具的要求更为严格。当 Claude 在一个轮次中返回多个其成员工具调用(批量操作)时,请按它们出现的顺序依次运行,并在第一次失败时停止;每个工具都定义了针对您跳过的调用应返回的确切文本。

测试并行工具调用

注意

大多数应用程序请使用 Tool Runner: SDK 的 Tool Runner 会处理包含多个工具调用的响应并为您格式化结果,因此您无需自己编写这些处理逻辑。当您需要直接控制调用的运行方式(例如自定义批处理、排序或错误处理)时,请使用本页的手动模式。

以下脚本发送一个应触发并行工具调用的请求,验证响应中包含这些调用,并格式化工具结果以保持并行性正常工作。请在环境中设置 ANTHROPIC_API_KEY 后运行它:

cURL

此端到端测试需要的逻辑超出了单个 shell 命令的能力。请使用 Claude SDK 来运行它。它发送的请求是一个定义了两个工具的标准工具使用请求。

CLI

此端到端测试需要的逻辑超出了单个 shell 命令的能力。请使用 Claude SDK 来运行它。

Python

client = Anthropic()

# 定义工具
tools = [
    {
        "name": "get_weather",
        "description": "Get the current weather in a given location",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA",
                }
            },
            "required": ["location"],
        },
    },
    {
        "name": "get_time",
        "description": "Get the current time in a given timezone",
        "input_schema": {
            "type": "object",
            "properties": {
                "timezone": {
                    "type": "string",
                    "description": "The timezone, e.g. America/New_York",
                }
            },
            "required": ["timezone"],
        },
    },
]

# 测试包含并行工具调用的对话
messages = [
    {
        "role": "user",
        "content": "What's the weather in SF and NYC, and what time is it there?",
    }
]

# 发起初始请求
print("Requesting parallel tool calls...")
response = client.messages.create(
    model="claude-opus-5-5", max_tokens=1024, messages=messages, tools=tools
)

# 检查是否存在并行工具调用
tool_uses = [block for block in response.content if block.type == "tool_use"]
print(f"\n✓ Claude made {len(tool_uses)} tool calls")

if len(tool_uses) > 1:
    print("✓ Parallel tool calls detected!")
    for tool in tool_uses:
        print(f"  - {tool.name}: {tool.input}")
else:
    print("✗ No parallel tool calls detected")

# 模拟工具执行并正确格式化结果
tool_results = []
for tool_use in tool_uses:
    if tool_use.name == "get_weather":
        if "San Francisco" in str(tool_use.input):
            result = "San Francisco: 68°F, partly cloudy"
        else:
            result = "New York: 45°F, clear skies"
    else:  # get_time
        if "Los_Angeles" in str(tool_use.input):
            result = "2:30 PM PST"
        else:
            result = "5:30 PM EST"

    tool_results.append(
        {"type": "tool_result", "tool_use_id": tool_use.id, "content": result}
    )

# 使用工具结果继续对话
messages.extend(
    [
        {"role": "assistant", "content": response.content},
        {"role": "user", "content": tool_results},  # All results in one message!
    ]
)

# 获取最终响应
print("\nGetting final response...")
final_response = client.messages.create(
    model="claude-opus-5-5", max_tokens=1024, messages=messages, tools=tools
)

final_text = next(
    block.text for block in final_response.content if block.type == "text"
)
print(f"\nClaude's response:\n{final_text}")

# 验证格式
print("\n--- Verification ---")
print(f"✓ Tool results sent in single user message: {len(tool_results)} results")
print("✓ No text before tool results in content array")
print("✓ Conversation formatted correctly for future parallel tool use")

TypeScript

const client = new Anthropic();

// 定义工具
const tools: Anthropic.Tool[] = [
  {
    name: "get_weather",
    description: "Get the current weather in a given location",
    input_schema: {
      type: "object" as const,
      properties: {
        location: {
          type: "string",
          description: "The city and state, e.g. San Francisco, CA"
        }
      },
      required: ["location"]
    }
  },
  {
    name: "get_time",
    description: "Get the current time in a given timezone",
    input_schema: {
      type: "object" as const,
      properties: {
        timezone: {
          type: "string",
          description: "The timezone, e.g. America/New_York"
        }
      },
      required: ["timezone"]
    }
  }
];

// 发起初始请求
console.log("Requesting parallel tool calls...");
const response = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: "What's the weather in SF and NYC, and what time is it there?"
    }
  ],
  tools: tools
});

// 检查是否存在并行工具调用
const toolUses = response.content.filter((block) => block.type === "tool_use");
console.log(`\n✓ Claude made ${toolUses.length} tool calls`);

if (toolUses.length > 1) {
  console.log("✓ Parallel tool calls detected!");
  for (const tool of toolUses) {
    if (tool.type === "tool_use") {
      console.log(`  - ${tool.name}: ${JSON.stringify(tool.input)}`);
    }
  }
} else {
  console.log("✗ No parallel tool calls detected");
}

// 模拟工具执行并正确格式化结果
const toolResults: Anthropic.ToolResultBlockParam[] = toolUses
  .filter((block) => block.type === "tool_use")
  .map((toolUse) => {
    const input = toolUse.input as Record<string, string>;
    let result: string;
    if (toolUse.name === "get_weather") {
      result = input.location?.includes("San Francisco")
        ? "San Francisco: 68F, partly cloudy"
        : "New York: 45F, clear skies";
    } else {
      result = input.timezone?.includes("Los_Angeles") ? "2:30 PM PST" : "5:30 PM EST";
    }

    return {
      type: "tool_result" as const,
      tool_use_id: toolUse.id,
      content: result
    };
  });

// 获取格式正确的最终响应
console.log("\nGetting final response...");
const finalResponse = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: "What's the weather in SF and NYC, and what time is it there?"
    },
    { role: "assistant", content: response.content },
    { role: "user", content: toolResults }
  ],
  tools: tools
});

for (const block of finalResponse.content) {
  if (block.type === "text") {
    console.log(`\nClaude's response:\n${block.text}`);
  }
}

// 验证格式
console.log("\n--- Verification ---");
console.log(`✓ Tool results sent in single user message: ${toolResults.length} results`);
console.log("✓ No text before tool results in content array");
console.log("✓ Conversation formatted correctly for future parallel tool use");

C#

AnthropicClient client = new();

var tools = new List<ToolUnion>
{
    new ToolUnion(new Tool()
    {
        Name = "get_weather",
        Description = "Get the current weather in a given location",
        InputSchema = new InputSchema()
        {
            Properties = new Dictionary<string, JsonElement>
            {
                ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The city and state, e.g. San Francisco, CA" }),
            },
            Required = ["location"],
        },
    }),
    new ToolUnion(new Tool()
    {
        Name = "get_time",
        Description = "Get the current time in a given timezone",
        InputSchema = new InputSchema()
        {
            Properties = new Dictionary<string, JsonElement>
            {
                ["timezone"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The timezone, e.g. America/New_York" }),
            },
            Required = ["timezone"],
        },
    }),
};

Console.WriteLine("Requesting parallel tool calls...");
var parameters = new MessageCreateParams
{
    Model = Model.ClaudeOpus5_5,
    MaxTokens = 1024,
    Messages = [new UserMessageParam { Content = "What's the weather in SF and NYC, and what time is it there?" }],
    Tools = tools
};

var response = await client.Messages.Create(parameters);

var toolUses = new List<ToolUseBlock>();
foreach (var block in response.Content)
{
    if (block.TryPickToolUse(out var toolUse))
    {
        toolUses.Add(toolUse);
    }
}
Console.WriteLine($"\n✓ Claude made {toolUses.Count} tool calls");

if (toolUses.Count > 1)
{
    Console.WriteLine("✓ Parallel tool calls detected!");
    foreach (var tool in toolUses)
    {
        Console.WriteLine($"  - {tool.Name}: {JsonSerializer.Serialize(tool.Input)}");
    }
}
else
{
    Console.WriteLine("✗ No parallel tool calls detected");
}

var toolResults = new List<ContentBlockParam>();
foreach (var toolUse in toolUses)
{
    string result;
    if (toolUse.Name == "get_weather")
    {
        result = JsonSerializer.Serialize(toolUse.Input).Contains("San Francisco")
            ? "San Francisco: 68°F, partly cloudy"
            : "New York: 45°F, clear skies";
    }
    else
    {
        result = JsonSerializer.Serialize(toolUse.Input).Contains("Los_Angeles")
            ? "2:30 PM PST"
            : "5:30 PM EST";
    }

    toolResults.Add(new ContentBlockParam(new ToolResultBlockParam()
    {
        ToolUseID = toolUse.ID,
        Content = result,
    }));
}

Console.WriteLine("\nGetting final response...");
var finalParameters = new MessageCreateParams
{
    Model = Model.ClaudeOpus5_5,
    MaxTokens = 1024,
    Messages = [
        new UserMessageParam { Content = "What's the weather in SF and NYC, and what time is it there?" },
        new AssistantMessageParam { Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList() },
        new UserMessageParam { Content = toolResults }
    ],
    Tools = tools
};

var finalResponse = await client.Messages.Create(finalParameters);
var text = finalResponse.Content.Select(block => block.Value).OfType<TextBlock>().FirstOrDefault();
Console.WriteLine($"\nClaude's response:\n{text?.Text}");

Console.WriteLine("\n--- Verification ---");
Console.WriteLine($"✓ Tool results sent in single user message: {toolResults.Count} results");
Console.WriteLine("✓ No text before tool results in content array");
Console.WriteLine("✓ Conversation formatted correctly for future parallel tool use");

Go

client := anthropic.NewClient()

tools := []anthropic.ToolUnionParam{
	{OfTool: &anthropic.ToolParam{
		Name:        "get_weather",
		Description: anthropic.String("Get the current weather in a given location"),
		InputSchema: anthropic.ToolInputSchemaParam{
			Properties: map[string]any{
				"location": map[string]any{
					"type":        "string",
					"description": "The city and state, e.g. San Francisco, CA",
				},
			},
			Required: []string{"location"},
		},
	}},
	{OfTool: &anthropic.ToolParam{
		Name:        "get_time",
		Description: anthropic.String("Get the current time in a given timezone"),
		InputSchema: anthropic.ToolInputSchemaParam{
			Properties: map[string]any{
				"timezone": map[string]any{
					"type":        "string",
					"description": "The timezone, e.g. America/New_York",
				},
			},
			Required: []string{"timezone"},
		},
	}},
}

fmt.Println("Requesting parallel tool calls...")
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
	Model:     anthropic.ModelClaudeOpus5_5,
	MaxTokens: 1024,
	Messages: []anthropic.MessageParam{
		anthropic.NewUserMessage(anthropic.NewTextBlock("What's the weather in SF and NYC, and what time is it there?")),
	},
	Tools: tools,
})
if err != nil {
	log.Fatal(err)
}

// 使用类型 switch 查找工具使用块
type toolUseInfo struct {
	ID    string
	Name  string
	Input json.RawMessage
}
var toolUses []toolUseInfo
for _, block := range response.Content {
	switch variant := block.AsAny().(type) {
	case anthropic.ToolUseBlock:
		toolUses = append(toolUses, toolUseInfo{
			ID:    variant.ID,
			Name:  variant.Name,
			Input: variant.Input,
		})
	}
}

fmt.Printf("\n✓ Claude made %d tool calls\n", len(toolUses))

if len(toolUses) > 1 {
	fmt.Println("✓ Parallel tool calls detected!")
	for _, tool := range toolUses {
		fmt.Printf("  - %s: %s\n", tool.Name, string(tool.Input))
	}
} else {
	fmt.Println("✗ No parallel tool calls detected")
}

// 构建工具结果
var toolResults []anthropic.ContentBlockParamUnion
for _, toolUse := range toolUses {
	var result string
	inputStr := string(toolUse.Input)

	if toolUse.Name == "get_weather" {
		if strings.Contains(inputStr, "San Francisco") {
			result = "San Francisco: 68°F, partly cloudy"
		} else {
			result = "New York: 45°F, clear skies"
		}
	} else {
		if strings.Contains(inputStr, "Los_Angeles") {
			result = "2:30 PM PST"
		} else {
			result = "5:30 PM EST"
		}
	}

	toolResults = append(toolResults, anthropic.NewToolResultBlock(toolUse.ID, result, false))
}

fmt.Println("\nGetting final response...")
finalResponse, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
	Model:     anthropic.ModelClaudeOpus5_5,
	MaxTokens: 1024,
	Messages: []anthropic.MessageParam{
		anthropic.NewUserMessage(anthropic.NewTextBlock("What's the weather in SF and NYC, and what time is it there?")),
		response.ToParam(),
		anthropic.NewUserMessage(toolResults...),
	},
	Tools: tools,
})
if err != nil {
	log.Fatal(err)
}

var finalText string
for _, block := range finalResponse.Content {
	if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
		finalText = textBlock.Text
		break
	}
}

fmt.Printf("\nClaude's response:\n%s\n", finalText)

fmt.Println("\n--- Verification ---")
fmt.Printf("✓ Tool results sent in single user message: %d results\n", len(toolResults))
fmt.Println("✓ No text before tool results in content array")
fmt.Println("✓ Conversation formatted correctly for future parallel tool use")

Java

AnthropicClient client = AnthropicOkHttpClient.fromEnv();

Tool weatherTool = Tool.builder()
    .name("get_weather")
    .description("Get the current weather in a given location")
    .inputSchema(InputSchema.builder()
        .properties(JsonValue.from(Map.of(
            "location", Map.of(
                "type", "string",
                "description", "The city and state, e.g. San Francisco, CA"
            )
        )))
        .putAdditionalProperty("required", JsonValue.from(List.of("location")))
        .build())
    .build();

Tool timeTool = Tool.builder()
    .name("get_time")
    .description("Get the current time in a given timezone")
    .inputSchema(InputSchema.builder()
        .properties(JsonValue.from(Map.of(
            "timezone", Map.of(
                "type", "string",
                "description", "The timezone, e.g. America/New_York"
            )
        )))
        .putAdditionalProperty("required", JsonValue.from(List.of("timezone")))
        .build())
    .build();

MessageCreateParams params = MessageCreateParams.builder()
    .model(Model.CLAUDE_OPUS_5_5)
    .maxTokens(1024L)
    .addTool(weatherTool)
    .addTool(timeTool)
    .addUserMessage("What's the weather in SF and NYC, and what time is it there?")
    .build();

IO.println("Requesting parallel tool calls...");
Message response = client.messages().create(params);

List<ToolUseBlock> toolUses = new ArrayList<>();
for (ContentBlock block : response.content()) {
    if (block.toolUse().isPresent()) {
        toolUses.add(block.toolUse().get());
    }
}

IO.println("\n✓ Claude made " + toolUses.size() + " tool calls");

if (toolUses.size() > 1) {
    IO.println("✓ Parallel tool calls detected!");
    for (ToolUseBlock tool : toolUses) {
        IO.println("  - " + tool.name() + ": " + tool._input());
    }
} else {
    IO.println("✗ No parallel tool calls detected");
}

List<ContentBlockParam> toolResults = new ArrayList<>();
for (ToolUseBlock toolUse : toolUses) {
    String result;
    if (toolUse.name().equals("get_weather")) {
        String location = toolUse._input().toString();
        result = location.contains("San Francisco")
            ? "San Francisco: 68°F, partly cloudy"
            : "New York: 45°F, clear skies";
    } else {
        String timezone = toolUse._input().toString();
        result = timezone.contains("Los_Angeles")
            ? "2:30 PM PST"
            : "5:30 PM EST";
    }
    toolResults.add(ContentBlockParam.ofToolResult(
        ToolResultBlockParam.builder()
            .toolUseId(toolUse.id())
            .content(result)
            .build()
    ));
}

IO.println("\nGetting final response...");
MessageCreateParams finalParams = MessageCreateParams.builder()
    .model(Model.CLAUDE_OPUS_5_5)
    .maxTokens(1024L)
    .addTool(weatherTool)
    .addTool(timeTool)
    .addUserMessage("What's the weather in SF and NYC, and what time is it there?")
    .addMessage(response)
    .addUserMessageOfContentBlockParams(toolResults)
    .build();

Message finalResponse = client.messages().create(finalParams);
finalResponse.content().stream()
    .flatMap(block -> block.text().stream())
    .forEach(textBlock -> IO.println("\nClaude's response:\n" + textBlock.text()));

IO.println("\n--- Verification ---");
IO.println("✓ Tool results sent in single user message: " + toolResults.size() + " results");
IO.println("✓ No text before tool results in content array");
IO.println("✓ Conversation formatted correctly for future parallel tool use");

PHP

$client = new Client();

$tools = [
    [
        'name' => 'get_weather',
        'description' => 'Get the current weather in a given location',
        'input_schema' => [
            'type' => 'object',
            'properties' => [
                'location' => [
                    'type' => 'string',
                    'description' => 'The city and state, e.g. San Francisco, CA'
                ]
            ],
            'required' => ['location']
        ]
    ],
    [
        'name' => 'get_time',
        'description' => 'Get the current time in a given timezone',
        'input_schema' => [
            'type' => 'object',
            'properties' => [
                'timezone' => [
                    'type' => 'string',
                    'description' => 'The timezone, e.g. America/New_York'
                ]
            ],
            'required' => ['timezone']
        ]
    ]
];

echo "Requesting parallel tool calls...\n";
$response = $client->messages->create(
    maxTokens: 1024,
    messages: [
        ['role' => 'user', 'content' => "What's the weather in SF and NYC, and what time is it there?"]
    ],
    model: 'claude-opus-5-5',
    tools: $tools,
);

$toolUses = array_filter($response->content, fn($block) => $block->type === 'tool_use');
echo "\n✓ Claude made " . count($toolUses) . " tool calls\n";

if (count($toolUses) > 1) {
    echo "✓ Parallel tool calls detected!\n";
    foreach ($toolUses as $tool) {
        echo "  - {$tool->name}: " . json_encode($tool->input) . "\n";
    }
} else {
    echo "✗ No parallel tool calls detected\n";
}

$toolResults = [];
foreach ($toolUses as $toolUse) {
    if ($toolUse->name === 'get_weather') {
        $result = str_contains(json_encode($toolUse->input), 'San Francisco')
            ? 'San Francisco: 68°F, partly cloudy'
            : 'New York: 45°F, clear skies';
    } else {
        $result = str_contains(json_encode($toolUse->input), 'Los_Angeles')
            ? '2:30 PM PST'
            : '5:30 PM EST';
    }

    $toolResults[] = [
        'type' => 'tool_result',
        'tool_use_id' => $toolUse->id,
        'content' => $result
    ];
}

echo "\nGetting final response...\n";
$finalResponse = $client->messages->create(
    maxTokens: 1024,
    messages: [
        ['role' => 'user', 'content' => "What's the weather in SF and NYC, and what time is it there?"],
        ['role' => 'assistant', 'content' => $response->content],
        ['role' => 'user', 'content' => $toolResults]
    ],
    model: 'claude-opus-5-5',
    tools: $tools,
);

$textBlock = array_find($finalResponse->content, static fn ($block): bool => $block->type === 'text');
echo "\nClaude's response:\n{$textBlock->text}\n";

echo "\n--- Verification ---\n";
echo '✓ Tool results sent in single user message: ' . count($toolResults) . " results\n";
echo "✓ No text before tool results in content array\n";
echo "✓ Conversation formatted correctly for future parallel tool use\n";

Ruby

client = Anthropic::Client.new

tools = [
  {
    name: "get_weather",
    description: "Get the current weather in a given location",
    input_schema: {
      type: "object",
      properties: {
        location: {
          type: "string",
          description: "The city and state, e.g. San Francisco, CA"
        }
      },
      required: ["location"]
    }
  },
  {
    name: "get_time",
    description: "Get the current time in a given timezone",
    input_schema: {
      type: "object",
      properties: {
        timezone: {
          type: "string",
          description: "The timezone, e.g. America/New_York"
        }
      },
      required: ["timezone"]
    }
  }
]

puts "Requesting parallel tool calls..."
response = client.messages.create(
  model: Anthropic::Model::CLAUDE_OPUS_5_5,
  max_tokens: 1024,
  messages: [
    { role: "user", content: "What's the weather in SF and NYC, and what time is it there?" }
  ],
  tools: tools
)

tool_uses = response.content.select { |block| block.type == :tool_use }
puts "\n✓ Claude made #{tool_uses.length} tool calls"

if tool_uses.length > 1
  puts "✓ Parallel tool calls detected!"
  tool_uses.each do |tool|
    puts "  - #{tool.name}: #{tool.input}"
  end
else
  puts "✗ No parallel tool calls detected"
end

tool_results = tool_uses.map do |tool_use|
  result = if tool_use.name == "get_weather"
    location = tool_use.input[:location].to_s
    location.include?("San Francisco") ? "San Francisco: 68°F, partly cloudy" : "New York: 45°F, clear skies"
  else
    timezone = tool_use.input[:timezone].to_s
    timezone.include?("Los_Angeles") ? "2:30 PM PST" : "5:30 PM EST"
  end

  {
    type: "tool_result",
    tool_use_id: tool_use.id,
    content: result
  }
end

puts "\nGetting final response..."
final_response = client.messages.create(
  model: Anthropic::Model::CLAUDE_OPUS_5_5,
  max_tokens: 1024,
  messages: [
    { role: "user", content: "What's the weather in SF and NYC, and what time is it there?" },
    { role: "assistant", content: response.content },
    { role: "user", content: tool_results }
  ],
  tools: tools
)

final_text = final_response.content.find { |block| block.type == :text }
puts "\nClaude's response:\n#{final_text.text}"

puts "\n--- Verification ---"
puts "✓ Tool results sent in single user message: #{tool_results.length} results"
puts "✓ No text before tool results in content array"
puts "✓ Conversation formatted correctly for future parallel tool use"

末尾的摘要行重申了保持并行性正常工作的两条格式规则:所有工具结果都在单条用户消息中返回,并且该消息中工具结果之前不出现任何文本内容。

最大化并行工具使用

当请求能从多个工具中受益时,Claude 4 及更高版本的模型默认会进行并行工具调用。对于所有模型,您都可以通过有针对性的提示来提高并行工具调用的可能性:

用于并行工具使用的系统提示

对于 Claude 4 及更高版本的模型,请将以下内容添加到您的系统提示中:

For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.

如需更强的并行工具使用(如果默认设置不够,建议使用),请使用:

<use_parallel_tool_calls>
For maximum efficiency, whenever you perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially. Prioritize calling tools in parallel whenever possible. For example, when reading 3 files, run 3 tool calls in parallel to read all 3 files into context at the same time. When running multiple read-only commands like `ls` or `list_dir`, always run all of the commands in parallel. Err on the side of maximizing parallel tool calls rather than running too many tools sequentially.
</use_parallel_tool_calls>
用户消息提示

您也可以在特定的用户消息中鼓励并行工具使用:

Instead of:
"What's the weather in Paris? Also check London."

Use:
"Check the weather in Paris and London simultaneously."

Or be explicit:
"Please use parallel tool calls to get the weather for Paris, London, and Tokyo at the same time."

注意

长智能体循环中的 Claude Fable 5.1

Claude Fable 5.1 发出的并行工具调用可能比早期模型少,这在下一步读取操作仅为隐含的长智能体循环中最为明显(自定义编码智能体、bash 和文本编辑器框架、计算机使用)。标准函数调用不受影响。有关要添加的批处理指令及其放置位置,请参阅在智能体循环中批量处理独立的工具调用。

禁用并行工具使用

并行工具使用默认开启。要关闭它,请在 tool_choice 对象内设置 disable_parallel_tool_use: true。它不是顶层请求参数。其效果取决于 tool_choice 的类型。

最多一次工具调用

当 tool_choice 类型为 auto(默认值)时,设置 disable_parallel_tool_use: true 意味着 Claude 在每个响应中最多调用一个工具。Claude 仍然可以不调用任何工具而以纯文本作答。高亮显示的行是与标准工具使用请求相比唯一的改动:

cURL

curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "tools": [{
      "name": "get_weather",
      "description": "Get the current weather in a given location",
      "input_schema": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "The city and state, e.g. San Francisco, CA"
          }
        },
        "required": ["location"]
      }
    }],
    "tool_choice": {"type": "auto", "disable_parallel_tool_use": true},
    "messages": [
      {"role": "user", "content": "What is the weather in San Francisco and New York?"}
    ]
  }'

CLI

ant messages create <<'YAML'
model: claude-opus-5-5
max_tokens: 1024
tools:
  - name: get_weather
    description: Get the current weather in a given location
    input_schema:
      type: object
      properties:
        location:
          type: string
          description: The city and state, e.g. San Francisco, CA
      required: [location]
tool_choice:
  type: auto
  disable_parallel_tool_use: true
messages:
  - role: user
    content: What is the weather in San Francisco and New York?
YAML

Python

client = Anthropic()

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[
        {
            "name": "get_weather",
            "description": "Get the current weather in a given location",
            "input_schema": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA",
                    }
                },
                "required": ["location"],
            },
        }
    ],
    tool_choice={"type": "auto", "disable_parallel_tool_use": True},
    messages=[
        {
            "role": "user",
            "content": "What is the weather in San Francisco and New York?",
        }
    ],
)
print(response.content)

TypeScript

const client = new Anthropic();

const response = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 1024,
  tools: [
    {
      name: "get_weather",
      description: "Get the current weather in a given location",
      input_schema: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "The city and state, e.g. San Francisco, CA"
          }
        },
        required: ["location"]
      }
    }
  ],
  tool_choice: { type: "auto", disable_parallel_tool_use: true },
  messages: [{ role: "user", content: "What is the weather in San Francisco and New York?" }]
});
console.log(response.content);

C#

AnthropicClient client = new();

var parameters = new MessageCreateParams
{
    Model = Model.ClaudeOpus5_5,
    MaxTokens = 1024,
    Tools = [
        new ToolUnion(new Tool()
        {
            Name = "get_weather",
            Description = "Get the current weather in a given location",
            InputSchema = new InputSchema()
            {
                Properties = new Dictionary<string, JsonElement>
                {
                    ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The city and state, e.g. San Francisco, CA" }),
                },
                Required = ["location"],
            },
        }),
    ],
    ToolChoice = new ToolChoiceAuto { DisableParallelToolUse = true },
    Messages = [new UserMessageParam { Content = "What is the weather in San Francisco and New York?" }]
};

var response = await client.Messages.Create(parameters);
Console.WriteLine(response);

Go

client := anthropic.NewClient()

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
	Model:     anthropic.ModelClaudeOpus5_5,
	MaxTokens: 1024,
	Tools: []anthropic.ToolUnionParam{
		{OfTool: &anthropic.ToolParam{
			Name:        "get_weather",
			Description: anthropic.String("Get the current weather in a given location"),
			InputSchema: anthropic.ToolInputSchemaParam{
				Properties: map[string]any{
					"location": map[string]any{
						"type":        "string",
						"description": "The city and state, e.g. San Francisco, CA",
					},
				},
				Required: []string{"location"},
			},
		}},
	},
	ToolChoice: anthropic.ToolChoiceUnionParam{
		OfAuto: &anthropic.ToolChoiceAutoParam{
			DisableParallelToolUse: anthropic.Bool(true),
		},
	},
	Messages: []anthropic.MessageParam{
		anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in San Francisco and New York?")),
	},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(response.Content)

Java

AnthropicClient client = AnthropicOkHttpClient.fromEnv();

InputSchema schema = InputSchema.builder()
    .properties(
        JsonValue.from(
            Map.of(
                "location", Map.of(
                    "type", "string",
                    "description", "The city and state, e.g. San Francisco, CA"
                )
            )
        )
    )
    .putAdditionalProperty("required", JsonValue.from(List.of("location")))
    .build();

MessageCreateParams params = MessageCreateParams.builder()
    .model(Model.CLAUDE_OPUS_5_5)
    .maxTokens(1024L)
    .addTool(
        Tool.builder()
            .name("get_weather")
            .description("Get the current weather in a given location")
            .inputSchema(schema)
            .build()
    )
    .toolChoice(ToolChoiceAuto.builder()
        .disableParallelToolUse(true)
        .build())
    .addUserMessage("What is the weather in San Francisco and New York?")
    .build();

Message response = client.messages().create(params);
IO.println(response.content());

PHP

$client = new Client();

$response = $client->messages->create(
    maxTokens: 1024,
    messages: [
        ['role' => 'user', 'content' => 'What is the weather in San Francisco and New York?']
    ],
    model: Model::CLAUDE_OPUS_5_5,
    toolChoice: ['type' => 'auto', 'disableParallelToolUse' => true],
    tools: [
        [
            'name' => 'get_weather',
            'description' => 'Get the current weather in a given location',
            'input_schema' => [
                'type' => 'object',
                'properties' => [
                    'location' => [
                        'type' => 'string',
                        'description' => 'The city and state, e.g. San Francisco, CA'
                    ]
                ],
                'required' => ['location']
            ]
        ]
    ],
);

echo $response;

Ruby

client = Anthropic::Client.new

response = client.messages.create(
  model: Anthropic::Model::CLAUDE_OPUS_5_5,
  max_tokens: 1024,
  tools: [
    {
      name: "get_weather",
      description: "Get the current weather in a given location",
      input_schema: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "The city and state, e.g. San Francisco, CA"
          }
        },
        required: ["location"]
      }
    }
  ],
  tool_choice: { type: "auto", disable_parallel_tool_use: true },
  messages: [
    { role: "user", content: "What is the weather in San Francisco and New York?" }
  ]
)
puts response.content

恰好一次工具调用

当 tool_choice 类型为 any 或 tool 时,设置 disable_parallel_tool_use: true 意味着 Claude 恰好调用一个工具。Claude Opus 5.5、Claude Sonnet 5.5、Claude Fable 5.1 和 Claude Mythos 5.1 不支持这些 tool_choice 类型(请参阅强制工具使用)。以下示例使用 any。同一字段也适用于 tool:

cURL

curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-opus-5",
    "max_tokens": 1024,
    "tools": [{
      "name": "get_weather",
      "description": "Get the current weather in a given location",
      "input_schema": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "The city and state, e.g. San Francisco, CA"
          }
        },
        "required": ["location"]
      }
    }],
    "tool_choice": {"type": "any", "disable_parallel_tool_use": true},
    "messages": [
      {"role": "user", "content": "What is the weather in San Francisco and New York?"}
    ]
  }'

CLI

ant messages create <<'YAML'
model: claude-opus-5
max_tokens: 1024
tools:
  - name: get_weather
    description: Get the current weather in a given location
    input_schema:
      type: object
      properties:
        location:
          type: string
          description: The city and state, e.g. San Francisco, CA
      required: [location]
tool_choice:
  type: any
  disable_parallel_tool_use: true
messages:
  - role: user
    content: What is the weather in San Francisco and New York?
YAML

Python

client = Anthropic()

response = client.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    tools=[
        {
            "name": "get_weather",
            "description": "Get the current weather in a given location",
            "input_schema": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA",
                    }
                },
                "required": ["location"],
            },
        }
    ],
    tool_choice={"type": "any", "disable_parallel_tool_use": True},
    messages=[
        {
            "role": "user",
            "content": "What is the weather in San Francisco and New York?",
        }
    ],
)
print(response.content)

TypeScript

const client = new Anthropic();

const response = await client.messages.create({
  model: "claude-opus-5",
  max_tokens: 1024,
  tools: [
    {
      name: "get_weather",
      description: "Get the current weather in a given location",
      input_schema: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "The city and state, e.g. San Francisco, CA"
          }
        },
        required: ["location"]
      }
    }
  ],
  tool_choice: { type: "any", disable_parallel_tool_use: true },
  messages: [{ role: "user", content: "What is the weather in San Francisco and New York?" }]
});
console.log(response.content);

C#

AnthropicClient client = new();

var parameters = new MessageCreateParams
{
    Model = Model.ClaudeOpus5,
    MaxTokens = 1024,
    Tools = [
        new ToolUnion(new Tool()
        {
            Name = "get_weather",
            Description = "Get the current weather in a given location",
            InputSchema = new InputSchema()
            {
                Properties = new Dictionary<string, JsonElement>
                {
                    ["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The city and state, e.g. San Francisco, CA" }),
                },
                Required = ["location"],
            },
        }),
    ],
    ToolChoice = new ToolChoiceAny { DisableParallelToolUse = true },
    Messages = [new UserMessageParam { Content = "What is the weather in San Francisco and New York?" }]
};

var response = await client.Messages.Create(parameters);
Console.WriteLine(response);

Go

client := anthropic.NewClient()

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
	Model:     anthropic.ModelClaudeOpus5,
	MaxTokens: 1024,
	Tools: []anthropic.ToolUnionParam{
		{OfTool: &anthropic.ToolParam{
			Name:        "get_weather",
			Description: anthropic.String("Get the current weather in a given location"),
			InputSchema: anthropic.ToolInputSchemaParam{
				Properties: map[string]any{
					"location": map[string]any{
						"type":        "string",
						"description": "The city and state, e.g. San Francisco, CA",
					},
				},
				Required: []string{"location"},
			},
		}},
	},
	ToolChoice: anthropic.ToolChoiceUnionParam{
		OfAny: &anthropic.ToolChoiceAnyParam{
			DisableParallelToolUse: anthropic.Bool(true),
		},
	},
	Messages: []anthropic.MessageParam{
		anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in San Francisco and New York?")),
	},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(response.Content)

Java

AnthropicClient client = AnthropicOkHttpClient.fromEnv();

InputSchema schema = InputSchema.builder()
    .properties(
        JsonValue.from(
            Map.of(
                "location", Map.of(
                    "type", "string",
                    "description", "The city and state, e.g. San Francisco, CA"
                )
            )
        )
    )
    .putAdditionalProperty("required", JsonValue.from(List.of("location")))
    .build();

MessageCreateParams params = MessageCreateParams.builder()
    .model(Model.CLAUDE_OPUS_5)
    .maxTokens(1024L)
    .addTool(
        Tool.builder()
            .name("get_weather")
            .description("Get the current weather in a given location")
            .inputSchema(schema)
            .build()
    )
    .toolChoice(ToolChoiceAny.builder()
        .disableParallelToolUse(true)
        .build())
    .addUserMessage("What is the weather in San Francisco and New York?")
    .build();

Message response = client.messages().create(params);
IO.println(response.content());

PHP

$client = new Client();

$response = $client->messages->create(
    maxTokens: 1024,
    messages: [
        ['role' => 'user', 'content' => 'What is the weather in San Francisco and New York?']
    ],
    model: Model::CLAUDE_OPUS_5,
    toolChoice: ['type' => 'any', 'disableParallelToolUse' => true],
    tools: [
        [
            'name' => 'get_weather',
            'description' => 'Get the current weather in a given location',
            'input_schema' => [
                'type' => 'object',
                'properties' => [
                    'location' => [
                        'type' => 'string',
                        'description' => 'The city and state, e.g. San Francisco, CA'
                    ]
                ],
                'required' => ['location']
            ]
        ]
    ],
);

echo $response;

Ruby

client = Anthropic::Client.new

response = client.messages.create(
  model: Anthropic::Model::CLAUDE_OPUS_5,
  max_tokens: 1024,
  tools: [
    {
      name: "get_weather",
      description: "Get the current weather in a given location",
      input_schema: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "The city and state, e.g. San Francisco, CA"
          }
        },
        required: ["location"]
      }
    }
  ],
  tool_choice: { type: "any", disable_parallel_tool_use: true },
  messages: [
    { role: "user", content: "What is the weather in San Francisco and New York?" }
  ]
)
puts response.content

故障排除

如果 Claude 没有按预期进行并行工具调用,请检查以下常见问题:

1. 工具结果格式不正确

最常见的问题是在对话历史中错误地格式化工具结果。这会“教会” Claude 避免并行调用。

具体针对并行工具使用:

  • 错误: 为每个工具结果使用单独的用户消息
  • 正确: 所有工具结果一起放在单条用户消息中
// Wrong: separate user messages reduce parallel tool use
[
  {"role": "assistant", "content": [tool_use_1, tool_use_2]},
  {"role": "user", "content": [tool_result_1]},
  {"role": "user", "content": [tool_result_2]}  // Separate message
]

// Correct: one user message with all results maintains parallel tool use
[
  {"role": "assistant", "content": [tool_use_1, tool_use_2]},
  {"role": "user", "content": [tool_result_1, tool_result_2]}  // Single message
]

有关其他格式规则,请参阅处理工具调用。

2. 提示力度不足

默认提示可能不够。请使用最大化并行工具使用中更强的系统提示。

3. 衡量并行工具使用情况

要验证并行工具调用是否正常工作:

cURL

要衡量并行工具使用情况,您的代码需要分析已收集的响应。这需要的逻辑超出了单个 shell 命令的能力。请改用 Claude SDK。

CLI

要衡量并行工具使用情况,您的代码需要分析已收集的响应。这需要的逻辑超出了单个 shell 命令的能力。请改用 Claude SDK。

Python

messages = []  # Message objects returned by client.messages.create across your run

tool_call_messages = [
    msg for msg in messages if any(block.type == "tool_use" for block in msg.content)
]
total_tool_calls = sum(
    len([block for block in msg.content if block.type == "tool_use"])
    for msg in tool_call_messages
)
avg_tools_per_message = (
    total_tool_calls / len(tool_call_messages) if tool_call_messages else 0.0
)
print(f"Average tools per message: {avg_tools_per_message}")
# 如果并行调用正常工作,该值应 > 1.0

TypeScript

const messages: Anthropic.Message[] = []; // Message objects returned by client.messages.create across your run

const toolCallMessages = messages.filter((message) =>
  message.content.some((block) => block.type === "tool_use")
);
const totalToolCalls = toolCallMessages.reduce(
  (sum, message) => sum + message.content.filter((block) => block.type === "tool_use").length,
  0
);
const avgToolsPerMessage =
  toolCallMessages.length > 0 ? totalToolCalls / toolCallMessages.length : 0;
console.log(`Average tools per message: ${avgToolsPerMessage}`);
// 如果并行调用正常工作,该值应 > 1.0

C#

List<Message> messages = []; // Message objects returned by client.Messages.Create across your run

var toolCallMessages = messages
    .Where(message => message.Content.Any(block => block.TryPickToolUse(out _)))
    .ToList();
var totalToolCalls = toolCallMessages
    .Sum(message => message.Content.Count(block => block.TryPickToolUse(out _)));
var avgToolsPerMessage = toolCallMessages.Count > 0 ? (double)totalToolCalls / toolCallMessages.Count : 0.0;
Console.WriteLine($"Average tools per message: {avgToolsPerMessage}");
// 如果并行调用正常工作,该值应 > 1.0

Go

var messages []anthropic.Message // Message values returned by client.Messages.New across your run

toolCallMessageCount := 0
totalToolCalls := 0
for _, message := range messages {
	callsInMessage := 0
	for _, block := range message.Content {
		if block.Type == "tool_use" {
			callsInMessage++
		}
	}
	if callsInMessage > 0 {
		toolCallMessageCount++
		totalToolCalls += callsInMessage
	}
}

avgToolsPerMessage := 0.0
if toolCallMessageCount > 0 {
	avgToolsPerMessage = float64(totalToolCalls) / float64(toolCallMessageCount)
}
fmt.Println("Average tools per message:", avgToolsPerMessage)
// 如果并行调用正常工作,该值应 > 1.0

Java

List<Message> messages = List.of(); // Message objects returned by client.messages().create() across your run

List<Message> toolCallMessages = messages.stream()
    .filter(message -> message.content().stream().anyMatch(ContentBlock::isToolUse))
    .toList();
long totalToolCalls = toolCallMessages.stream()
    .mapToLong(message -> message.content().stream().filter(ContentBlock::isToolUse).count())
    .sum();
double avgToolsPerMessage = toolCallMessages.isEmpty() ? 0.0 : (double) totalToolCalls / toolCallMessages.size();
IO.println("Average tools per message: " + avgToolsPerMessage);
// 如果并行调用正常工作,该值应 > 1.0

PHP

// $messages:在整个运行过程中由 $client->messages->create() 返回的 Message 对象
$messages = [];

$toolCallMessages = array_values(array_filter(
    $messages,
    fn ($message) => count(array_filter($message->content, fn ($block) => $block->type === 'tool_use')) > 0
));
$totalToolCalls = array_sum(array_map(
    fn ($message) => count(array_filter($message->content, fn ($block) => $block->type === 'tool_use')),
    $toolCallMessages
));
$avgToolsPerMessage = count($toolCallMessages) > 0 ? $totalToolCalls / count($toolCallMessages) : 0.0;
echo "Average tools per message: {$avgToolsPerMessage}\n";
// 如果并行调用正常工作,该值应 > 1.0

Ruby

messages = [] # Message objects returned by client.messages.create across your run

tool_call_messages = messages.select { |message| message.content.any? { |block| block.type == :tool_use } }
total_tool_calls = tool_call_messages.sum { |message| message.content.count { |block| block.type == :tool_use } }
avg_tools_per_message = tool_call_messages.empty? ? 0.0 : total_tool_calls.to_f / tool_call_messages.size
puts "Average tools per message: #{avg_tools_per_message}"
# 如果并行调用正常工作,该值应 > 1.0

4. 批次中的调用似乎相互依赖

执行顺序由您选择。如果您的工具存在顺序依赖,依次运行该批次并在第一次失败时停止是一种有效的策略(也是计算机使用和浏览器使用工具所要求的策略):对于您未运行的任何调用,返回 is_error: true。如果您并行运行,而某个调用因其前置条件尚未完成而失败,请返回 is_error: true 并附上自然的错误消息。Claude 将在下一轮次重新发出该调用。要减少相互依赖的调用一起出现的情况,请将以下内容添加到您的系统提示中:“Only batch tool calls that are independent of each other.”

后续步骤

  • Tool runner(SDK):使用 SDK 的 tool runner 抽象自动处理代理循环、错误包装和类型安全。

  • 处理工具调用:解析 tool_use 块,格式化 tool_result 响应,并使用 is_error 处理错误。

  • 定义工具:指定工具模式、编写有效的描述,并控制 Claude 何时调用您的工具。

可用性

并行工具使用可用或处于 beta 阶段,具体取决于API。

API 支持

Messages API

  • 可用性:默认开启
  • 用法:如需关闭,请在 tool_choice 中设置 disable_parallel_tool_use: true。
状态 云平台
可用 Claude API, Claude Platform on AWS, Amazon Bedrock, Amazon Bedrock (InvokeModel), Claude Models on Google Cloud (Google managed), Microsoft Foundry

Managed Agents

  • 可用性:始终开启
状态 云平台
Beta Claude API, Claude Platform on AWS