> For the complete documentation index, see [llms.txt](https://yeasy.gitbook.io/claude_guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yeasy.gitbook.io/claude_guide/di-er-bu-fen-gong-ju-pian/03_tools/3.5_programmatic.md).

# 3.5 高级特性：程序化工具调用与代码执行

## 3.5.1 什么是程序化工具调用？

在标准的工具使用模式中，Claude 扮演的是一个“调度员”：它输出指令，代码执行指令。虽然这种模式简单清晰，但在面对复杂任务时，它通过 HTTP 请求往返的“乒乓”效应会导致显著的延迟。

2025 年，Anthropic 引入了 **程序化工具调用**。这是一种范式转变：Claude 不再仅仅输出单一的 JSON 指令，而是能够编写并在安全的沙箱容器中执行 **Python 代码脚本**。这使得 Claude 能够通过代码逻辑（循环、判断、变量传递）一次性编排多个工具调用，甚至直接在沙箱中处理数据。

### 支持的模型

程序化工具调用支持以下模型（均使用 `code_execution_20260120` 工具类型；该特性现已 GA，无需额外 beta 头）：

| 模型                           | 说明                                                                    |
| ---------------------------- | --------------------------------------------------------------------- |
| `claude-fable-5`             | 新命名体系旗舰（2026-06-09 GA），Adaptive Thinking 常开；2026-06-12 起访问暂停，启用前核验可用性 |
| `claude-mythos-5`            | 仅限 Project Glasswing 受邀；2026-06-12 起访问暂停                              |
| `claude-opus-4-8`            | Opus 档旗舰，支持 Adaptive Thinking 与 1M 上下文                                |
| `claude-opus-4-7`            | Opus 前版本（2026-04-16发布）                                                |
| `claude-opus-4-6`            | Opus 前版本                                                              |
| `claude-sonnet-4-6`          | Sonnet 最新版                                                            |
| `claude-opus-4-5-20251101`   | Opus 4.5（dated ID）                                                    |
| `claude-sonnet-4-5-20250929` | Sonnet 4.5（dated ID）                                                  |

该功能通过 Claude API、Claude Platform on AWS 和 Microsoft Foundry 提供；目前在 Amazon Bedrock 与 Vertex AI 上不可用。

## 3.5.2 架构对比：打破“乒乓”效应

以下通过一个场景来对比两种模式：**“获取系统中所有活跃用户的名单，并为每个人生成一份月度报表。”**

### 传统模式

1. **Claude**: `get_active_users()`
2. **App**: 返回 100 个用户的 JSON List（消耗大量 Token）。
3. **Claude**: `generate_report(user_id=1)`
4. **App**: 返回结果。
5. **Claude**: `generate_report(user_id=2)`
6. ... (重复100次)

**由于上下文窗口 (Context Window) 限制和网络延迟，这种任务在传统模式下几乎不可行。**

### 程序化模式

1. **Claude**: 生成并执行如下 Python 代码：

   ```python
   users = await get_active_users() # 调用工具（异步）
   results = []

   # 在容器内进行逻辑处理，无需回传给 LLM 上下文
   for user in users:
       if user['status'] == 'active':
           rep = await generate_report(user_id=user['id'])
           results.append(rep)

   print(f"Generated {len(results)} reports.")
   ```
2. **App/Container**: 执行整段代码。
3. **App**: 仅返回最终的一句话结果。

**优势总结**：

* **极低延迟**：将 100 次网络往返压缩为 1 次。
* **节省 Token**：中间数据（如那 100 个用户的详细信息）在代码空间中流转，无需进入 LLM 的上下文窗口。程序化调用的工具结果 **不计入** 输入/输出 Token——只有最终代码执行结果和 Claude 的回复才计费。
* **逻辑完备**：支持 `if-else`、`for` 循环和异常处理。

> \[!TIP] **Token 效率**：官方基准给出了量级——在 BrowseComp、DeepSearchQA 等智能体搜索基准上，于基础搜索工具之上启用程序化调用平均提升 11% 准确率、减少 24% 输入 token；在一个 75 工具的项目管理基准中，计费输入 token 减少约 38%。注意：顺序单调用型工作流（每轮只调一两个工具）无收益，成本反而略增约 8%。

## 3.5.3 配置与启用

要启用此功能，需要告知 Claude 哪些工具允许被代码调用。

### `allowed_callers` 字段

在定义工具 Schema 时，新增 `allowed_callers` 属性：

```python
tools = [
    {
        "type": "code_execution_20260120",  # 代码执行工具
        "name": "code_execution"
    },
    {
        "name": "query_database",
        "description": "执行 SQL 查询",
        "input_schema": { ... },
        # 关键配置
        "allowed_callers": [
            "code_execution_20260120"  # 允许被代码容器调用
        ]
    }
]
```

`allowed_callers` 的可选值：

| 值                                       | 含义                           |
| --------------------------------------- | ---------------------------- |
| `["direct"]`                            | 引导 Claude 直接调用（标准模式，省略时的默认值） |
| `["code_execution_20260120"]`           | 引导 Claude 仅从代码执行环境中调用        |
| `["direct", "code_execution_20260120"]` | 两种模式均可                       |

> \[!TIP] 建议为每个工具只选择 `['direct']` 或 `['code_execution_20260120']` 其中之一，这样能给 Claude 提供更明确的使用指引。

> \[!IMPORTANT] `allowed_callers` 决定的是工具如何呈现给 Claude，并会与 `tool_choice` 做校验，但它**不是 API 层面的硬性拦截**：Claude 会被强烈引导遵守该约定，客户端仍应准备好处理任何已定义工具的直接 `tool_use` 调用。不要把 `allowed_callers` 当作安全边界。

### `caller` 响应字段

每个 `tool_use` 响应块都包含一个 `caller` 字段，标识该工具是如何被调用的：

```jsonc
// 传统直接调用
{
  "type": "tool_use",
  "id": "toolu_abc123",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": { "type": "direct" }
}

// 程序化调用（从代码执行环境发起）
{
  "type": "tool_use",
  "id": "toolu_xyz789",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": {
    "type": "code_execution_20260120",
    "tool_id": "srvtoolu_abc123"  // 引用发起调用的代码执行工具
  }
}
```

这对于在应用层区分处理直接调用与程序化调用非常有用。

### 容器环境

当 Claude 决定使用程序化工具时，它不仅会生成 `tool_use`，还会请求一个 `code_execution` 环境。

* **生命周期**：每个 Session 默认创建一个新容器，并在闲置约 4.5 分钟后销毁。
* **状态保持**：读取响应里的 `container.id`，并在后续请求中通过 `container` 传入该 ID，可以让 Claude 访问之前定义的变量（如 `pd.DataFrame`）。
* **超时监控**：当容器等待工具结果时，必须在容器过期前响应。可通过 `expires_at` 字段监控剩余时间。如果容器过期，Claude 可能会将该工具调用视为超时并重试。

## 3.5.4 高级模式

程序化调用支持多种强大的代码模式，以下是典型场景。

下面片段展示的是工具内部的异步控制流。复制到真实项目时，需要把它们放入 `async def main()` 并用 `asyncio.run(main())` 启动，或放进已有的异步业务函数中。

### 批量处理

```python
regions = ["West", "East", "Central", "North", "South"]
results = {}
for region in regions:
    data = await query_database(f"<sql for {region}>")
    results[region] = sum(row["revenue"] for row in data)

top_region = max(results.items(), key=lambda x: x[1])
print(f"Top region: {top_region[0]} with ${top_region[1]:,} in revenue")
```

此模式将 N 次模型往返减少为 1 次，且大量中间数据无需进入上下文窗口。

### 提前终止

```python
endpoints = ["us-east", "eu-west", "apac"]
for endpoint in endpoints:
    status = await check_health(endpoint)
    if status == "healthy":
        print(f"Found healthy endpoint: {endpoint}")
        break  # 提前结束，不检查剩余节点
```

### 条件选择工具

```python
file_info = await get_file_info(path)
if file_info["size"] < 10000:
    content = await read_full_file(path)
else:
    content = await read_file_summary(path)
print(content)
```

### 数据过滤

```python
logs = await fetch_logs(server_id)
errors = [log for log in logs if "ERROR" in log]
print(f"Found {len(errors)} errors")
for error in errors[-10:]:  # 仅返回最近 10 条错误
    print(error)
```

## 3.5.5 设计适合程序化调用的工具

程序化调用的工具设计理念与传统模式有所不同。

* **返回结构化数据**：工具最好返回 JSON 对象、列表或 Pandas DataFrame 兼容的数据，方便代码处理。避免返回“自然语言描述”。
  * *Bad*: “张三的年龄是30岁。”
  * *Good*: `{"name": "Zhang San", "age": 30}`
* **提供详细的输出描述**：由于 Claude 会在代码中反序列化工具结果，请清楚记录返回格式（JSON 结构、字段类型等）。
* **原子化 (Atomic)**：工具功能应尽量单一。组合逻辑交给 Claude 写代码去完成。
* **批量接口**：虽然循环调用很快，但提供 `batch_get_users([ids])` 这样的批量接口依然是性能优化的首选。
* **保持响应简洁**：仅返回必要数据，减少处理开销。

## 3.5.6 工具学习：Tool Learning / Few-Shot

对于极其复杂或非直观的工具，仅靠 `description` 可能不够。可以通过 **Few-Shot Examples（少样本示例）** 来“教会” Claude 如何编写正确的调用代码。

Anthropic 推荐使用 XML 格式提供示例：

```xml
<tool_examples>
    <example tool="data_visualizer">
        <task>为上季度的销售额绘制柱状图</task>
        <thinking>
            我需要先获取数据，然后使用 visualize 工具。注意数据格式必须是 List[Dict]。
        </thinking>
        <correct_usage>
            sales_data = await get_sales(quarter="Q3")
            # 数据预处理
            chart_data = [{"x": item["month"], "y": item["amount"]} for item in sales_data]
            await visualize(
                type="bar",
                data=chart_data,
                title="Q3 Sales"
            )
        </correct_usage>
    </example>
</tool_examples>
```

将这部分内容包含在 `system prompt` 中，能显著提高一次成功率。

## 3.5.7 限制与注意事项

尽管功能强大，但目前仍存在边界：

### 功能不兼容项

| 限制         | 说明                                                |
| ---------- | ------------------------------------------------- |
| **结构化输出**  | 设置了 `strict: true` 的工具不支持程序化调用                    |
| **工具选择**   | 无法通过 `tool_choice` 强制触发程序化调用                      |
| **并行工具**   | `disable_parallel_tool_use: true` 与程序化调用不兼容       |
| **MCP 工具** | 通过 MCP Connector 接入的工具**不能**被程序化调用（官方文档明确将其列入排除项） |

### 响应格式限制

当回复程序化工具调用时，响应消息 **必须仅包含 `tool_result` 块**，不能夹带任何文本内容。这是一个严格的格式要求——如果包含了额外文本，会导致解析错误。

```jsonc
// ❌ 错误 - 不能在回复程序化调用时包含文本
{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_01", "content": "[...]" },
    { "type": "text", "text": "What should I do next?" }
  ]
}

// ✅ 正确 - 仅包含 tool_result
{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_01", "content": "[...]" }
  ]
}
```

> \[!IMPORTANT] 此限制仅适用于程序化（代码执行）工具调用的回复。对于常规的客户端工具调用，仍然可以在 `tool_result` 之后包含文本内容。

### 安全注意事项

* 虽然运行在沙箱中，但让 AI 写代码并执行总是伴随风险。建议对代码容器进行网络隔离，仅允许其访问白名单内的 API。
* 工具结果以字符串形式返回，可能包含代码片段或可执行命令。如果工具返回来自外部来源的数据，需注意代码注入风险。

## 3.5.8 何时使用程序化调用

| 适合场景                  | 不太适合场景           |
| --------------------- | ---------------- |
| 处理大数据集，仅需聚合/摘要        | 单次工具调用且返回简单      |
| 涉及 3 个以上相互依赖的工具调用     | 需要即时用户反馈的工具      |
| 需要对工具结果进行过滤、排序或转换     | 极快操作（代码执行开销大于收益） |
| 中间数据不应影响 Claude 推理的场景 | -                |
| 跨多项目并行操作（如检查 50 个端点）  | -                |

## 3.5.9 最佳实践清单

| 维度       | 建议                                                |
| -------- | ------------------------------------------------- |
| **工具粒度** | 保持工具简单、原子化。避免“万能工具”。                              |
| **数据格式** | 始终针对“机器读取”而非“人类阅读”优化工具返回值。                        |
| **输出描述** | 详细记录工具返回的 JSON 结构和字段类型。                           |
| **错误处理** | 在工具内部捕获异常并返回清晰的错误 JSON，让 Claude 的代码能 `try-catch`。 |
| **容器复用** | 发起多个相关请求时复用容器以保持状态。                               |
| **监控**   | 记录 Claude 生成的所有代码，这对于调试和安全审计至关重要。                 |

***

当工具数量增长到数百个时，如何让 Claude 找到正确的工具？需要“工具搜索”。

➡️ [工具搜索与大规模管理](/claude_guide/di-er-bu-fen-gong-ju-pian/03_tools/3.6_tool_search.md)
