并行工具使用

启用和格式化并行工具调用,包含消息历史指导和故障排除。


本页介绍并行工具调用:当 Claude 在一个回合中调用多个工具时,如何格式化消息历史以保持并行性正常工作,以及如何禁用它。有关单次调用流程,请参阅处理工具调用

默认情况下,Claude 可能会使用多个工具来回答用户查询。你可以通过以下方式禁用此行为:

  • tool_choice 类型为 auto 时设置 disable_parallel_tool_use=true,确保 Claude 最多使用一个工具
  • tool_choice 类型为 anytool 时设置 disable_parallel_tool_use=true,确保 Claude 恰好使用一个工具

执行语义

单个助手回合中的工具调用是无序的。你可以并发运行它们(Promise.allasyncio.gather)、按顺序运行或以任何顺序运行。Claude 不会假设批次中的一个调用在另一个之前完成。Claude 在不同的回合中发出依赖调用。

Claude 可能偶尔会批量处理实际上相互依赖的调用(例如,创建后跟更新同一资源)。你不需要提前检测这一点:分派所有调用,如果一个调用因为缺少前置条件而失败,在 tool_result 中返回自然错误消息并附带 is_error: true。Claude 会识别依赖关系并在前置条件完成后重新发出调用。

{
  "type": "tool_result",
  "tool_use_id": "toolu_02",
  "is_error": true,
  "content": "cat: report.md: No such file or directory"
}

完整示例

Note

使用工具运行器更简单:下面的示例展示了手动并行工具处理。对于大多数用例,工具运行器可以自动处理并行工具执行,代码量更少。

这是一个完整的、可运行的脚本,用于测试和验证并行工具调用是否正常工作:

#!/usr/bin/env python3
"""Test script to verify parallel tool calls with the Claude API"""

import os
from anthropic import Anthropic

# Initialize client
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

# Define tools
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"],
        },
    },
]

# Test conversation with parallel tool calls
messages = [
    {
        "role": "user",
        "content": "What's the weather in SF and NYC, and what time is it there?",
    }
]

# Make initial request
print("Requesting parallel tool calls...")
response = client.messages.create(
    model="claude-opus-4-7", max_tokens=1024, messages=messages, tools=tools
)

# Check for parallel tool calls
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")

# Simulate tool execution and format results correctly
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}
    )

# Continue conversation with tool results
messages.extend(
    [
        {"role": "assistant", "content": response.content},
        {"role": "user", "content": tool_results},  # All results in one message!
    ]
)

# Get final response
print("\nGetting final response...")
final_response = client.messages.create(
    model="claude-opus-4-7", max_tokens=1024, messages=messages, tools=tools
)

print(f"\nClaude's response:\n{final_response.content[0].text}")

# Verify formatting
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")
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

// Define tools
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"]
    }
  }
];

async function testParallelTools() {
  // Make initial request
  console.log("Requesting parallel tool calls...");
  const response = await client.messages.create({
    model: "claude-opus-4-7",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: "What's the weather in SF and NYC, and what time is it there?"
      }
    ],
    tools: tools
  });

  // Check for parallel tool calls
  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!");
    toolUses.forEach((tool) => {
      if (tool.type === "tool_use") {
        console.log(`  - ${tool.name}: ${JSON.stringify(tool.input)}`);
      }
    });
  } else {
    console.log("✗ No parallel tool calls detected");
  }

  // Simulate tool execution and format results correctly
  const toolResults: Anthropic.ToolResultBlockParam[] = toolUses
    .filter((block): block is Anthropic.ToolUseBlock => 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
      };
    });

  // Get final response with correct formatting
  console.log("\nGetting final response...");
  const finalResponse = await client.messages.create({
    model: "claude-opus-4-7",
    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}`);
    }
  }

  // Verify formatting
  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");
}

testParallelTools().catch(console.error);

此脚本演示:

  • 如何正确格式化并行工具调用和结果
  • 如何验证并行调用正在执行
  • 鼓励未来并行工具使用的正确消息结构
  • 应避免的常见错误(如工具结果前的文本)

运行此脚本以测试你的实现,确保 Claude 有效地进行并行工具调用。

最大化并行工具使用

虽然 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 在预期时没有进行并行工具调用,请检查以下常见问题:

1. 错误的工具结果格式化

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

特别是对于并行工具使用:

  • 错误:为每个工具结果发送单独的用户消息
  • 正确:所有工具结果必须在一条用户消息中
// ❌ 这会减少并行工具使用
[
  {"role": "assistant", "content": [tool_use_1, tool_use_2]},
  {"role": "user", "content": [tool_result_1]},
  {"role": "user", "content": [tool_result_2]}  // 单独的消息
]

// ✅ 这保持了并行工具使用
[
  {"role": "assistant", "content": [tool_use_1, tool_use_2]},
  {"role": "user", "content": [tool_result_1, tool_result_2]}  // 单条消息
]

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

2. 提示不够强

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

3. 测量并行工具使用

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

messages = []  # Your conversation history of assistant Message objects
# Calculate average tools per tool-calling message
tool_call_messages = [
    msg for msg in messages if any(block.type == "tool_use" for block in msg.content)
]
total_tool_calls = sum(
    len([b for b in msg.content if b.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}")
# Should be > 1.0 if parallel calls are working

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

如果工具调用因为依赖同一批次中的另一个调用而失败,返回 is_error: true 和自然错误消息(你不需要解释依赖关系)。Claude 会恢复并重新发出调用。不要切换到顺序执行;那会增加延迟并掩盖问题。要减少这种情况,请将此添加到你的系统提示中:"Only batch tool calls that are independent of each other."

后续步骤

  • 有关单次工具调用流程和 tool_result 格式化规则,请参阅处理工具调用
  • 有关自动处理并行执行的 SDK 抽象,请参阅工具运行器
  • 有关完整的工具使用工作流,请参阅定义工具