跳到主要内容

AI Agent 工作负载的缓存层级:多数团队止步于第二层的五层架构

· 阅读需 13 分钟
Tian Pan
Software Engineer

大多数部署 AI Agent 的团队在实现了提示词缓存 (Prompt Caching),或许再加上语义缓存之后,就认为大功告成了。但他们实际上错失了 40-60% 的潜在节省空间。原因并非在于懒惰 —— 而是 Agent 工作负载产生的缓存问题在简单的请求-响应式 LLM 调用中并不存在,其解决方案需要从传统 Web 缓存从未涉及的层级进行思考。

单个 Agent 任务可能涉及一个 4,000 Token 的系统提示词、三个分别返回不同结构数据的工具调用、一个在结构上与昨天完全相同的多步计划,以及一个需要在对话中持久化但绝不能跨用户共享的会话上下文。其中每一项都代表了不同的缓存机会,具有不同的 TTL (生存时间) 要求、不同的失效触发机制,以及在缓存失效时不同的故障模式。

以下是生产级 Agent 系统需要的五个缓存层级,以及为什么每一层都能解决独特的问题,以及为什么在更高层级搞错缓存键 (Cache Key) 会导致最昂贵的失败。

第 1 层:提示词缓存 —— 每个人都有的基础

提示词缓存是最直接的一层。现在所有主要的 LLM 供应商都提供此功能:Anthropic 缓存提示词前缀 5-60 分钟,OpenAI 默认启用自动缓存,Google 的 Gemini 支持显式的缓存上下文。其机制很简单 —— 供应商存储提示词前缀的计算内部状态 (KV 缓存),以便在后续请求中不再重新处理这些 Token。

数据非常有说服力。根据供应商的不同,输入 Token 的成本可节省 50% 到 90%,首个 Token 响应时间 (TTFT) 延迟降低 13-31%。对于一个拥有 4,000 Token 系统提示词且每个会话进行 20 次调用的 Agent 来说,这就是处理 80,000 个输入 Token 与仅处理一次 4,000 Token 外加 20 次较小的增量调用之间的区别。

但这里有一个让大多数 Agent 构建者掉入的陷阱:盲目地启用全上下文缓存反而会增加延迟。针对长周期 (Long-horizon) Agent 任务的研究发现,缓存整个对话(包括工具调用和结果)会针对那些永远不会被重复使用的内容触发缓存写入。工具结果通常包含特定于用户的数据,跨会话价值为零。缓存中充满了垃圾内容,写入开销超过了读取节省的收益。

解决方法是选择性缓存。 仅缓存稳定的前缀:系统提示词、工具定义和静态上下文。将工具调用、结果和动态对话视为临时内容。将动态内容放置在提示词的末尾,以最大化可缓存的前缀长度。这一看似显而易见的建议,却被大多数仅通过连接消息而忽略缓存边界的 Agent 框架所违背。

第 2 层:语义缓存 —— 识别不同措辞下的相同问题

语义缓存位于 LLM 调用之前,拦截与之前查询在意义上相似的查询。它不进行精确的字符串匹配,而是将查询嵌入到向量空间中,并对缓存的查询-响应对执行相似性搜索。“我该如何重置密码?”和“我无法登录 —— 如何更改密码?”会命中同一个缓存条目。

性能提升是显著的:缓存响应在 5-20 毫秒内返回,而完整的 LLM 调用需要 1-5 秒。当缓存命中时,速度提升了 15 倍。在生产级客户支持系统中,31% 的 LLM 查询表现出足够的语义相似性,可以从这一层级受益。

Agent 工作负载面临的挑战是缓存资格。并非每个 Agent 响应都应该进行语义缓存。事实性查询(例如“退款政策是什么?”)具有很高的缓存价值。而个性化推荐(例如“根据该用户的购买记录,建议...”)则不然 —— 其响应取决于查询向量化 (Embedding) 无法捕获的上下文。有效的语义缓存需要校验策略标签:附加在每个条目上的元数据,用于指定缓存响应保持有效的条件。

失效机制 (Invalidation) 是语义缓存对 Agent 变得危险的地方。 聊天机器人的陈旧响应令人懊恼。但如果一个 Agent 根据陈旧响应采取行动 —— 下单、修改数据库、发送电子邮件 —— 那就是生产事故。Agent 工作负载的语义缓存需要更严耕的 TTL 和依赖感知失效:如果底层数据源发生变化,所有由此衍生的缓存响应都必须被清除,而不仅仅是那些刚好过期的响应。

第 3 层:工具结果缓存 —— 大多数团队完全遗漏的层级

每个 Agent 调用都涉及工具使用,而工具调用通常是整个流水线中最慢且最昂贵的部分。一个 Agent 在查询数据库、调用 API 或搜索文档库时,每个工具调用可能会等待 200-2000 毫秒。在包含 5-10 次工具调用的多步任务中,工具延迟主导了总响应时间。

工具结果缓存存储以工具名称和输入参数为键 (Key) 的工具调用输出。当 Agent 调用 search_documents(query="Q3 收入预测") 且 30 秒前执行过相同的查询时,缓存的结果会立即返回。

关键的设计决策是针对不同类别的 TTL。天气 API 的结果在 15 分钟后就会失效。针对每小时更新一次的表的数据库查询可以缓存 30 分钟。针对每天变化一次的语料库的文档搜索可以缓存数小时。大多数实现对所有工具使用单一的 TTL,这意味着要么过度缓存易变工具(提供陈旧数据),要么对稳定工具缓存不足(浪费计算资源)。

正确的方法是采用带有依赖跟踪的、针对特定类别的 TTL。 每个工具注册其数据波动配置文件,缓存层强制执行适当的生命周期。当工具调用修改了状态(写操作)时,必须立即失效所有相关读操作的缓存结果。这是数据库缓存几十年前就已经解决的问题,但 Agent 框架很少实现它,因为它们将工具视为无状态函数,而不是具有一致性要求的数据访问点。

还有一个许多团队都会忽视的隐私维度。工具结果通常包含特定于用户的数据。跨用户共享的工作流级工具缓存会泄露信息。解决方案是两层工具缓存:用于通用工具结果(API 文档、公共数据查询)的共享缓存,以及用于特定用户结果的会话范围缓存,并在会话之间进行严格隔离。

第 4 层:计划缓存 —— 复用策略,而不仅仅是数据

这是区分复杂智能体部署与其他部署的关键层。当智能体接收到任务时,它通常会生成一个计划 —— 完成任务所需的一系列步骤和工具调用。计划缓存意识到,即使具体细节不同,许多任务在结构上也是相同的。

“预订 6 月 15 日从 SFO 到 JFK 的机票”和“预订 7 月 3 日从 LAX 到 ORD 的机票”需要相同的计划:搜索航班、按标准过滤、选择选项、确认预订。实体发生了变化,但策略没有变。计划缓存从已完成的任务中提取这些结构化模板,并将其复用于新的请求。

最近关于智能体计划缓存 (Agentic Plan Caching) 的研究证明了其影响:在保持 96.6% 的最优任务性能的同时,成本降低了 50%,延迟降低了 27%。关键词提取和缓存管理的开销仅占总成本的 1%。该机制分为三个步骤:

  1. 提取 (Extract):在任务成功完成后,一个基于规则的过滤器将执行日志剥离为结构化骨架,然后由一个轻量级 LLM 移除特定上下文的数值,从而创建一个通用模板。
  2. 匹配 (Match):使用关键词提取而非嵌入相似度 (embedding similarity) 来匹配新任务与缓存计划。这听起来有悖常理,但在计划检索方面,关键词匹配产生的误报和漏报比语义相似度更少 —— 因为计划需要的是结构相似性,而不是语义相似性。
  3. 适配 (Adapt):一个轻量级模型(而非昂贵的前沿模型)获取匹配的模板并填入特定任务的细节。只有在没有匹配的缓存计划时,才需要前沿模型 (frontier model)。

这里的失败模式是整个层级中最昂贵的。 错误的计划缓存命中 —— 即将一个在结构上不适合当前任务的模板应用到任务中 —— 会导致智能体在最终失败前执行一整套错误的动作序列。与返回错误答案的陈旧数据缓存不同,陈旧的计划缓存会导致错误的动作。计划缓存需要更高的匹配阈值,并且当置信度低于宽裕的余量时,应回退到重新制定计划。

第 5 层:会话状态缓存 —— 无需重新计算的连贯性

最后一层处理在用户会话内持久存在,但在用户之间必须严格隔离的对话上下文。当用户处于多轮智能体交互的中间阶段时 —— 例如,通过五条消息调试部署问题 —— 会话状态缓存会保留智能体对问题的理解、已调用的工具、已排除的假设以及当前的行动计划。

如果没有会话状态缓存,对话中的每条新消息都需要智能体从完整的消息历史记录中重新推导上下文。对于一个包含 20 条消息的对话,这意味着要处理不断增大的上下文窗口,触碰 Prompt Token 限制,并为相同的上下文重复付费。

会话状态缓存存储了智能体工作记忆的结构化表示:当前目标、已完成的步骤、收集到的事实和待处理的操作。这与简单地增加对话历史记录不同 —— 它是一个经过压缩、具有语义意义的摘要,可以在不导致 Token 消耗线性增长的情况下,为智能体提供连贯性。

会话状态的 TTL(生存时间)与用户参与模式相关。客户支持会话可能会在最后一条消息后保持 30 分钟的热度。开发人员调试会话可能需要 2 小时的窗口。过期后,状态将被驱逐 —— 不会持久化到长期存储中,因为几小时前的会话状态几乎总是陈旧到具有误导性,而非有帮助。

核心设计约束是隔离性。 会话状态绝不能在用户之间泄露,也绝不能在同一用户的不同会话之间共享,除非是显式设计的。这看似显而易见,但那些使用共享 Redis 实例而没有正确进行键命名空间划分的实现,已在生产智能体系统中导致了真实的实验数据泄露事件。

复合效应:为什么顺序很重要

这五层并不是独立的 —— 它们形成了一个层级结构,每一层都减轻了其下方各层的负载。计划缓存命中意味着更少的工具调用,这意味着更少的工具缓存查询,这意味着更少的 LLM 调用,这意味着 Prompt 缓存和语义缓存的压力更小。这种复合效应意味着,当上层运行良好时,每一层的边际价值都会增加。

成本计算清楚地说明了这一点。假设一个智能体每天处理 10,000 个任务:

  • 仅使用 Prompt 缓存:节省 50% 的输入 Token → 影响显著但不完整。
  • 添加语义缓存(31% 命中率):触达模型的 LLM 调用进一步减少 30%。
  • 添加工具结果缓存:外部 API 调用和数据库查询减少 40-60%。
  • 添加计划缓存:规划 Token 减少 50%,此外每个任务的工具调用次数更少。
  • 添加会话状态:多轮会话的上下文 Token 减少 30-40%。

止步于前两层的团队是在优化流水线中最便宜的部分(Token 成本),而忽视了昂贵的部分(工具调用、规划和上下文重新计算)。

快速上手:务实的优先级排序

如果你正在逐步构建这个层级体系,以下是按工程投入产出比最大化的排序建议:

  1. Prompt cache (提示词缓存):直接启用它。大多数供应商都免费提供该功能,且无需任何基础设施投入。
  2. Tool result cache (工具结果缓存):为你调用最频繁的工具添加基于类别的过期时间 (TTL)。这通常是一个 Redis 层,并针对特定工具设计 Key 的 Schema。
  3. Plan cache (计划缓存):从针对最常见任务类型的关键词匹配开始。你不需要在第一天就构建完整的“提取-适配”流水线 —— 即使是手动整理的一组计划模板,也能显著节省成本。
  4. Semantic cache (语义缓存):只有当查询量大到足以产生可观的命中率时再添加。如果每天类似类型的查询少于几百次,Embedding 的开销反而会超过节省的成本。
  5. Session state cache (会话状态缓存):当多轮对话在你的流量中占据重要比例,且上下文窗口成本不断增长时,再实施这一层。

这个顺序可能会让大多数团队感到意外,因为语义缓存 —— 这个在博客文章和供应商推介中受到最多关注的层级 —— 在 Agent 工作负载的务实价值中其实仅排在第四位。这是因为 Agent 的访问模式与聊天机器人有着本质的不同:它们更多地是执行计划和调用工具,而不是回答重复的问题。请针对你的 Agent 实际执行的操作进行优化,而不是针对 LLM 缓存教程中假设它们会执行的操作进行优化。

References:Let's stay in touch and Follow me for more thoughts and updates