标题:动态工具会破坏提示缓存吗? | Mohammed Reschreiter
简短回答:不会。
动态工具激活并不会摧毁提供商侧的提示缓存。在一项对 OpenAI、Google Gemini 和 OpenRouter 上 10,186 个助手回合的审计中,工具切换仅在 2.4% 到 3.4% 的回合中造成缓存未命中,同时避免了 7120 万未使用的 schema token,并将平均回合成本降低了 32.5%。
超过 82.5% 的提示缓存未命中源于常规的多文件读取、上下文压缩,以及云服务商 5 分钟空闲超时,而非工具切换。
这项研究是我上一篇架构深度分析《How I Cut 80%+ of Context Overhead in My Coding Agent》的实证后续。
我在发布 AI 编程代理中的动态工具激活架构时,Hacker News 上最常见的反驳意见立刻出现了:
「动态添加工具看起来没问题,但移除工具是个糟糕的主意。切换 schema 会改变提示前缀,不断摧毁你提供商侧的提示缓存。」
从理论上看,这个反驳似乎很合理。主流大模型提供商(Anthropic、OpenAI、Google)都会缓存提示前缀。如果你修改系统提示或工具 schema,前缀哈希就会改变,模型必须以标准或更高的写入价格重新写入缓存条目。
为了验证动态工具激活在实际生产中是否真的损害提示缓存,我审计了 Pi 在 OpenAI、Google Gemini 和 OpenRouter 上的 93 次多轮编程会话、10,186 个助手回合。
以下是实证遥测数据揭示的结果。
数据集
我分析了 11 天内在日常工程工作流中启用动态工具激活的所有交互式编程会话。
- 总会话数:93 次
- 总助手回合数:10,186 个
- 总工具调用次数:11,461 次执行
- 测试模型:OpenAI(GPT-5.6 Sol、GPT-5.6 Luna、Codex)、Google Gemini 3.7 Flash,以及 OpenRouter 社区端点
- 处理的总 token 数:12.2 亿
1. 代理实际多久调用一次待机工具?
「缓存摧毁」担忧的核心假设是:代理每两个回合就会不停地切换工具。
但实践中,软件开发遵循严格的幂律分布(即少数工具占据绝大多数调用,多数工具很少被用到)。
在总计 11,461 次工具执行中:
-
Core 4 个工具(bash、read、edit、write):10,805 次调用(94.28%)
-
按需激活的备用工具:656 次调用(5.72%)
核心工具 (bash, read, edit, write): ██████████████████████████████ 94.28%
备用工具 (browser, loops, image): █ 5.72%
智能体(agent)超过 94% 的工作量都花在读取文件、编辑文本和运行 shell 命令上。由于这 4 个默认工具始终留在提示词(prompt)里,工具结构(schema)前缀保持 100% 一致,在整个会话期间有 85% 到 90% 以上的时间都能命中缓存。
2. 真正导致缓存未命中的原因是什么?
在全部 10,186 次助手回合中,共出现 2,030 次缓存未命中或零缓存事件(总未命中率 19.93%,整体缓存命中率 80.08%)。
我把每一次缓存未命中都按技术触发原因做了分类:
图 1:10,186 次助手回合中缓存未命中的根因分布。超过 82.5% 的未命中源于日常文件操作和云端 TTL(超时机制),与工具切换无关。
未命中数据的关键结论:
-
工具激活只占回合数的 3.47%。在所有提供商和模型上,切换工具、或工具因超过 2 回合 TTL 而过期,只在 354 个回合中触发了缓存切换。其余 96.53% 的回合完全没有出现与工具相关的缓存中断。
-
在 OpenAI 模型上,工具相关的未命中率为 2.40%。在 4,164 个 OpenAI 回合里,工具激活恰好制造了 100 次缓存切换。
-
超过 82.5% 的缓存未命中与工具无关。缓存频繁失效的主要元凶,是往提示词里塞入 2,000 行的文件内容,这会让上下文边界发生偏移,并迫使会话压缩(session compaction)。
3. 为什么各家提供商的缓存命中率不一样?
对比不同提供商的命中率,差异实际上来自各家缓存引擎的架构设计:
图 2:各模型和提供商端点的实际缓存命中率。
Google Gemini 的 32k 阈值
Google Gemini 的 78.8% 命中率乍一看低于 OpenAI 的 86.5%。查阅 Google Cloud 文档后,原因就很清楚了:
-
OpenAI 在输入超过 1,024 个 token 时,会自动开始缓存提示词。
-
Google Gemini 的上下文缓存机制要求提示词超过 32,768 个 token 才会启用缓存。
在短会话或早期回合中,只要上下文不足 32k token,Gemini 就会按设计返回 cacheRead: 0。在 772 次 Gemini 缓存未命中里,有 471 次(61.0%)仅仅是因为提示词还没达到 Google 的 32k 阈值。一旦会话超过 32k token,Gemini 的缓存命中率就攀升到 90% 以上。
4. 经济账:缓存写入与工具结构拖拽的成本
提示词缓存读取虽然有大幅折扣,但并非免费。OpenAI 对缓存读取的收费是基础输入价格的 10% 到 50%(GPT-5.6 / GPT-4o 上约为每百万 token 0.30 到 1.25 美元)。
当你在 Codex 这类环境里保持 79 个静态工具常驻时,每一轮都要多发送约 12,000 个工具结构 token。
以下是我在 4,164 个 OpenAI 回合中得到的精确账目:
拖拽 79 个静态工具,意味着每一轮、整个会话期间,都要为 12,000 个从未使用的 token 支付缓存读取费用。
通过在备用工具闲置 2 回合后将其裁剪掉,我花了大约 0.80 美元进行 100 次缓存重建,从而节省了 62.45 美元的缓存读取费用。投资回报率约为 77 倍。
在所有模型和会话中,我的平均单回合成本从 0.0609 美元降至 0.0411 美元(账单直接减少 32.5%)。
如何在你的智能体里最大化缓存稳定性
基于这 10,000 回合的实测数据,维护提示词缓存稳定性有以下三条规则:
1. 把高频搜索工具提升为核心工具
最初 web_search 和 web_fetch 都放在备用列表里。由于网络搜索占全部工具激活次数的 36%,把这两个工具提升到默认工具集后,只多花了约 350 个 token,就消除了 36 次缓存切换。
把重量级引擎(browser_use、多智能体循环、图像生成器)留在备用列表,而把轻量、高频的查询类工具放在核心列表。
2. 强制提示词内容按确定性排序
备用工具列表在拼入提示词之前,应按字母序排序(standby.sort())。如果工具发现机制在多次运行中返回的顺序不一致,字节串就会变化,从而破坏前缀匹配。
3. 用有边界读取代替整文件导入
大文件读取导致了 70% 的缓存未命中。在项目说明里加一条简单的规范,就能阻止智能体把 2,000 行的文件整个塞进上下文:
先用
rg -n定位目标行,再用带offset和limit(100 到 200 行)参数的read读取,而不要一次性把大文件全部载入上下文。