在 Claude 开发者平台上引入高级工具使用能力

作者:Anthropic 工程团队

AI 智能体(AI Agents)的未来,在于模型能够跨越数百甚至数千种工具,实现无缝协作。想象一下:一个集成了 Git 操作、文件管理、包管理器、测试框架和部署流水线的 IDE 助手;或者一个能同时连接 Slack、GitHub、Google Drive、Jira、公司数据库以及数十个 MCP 服务器的运营协调员。

为了构建高效的智能体,它们需要能够调用无限的工具库,而无需在一开始就把所有工具定义都硬塞进上下文(Context)里。我们在关于使用 MCP 进行代码执行的博客文章中曾讨论过,仅仅是工具的结果和定义,有时在智能体还没开始阅读请求之前,就已经消耗了 50,000 多个 Token。智能体应该按需发现和加载工具,只保留当前任务真正相关的内容。

此外,智能体还需要具备通过代码调用工具的能力。如果使用自然语言进行工具调用,每一次调用都需要经过一次完整的推理过程(Inference pass),而且中间产生的临时结果——无论有用与否——都会堆积在上下文中。代码天然适合处理编排逻辑,比如循环、条件判断和数据转换。智能体需要灵活地根据手头的任务,在“执行代码”和“模型推理”之间做出选择。

最后,智能体还需要通过示例来学习如何正确使用工具,而不仅仅是依赖 Schema 定义。JSON Schema 定义了什么是结构上“合法”的数据,但它无法表达使用模式:什么时候该包含可选参数?哪些参数组合是有意义的?或者你的 API 期望什么样的惯例?

今天,我们要发布三个新功能来实现这一愿景:

  • 工具搜索工具 (Tool Search Tool): 允许 Claude 使用搜索工具访问成千上万的工具,而不会消耗它的上下文窗口。

  • 编程方式调用工具 (Programmatic Tool Calling): 允许 Claude 在代码执行环境中调用工具,从而减少对模型上下文窗口的占用。

  • 工具使用示例 (Tool Use Examples): 提供了一个通用标准,用于演示如何有效地使用给定的工具。

在内部测试中,我们发现这些功能帮助我们构建出了以前使用传统工具调用模式无法实现的东西。例如,Claude for Excel 就使用了“编程方式调用工具”来读取和修改包含数千行数据的电子表格,而不会让模型的上下文窗口过载。

基于我们的经验,我们相信这些功能将为你利用 Claude 构建应用开辟新的可能性。

挑战

MCP(Model Context Protocol,模型上下文协议,一种让 AI 模型连接外部数据和工具的标准接口)工具定义虽然提供了重要的上下文,但随着连接的服务器越来越多,这些 Token 也会积少成多。试想一个包含五个服务器的设置:

  • GitHub: 35 个工具 (~26K tokens)

  • Slack: 11 个工具 (~21K tokens)

  • Sentry: 5 个工具 (~3K tokens)

  • Grafana: 5 个工具 (~3K tokens)

  • Splunk: 2 个工具 (~2K tokens)

这意味着在对话还没开始前,这 58 个工具就已经消耗了大约 55,000 个 Token。如果再添加像 Jira 这样的服务器(仅它自己就要消耗约 17K Token),你会很快逼近 100,000+ Token 的额外开销。在 Anthropic 内部,我们曾见过在未优化的情况下,仅工具定义就消耗了 134,000 个 Token。

但这不仅仅是 Token 成本的问题。最常见的失败原因往往是工具选择错误和参数不正确,特别是当工具名称相似时,比如 notification-send-user(给用户发通知) 和 notification-send-channel(给频道发通知)。

我们的解决方案

工具搜索工具 (Tool Search Tool) 不会预先加载所有工具定义,而是按需发现工具。Claude 只会在上下文中看到它当前任务实际需要的工具。

Tool Search Tool diagram

与 Claude 的传统方法(消耗 122,800 Token)相比,工具搜索工具保留了 191,300 Token 的上下文空间。

传统方法:

  • 预先加载所有工具定义(50+ 个 MCP 工具约占 72K Token)

  • 对话历史和系统提示词(System Prompt)不得不争抢剩余的空间

  • 在任何工作开始之前,总上下文消耗约为 77K Token

使用工具搜索工具:

  • 仅预先加载“工具搜索工具”本身(约 500 Token)

  • 根据需要按需发现工具(加载 3-5 个相关工具,约 3K Token)

  • 总上下文消耗:约 8.7K Token,节省了 95% 的上下文窗口

这意味着在保持对完整工具库访问权限的同时,减少了 85% 的 Token 使用量。内部测试显示,在处理大型工具库时,MCP 评估的准确性有了显著提高。启用工具搜索工具后,Opus 4 的准确率从 49% 提升至 74%,Opus 4.5 从 79.5% 提升至 88.1%。

工具搜索工具让 Claude 能够动态地发现工具,而不是一开始就加载所有定义。你需要向 API 提供所有的工具定义,但将那些希望按需发现的工具标记为 defer_loading: true(延迟加载)。被延迟加载的工具最初不会进入 Claude 的上下文。Claude 只会看到“工具搜索工具”本身,以及任何标记为 defer_loading: false 的工具(即你最关键、最常用的工具)。

当 Claude 需要特定能力时,它会搜索相关工具。工具搜索工具会返回匹配工具的引用,然后这些引用会在 Claude 的上下文中展开为完整的定义。

例如,如果 Claude 需要与 GitHub 交互,它会搜索 "github",然后只有 github.createPullRequestgithub.listIssues 会被加载——而不会加载你来自 Slack、Jira 和 Google Drive 的其他 50 多个工具。

通过这种方式,Claude 既能访问你的全套工具库,又只需为它实际使用的工具支付 Token 成本。

实现代码:

{
  "tools": [
    // 包含一个工具搜索工具 (基于正则, BM25, 或自定义)
    {"type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex"},

    // 标记需要按需发现的工具
    {
      "name": "github.createPullRequest",
      "description": "Create a pull request",
      "input_schema": {...},
      "defer_loading": true
    }
    // ... 还有数百个设置了 defer_loading: true 的延迟加载工具
  ]
}

对于 MCP 服务器,你可以延迟加载整个服务器,同时保留特定的高频工具:

{
  "type": "mcp_toolset",
  "mcp_server_name": "google-drive",
  "default_config": {"defer_loading": true}, // 延迟加载整个服务器
  "configs": {
    "search_files": {
"defer_loading": false
    }  // 保持最常用的工具已加载
  }
}

Claude 开发者平台开箱即用提供了基于正则(Regex)和 BM25 算法的搜索工具,但你也可以使用嵌入(Embeddings)或其他策略来实现自定义搜索工具。

像任何架构决策一样,启用工具搜索工具也涉及权衡。该功能在工具调用之前增加了一个搜索步骤,因此只有当节省的上下文和提升的准确性超过了额外的延迟时,它才能带来最大的投资回报率 (ROI)。

适用场景:

  • 工具定义消耗超过 10K Token

  • 遇到工具选择准确性问题

  • 构建包含多个服务器的 MCP 驱动系统

  • 可用工具数量超过 10 个

不太适用的场景:

  • 小型工具库(少于 10 个工具)

  • 所有工具在每次会话中都会频繁使用

  • 工具定义非常紧凑

编程方式调用工具 (Programmatic Tool Calling)

挑战

随着工作流变得越来越复杂,传统的工具调用方式制造了两个根本性问题:

  • 中间结果造成的上下文污染 (Context Pollution):当 Claude 分析一个 10MB 的日志文件以查找错误模式时,整个文件都会进入它的上下文窗口,哪怕 Claude 只需要一个错误频率的摘要。当跨多个表获取客户数据时,每条记录无论是否相关都会堆积在上下文中。这些中间结果不仅消耗大量的 Token 预算,甚至可能把重要的信息挤出上下文窗口。

  • 推理开销和人工合成:每一次工具调用都需要经过一次完整的模型推理过程。收到结果后,Claude 必须“肉眼”检查数据以提取相关信息,推理各个部分如何组合,并用自然语言决定下一步做什么。一个涉及 5 个工具的工作流意味着 5 次推理过程,加上 Claude 解析每个结果、比较数值和综合结论的过程。这既缓慢又容易出错。

我们的解决方案

编程方式调用工具 (Programmatic Tool Calling) 使 Claude 能够通过代码而不是通过单独的 API 往返交互来编排工具。Claude 不是一次请求一个工具并将每个结果返回到其上下文,而是编写代码来同时调用多个工具,处理它们的输出,并控制哪些信息真正进入它的上下文窗口。

Claude 擅长编写代码,通过让它用 Python 而不是自然语言的工具调用来表达编排逻辑,你可以获得更可靠、更精确的控制流。循环、条件判断、数据转换和错误处理都在代码中显式表达,而不是隐含在 Claude 的推理过程中。

示例:预算合规性检查

考虑一个常见的业务任务:“哪些团队成员超出了他们的 Q3 差旅预算?”

你有三个可用工具:

  • get_team_members(department) - 返回包含 ID 和级别的团队成员列表

  • get_expenses(user_id, quarter) - 返回用户的费用明细项

  • get_budget_by_level(level) - 返回员工级别的预算限额

传统方法

  • 获取团队成员 → 20 人

  • 为每个人获取 Q3 费用 → 20 次工具调用,每次返回 50-100 个条目(机票、酒店、餐饮、发票)

  • 获取各员工级别的预算限额

  • 所有这些都进入 Claude 的上下文:2,000+ 个费用明细项(50 KB+)

  • Claude 手动对每个人的费用求和,查找他们的预算,将费用与预算限额进行比较

  • 更多的模型往返交互,巨大的上下文消耗

使用编程方式调用工具

Claude 不是将每个工具的结果返回给自己,而是编写一个 Python 脚本来编排整个工作流。该脚本在“代码执行工具”(一个沙箱环境)中运行,并在需要工具结果时暂停。当你通过 API 返回工具结果时,它们会被脚本处理,而不是被模型“阅读”。脚本继续执行,Claude 只会看到最终的输出。

编程方式调用工具允许 Claude 通过代码来编排工具,而不是通过单独的 API 往返,从而实现工具的并行执行。

以下是 Claude 为预算合规性任务编写的编排代码示例:

team = await get_team_members("engineering")

# 为每个唯一的级别获取预算
levels = list(set(m["level"] for m in team))
budget_results = await asyncio.gather(*[
    get_budget_by_level(level) for level in levels
])

# 创建查找字典: {"junior": budget1, "senior": budget2, ...}
budgets = {level: budget for level, budget in zip(levels, budget_results)}

# 并行获取所有费用
expenses = await asyncio.gather(*[
    get_expenses(m["id"], "Q3") for m in team
])

# 找出超出差旅预算的员工
exceeded = []
for member, exp in zip(team, expenses):
    budget = budgets[member["level"]]
    total = sum(e["amount"] for e in exp)
    if total > budget["travel_limit"]:
        exceeded.append({
            "name": member["name"],
            "spent": total,
            "limit": budget["travel_limit"]
        })

print(json.dumps(exceeded))

Claude 的上下文只接收最终结果:那两三个超支的人。那 2,000 多个明细项、中间的求和过程以及预算查找都不会影响 Claude 的上下文,将消耗从 200KB 的原始费用数据减少到仅 1KB 的结果。

效率的提升是巨大的:

  • 节省 Token:通过将中间结果排除在 Claude 的上下文之外,PTC(编程方式调用工具)大幅降低了 Token 消耗。在复杂的研究任务中,平均使用量从 43,588 Token 降至 27,297 Token,减少了 37%。

  • 降低延迟:每一次 API 往返都需要模型推理(几百毫秒到几秒)。当 Claude 在单个代码块中编排 20 多个工具调用时,你消除了 19 次以上的推理过程。API 处理工具执行,而无需每次都返回模型。

  • 提高准确性:通过编写明确的编排逻辑,Claude 犯的错误比用自然语言处理多个工具结果时要少。内部知识检索准确率从 25.6% 提高到 28.5%;GIA 基准测试从 46.5% 提高到 51.2%。

生产环境的工作流涉及杂乱的数据、条件逻辑和需要扩展的操作。编程方式调用工具让 Claude 能够通过编程处理这些复杂性,同时将注意力集中在可操作的结果上,而不是原始数据处理上。

编程方式调用工具是如何工作的

1. 将工具标记为可从代码调用

code_execution 添加到工具中,并设置 allowed_callers 以选择加入编程执行的工具:

{
  "tools": [
    {
      "type": "code_execution_20250825",
      "name": "code_execution"
    },
    {
      "name": "get_team_members",
      "description": "Get all members of a department...",
      "input_schema": {...},
      "allowed_callers": ["code_execution_20250825"] // 选择加入编程方式工具调用
    },
    {
      "name": "get_expenses",
      ...
    },
    {
      "name": "get_budget_by_level",
    ...
    }
  ]
}

API 会将这些工具定义转换为 Claude 可以调用的 Python 函数。

2. Claude 编写编排代码

Claude 不再一次请求一个工具,而是生成 Python 代码:

{
  "type": "server_tool_use",
  "id": "srvtoolu_abc",
  "name": "code_execution",
  "input": {
    "code": "team = get_team_members('engineering')\n..." // 上面的代码示例
  }
}

3. 工具执行而不触及 Claude 的上下文

当代码调用 get_expenses() 时,你会收到一个带有 caller 字段的工具请求:

{
  "type": "tool_use",
  "id": "toolu_xyz",
  "name": "get_expenses",
  "input": {"user_id": "emp_123", "quarter": "Q3"},
  "caller": {
    "type": "code_execution_20250825",
    "tool_id": "srvtoolu_abc"
  }
}

你提供结果,该结果在代码执行环境中处理,而不是在 Claude 的上下文中处理。对于代码中的每个工具调用,这个请求-响应循环都会重复。

4. 只有最终输出进入上下文

当代码运行结束时,只有代码的结果会返回给 Claude:

{
  "type": "code_execution_tool_result",
  "tool_use_id": "srvtoolu_abc",
  "content": {
    "stdout": "[{\"name\": \"Alice\", \"spent\": 12500, \"limit\": 10000}...]"
  }
}

这就是 Claude 看到的全部内容,而不是处理过程中涉及的那 2000 多个费用明细项。

何时使用编程方式调用工具

编程方式调用工具会在你的工作流中增加一个代码执行步骤。当 Token 节省、延迟改善和准确性提升显著时,这种额外的开销是值得的。

最有利的场景:

  • 处理大型数据集,且你只需要聚合数据或摘要

  • 运行具有三个或更多依赖工具调用的多步骤工作流

  • 在 Claude 看到结果之前过滤、排序或转换工具结果

  • 处理中间数据不应影响 Claude 推理的任务

  • 对许多项目运行并行操作(例如,检查 50 个端点)

不太有利的场景:

  • 进行简单的单次工具调用

  • 任务需要 Claude 查看所有中间结果并进行推理

  • 运行响应很小的快速查找

工具使用示例 (Tool Use Examples)

挑战

JSON Schema 虽然擅长定义结构——类型、必填字段、允许的枚举值——但它无法表达使用模式:何时包含可选参数,哪些组合是有意义的,或者你的 API 期望什么样的惯例。

考虑一个支持工单 API:

{
  "name": "create_ticket",
  "input_schema": {
    "properties": {
      "title": {"type": "string"},
      "priority": {"enum": ["low", "medium", "high", "critical"]},
      "labels": {"type": "array", "items": {"type": "string"}},
      "reporter": {
        "type": "object",
        "properties": {
          "id": {"type": "string"},
          "name": {"type": "string"},
          "contact": {
            "type": "object",
            "properties": {
              "email": {"type": "string"},
              "phone": {"type": "string"}
            }
          }
        }
      },
      "due_date": {"type": "string"},
      "escalation": {
        "type": "object",
        "properties": {
          "level": {"type": "integer"},
          "notify_manager": {"type": "boolean"},
          "sla_hours": {"type": "integer"}
        }
      }
    },
    "required": ["title"]
  }
}

Schema 定义了什么是有效的,但留下了关键问题悬而未决:

  • 格式歧义: due_date 应该用 "2024-11-06","Nov 6, 2024",还是 "2024-11-06T00:00:00Z"?

  • ID 惯例: reporter.id 是 UUID,"USR-12345",还是仅仅 "12345"?

  • 嵌套结构用法: Claude 什么时候应该填充 reporter.contact

  • 参数相关性: escalation.levelescalation.sla_hours 如何与优先级(priority)关联?

这些歧义可能导致畸形的工具调用和不一致的参数使用。

我们的解决方案

工具使用示例允许你直接在工具定义中提供工具调用样本。与其仅依赖 Schema,不如向 Claude 展示具体的使用模式:

通过这三个例子,Claude 学会了:

  • 格式惯例:日期使用 YYYY-MM-DD,用户 ID 遵循 USR-XXXXX,标签使用 kebab-case(短横线连接)。

  • 嵌套结构模式:如何构建带有嵌套联系人对象的 reporter 对象。

  • 可选参数相关性:严重 Bug 需要完整的联系信息 + 带有严格 SLA 的升级策略;功能请求有报告人但没有联系/升级信息;内部任务只有标题。

在我们自己的内部测试中,工具使用示例将复杂参数处理的准确率从 72% 提高到了 90%。

何时使用工具使用示例

工具使用示例会增加工具定义的 Token 消耗,因此当准确性的提升超过额外成本时,它们最有价值。

最有利的场景:

  • 复杂的嵌套结构,其中“符合 JSON 语法”并不意味着用法正确

  • 有许多可选参数且包含模式很重要的工具

  • API 具有 Schema 无法捕获的特定领域惯例

  • 相似的工具,需要通过示例来阐明使用哪一个(例如,create_ticket vs create_incident

不太有利的场景:

  • 用法显而易见的简单单参数工具

  • Claude 已经理解的标准格式,如 URL 或电子邮件

  • 最好由 JSON Schema 约束处理的验证问题

最佳实践

构建能够采取现实世界行动的智能体意味着同时处理规模、复杂性和精度。这三个功能共同解决了工具使用工作流中的不同瓶颈。以下是如何有效地组合它们。

策略性地分层功能

并非每个智能体的每个任务都需要使用所有这三个功能。从你最大的瓶颈开始:

  • 工具定义导致的上下文膨胀 → 工具搜索工具

  • 大型中间结果污染上下文 → 编程方式调用工具

  • 参数错误和畸形调用 → 工具使用示例

这种有的放矢的方法让你能够解决限制智能体性能的具体约束,而不是一开始就增加复杂性。

然后根据需要叠加其他功能。它们是互补的:工具搜索工具确保找到正确的工具,编程方式调用工具确保高效执行,工具使用示例确保正确调用。

工具搜索是根据名称和描述进行匹配的,因此清晰、描述性的定义可以提高发现的准确性。

// Good
{
    "name": "search_customer_orders",
    "description": "Search for customer orders by date range, status, or total amount. Returns order details including items, shipping, and payment info."
}

// Bad
{
    "name": "query_db_orders",
    "description": "Execute order query"
}

添加系统提示词(System Prompt)指导,以便 Claude 知道有哪些能力可用:

你可以访问用于 Slack 消息传递、Google Drive 文件管理、
Jira 工单跟踪和 GitHub 仓库操作的工具。使用工具搜索
来查找特定的功能。

保持你最常用的三到五个工具始终加载,推迟加载其他的。这在常用操作的即时访问和所有其他操作的按需发现之间取得了平衡。

为正确执行设置编程方式调用工具

由于 Claude 编写代码来解析工具输出,请清晰地记录返回格式。这有助于 Claude 编写正确的解析逻辑:

{
    "name": "get_orders",
    "description": "Retrieve orders for a customer.
Returns:
    List of order objects, each containing:
    - id (str): Order identifier
    - total (float): Order total in USD
    - status (str): One of 'pending', 'shipped', 'delivered'
    - items (list): Array of {sku, quantity, price}
    - created_at (str): ISO 8601 timestamp"
}

以下是适合编程编排的选入(opt-in)工具类型:

  • 可以并行运行的工具(独立操作)

  • 可以安全重试的操作(幂等性)

为参数准确性设置工具使用示例

精心制作示例以明确行为:

  • 使用真实数据(真实的城市名称,合理的价格,而不是 "string" 或 "value")

  • 展示多样性,包括最小化、部分和完整参数的模式

  • 保持简洁:每个工具 1-5 个示例

  • 关注歧义(只在正确用法无法从 Schema 中明显看出的地方添加示例)

开始使用

这些功能目前处于 Beta 阶段。要启用它们,请添加 beta 标头并包含你需要的工具:

client.beta.messages.create(
    betas=["advanced-tool-use-2025-11-20"],
    model="claude-sonnet-4-5-20250929",
    max_tokens=4096,
    tools=[
        {"type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex"},
        {"type": "code_execution_20250825", "name": "code_execution"},
        # 你的包含 defer_loading, allowed_callers 和 input_examples 的工具
    ]
)

有关详细的 API 文档和 SDK 示例,请参阅我们的:

这些功能将工具使用从简单的函数调用推向了智能编排。随着智能体开始处理跨越数十种工具和大型数据集的更复杂工作流,动态发现、高效执行和可靠调用将成为基础。

我们期待看到你构建的成果。

致谢

本文由 Bin Wu 撰写,Adam Jones, Artur Renault, Henry Tay, Jake Noble, Nathan McCandlish, Noah Picard, Sam Jiang 以及 Claude 开发者平台团队做出了贡献。这项工作建立在 Chris Gorgolewski, Daniel Jiang, Jeremy Fox 和 Mike Lambert 的基础研究之上。我们也从整个 AI 生态系统中汲取了灵感,包括 Joel Pobar 的 LLMVMCloudflare 的 Code ModeCode Execution as MCP。特别感谢 Andy Schumeister, Hamish Kerr, Keir Bradwell, Matt Bleifer 和 Molly Vorwerck 的支持。


来源: https://www.anthropic.com/engineering/advanced-tool-use