淘天一面:Prefix Caching 原理是什么?Agent 框架怎么保证不破坏缓存?

前言

前段时间有个师妹去面淘天,一面技术面聊到大模型推理优化。面试官问了一个看起来很基础的问题:

👔 说下 Prefix Caching 的原理,然后再说下 Agent 框架怎么保证不破坏缓存?

她当时挺自信,因为之前用过 Anthropic 的 Prompt Caching 接口,觉得不就是开个开关的事。

她和面试官说:"只要开启 prompt caching 这个 API,就自动会命中了。"面试官摇了摇头:

👔 那你说说,为什么很多人开了这个功能,缓存命中率还是接近零?

她愣了一下,说可能是网络延迟。面试官又问:

👔 那 Agent 框架里,system prompt 每次请求一模一样,就一定能命中吗?

她想想说应该能吧------面试官没再追问,只是说"你回去再想想"。

读完这篇文章,你能搞明白:

  • Prefix Caching 到底缓存了什么------KV Cache 的可复用性
  • 前缀必须逐字节相同------改一个字符就全部失效
  • Agent 框架为什么容易破坏缓存------动态注入/字段顺序/时间戳
  • 常见的缓存杀手------那些你意识不到的细节
  • 怎么保证 Agent 不破坏缓存------缓存友好的请求构造
  • 面试话术三层模板------60 分答法和 90 分答法的差距在哪

不管你是做 Agent 开发的工程师,还是需要在面试里讲清推理优化的开发者,这道题都值得提前想清楚。开拆!

一、Prefix Caching 到底缓存了什么

Transformer 每生成一个 token 时,都需要让这个 token 去看前面所有 token 并计算注意力。如果每生成一个新 token 就把前面所有 token 重新算一遍,开销太大。所以推理引擎会把每个 token 算出来的 Key 和 Value 向量存起来------这就是 KV Cache

一次大模型请求的推理分两个阶段:

  • Prefill(预填充):把输入 prompt 一次性喂给模型,并行计算所有输入 token 的 KV 向量存入 KV Cache。计算量跟输入长度成正比。
  • Decode(解码):模型一个个吐出新 token,每吐一个只需查询已缓存好的 KV,不需要重新计算历史部分。

Prefix Caching 的核心思路:如果两次请求的前面一段 token 完全相同(逐字节相同),这段的 KV Cache 就可以直接复用,不需要重新计算,只对新增部分做 Prefill。

这不是什么魔法开关,它的本质是利用 KV Cache 的可复用性,前提是前缀必须逐字节一致。光说"开了就行",等于没说。

二、引擎怎么识别相同前缀

主流实现思路:把 KV Cache 按固定大小的块组织,用**前缀树(Radix Tree)**管理这些块。

具体来说:把 token 序列按块切分,每个块算一个哈希值------这个哈希不只基于本块内容,还要把前面所有块的哈希串联上去,保证路径唯一。请求进来时从根节点开始沿 token 序列在前缀树里查找,命中的块直接复用 KV Cache,从第一个不匹配的块开始往后都得重新计算。

代表性实现包括:vLLM 的 Automatic Prefix Caching、SGLang 的 RadixAttention、Anthropic API 的显式 Prompt Caching(通过 cache_control 断点告诉服务端"这里往前的内容请缓存")。

像 Anthropic 的缓存命中率部分通常只收正常价格的 10% 到 25%------前缀越长、复用率越高,首 token 延迟和计算成本都大幅下降。

三、前缀必须逐字节相同

这是整篇文章里最重要的一句话:前缀必须逐字节相同。

哪怕只改动了一个字符、一个空格、一个标点符号,或 JSON 序列化时字段顺序变了,这段前缀在引擎眼里就是全新的内容。缓存直接失效,从这个位置往后的所有内容都得重新计算。

这也是为什么很多人自己写的 Agent,看起来复用了 90% 的历史对话,但实际命中率却很低------破坏缓存往往发生在你意识不到的那些细节里。

四、Agent 框架为什么天生容易破坏缓存

一个典型的 Agent 循环:用户提问 → 模型输出(可能带 tool_use) → 执行工具拿到结果 → 模型再思考 → 再调工具 → 循环好几轮 → 最后给回复。

一次对话下来可能调用大模型 5-10 次。system prompt 800 字的角色设定+工具说明+few-shot 示例,每次请求都要带上。如果每次调用都让模型把这 800 字从头读一遍重新算一遍,账单和延迟都会非常难看。

Agent 框架天生容易破坏缓存,原因在于:请求结构里有很多"看起来一样但其实每次都在变"的部分。 system prompt 本身可能确实没变,但工具结果、对话历史、动态注入的元数据,每一轮都在变。只要这些变动的部分插在了前缀中间,后面的所有内容就全部缓存失效了。

五、常见的缓存杀手

常见的缓存杀手,每一个都隐蔽:

杀手一:动态时间戳/日期注入。 有些 Agent 框架会在 system prompt 里注入"当前时间是 2026-07-10 15:30:22"------每次请求时间都不同,system prompt 就变了,缓存直接失效。解法:把时间戳放在请求的最后面(后缀),不要放在前缀里。

杀手二:JSON 字段顺序不稳定。 工具定义用 JSON 描述,如果序列化时字段顺序不固定(比如 Python dict 在不同版本里顺序不同),每次序列化结果不同,前缀就变了。解法:序列化时按字段名排序,保证确定性输出。

杀手三:随机 ID / Session ID。 有些框架给每次请求生成随机 request_id 并注入 system prompt,导致前缀每次都不同。解法:request_id 放在请求末尾或 HTTP header 里,不要放进 prompt 前缀。

杀手四:工具结果插在中间。 如果请求结构是 systemtool_resultuser_message,tool_result 每次不同,它后面的 user_message 就永远命中不了缓存。解法:把不变的内容(system prompt + tool definitions)放前面,把变化的内容(对话历史+工具结果)放后面。

杀手五:对话历史的格式化方式变化。 第一轮对话用 {"role":"user","content":"你好"} 格式,第二轮突然变成了 User: 你好 格式------前缀就断了。解法:全流程用统一的序列化格式,不要中途换。

六、怎么保证 Agent 不破坏缓存

保证 Agent 不破坏缓存的核心原则:把不变的内容放前面,把变化的内容放后面。

一个缓存友好的请求结构:

css 复制代码
[system prompt (固定)] → [tool definitions (固定)] → [few-shot (固定)] → [对话历史 (增长但不变旧)] → [本轮新消息]

前三个部分是"冷区"------内容固定,缓存命中率接近 100%。对话历史是"温区"------旧消息不变(前缀复用),只新增最新消息。本轮新消息是"热区"------每次都不同,但这部分本来就不长。

关键工程实践:

  1. system prompt 里不要注入动态内容(时间戳/ID/随机值)
  2. JSON 序列化保证字段顺序确定(按 key 排序)
  3. 工具定义固定不变,放在对话历史之前
  4. 对话历史只追加不修改(改了旧的就会断缓存)
  5. 用 Anthropic 的 cache_control 断点显式标记缓存边界

这些实践看起来都是"小细节",但每个都直接影响缓存命中率。缓存命中率从 0% 到 90% 的差距,往往就在这几个细节里。

七、从架构师视角看缓存设计的几个工程取舍

从架构师视角看缓存设计的几个工程取舍。

取舍一:缓存断点的位置------放几个。 Anthropic 允许最多 4 个 cache_control 断点。放太多会增加管理复杂度,放太少可能漏掉可缓存的部分。工程上建议:system prompt 一个断点,tool definitions 一个断点,对话历史每 N 轮一个断点。

取舍二:对话历史的缓存策略------全量 vs 摘要。 对话越长,前缀越长但缓存命中的绝对量也越大。但如果对话太长(超 100 轮),应该做摘要压缩------但摘要改变了前缀内容会导致缓存失效。工程上建议:每 20 轮做一次摘要,摘要后的内容作为新的"固定前缀",老对话归档。

取舍三:多用户场景的缓存共享。 不同用户的对话历史不同,前缀不共享。但 system prompt + tool definitions 是跨用户共享的。工程上建议:把共享部分放最前面(跨用户命中),用户特定部分放后面(仅单用户命中)。

取舍四:缓存的有效期管理。 Anthropic 的缓存默认 5 分钟过期。如果 Agent 两次请求间隔超过 5 分钟,缓存就失效了。工程上建议:长间隔对话用"心跳请求"保活(每 4 分钟发一个最小请求),或接受缓存失效的代价。

取舍五:缓存命中率的监控。 不监控就不知道缓存有没有生效。工程上建议:每次请求记录 cache_read_input_tokens 和 cache_creation_input_tokens,计算命中率。命中率低于 50% 时排查缓存杀手。

取舍六:缓存友好 vs 逻辑灵活的权衡。 有时候为了缓存友好,需要牺牲一些逻辑灵活性(比如不能在 system prompt 里注入动态上下文)。工程上判断:如果缓存能省 50%+ 的成本,值得牺牲灵活性;如果省不到 10%,不值得为了缓存改架构。

八、面试话术:考官想听的是什么

回到面试场景。这道题考的不是"你知不知道 Prefix Caching 这个功能",而是"你有没有理解它的工作原理和 Agent 框架里的缓存陷阱"。

常见错误回答一:"开了 prompt caching 就自动命中"。 这是零分------面试官追问"为什么很多人开了还是命中率接近零"时直接卡住。

常见错误回答二:"system prompt 一样就能命中"。 方向对但不够------面试官会追问"如果 system prompt 里注入了时间戳呢"。

高分答题模板:三层结构。

第一层(抛原理):"Prefix Caching 的本质是 KV Cache 的可复用性------如果两次请求的前缀逐字节相同,这段的 KV Cache 直接复用,只对新增部分做 Prefill。它不是魔法开关,前提是前缀必须逐字节一致。"

第二层(讲缓存杀手):"Agent 框架天生容易破坏缓存:动态时间戳注入、JSON 字段顺序不稳定、随机 ID、工具结果插在中间、对话历史格式变化------每个都会让前缀断裂。解法是把不变的内容放前面(system+tools),变化的内容放后面(history+new message),system prompt 里不注入动态内容。"

第三层(升华):"缓存命中率从 0% 到 90% 的差距就在这些细节里。用 cache_control 断点显式标记缓存边界,监控 cache_read_input_tokens 计算命中率,低于 50% 时排查缓存杀手。"

60 分 vs 90 分对比:

追问点 60 分回答 90 分回答
"为什么开了还是不命中?" "网络延迟" "前缀不逐字节相同:时间戳注入/JSON字段顺序/随机ID/工具结果插中间,任一个都会让缓存断裂"
"system prompt 一样能命中吗?" "能" "不一定:如果system里注入了动态内容(时间戳/session_id)就不行;必须保证前缀完全逐字节一致"
"Agent 怎么保证不破坏缓存?" "不知道" "不变内容放前面(system+tools),变化内容放后面(history+message);system不注入动态值;JSON按key排序;用cache_control标记断点"
"KV Cache 复用的原理?" "缓存了结果" "Prefill阶段算出所有输入token的KV向量存入KV Cache;如果前缀相同,KV Cache直接复用只算新增部分;用Radix Tree管理缓存块"

加分项提示: 如果你能主动提到"Anthropic 缓存命中部分只收 10%-25% 的价格,缓存命中率直接影响成本------从 0% 到 90% 的差距就在那几个细节里",面试官会认为你有成本意识。

总结

回到开头那道面试题。"Prefix Caching 原理是什么?Agent 框架怎么保证不破坏缓存"------这道题考察的是你对推理优化和 Agent 架构设计的双重理解。

  • Prefix Caching 本质:KV Cache 可复用性,前提是前缀逐字节相同。
  • 引擎实现:Radix Tree 管理缓存块,按哈希路径匹配。
  • 前缀必须逐字节相同:改一个字符/空格/标点,或 JSON 字段顺序变了,缓存全部失效。
  • Agent 天生容易破坏缓存:动态时间戳/JSON字段顺序/随机ID/工具结果插中间/格式变化。
  • 保证不破坏缓存:不变内容放前面,变化内容放后面,system 不注入动态值,用 cache_control 标记断点。
  • 缓存命中率 0%→90% 的差距就在细节里:监控 cache_read_input_tokens,低于 50% 时排查缓存杀手。

Prefix Caching 不是魔法开关,它的本质是 KV Cache 的可复用性。 真正决定缓存命中率的不是"开没开这个功能",而是你的请求结构有没有给缓存留出"逐字节相同的前缀"。

你的 Agent 缓存命中率是多少?排查过哪些缓存杀手?欢迎评论区交流。

相关推荐
agent8971 小时前
实战升级|SpringBoot WebSocket实现多轮对话AI流式问答(上下文记忆+自动重连+会话隔离)
人工智能·spring boot·websocket
染指11101 小时前
80.高级RAG-LLamaIndex实际应用-金融助手
人工智能·rag·llama_index·llamaindex
Clipp_Huang1 小时前
光学跟踪系统标定
人工智能·计算机视觉·重构·机器视觉
阿图灵1 小时前
Agentic AI 架构入门(三):Agent 的七大组件与 PRAL 循环
人工智能·架构·llm·rag·ai agent·智能体·agentic ai
weixin_468466851 小时前
目标检测精度上限与影响因素分析
图像处理·人工智能·目标检测·计算机视觉·图像分类·coco·检测精度
宋哥转AI1 小时前
深入理解 AI Agent · MCP 子系列 #01:MCP 协议全解—从消息格式到传输层的完整拆解
人工智能·agent·mcp
看山先生1 小时前
凌晨两点,我把一块开发板接进了自己的世界
人工智能·agent
阿源聊AI1 小时前
Claude Code 跨会话消息:并行开发终于有了信息通道
人工智能
aircrushin1 小时前
Claude 5 之后,上下文工程该做减法了
前端·人工智能·后端