进阶工具使用:让 agent 用得起成千上万个工具

AI agent 的未来,是模型能在成百上千个工具之间游刃有余地工作。但要做到这点,agent 不能把每个工具定义都一股脑塞进 context(上下文)。这篇介绍三个新功能:工具搜索(Tool Search Tool)编程式工具调用(Programmatic Tool Calling)工具使用示例(Tool Use Examples),分别解决「找工具、用工具、调对工具」三类瓶颈。

设想这样两种 agent:一个 IDE 助手,集成了 git 操作、文件处理、包管理、测试框架和部署流水线;一个运维协调员,同时连着 Slack、GitHub、Google Drive、Jira、公司数据库和几十个 MCP server。要让这种 agent 真正好用,得满足几个条件:

今天我们发布三个功能来满足这些需求:

在内部测试里,这些功能让我们做出了用传统工具使用方式做不到的东西。比如 Claude for Excel 就用编程式工具调用读写有几千行的表格,而不会把模型的 context window 撑爆。


一、工具搜索(Tool Search Tool)

问题在哪

MCP 工具定义提供了重要信息,但 server 一多,这些 token 就累加起来。看一个五 server 的配置:

MCP server工具数约占 token
GitHub35~26K
Slack11~21K
Sentry5~3K
Grafana5~3K
Splunk2~2K

合计 58 个工具,对话还没开始就消耗了约 55K token。再加上 Jira(光它一个就 ~17K),很快逼近 10 万 token 的开销。在 Anthropic,我们见过优化前工具定义吃掉 134K token 的情况。

而且 token 成本还不是唯一的问题。最常见的失败是选错工具、传错参数——尤其当工具名字长得像,比如 notification-send-usernotification-send-channel

解法

工具搜索工具不再前置加载全部工具定义,而是按需发现。Claude 只看到当前任务真正需要的工具。

这相当于 token 用量减少 85%,同时仍能访问你的完整工具库。在大型工具库的内部 MCP 评测里,准确率也明显提升:Opus 4 从 49% 升到 74%,Opus 4.5 从 79.5% 升到 88.1%

怎么运作

你把全部工具定义都交给 API,但用 defer_loading: true 标记哪些工具「延迟加载」。被延迟的工具一开始不进 Claude 的 context,Claude 只看到工具搜索工具本身,外加那些 defer_loading: false 的工具(也就是你最关键、最常用的几个)。

当 Claude 需要某种能力时,它去搜索相关工具,工具搜索工具返回匹配工具的「引用」,这些引用再被展开成完整定义进入 context。比如 Claude 要操作 GitHub,它搜 “github”,于是只有 github.createPullRequestgithub.listIssues 被加载——而不是你来自 Slack、Jira、Google Drive 的其余 50 多个工具。

关于 prompt caching:工具搜索不会破坏提示词缓存。因为被延迟的工具压根没进初始提示词,只有在 Claude 搜索之后才加入 context,所以你的 system prompt 和核心工具定义仍然可缓存。

实现上是这样:

{
  "tools": [
    // 加入一个工具搜索工具(regex、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 server,你可以整个 server 延迟加载,同时保留个别高频工具:

{
  "type": "mcp_toolset",
  "mcp_server_name": "google-drive",
  "default_config": {"defer_loading": true},
  "configs": {
    "search_files": {"defer_loading": false}
  }
}

平台自带 regex 和 BM25 两种搜索工具,你也可以用 embedding 或别的策略自己实现。

什么时候用

✅ 适合❌ 收益不大
工具定义占用超过 10K token工具库很小(少于 10 个)
遇到工具选择准确率问题每次会话几乎都会用到所有工具
构建多 server 的 MCP 系统工具定义本身就很紧凑
可用工具有 10 个以上

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

问题在哪

随着工作流变复杂,传统工具调用会带来两个根本问题:

  1. 中间结果污染 context:Claude 要在一个 10MB 的日志文件里找错误模式,整个文件都进了 context window,哪怕它只需要一份「错误频次摘要」。跨多张表拉客户数据时,每条记录不管相不相关都堆进 context。这些中间结果吃掉巨量 token,甚至会把重要信息从 context window 里挤出去。
  2. 推理开销 + 手动综合:每次工具调用都要走一遍完整模型推理。拿到结果后,Claude 还得「肉眼」去提取有用信息、推理各部分怎么拼起来、决定下一步——全靠自然语言处理。一个五工具的流程意味着五次推理,外加 Claude 逐个解析结果、比对数值、综合结论。又慢又容易错。

解法

编程式工具调用让 Claude 用代码来编排工具,而不是一次次单独的 API 往返。Claude 写一段代码去调多个工具、处理它们的输出,并精确控制哪些信息真正进入 context window

Claude 本来就擅长写代码。把编排逻辑用 Python 表达,而不是用自然语言去调工具,你就能得到更可靠、更精确的控制流——循环、条件、数据变换、错误处理全都明明白白写在代码里,而不是隐含在 Claude 的推理里。

例子:预算合规检查。 任务是「哪些团队成员的 Q3 差旅费超预算了?」你有三个工具:get_team_members(department)get_expenses(user_id, quarter)get_budget_by_level(level)

传统做法编程式工具调用
流程拉 20 人 → 每人 20 次调用、各返回 50-100 条明细 → 再查预算Claude 写一段 Python,跑在代码执行沙箱里
进 context 的数据2,000+ 条报销明细(50KB+),逐人手动求和、查预算、比对只有最终结果(超预算的那 2-3 人)
消耗大量往返 + 巨量 context从 200KB 原始数据压到 ~1KB 结果

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
])
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))

那 2,000+ 条明细、中间求和、预算查询,全都不影响 Claude 的 context。

效率提升很实在:

怎么运作

  1. 把工具标记为可从代码调用:加入 code_execution,给要参与的工具设 allowed_callers: ["code_execution_20250825"]。API 会把这些工具定义转成 Claude 能调用的 Python 函数。
  2. Claude 写编排代码:不再一次请求一个工具,而是生成一段 server_tool_use 的 Python 代码。
  3. 工具执行但不碰 context:当代码调用 get_expenses(),你收到带 caller 字段的工具请求;你返回的结果在代码执行环境里被处理,而不是进 Claude 的 context。这个请求-响应循环对代码里每次工具调用重复进行。
  4. 只有最终输出进 context:代码跑完,只有 stdout 里的结果回到 Claude——而不是沿途处理过的那 2,000+ 条明细。

什么时候用

✅ 适合❌ 收益不大
处理大数据集、只要聚合或摘要简单的单工具调用
多步工作流,3 个及以上有依赖的调用你希望 Claude 看到并推理所有中间结果
在 Claude 看到之前就过滤/排序/变换结果响应很小的快速查询
中间数据不该影响 Claude 推理
跨大量条目的并行操作(比如检查 50 个端点)

三、工具使用示例(Tool Use Examples)

问题在哪

JSON Schema 擅长定义结构——类型、必填字段、允许的枚举值——但它表达不了使用模式

拿一个工单 API 来说,schema 能说清什么合法,却留下一堆没答案的问题:

这些模糊地带会导致调用格式错误、参数用法前后不一。

解法

工具使用示例让你直接在工具定义里给出样例调用。不再只靠 schema,而是给 Claude 看具体的用法。你可以在 input_examples 里放几个示范,比如一个「完整填写」的严重 bug 工单、一个「部分填写」的需求工单、一个「只填标题」的内部任务。

从这三个例子里,Claude 能学到:

在我们内部测试里,工具使用示例让复杂参数处理的准确率从 72% 升到 90%

什么时候用

✅ 适合❌ 收益不大
复杂嵌套结构,合法 JSON 不等于用对了用法显而易见的单参数工具
工具有很多可选参数、且取舍有讲究URL、email 这类 Claude 已熟悉的标准格式
API 有 schema 里没体现的领域约定用 JSON Schema 约束就能解决的校验问题
相似工具,需要示例点明该用哪个

把三者组合起来:最佳实践

构建能在真实世界里行动的 agent,意味着同时应对规模、复杂度、精度。这三个功能解决工具使用流程里的不同瓶颈。

⭐ 按需分层,从最大的瓶颈下手:

你的瓶颈对应功能
工具定义把 context 撑爆工具搜索
大块中间结果污染 context编程式工具调用
参数出错、调用格式错误工具使用示例

不必一上来就三个全用。它们是互补的:工具搜索保证找对工具,编程式调用保证高效执行,工具使用示例保证调用正确。

几条具体建议:


怎么开始用

这些功能目前是 beta。加上 beta header,引入你需要的工具即可:

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
    ]
)

直觉:这三个功能把工具使用从「简单的函数调用」推向「智能的编排」。当 agent 要应对横跨几十个工具、动辄大数据集的复杂工作流,动态发现(找对)、高效执行(用好)、可靠调用(调对) 这三件事,就成了地基。说到底,它们的共同内核只有一句——别让无关信息占着 context

本译文采用 CC BY-NC-SA 4.0 协议发布,仅供学习交流。

原作品版权归 Anthropic(Anthropic Engineering)所有,原文请见 这里