昨天用户问 A1042 的运费,今天又问了一遍。为了降低延迟,你想复用昨天的结果。但今天质量核实已经完成,政策也可能更新;问题文字相同,并不保证答案相同。
上下文工程里的缓存必须先明确复用的对象。复用一次模型内部的前缀计算,与直接返回昨天的完整答案,需要满足的条件完全不同。
四种缓存分别省掉哪一步
| 缓存层 | 保存的对象 | 命中后仍需进行的工作 |
|---|---|---|
| 模型前缀缓存 | 提供方支持的前缀计算状态 | 处理后续输入并生成输出 |
| 上下文组装缓存 | 候选清单、选择结果或渲染结果 | 校验依赖与权限,必要时重新组装 |
| 检索、只读工具缓存 | 文档候选、订单读取结果等 | 检查版本与时效,完成本轮推理 |
| 答案缓存 | 已生成的最终回答 | 判断是否适合直接复用并重新授权 |
前缀缓存没有替你保存“用户偏好 Python”这个业务事实,也不会让模型天然跨会话记住用户。长期记忆是需要维护的内容,缓存是可重建的计算或读取结果;二者可能互相使用,但不能混成同一个生命周期。
前缀缓存为什么需要稳定输入
若连续请求共享相同规则与工具定义,服务可能复用前面已经处理过的部分。用户问题与动态状态放在后部,有机会减少稳定前缀被无关变化打断。
这不意味着应用可以自行改变消息顺序来追求命中。系统、工具和消息的实际组织由接口协议决定,缓存条件也由提供方实现决定。不能只比较自己拼接的字符串,就断言服务端一定命中。
以 Claude Prompt caching 文档 为例,缓存基于请求前缀,并有具体的匹配与配置要求。本文不固定记录其价格、容量门槛或时效值;接入时应核对当前文档与返回的缓存用量字段。
稳定前缀减少重复处理,不等于已经拥有完整答案。新增用户问题仍需推理,输出仍需生成,命中的输入通常也仍属于上下文长度的一部分。KV Cache 的计算原理可回看 上下文与推理,这里重点讨论应用如何识别变化。
缓存键是在声明什么情况下结果等价
假设应用使用 cache[question]。用户甲与用户乙都问“这个订单能退吗”,文本完全相同,订单、归属和状态却不同。正确设计必须把影响结果的条件表达出来。
只读订单结果的键可以包含主体范围、订单 ID、查询字段和数据版本;上下文组装还需要任务状态版本、策略版本、知识快照和记忆版本。答案缓存可能还依赖模型配置、提示版本、问题和完整证据组合。
下面是组装缓存的教学键,不是所有系统都适用的完整字段集合:
import hashlib
import json
# 这些值必须由应用取得,而不是让模型自己报告权限版本和订单版本。
dependencies = {
"tenant": "shop-a", "user": "user-417",
"task": "T9", "state_revision": 4,
"policy_revision": "v2", "auth_epoch": 7,
"assembly_strategy": "consultation-v3",
"query": "A1042 的退货运费谁承担?",
}
# 排序与固定分隔符使相同字典产生稳定序列化结果。
# 哈希不是匿名化保障;低熵内容可被猜测,不要拿它代替日志访问控制。
serialized = json.dumps(dependencies, ensure_ascii=False,
sort_keys=True, separators=(",", ":"))
cache_key = hashlib.sha256(serialized.encode("utf-8")).hexdigest()
print(cache_key)
如果漏掉 state_revision,用户刚补充核实结果,系统仍可能使用旧上下文。若漏掉权限变化,资料已经撤回,缓存却继续带入模型。键越完整不一定越好:无关字段如本次随机 request_id 会让每次都无法命中。应列出实际依赖,而不是机械塞入所有字段。
TTL 与版本失效解决不同问题
TTL 表示经过一段时间后不再使用,适合限制陈旧程度。版本号表示某个依赖已经改变,可以立即让旧结果不再匹配。二者可以结合。
考虑这条教学时间线:09:00 读取政策 v2 并缓存十分钟;09:03 v2 被撤回;09:04 再次命中。如果只看 TTL,缓存仍然有效;如果读取路径检查当前政策版本或撤回标记,就应该拒绝使用。
主动失效依赖通知能否及时到达。通知丢失时,短 TTL 可以限制错误持续时间,但无法满足立即撤权。高要求场景需要在实际使用前查询有效权限或撤回状态。一个版本号只有及时读取,才有实际保护作用。
跨来源查询还可能无法获得原子快照。应用应记录各来源的读取时间和版本,并针对影响结论的变化决定重取。不能仅因为缓存键里写了三个版本,就宣称它们来自同一个事务时刻。
语义接近为什么不足以复用答案
“怎么申请退款”与“帮我提交退款”表达相近,前者可以是通用解释,后者可能涉及操作。相似度阈值只度量表示空间中的接近程度,不决定请求在业务上等价。
即使两个问题都只是咨询,“未核实质量问题”与“已核实质量问题”的答案条件也不同。答案缓存适合明确只读、稳定、范围可判断的任务;对需要实时事实的回答,复用检索结果再重新推理可能更合适。
任何涉及副作用的操作,都不能因为命中旧答案就假装已经执行。若返回之前操作的结果,需要通过业务操作记录确认它对应的是同一操作,而不是把一段聊天回复当成执行回执。
缓存故障怎样表现
同一个热门请求失效后,大量并发同时回源,可能造成突发负载。可以对相同键合并正在进行的读取,或限制回源并发。允许返回旧结果的策略只能用于明确允许陈旧的场景,不能覆盖权限撤销和高时效业务事实。
cache_versions.py下载 演示同一键命中、版本变化未命中、到期未命中以及权限重新校验。它使用本地字典与显式时间,不模拟真实模型前缀缓存。
观察时应分层记录命中率、回源耗时、错误复用和陈旧拒绝。前缀缓存命中率高,不能证明答案缓存正确;整体延迟下降,也不能证明用户拿到的是当前订单的结论。
继续阅读
上一篇: 长会话怎样压缩:从聊天摘要到可继续执行的任务快照 。
下一篇: 谁可以看到什么:上下文隔离、可信边界与多 Agent 交接 。
完整文件、依赖与运行边界见配套指南下载。