← 返回文章列表

Agent 缓存设计:省 Token,但不能缓存错误

区分提示缓存、检索缓存、工具结果缓存与语义缓存,处理权限、新鲜度和错误放大问题。

Agent 缓存比普通接口缓存更容易出错

普通接口常用 URL 和参数作为缓存键,Agent 的结果却同时依赖系统指令、模型版本、用户权限、检索索引、工具状态和时间。少放一个维度,就可能把旧答案、别人的数据或不同规则下的结果返回给当前任务。

缓存确实能降低 Token、延迟和外部 API 压力,但前提是先定义“什么在什么条件下仍然等价”。不能因为两段问题语义相似,就默认它们可以共享结果。

四类缓存解决不同问题

提示前缀缓存复用稳定系统规则、工具 Schema 和公共背景。它适合长而重复的输入前缀。关键是保持前缀顺序和内容稳定,把动态用户数据放在后面,并在规则或工具版本变化时失效。

检索缓存保存查询到文档候选的结果。缓存键至少包含规范化查询、索引版本、过滤条件、租户和权限范围。知识库更新后,旧结果不能继续伪装成最新证据。

工具结果缓存适合只读、确定性且有明确新鲜度的数据,例如产品目录或静态配置。订单状态、库存和监控指标变化快,应使用短 TTL 或完全不缓存。任何写工具都不能用普通响应缓存替代幂等机制。

语义缓存尝试让相似问题复用答案。它节省最多,也最危险。适合低风险、稳定知识和可容忍近似的场景,不适合权限敏感、精确数值、时间敏感或个性化任务。

缓存键必须包含信任边界

一个工具查询缓存键可以表示为:

hash(
  toolName,
  canonicalArguments,
  tenantId,
  permissionScope,
  dataVersion,
  toolSchemaVersion
)

不能把完整敏感参数直接作为可见键,可以先规范化再哈希。数组顺序是否重要、默认值如何表示、时间区间如何对齐,都要有固定规则,否则等价请求无法命中,非等价请求又可能碰撞。

租户和权限范围是强制维度。拥有“全部客户”权限的结果不能被只允许查看单个工单的用户命中。即使最终回答会脱敏,检索候选本身也可能泄露资源存在性。

新鲜度是业务语义

TTL 不应由工程师随便选择一个整数。产品说明可能缓存数小时,订单支付状态可能只能缓存几秒,事故告警甚至要求每次实时查询。

比固定 TTL 更好的方式是使用数据版本或变更事件。知识库发布新版本时主动失效相关检索缓存;配置更新时增加版本号;订单变更事件清除对应业务键。

无法确定新鲜度时,宁愿把缓存结果作为候选提示,并再次用权威工具验证关键事实。界面和最终答案也应说明数据时间,尤其是价格、库存和系统状态。

不要缓存失败,除非你知道原因

限流、网络超时和依赖故障通常是暂时的,长时间缓存会把短暂故障放大。业务上的“不存在”可以短暂负缓存,减少重复查询,但要确认创建事件能使它失效。

模型生成失败、结构解析错误或权限拒绝不应进入共享语义缓存。否则一个错误会影响大量后续任务。即使缓存成功结果,也应保存验证状态和来源版本,而不是只保存最终文本。

防止缓存击穿与污染

热门问题过期时,大量 Agent 可能同时重新计算。可以用请求合并让第一个任务刷新,其他任务短暂等待;低风险数据也可以在短窗口内使用旧值并后台更新。

外部工具返回可能包含恶意或错误内容。写入缓存前先执行与实时路径相同的清洗、权限和大小检查。缓存不能成为绕过安全处理的“可信捷径”。

用户提供的私有文档、会话摘要和记忆默认只允许在该用户或租户范围内复用。公共缓存与私有缓存最好物理或逻辑分层,避免错误配置造成跨域读取。

可观测性决定缓存是否真的省钱

记录命中类型、缓存版本、节省的模型或工具调用、数据年龄和验证结果。除了命中率,还要观察错误命中率、旧数据导致的回退、权限拒绝和命中后的任务成功率。

高命中率未必是好事。如果语义缓存让答案更便宜,却降低关键任务正确率,最终成本可能更高。应按任务风险分组评估,不要用一个全局命中率决定策略。

做缓存实验时保留无缓存对照组。比较端到端成功率、延迟分布、Token、外部调用费用和人工修正。缓存策略与模型、Prompt、索引一样需要版本化和回滚。

一个稳妥的采用顺序

先缓存稳定提示前缀,再为昂贵且确定性的只读工具加入短期缓存;随后对版本明确的检索结果做权限隔离缓存;最后才在低风险场景尝试语义答案缓存。

每增加一层,都先定义键、作用域、失效、验证和观测。没有明确失效机制的缓存,不是性能优化,而是一份等待过期的隐藏数据副本。

小结

Agent 缓存的收益来自避免重复计算,风险来自复用错误上下文。按缓存类型设计键,把权限和版本纳入边界,用业务新鲜度控制失效,并持续评估命中后的真实成功率,才能省下 Token 而不缓存错误。