Skip to main content

05 - 工具与检索结果的占用

前置01 篇第三节那张构成图 —— 第 1 轮里 90% 是工具定义。

本篇回答:怎么让内容根本不进上下文。这是三类手段里投入产出比最高的一类,因为省下的是每一轮都要重复付的开销。

本篇会用到的词

意思
工具定义名字、描述、参数 schema 三部分。它们渲染在请求最前面,每一轮都完整发送
延迟加载在工具上标 defer_loading: true,定义先不进上下文,等被搜到再加载
工具搜索一个服务端工具,让模型按需搜索自己的工具目录。有正则和 BM25 两个变体
工具引用(tool_reference)搜索结果里指向某个工具的引用。它对应的完整定义必须在 tools 数组里存在
命名空间前缀按服务给工具名加前缀(github_slack_),让一次搜索能命中一整组

一、工具定义:还没干活就付掉的钱

官方文档给的实测量级:一套典型的多 MCP 服务配置(GitHub、Slack、Sentry、Grafana、Splunk)在 Claude 做任何事之前就要消耗约 55K token 的工具定义

这笔开销的性质和其他开销完全不同:

工具定义是常数开销,但"常数×轮数"不是常数单轮55K全量加载,第 1 轮40 轮累计2.2M同一份定义被发送了 40 遍开工具搜索后320K单轮约 8K:几个常驻工具 + 本轮搜到的 3 到 5 个前缀缓存能把重复部分的价格压到约 1/10,但前提是工具集完全不变 —— 按用户动态拼工具的写法(06 篇)会让这条也失效。
官方给的减少幅度是"通常超过 85%",图中 55K → 8K 就是按这个量级取的。真正的重点在第二行:把一个常数乘以轮数之后,它往往比那些看起来"在增长"的项加起来还大。

二、第二个问题:工具一多,模型就挑不准了

比 token 更麻烦的是选择准确率。官方给的拐点很具体:超过 30 到 50 个可用工具之后,Claude 挑对工具的能力开始下降

横轴是可用工具总数,纵轴是模型挑对工具的比例用工具搜索全量加载30 到 50 个:拐点102001000+两条线的差别不在模型能力,在"这一轮它面前摆着几个选项"。搜索把选项数从几百压回三五个,选择问题就消失了。
曲线形状是示意,拐点位置来自官方文档。这条比 token 那条更重要:token 超支表现为账单变贵,选择准确率下降表现为 Agent 开始做错事 —— 后者往往先被用户发现。

三、工具搜索怎么配

const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 16000,
tools: [
// 1) 搜索工具本身。⚠️ 它绝对不能标 defer_loading —— 全部工具都延迟会 400
{ type: "tool_search_tool_regex_20251119", name: "tool_search_tool_regex" },

// 2) 高频工具保持非延迟:每次请求都会用到的那 3 到 5 个,
// 让它们常驻比让模型每轮搜一遍更省(搜索本身也要一次往返)
{ name: "read_file", description: "...", input_schema: {/* ... */} },
{ name: "write_file", description: "...", input_schema: {/* ... */} },

// 3) 其余全部延迟加载。定义必须完整写在这里 ——
// 搜索返回的是 tool_reference,指向的定义不在数组里会 400
{ name: "github_create_pr", description: "...", input_schema: {/* ... */}, defer_loading: true },
{ name: "github_list_issues", description: "...", input_schema: {/* ... */}, defer_loading: true },
{ name: "slack_post_message", description: "...", input_schema: {/* ... */}, defer_loading: true },
// ... 最多 10,000 个
],
messages,
});

3.1 两个变体

变体type匹配方式适合
正则tool_search_tool_regex_20251119模型写一个正则去匹配工具名有严格命名规范时,精确度高
BM25tool_search_tool_bm25_20251119词频检索描述写得自然、用户措辞多变时

两者都不需要 beta 头。搜索的范围是工具名、描述、参数名、参数描述四个字段全都算 —— 所以参数描述写得好,也能提高被搜到的概率。

3.2 限额

限额数值
最多延迟加载工具数10,000 个 / 请求
单次搜索默认返回5 个(模型可在搜索输入里把 limit 设成 1 到 10,000 的任意整数)
正则长度上限200 字符
BM25 查询长度上限500 字符

另外一条容易误判的:工具搜索不单独计费。响应的 usage.server_tool_use 里没有它的字段,搜索加载进来的工具定义就按普通输入 token 计。

3.3 四个常见错误

错误表现原因与修法
全部工具都延迟400,报 all tools are deferred搜索工具本身不能标 defer_loading;且至少要有一个非延迟工具
引用指向不存在的定义400,missing tool definition每个可能被搜到的工具都要在 tools 里写完整定义(含 descriptioninput_schema),只写名字不行
模型搜不到该用的工具任务失败但不报错正则没匹配上名字/描述/参数名/参数描述任一字段。用 re.search(pattern, name, re.IGNORECASE) 本地验一下
高频工具也被延迟了每轮多一次搜索往返,延迟上升把最常用的 3 到 5 个改成非延迟

第三个最难查,因为它不报错 —— Agent 只是表现得像"不会做这件事"

3.4 让工具更容易被搜到

官方给的几条,都是低成本高回报的:

  • 一致的命名空间前缀github_slack_jira_。一次搜索能命中一整组
  • 描述里用用户会用的词,不是内部术语。用户说"提个 PR",描述里就该有"pull request"和"PR"
  • 在系统提示词里列出工具类别:一句"你可以搜索与 Slack、GitHub、Jira 交互的工具"就够。这几十 token 让模型知道去搜什么
  • 监控模型实际搜到了哪些,据此回头改描述

四、什么时候不该用工具搜索

官方给的反向判据同样明确 —— 满足以下任一条就别上:

  • 工具少于 10 个
  • 每个工具在每次请求里都会用到
  • 工具定义总量很小(不到 100 token)

理由是搜索本身要一次模型往返。工具只有八个的时候,全量加载的那点 token 远比每轮多一次搜索便宜

正向判据(满足任一即可上):工具 ≥ 10 个、定义总量 > 10K token、随工具增多选择准确率在下降、聚合了多个 MCP 服务(200+ 工具)、工具库还在增长。

五、工具结果:另一半占用

工具定义是静态的,工具结果是动态的 —— 01 篇那张图里,第 40 轮 82% 的占用在这里。除了裁剪之外,更省的是让它一开始就别那么大

同一次"读一个 4,000 行的日志文件",三种返回方式全量返回4,000 行全进上下文约 60K token模型真正要看的可能只有 20 行剩下的全是干扰项(02 篇 2.2)分页返回先给前 200 行附带:共 4,000 行,怎么取下一段约 3K token代价:可能要多几次往返结构化返回先给:ERROR 12 条、WARN 340 条再让模型点名要哪一类约 400 token 就能定位代价:要为每类工具写归约逻辑
右边两种都要求工具的返回值是你自己设计的,而不是把底层 API 的响应原样透传。这是"工具设计"和"上下文工程"重合的地方 —— 一个把 4,000 行原样丢回来的工具,无论后面怎么裁剪都已经晚了。

三条可以直接落地的规则:

# 1) 每个工具都要有返回上限,且超限时明确告诉模型被截断了、总量是多少
MAX_RESULT_TOKENS = 4000
def truncate(result: str, total_items: int) -> str:
if count_tokens(result) <= MAX_RESULT_TOKENS:
return result
# ❌ 只截断不说明:模型会以为这就是全部,基于半份数据下结论
# ✅ 说清楚,模型才知道要不要翻页
return head(result, MAX_RESULT_TOKENS) + \
f"\n[已截断:共 {total_items} 条,此处显示前 {shown} 条。用 offset 参数取后续]"

# 2) 错误返回要短。一个完整的 Python traceback 可能几千 token,
# 而模型需要的是异常类型 + 最后三帧
def format_error(e: Exception) -> str:
return f"{type(e).__name__}: {e}\n" + "".join(traceback.format_tb(e.__traceback__)[-3:])

# 3) 别把底层响应原样透传。一个 HTTP API 的 JSON 里往往七成是分页元信息、
# 链接、时间戳这类模型用不上的字段 —— 在工具里就挑出需要的字段

六、小结

  • 工具定义是"还没干活就付掉、且每轮重复付"的常数开销;五个 MCP 服务约 55K token,40 轮累计 2.2M
  • 比 token 更要紧的是选择准确率:超过 30 到 50 个工具就开始下降,表现为 Agent 做错事而不是变贵
  • 工具搜索的三条硬规则:搜索工具本身不能延迟、至少留一个非延迟工具、延迟工具的完整定义仍必须在 tools
  • 高频的 3 到 5 个工具保持非延迟;命名空间前缀和"用用户的词写描述"是最便宜的两项改进
  • 少于 10 个工具、或每次都全用、或定义总量很小,就别上工具搜索
  • 工具结果的治理要做在工具里:设返回上限并说明截断、错误只回类型和最后几帧、不透传底层响应

下一篇:06 - 前缀缓存与排布,前面三篇每一招都会动到请求前缀 —— 这一篇是它们共同的约束。

← 回到 专题索引