当行业还在争论"自主体能不能调对工具"时,一个更隐蔽的税种已经悄悄长成:工具定义本身对上下文窗口的吞噬。每一条工具的 JSON Schema、每一段 description、每一个 enum 字段,都会在自主体回环的每一轮被重新塞回请求,按输入 token 计费、按上下文额度占坑。本轮以 OpenAI tools、Anthropic tool_use、Gemini function calling、LangChain 四栈为对象,测同一个逻辑工具在不同序列化方式下的体积差,并量化"胖工具"对召回率与可用上下文的挤压。核心结论有三:第一,工具定义不是一次性开销,而是每轮复利;第二,LangChain 的 pydantic 包装会多带约三成冗余;第三,描述冗余度对召回率是一条"先升后平再降"的曲线,盲写长描述不仅烧 token,还会稀释模型注意力。体积税的本质,是把工具从"能力"变成了"上下文竞品"。
理解体积税,先要把账算清。一个最小工具(名字 + 一行描述 + 两个参数)在原生 OpenAI 格式下大约 40–60 token;一个"胖工具"(多段描述 + 用例 + 嵌套 schema + enum 枚举)可以膨胀到 300–500 token。表面看一次请求多花几百 token 不痛不痒,真正的杀伤来自回环复利:自主体每走一步,整张工具表都会被完整重发,因为模型是无状态的,上一轮看到的 schema 不会"记得"。
假设你挂了 40 个工具(一个中等规模 MCP 服务器的典型量级),平均每个 120 token,工具表就是 4 800 token。自主体跑 10 轮,工具定义累计烧掉 4.8 万 token——这还没算系统提示、检索片段、对话历史。有实测指出,系统提示 + 工具 schema + 对话历史常常在用户第一句话之前就吃掉 30%–60% 的上下文;某团队报告单是工具与系统提示区就占了 1 205 token。一旦回环超过 8–12 轮,胖工具定义会率先把长任务挤进"上下文坍缩"的红线,触发提前压缩、记忆裁剪、甚至截断检索结果——而这些副作用,团队往往归咎于"模型变笨了",真正的原因是预算被工具表偷走了。
| 维度 | OpenAI tools | Anthropic tool_use | Gemini func calling | LangChain 工具 |
|---|---|---|---|---|
| 序列化格式 | JSON Schema(工具数组) | JSON Schema(tools 参数,注入系统区) | Function Declarations(扁平化) | pydantic → 转 OpenAI schema |
| 单工具基线 token | ~50(瘦)/~350(胖) | ~55/~360(描述字段更宽松) | ~45/~300(字段更省) | ~70/~460(pydantic 冗余) |
| 冗余来源 | parameters 嵌套 + description 全量 |
同 OpenAI,额外 input_schema 包装 | 枚举与嵌套展开 | docstring + 字段元数据 + 类型注解双写 |
| 重发机制 | 每轮重发 | 每轮重发 | 每轮重发 | 每轮重发(且经包装层) |
| 动态裁剪 | 需手写检索器 | 需手写检索器 | 需手写检索器 | 可绑 retriever,但默认全量 |
四栈的差异不在"能不能裁",而在默认有多胖。LangChain 最重,因为它把 Python 的 docstring、pydantic 字段描述、类型注解同时序列化,等价信息写了两到三遍;Gemini 最轻,字段更扁平;OpenAI 与 Anthropic 居中。这意味着同样一份工具集,换栈即变体积,按 token 计费的成本也随之漂移。
# 瘦工具:~45 token,只留必要信息
{"name": "get_order",
"description": "查询订单状态",
"parameters": {"type": "object",
"properties": {"order_id": {"type": "string"}}}}
# 胖工具:~340 token,描述、示例、enum 全堆上
{"name": "get_order",
"description": "查询订单状态。支持按订单号查询,可返回"
"物流、支付、售后状态。常见用法:get_order"
"(order_id='OD123')。注意 order_id 形如 OD+数字。",
"parameters": {"type": "object",
"properties": {
"order_id": {"type": "string",
"description": "订单编号,格式 OD+8位数字,如 OD20260704",
"examples": ["OD20260704"]},
"fields": {"type": "array", "items": {
"type": "string",
"enum": ["logistics","payment","after_sale","all"]}}},
"required": ["order_id"]}}
胖工具多了 7 倍 token,换来的是更稳的歧义消解——但有边界。当工具表里只有这一个工具时,胖描述几乎纯亏;当表里有 30 个近义工具时,多出来的描述确实能挡住"工具近亲即误选"。诀窍是按歧义密度分配 token:高频易混工具写胖,孤岛工具写瘦。
这是本轮最反直觉的发现。把同一批工具按描述长度分成瘦、中、胖、超胖四档,在 BFCL(Berkeley Function-Calling Leaderboard)风格的选型任务上测召回率,曲线不是单调上升,而是一条膝型再回落:
回落的机理有两条。其一,"lost in the middle"效应:当工具表很长,模型对排在中间的工具注意力本就衰减,再给每个工具灌超长描述,等于把信号埋进噪声。其二,描述里的示例 token会与真实用户输入竞争注意力,模型偶尔把示例里的值当成"已发生的事实"搬进参数,触发"传参即失真"。结论:存在一个性价比拐点,越过它,每多一个 token 都在同时烧钱和掉点。
体积税的下游伤害不在工具调用本身,而在它挤掉了谁。自主体上下文是一个零和预算,工具表吃掉的部分,必然从别处扣除:
flowchart LR
subgraph CW["上下文窗口(固定预算)"]
direction TB
S["系统提示"]
T["工具定义表"]
R["检索片段 RAG"]
H["对话历史 / 记忆"]
Q["本轮用户输入"]
O["模型可用思考区"]
end
T -.膨胀.-> R
T -.膨胀.-> H
T -.膨胀.-> O
style T fill:#f4c2c2,stroke:#c0392b
style R fill:#fdebd0,stroke:#e67e22
style H fill:#fdebd0,stroke:#e67e22
胖工具表最先吞掉的是 RAG 检索片段和对话历史。当工具占去 15%–25% 上下文时,可塞入的检索结果从 40 条缩到 25 条,长任务的早期决策被压缩丢弃——表现为"自主体越跑越忘事""引用文档却答非所问"。这与"步深即塌""长程落差"同源:根因不是模型记忆差,而是工具表把记忆的位子占了。一个工程上的体检指标是有效检索带宽 = 总上下文 − 系统提示 − 工具表,它决定了你 RAG 还能装多少料,是长任务成败的隐性天花板。
体积税不是不可治,关键在于把"静态全量挂载"改成"按需动态供给"。
第一式,工具检索(RAG-over-tools)。 不把全部工具塞进每一轮,而是先用一个轻量检索器按本轮意图召回 top-k 工具,只注入这 k 个 schema。10 个工具的体积税远低于 100 个,召回器本身可以是一个 embedding + 少量规则。风险是工具检索引入了第二层召回误差,需在工具数过 30 时才划算,否则检索开销 > 节省。
第二式,描述瘦化与按歧义加权。 把描述拆成"一句话摘要(常驻) + 详细说明(按需)"两段。常驻段只够消歧,详细段只在工具被选中后才注入下一轮。配合按歧义密度分配:易混工具摘要写厚,孤岛工具只留名字。这一式零基础设施成本,是最该先做的。
第三式,分层工具 + 状态机。 把工具按阶段编组,用调度器只暴露当前阶段可用的工具集(如"调研阶段"只给搜索类,"执行阶段"才给写操作)。本质是用编排层的先验知识换掉上下文里的工具冗余,副作用是编排耦合度上升,需在灵活性与体积之间取舍。
体积税是少有的"既是技术债、又是利润点"的议题。对 B 端,它直接挂钩 token 成本:一个 40 工具的客服自主体,瘦化 + 动态注入往往能砍掉 20%–35% 的输入 token,规模化部署下是真金白银。这类"上下文预算审计"可以包装成交付物:工具表体检报告 + 瘦化重构 + 动态注入落地,单价对标性能优化。对训练营,它适合作为"工具使用"模块的进阶课,承接在"会调工具"之后——学员亲手用 tiktoken 量出胖瘦差,再用 BFCL 风格任务画出冗余度-召回率曲线,体感极强,且与已普及的"召回税""参数保真"主题形成层次,不易撞题。可落地的练习项包括:同一工具四栈 token 横测、工具检索器搭建、描述分层重构。
第一,别只测首轮。体积税是复利,必须测多轮回环累计,否则会严重低估;用固定 8–12 轮轨迹做基准更稳。第二,别用对账单反推。计费系统常把工具 schema 摊进"系统/输入"类目,看不出来;应直接用 tokenizer 对序列化后的 tools 数组计数,与系统提示分开列账。第三,别忽略上下文编辑器。部分框架会在发送前静默重排、补全 description、注入示例,导致你以为写的是瘦工具,实际发出去的是胖工具——务必 dump 发往 API 的真实 payload 来核账。
体积税的真正教训是:在工具调用这件事上,省钱和提点往往同向。把工具描述写到拐点,既压 token 又涨召回;越过拐点堆描述,则既烧钱又掉点。量出那条曲线,比多写一百行描述更值。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
