# Claude 工具调用：用 Python 定义工具并处理调用

> Claude 工具调用，是让模型请你的代码去运行一个函数。你用名称、描述和 JSON Schema 来描述每个工具。Claude 想用某个工具时，响应会以 stop_reason 为 tool_use 结束。你运行函数，再在下一条用户消息里发回 tool_result。模型自己从不运行任何东西。

Mindket 编辑部 · 发布于 2026-11-04 · 更新于 2026-11-05 · https://mindket.com/cn/learn/claude-api/claude-tool-use

## 要点

- 用名称、足够长的描述和 JSON Schema 来描述每个工具。
- 每个 tool_use 块都要回一个带相同 id 的 tool_result。
- 并行结果要一起发，失败要用 is_error 上报。
- 知道哪些 tool_choice 取值会被当前模型拒绝。

## 开始之前

请先学完[第 1 课](https://mindket.com/cn/learn/claude-api/claude-api-quickstart)：这一课沿用了它的客户端、密钥和遍历内容块的循环。工具调用走的是同一个 Messages 接口，只是请求里多了一个 `tools` 列表。

[工具调用总览](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview)把工具分成两类，但模型自己都不运行。客户端工具，也就是你定义的那些，由你的代码运行函数并回报结果。网页搜索、网页抓取和代码执行这类服务端工具，则在 Anthropic 那边运行，所以响应到达时，结果可能已经在里面了。这一课讲的是客户端工具。

## 跑通 tool_use 与 tool_result 往返

一次请求，一个工具，两次调用。下面的代码定义了 `get_weather`，提出一个需要它的问题，并处理一次往返。[第 4 课](https://mindket.com/cn/learn/claude-api/build-ai-agent-claude-api)会把同样的步骤包进循环里。天气函数是为本课写的占位实现，请换成真正的查询。

第二次请求能否被接受，取决于两条规则。带结果的用户消息必须紧跟在含有 `tool_use` 块的助手消息之后，而且 `tool_result` 块必须排在它内容的最前面，位于任何文字之前。违反任何一条，API 都会返回 400。

````python
import json
import anthropic

client = anthropic.Anthropic()

tools = [
    {
        "name": "get_weather",
        "description": (
            "Get the current weather for a city. Use it when the user asks about "
            "weather, temperature or whether to bring an umbrella. It returns the "
            "temperature in Celsius and a short condition. It does not forecast."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "A city name, for example Lisbon"},
            },
            "required": ["city"],
        },
    }
]


def get_weather(city: str) -> str:
    # Stand-in for a real weather API call.
    return json.dumps({"city": city, "temp_c": 18, "condition": "light rain"})


messages = [{"role": "user", "content": "Do I need an umbrella in Lisbon today?"}]

response = client.messages.create(
    model="claude-opus-5-5", max_tokens=1024, tools=tools, messages=messages
)

if response.stop_reason == "tool_use":
    messages.append({"role": "assistant", "content": response.content})
    results = []
    for block in response.content:
        if block.type == "tool_use":
            results.append(
                {
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": get_weather(**block.input),
                }
            )
    messages.append({"role": "user", "content": results})
    response = client.messages.create(
        model="claude-opus-5-5", max_tokens=1024, tools=tools, messages=messages
    )

for block in response.content:
    if block.type == "text":
        print(block.text)
````

1. 用 `name`、`description` 和 `input_schema` 定义工具，再把列表作为 `tools` 传入。
2. 发送用户消息。Claude 回复的 `stop_reason` 是 `tool_use`，并带有一个或多个 `tool_use` 块，每个块有 `id`、`name` 和 `input`。
3. 把 `response.content` 完整、不加修改地追加到历史里，作为助手消息。使用工具时，文档要求每个 thinking 块都原样传回。
4. 对每个 `tool_use` 块运行你的函数，并构造一个 `tool_result` 块，其 `tool_use_id` 与它的 id 相同。
5. 把所有结果放进一条用户消息里，再调用一次 API。这时 Claude 会写出最终回答。

## 写出 Claude 能用好的工具定义

描述决定了大部分效果。[文档](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools)把它称作影响工具表现的最重要因素，要求至少写三四句话：这个工具做什么、什么时候用、每个参数是什么意思，以及它返回什么。

少而宽的工具，比多而窄的工具效果更好，所以把相关操作合并成一个带 `action` 参数的工具。几个工具并排时，名称按服务加前缀，比如 `github_list_prs`。函数只返回信息量高的数据，因为它返回的一切，都会变成下一次调用的输入 token。

| 字段 | 是否必需 | 要知道的事 |
| --- | --- | --- |
| `name` | 是 | 必须匹配 `^[a-zA-Z0-9_-]{1,128}$` |
| `description` | 强烈建议 | 纯文本，至少三四句话 |
| `input_schema` | 是 | 带 `properties` 和 `required` 的 JSON Schema 对象 |
| `input_examples` | 否 | 示例输入，必须能通过 schema 校验，简单工具每个约 20 到 50 个 token，嵌套结构约 100 到 200 个 |
| `strict` | 否 | 设为 `true`，让 Claude 的输入符合 schema |
| `cache_control` | 否 | 放在工具列表上的缓存断点，第 3 课会讲 |

## 用 tool_choice 引导调用

默认情况下，Claude 自己判断请求需不需要工具。`tool_choice` 可以改写这一点，在当前的模型上，它的四个取值表现各不相同。从发布说明可以看到，九月以来，强制调用的取值被一个模型接一个模型地关掉了。

并行调用也由同一个字段控制。在 `auto` 下，把 `disable_parallel_tool_use` 设为 `true`，就把 Claude 限制在每个回合最多调用一次工具。

> **新模型上强制 tool_choice 会返回 400**
>
> 在 Opus 5.5、Sonnet 5.5、Fable 5.1 和 Mythos 5.1 上，`any` 和 `tool` 两个取值会返回 400。Haiku 5.5 仍然接受，不过强制调用会跳过思考。需要固定输出格式时，文档建议用 `auto` 加上工具上的 `strict: true`，或者改用结构化输出。在两次请求之间改变 `tool_choice`，还会让已缓存的消息失效。

**Claude 发布说明里的工具调用变化**

1. **2024年5月30日** 工具调用结束 beta。在 Claude API、Bedrock 和 Vertex AI 上都不再需要 beta 请求头。
2. **2024年10月3日** 新增 `disable_parallel_tool_use`。写在 `tool_choice` 里，让 Claude 每个回合最多调用一个工具。
3. **2025年2月27日** `tool_choice` 新增 `none`。可以禁止 Claude 调用任何工具。
4. **2025年9月17日** 工具辅助函数和工具运行器进入 beta。Python 和 TypeScript SDK 可以替你跑循环。
5. **2026年2月17日** 工具使用示例不再需要 beta 请求头。工具搜索、记忆、代码执行和网页抓取工具也一样。
6. **2026年9月1日** Fable 5.1 拒绝强制 `tool_choice`。在 Fable 5.1 和 Mythos 5.1 上，`any` 和 `tool` 会返回错误。
7. **2026年9月22日** Opus 5.5 同样拒绝。另外，工具可以写进对话中途的系统消息里，目前是 beta。
8. **2026年9月28日** Sonnet 5.5 也一样。强制调用工具同样返回错误。

来源：[Claude Docs: Release notes](https://platform.claude.com/docs/en/release-notes/overview) (2026-11-05 查阅)

| tool_choice | 行为 | 注意 |
| --- | --- | --- |
| `{"type": "auto"}` | Claude 自己决定要不要调用工具 | 传入工具时的默认值 |
| `{"type": "any"}` | Claude 必须调用你的某一个工具 | 下面方框里的模型会返回 400 |
| `{"type": "tool", "name": "get_weather"}` | Claude 必须调用指定的工具 | 同样返回 400，而且强制调用会跳过思考 |
| `{"type": "none"}` | Claude 不能调用工具 | 没有传入工具时的默认值 |

## 一次响应里的多个工具调用

问两座城市的天气，Claude 可以在同一个响应里返回两个 `tool_use` 块。你的代码决定是一个接一个地运行，还是同时运行。关键在于回复：把每个 `tool_result` 都放进同一条用户消息里。

把每个结果分成单独的用户消息发送，一开始也能用，但[并行工具调用页面](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use)提醒说，这样会教会 Claude 不再做并行调用。每个结果仍然需要自己那次调用的 id，API 靠 id 来配对，不看位置。

````json
{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_01", "content": "{\"temp_c\": 18}" },
    { "type": "tool_result", "tool_use_id": "toolu_02", "content": "{\"temp_c\": 24}" }
  ]
}
````

## 上报错误，让 Claude 能恢复

工具有两种失败方式：你的函数抛出异常，或者 Claude 发来的输入不合规。两种都用 `tool_result` 上报，把 `is_error` 设为 `true`，并附上说明怎样修正调用的消息。对于参数无效或缺失的调用，文档说 Claude 会先带着修正重试两三次，再向用户道歉。像下面这样的消息给了它修正的依据，一个光秃秃的“Error”则没有。

你决定不运行的调用，也需要一个 `tool_result`，带上 `is_error` 和原因。漏掉一个，下一次请求就会失败，错误信息说的是发现了 tool_use id，但后面没有紧跟 tool_result 块。

把工具返回的一切都当作不可信内容。[处理工具调用](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls)页面提醒，网页或文件里可能带着专门用来引导模型的文字，文档把这叫作间接提示词注入（indirect prompt injection）。所以工具能碰什么，要在你自己的代码里强制限制，不要只写在提示词里。

````json
{
  "type": "tool_result",
  "tool_use_id": "toolu_01",
  "is_error": true,
  "content": "City not found. Pass a city name such as Lisbon, not a country."
}
````

## Anthropic 和 Alex Albert 在 X 上谈工具设计

Anthropic 在 2025-09-11 [在 X 上发帖](https://x.com/AnthropicAI/status/1966236220868247701)，推荐了工程博客上的[为智能体写好工具](https://www.anthropic.com/engineering/writing-tools-for-agents)一文。文章认为，工具越多，效果不一定越好。它举的例子是：只返回匹配行的 `search_logs`，比把日志全部返回的 `read_logs` 更好用。定义工具页面至今仍把这篇文章列为命名和返回内容的延伸阅读，所以 `get_weather` 也照此办理，只返回两个字段，而不是整个天气 API 的响应。

Alex Albert 在 Anthropic 做研究，他在 2026-02-17 [发帖说](https://x.com/alexalbert__/status/2023834875678298535)，工具使用示例、工具搜索和程序化工具调用都已正式可用。工具使用示例就是工具字段表里的 `input_examples`，现在不需要 beta 请求头。先把描述写好，再给带嵌套输入的工具加两三个示例。

> 代码执行、网页抓取、记忆、程序化工具调用、工具搜索和工具使用示例，现在都已正式可用。
>
> Alex Albert, 在 Anthropic 做研究，引文为译文, [X](https://x.com/alexalbert__/status/2023834875678298535) (2026-11-05 查阅)

[![Anthropic（@AnthropicAI）2025 年 9 月 11 日在 X 上发帖，介绍 Anthropic 工程博客的新文章：为 LLM 智能体写好工具，并给开发者分享让工具更好用的建议。](https://mindket.com/blog/shots/x-anthropicai-effective-tools.webp?v=585cb8fc6b6e)](https://x.com/AnthropicAI/status/1966236220868247701)

Anthropic 在 X 上，2025 年 9 月 11 日

[![Alex Albert（@alexalbert__）2026 年 2 月 17 日在 X 上发帖：代码执行、网页抓取、记忆、程序化工具调用、工具搜索和工具使用示例现已正式可用。](https://mindket.com/blog/shots/x-alexalbert-tool-use-examples-ga.webp?v=5651907c9990)](https://x.com/alexalbert__/status/2023834875678298535)

Alex Albert 在 X 上，2026 年 2 月 17 日

> X 官方嵌入卡片的截图，拍摄于 2026-11-05，时间为 UTC。点击截图可到 X 上打开原帖。两张卡片都省略了原帖结尾的链接。

## 常见错误

- 把每个 tool_result 放进单独的用户消息，这会减少并行调用。
- 在同一条消息里，把文字放在 tool_result 块的前面。
- 在结果后面紧跟着加一个文本块，这可能让 Claude 用空回复结束回合。
- 只用文本块重建助手消息，结果丢掉了 API 要求传回的 thinking 块。
- 描述只写一行，然后纳闷 Claude 为什么选错了工具。
- 在会拒绝它的模型上，用 tool_choice 强制调用工具。
- 明明 Claude 只要两个字段，却返回整行数据库记录。

## 自己试一试

- 再加一个工具 `get_time`，提一个两个工具都要用的问题。返回了几个 `tool_use` 块，你是不是在一条用户消息里回复的？
- 让 `get_weather` 在城市是“Atlantis”时抛出异常，并返回本课的错误 JSON。Claude 是换一个城市重试，还是告诉用户？
- 把描述删到只剩一句话，比较哪些问题仍然会触发这个工具。
- 把 `disable_parallel_tool_use` 设为 `true`，再问一遍两座城市的问题。

## 接下来

这一课的循环只跑一轮就停了，而每多一轮，都要重新发送同样的工具列表和系统提示词。[第 3 课](https://mindket.com/cn/learn/claude-api/claude-prompt-caching)会把它们缓存起来。如果你更想接入现成的工具，不想自己写 schema，可以看 MCP 课程里[连接服务器](https://mindket.com/cn/learn/mcp/connect-mcp-servers)的那一课。

## 自测一下

1. 响应以 stop_reason "tool_use" 结束，接下来该发送什么？

   A. 一条新的系统提示词
   B. 一条带工具输出的助手消息
   C. 一条用户消息，里面的 tool_result 块带着对应的 tool_use id
   D. 一个 tool_choice 设为 none 的请求

   答案：C。 Claude 从不运行工具，所以由你的代码运行，并在用户消息里回复，每个 tool_use id 对应一个 tool_result。

2. Claude 在一个响应里返回了两个 tool_use 块，结果该怎么发？

   A. 放进一条用户消息，里面有两个 tool_result 块
   B. 分成两条用户消息，每个结果一条
   C. 只发第一个结果，然后再问一次
   D. 放进一条助手消息

   答案：A。 分开的用户消息会教会 Claude 不再做并行调用，所以所有结果都放进同一条消息。

3. 工具在你的代码里抛出了异常，tool_result 应该带什么？

   A. 什么都不带，直接跳过这个块
   B. 一个光秃秃的 "Error" 字符串
   C. 只带完整的堆栈信息
   D. is_error 为 true，加一条说明怎样修正调用的消息

   答案：D。 有指导意义的错误信息，会告诉 Claude 哪里出了错、接下来该试什么，它就不用瞎猜了。

4. 在 Opus 5.5 上，tool_choice 设为 type "tool" 的请求返回了 400，文档建议怎么办？

   A. 加一个 beta 请求头
   B. 用 auto，并在工具上设 strict 为 true，或者用结构化输出
   C. 调高 max_tokens
   D. 去掉系统提示词

   答案：B。 这些模型拒绝强制的 tool_choice，所以文档指向 auto 加 strict，或者结构化输出。

## 常见问题

### 该用工具运行器，还是自己写循环？

工具运行器在 Python 和 TypeScript SDK 里还是 beta，它替你运行循环，并把工具抛出的异常变成 is_error 结果。需要人工审批、自定义日志或有条件执行时，文档建议手写循环。

### 漏掉 tool_result 会得到什么错误？

一个 invalid_request_error，内容是发现了 tool_use id，但后面没有紧跟 tool_result 块。检查结果是不是在紧接着的那条用户消息里，以及每个 tool_use id 是否都有对应的结果。

### 为什么 API 要我把 thinking 块传回去？

使用工具时，文档要求助手回合里的每个 thinking 和 redacted_thinking 块都按收到时的样子传回，连空的也不例外。编辑、重排或过滤它们会让请求失败，所以请追加完整的响应内容。

### 工具定义要花 token 吗？

要。工具定义、tool_use 块和 tool_result 块都计入 token。在 Opus 5.5 上，文档还列出一段 286 个 token 的工具调用系统提示词，tool_choice 为 auto 或 none 时会加上它。

### 工具定义可以缓存吗？

可以。缓存顺序是工具、系统提示词、消息，缓存断点 cache_control 可以放在最后一个工具上。改动任何一个工具定义，都会让整个缓存失效，第 3 课会讲。

## 资料来源

- [Anthropic Docs: Tool use with Claude](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) (2026-11-05 查阅)
- [Anthropic Docs: Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools) (2026-11-05 查阅)
- [Anthropic Docs: Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls) (2026-11-04 查阅)
- [Anthropic Docs: Parallel tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use) (2026-11-04 查阅)
- [Anthropic Docs: How tool use works](https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works) (2026-11-04 查阅)
- [Anthropic Docs: Tool runner](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner) (2026-11-04 查阅)
- [Anthropic Docs: Stop reasons and fallback](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons) (2026-11-04 查阅)
- [Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) (2026-11-04 查阅)
- [Anthropic Docs: Claude API errors](https://platform.claude.com/docs/en/api/errors) (2026-11-04 查阅)
- [Claude Docs: Release notes](https://platform.claude.com/docs/en/release-notes/overview) (2026-11-05 查阅)
- [Anthropic Engineering: Writing effective tools for agents](https://www.anthropic.com/engineering/writing-tools-for-agents) (2026-11-05 查阅)
- [X: Anthropic shares its guide to writing tools for agents](https://x.com/AnthropicAI/status/1966236220868247701) (2026-11-05 查阅)
- [X: Alex Albert on tool use examples and tool search becoming generally available](https://x.com/alexalbert__/status/2023834875678298535) (2026-11-05 查阅)

[浏览 AI 智能体](https://mindket.com/cn/agents)
