提示词缓存命中率翻倍,面向缓存设计你的工具集

为什么你的 Agent 越用越贵:被忽视的缓存机制

很多资深工程师在构建 AI Agent 时,都经历过这样的困惑:明明代码逻辑没变,工具也还是那些工具,但随着会话进行,Token 消耗却像滚雪球一样失控,响应速度也越来越慢。我们习惯性地认为这是模型变"笨"了,或者是上下文太长导致的,于是拼命优化 RAG、压缩历史对话,却往往收效甚微。

问题的核心可能并不在模型本身,而在于我们是否真正理解了 Prompt Caching(提示词缓存) 的工作机制。在当前的 LLM API 架构中,缓存并非基于传统的 Key-Value 查找,而是严格依赖前缀匹配。你可以把一次 API 调用想象成搭积木:最底层是 System Prompt 和工具定义(Tool Schema),中间是历史对话,最顶层才是用户当下的问题。

这个机制带来了一个极其反直觉的工程约束:只要你动了底层的一块积木,上面所有的积木(也就是之前聊过的几十万 Tokens)都要重新全价计算。 这意味着,如果你在会话中途动态修改了系统提示词,或者增删了工具列表,之前积累的所有缓存瞬间失效。对于追求极致效率和成本控制的开发者来说,理解并顺应这一机制,是设计高阶 Agent 工作流的第一课。

误区一:按需加载工具反而导致缓存击穿

在传统软件开发中,"按需加载"是优化的金科玉律。很多工程师在设计 Agent 工具集(Tool Set)时,直觉地认为:既然当前任务只需要读取文件,那就只给模型 read 工具;等需要写代码时,再动态注入 write 工具。这样既能节省 Token,又能减少模型幻觉。

然而,在 Prompt Caching 的框架下,这种"聪明"的做法恰恰是导致成本飙升的元凶。

想象一下,你的 Agent 已经运行了 50 轮对话,积累了大量的上下文缓存。此时用户突然提出一个新需求,需要调用一个新的 MCP 工具。如果你选择动态更新工具列表,API 请求中的"前缀"就发生了改变。结果是,之前那 50 轮对话建立的缓存全部作废,模型必须重新处理所有历史数据,费用直接翻倍。

Claude Code 团队的最佳实践是:保持工具集恒定。

不要试图通过修改工具定义来反映状态变化。相反,应该将"状态转换"本身封装为工具调用。例如,不要进入"Plan Mode"时替换掉所有写入工具,而是保留完整的工具集,但提供一个名为 EnterPlanMode 的工具。当用户触发该模式时,Agent 调用这个工具,系统返回一条消息告知模型:"现在处于计划模式,请仅使用读取类工具"。

这种设计有两个显著优势:

  1. 缓存命中率最大化:底层的 Tool Schema 从未改变,无论会话进行多久,缓存始终有效。
  2. 赋予 Agent 自主权EnterPlanMode 是一个 Agent 可以自主调用的工具。当模型遇到复杂问题意识到需要规划时,它可以主动进入该模式,而不是被动等待用户指令。

记住,为了缓存命中率,可用工具来模拟状态转换,而不是修改工具集本身

误区二:在系统提示词中注入动态信息

另一个常见的工程误区是将动态信息硬编码在 System Prompt 中。比如,为了让模型知道当前时间,很多开发者会在 System Prompt 里写入 Current Date: 2026-06-01;或者为了同步项目状态,把 Last Modified File: main.py 这样的信息也塞进去。

这种做法同样会破坏前缀匹配。因为日期每天都在变,文件随时在改,这意味着每一次新的对话请求,其 System Prompt 都与上一次不同,缓存永远无法命中。

正确的做法是利用"消息层"注入动态信息。

不要在 System Prompt 里写死日期。相反,在每一轮对话的用户消息或工具结果中,插入一个特殊的标记,例如 <system-reminder>Today is Wednesday</system-reminder>。对于文件变更,可以通过文件系统监听工具,将变更作为工具执行的结果返回给模型。

这样一来,System Prompt 保持了绝对的静态和稳定,只有顶层的消息内容在变化。底层的庞大上下文依然能被缓存复用,只有新增的那一点点动态信息需要实时计算。这种细微的架构调整,在长会话中能节省惊人的 Token 成本。

误区三:盲目切换模型以节省成本

当面对一个耗资巨大的长会话时,很多人的第一反应是:"这个问题很简单,没必要继续用 Opus 这种昂贵模型了,切成 Haiku 吧,能省不少钱。"

这听起来符合直觉,但在缓存机制下,这往往是一笔亏本买卖。

假设你已经和 Opus 聊了 100k tokens,建立了深厚的上下文缓存。此时切换到 Haiku,意味着新模型完全没有这 100k tokens 的缓存。它必须从头开始全价处理所有历史数据。考虑到 Haiku 虽然单价低,但全量重算的成本可能远超让 Opus 直接回答一个简单的结尾问题。

如果确实需要切换模型,正确的模式是 Subagent Handoff(子代理交接)。

不要让主对话直接切换模型,而是让当前的 Opus 实例生成一份精简的"交接摘要",压缩任务背景和当前进展。然后,启动一个新的、独立的 Subagent(指定为 Haiku 模型),并将这份简短的摘要作为它的初始上下文。

这样做的好处显而易见:

  • 新 Agent 上下文极短:Haiku 只需要处理几百 token 的摘要,无需重算之前的 100k tokens。
  • 专注度提升:新 Agent 不会被主对话中积累的无关噪音干扰,响应速度更快。
  • 权限隔离:Subagent 可以遵循"最小权限原则",例如内置的 Explore 模式可以通过黑名单剥夺写入权限,防止意外操作。

这种"主代理负责重型推理与上下文维护,子代理负责轻量级任务执行"的架构,才是兼顾性能与成本的最优解。

误区四:用独立请求做上下文压缩

随着对话进行,上下文窗口终将逼近上限。这时我们需要对历史对话进行压缩(Compaction),将冗长的历史记录总结为摘要,以便开启新的会话段落。

朴素的实现方式是:单独发起一个 API 请求,使用不同的 System Prompt(例如"你是一个总结助手"),不带任何工具,让模型生成摘要。

这是一个典型的缓存杀手。 因为这个压缩请求的 System Prompt 和工具列表与主对话完全不同,它无法利用主对话的任何缓存,所有 Tokens 都要全价计费。更糟糕的是,生成摘要后,你还需要再发起一个新请求继续对话,这又可能因为上下文结构的变动再次影响缓存。

工业级的解决方案是:Cache-Safe Forking(缓存安全分叉)。

在进行压缩时,复用主对话完全相同的 System Prompt、工具定义和上下文历史。唯一的区别是,在消息列表的最后追加一条压缩指令。

text 复制代码
[... 历史消息 ...]
[User]: 请总结上述对话的关键进展,并生成一个紧凑的摘要用于后续会话。

由于前缀(System Prompt + 工具 + 绝大部分历史消息)完全一致,这个请求可以直接命中缓存。模型只需要计算最后那条压缩指令以及生成的摘要部分。这不仅大幅降低了压缩成本,还保证了上下文的一致性,避免了因切换 Prompt 导致的风格漂移或信息丢失。

应对 MCP 生态膨胀:工具懒加载策略

随着 Model Context Protocol (MCP) 生态的爆发,一个 Agent 接入的工具数量急剧增长。在某些复杂场景下,7 个以上的 MCP 服务器光工具描述就能消耗 70k tokens。这是一个真实的工程瓶颈:我们不能无限制地在每次请求中塞入所有工具描述,但又不能在需要时才临时添加(这会破坏缓存)。

工具懒加载(Tool Lazy Loading) 是解决这一矛盾的关键策略。

其核心思想是:当检测到大模型可用的工具描述总长度超过上下文预算的一定比例(例如 10%)时,自动将这些工具替换为轻量级的 Stub(桩) 。Stub 只保留工具名称和一个 defer_loading: true 的标记,不包含详细的参数 schema。

同时,提供一个原子工具 ToolSearchLoadTool。当 Agent 在推理过程中发现需要某个被"桩"化的工具时,它可以自主调用 ToolSearch,传入工具名称。系统随后动态加载该工具的完整定义,并将其注入到上下文中。

这种机制巧妙地平衡了两者:

  1. 前缀稳定性:大部分时间,工具列表保持固定且轻量,缓存命中率极高。
  2. 按需扩展:只有在真正需要时,才承担加载完整定义的 Token 成本,且这种加载是通过工具调用完成的,不会破坏 System Prompt 的缓存。

构建面向缓存的工具设计规范

综上所述,要打造高性能、低成本的 Agent 系统,我们必须从"面向接口设计"转向"面向缓存设计"。以下是一套可直接复用的设计规范:

设计维度 传统做法(缓存不友好) 高阶做法(缓存友好)
工具集管理 根据任务阶段动态增删工具 工具集恒定,通过工具调用模拟状态切换
动态信息 写入 System Prompt (如日期、版本) 注入到用户消息工具返回结果
模型切换 直接更换主对话模型 Subagent Handoff,传递压缩摘要
上下文压缩 独立请求,使用专用 Prompt Cache-Safe Forking,复用前缀追加指令
工具定义 全量加载所有 MCP 工具 懒加载,超限时替换为 Stub + 搜索工具

在实际工程中,这些原则往往需要组合使用。例如,在设计一个复杂的代码重构 Agent 时,我们可以预先定义好包含 Read, Write, Grep, Git 等在内的完整工具集,并在整个会话生命周期中保持不变。当需要进入"审查模式"时,不调用外部脚本修改配置,而是让 Agent 调用 EnterReviewMode 工具,该工具返回一段系统提示,要求模型在后续操作中禁止调用 Write 工具(通过逻辑约束而非物理移除)。

同时,对于项目特有的大量自定义脚本工具,采用懒加载策略。只有当 Agent 明确表达出需要执行某个特定脚本时,才动态加载其详细参数。对于时间敏感的任务,如每日构建报告,通过 Subagent 机制,由主 Agent 分发任务摘要,子 Agent 快速执行并返回结果,避免主上下文被琐碎的构建日志污染。

这些看似微小的架构决策,累积起来就是巨大的效率差异。在一个典型的长周期开发任务中,遵循这套规范可以将 Token 成本降低 50% 以上,同时将平均响应延迟减少 30%-40%。更重要的是,稳定的上下文让模型表现出更强的连贯性和记忆力,减少了因缓存失效导致的"失忆"和幻觉。

技术演进的速度总是超出预期,但底层的工程原理往往恒久不变。在 LLM 应用开发的深水区,拼的不再是谁能写出更华丽的 Prompt,而是谁能在系统架构层面更深刻地理解模型的运作机制。面向缓存设计,不仅仅是一种优化手段,更是构建下一代高效 Agent 系统的思维基石。当你开始像模型一样思考"前缀"的价值时,你的工具集设计才算真正迈入了高阶行列。

相关推荐
doiito1 天前
【Agent Harness】流马(Gliding Horse)DeepSeek Responses API 接入介绍
rust·系统设计·ai agent
暗黑小白1 天前
LLM 业务级可观测
大模型·ai agent
大龄码农有梦想1 天前
AI Agent 目前最大的瓶颈是什么?
人工智能·机器学习·ai agent·智能体·ai工作流·智能体平台
暗黑小白2 天前
脱敏引擎工程化
后端·ai agent
deepseek232 天前
GPT-5.6 Luna 降价 80%:从 24% 到 90% 的缓存命中率,OpenAI 的价格战打在了工程层
大模型·openai·ai agent
暗黑小白3 天前
LLM 流量网关
ai agent
暗黑小白3 天前
路由策略与引擎可替换性
后端·python·ai agent
CaspianSea3 天前
配置 Claude Code for VS + Deepseek
ai agent
海市公约3 天前
AI Agent 核心:ReAct 机制原理、缺陷与 Plan-and-Execute 解决方案面试详解
ai agent·planandexecute·智能体开发·react 架构·大模型工具调用·大模型推理机制
暗黑小白3 天前
自动操作回复与自愈闭环
大模型·ai agent