{"code":0,"data":{"id":539,"title":"大模型缓存（Prompt Caching）：给非技术人员的科普","content":"## 什么是大模型缓存？\n\n你每次跟 AI 对话，都要把**整段对话历史**发给大模型。模型需要\"读完\"所有内容才能回复你。\n\n问题来了：读一遍很贵。\n\n大模型按 token（大约 0.75 个中文字 = 1 token）收费，\"读\"（输入）和\"写\"（输出）分别计价。一段 10000 token 的对话，每次你发新消息，模型都要重新读这 10000 token —— 即使前 9900 token 跟上次完全一样。\n\n**缓存就是让模型记住已经读过的部分，下次不用重新读。**\n\n---\n\n## 用生活比喻理解\n\n想象你请了一个翻译，每次开会前都要把之前所有会议纪要重新念一遍给他听，他才能理解上下文。\n\n- **没有缓存**：每次开会，翻译重新念全部纪要（收全价）\n- **有缓存**：翻译说\"上次的我都记着呢，只念新增的部分就行\"（旧内容打折）\n\n---\n\n## 缓存怎么省钱？\n\n以 Anthropic Claude 为例：\n\n| 场景 | 输入价格 | 节省 |\n|------|---------|------|\n| 正常读取 | $3 / 百万 token | — |\n| 首次写入缓存 | $3.75 / 百万 token | 多花 25%（一次性成本） |\n| 命中缓存 | $0.30 / 百万 token | **省 90%** |\n\n也就是说：\n- 第一次对话稍微贵一点（要\"存\"进缓存）\n- 之后每次对话，**重复的部分只收 1/10 的价格**\n\n如果你的 system prompt（系统提示词）有 5000 token，每次对话都要发送它。一天 100 次对话：\n- 不缓存：5000 × 100 = 50万 token 全价\n- 有缓存：5000 × 1（全价写入）+ 5000 × 99（1折读取）= 约等于 **15万 token 的钱**\n\n**省了 70%。**\n\n---\n\n## 缓存的工作原理\n\n```plantuml\n@startuml\n!theme plain\nskinparam backgroundColor #FEFEFE\n\nparticipant \"你\" as U\nparticipant \"API\" as A\nparticipant \"大模型\" as M\nparticipant \"缓存\" as C\n\n== 第一次请求 ==\nU -> A : 发送消息\\n(system prompt + 对话历史)\nA -> C : 计算前缀 hash\nC --> A : 未命中\nA -> M : 完整处理所有 token\nM --> A : 回复\nA -> C : 存入缓存（前缀部分）\nA --> U : 返回回复\\n(cache_creation_input_tokens: 5000)\n\n== 第二次请求 ==\nU -> A : 发送消息\\n(同样的 system prompt + 更新的历史)\nA -> C : 计算前缀 hash\nC --> A : ✅ 命中！\nA -> M : 只处理新增 token\\n(缓存部分跳过计算)\nM --> A : 回复\nA --> U : 返回回复\\n(cache_read_input_tokens: 5000)\n@enduml\n```\n\n关键点：\n1. 缓存匹配的是**前缀**（从头开始连续相同的部分）\n2. 只要前面的内容不变，后面新增的内容不影响缓存命中\n3. 缓存有**有效期**（通常 5 分钟不使用就过期）\n\n---\n\n## 如何提高缓存命中率？\n\n### 1. 把不变的内容放在最前面\n\n消息顺序很重要。缓存只匹配**从头开始连续相同的部分**。\n\n```\n✅ 好的顺序：\n[System Prompt]          ← 不变，会被缓存\n[长篇参考资料]            ← 不变，会被缓存\n[对话历史]               ← 大部分不变\n[用户最新消息]            ← 新内容\n\n❌ 差的顺序：\n[时间戳: 2024-01-01 12:34:56]  ← 每次都变！\n[System Prompt]                 ← 因为前面变了，这里也无法命中\n[对话历史]                      ← 全部无法命中\n```\n\n**一个变化的字符在开头，就会导致整个缓存失效。**\n\n### 2. System Prompt 保持稳定\n\n不要在 system prompt 里放：\n- 当前时间（每秒都变）\n- 随机数\n- 每次请求都不同的用户信息\n\n这些应该放在用户消息里，而不是 system prompt 里。\n\n### 3. 参考资料放在 system prompt 后面、对话前面\n\n如果你要让模型参考一篇长文档：\n\n```\n[System Prompt]        ← 缓存 ✅\n[参考文档全文]          ← 缓存 ✅（只要不换文档）\n[对话历史...]          ← 部分缓存\n[新消息]              ← 不缓存（新内容）\n```\n\n### 4. 保持对话连续性\n\n频繁开新对话 = 频繁创建新缓存 = 多花 25% 的写入成本。\n\n在同一个对话里持续聊天，缓存命中率最高。\n\n### 5. 达到最小缓存长度\n\n不同模型有最小缓存要求：\n- Anthropic Claude：1024 token（约 750 个中文字）\n- OpenAI GPT：1024 token\n\n太短的内容不会被缓存。所以简短的 system prompt 可能享受不到缓存优惠。\n\n---\n\n## 不同厂商的缓存策略\n\n| 厂商 | 缓存方式 | 有效期 | 最小长度 |\n|------|---------|--------|----------|\n| Anthropic (Claude) | 显式标记缓存断点 | 5 分钟 | 1024 token |\n| OpenAI (GPT) | 自动缓存（无需标记） | ~5-10 分钟 | 1024 token |\n| Google (Gemini) | 显式创建缓存对象 | 可自定义 | 较长 |\n\nAnthropic 需要你在 API 请求中标记 `cache_control`，告诉它\"到这里为止可以缓存\"。\n\nOpenAI 则是全自动的，不需要任何标记，系统自动判断哪些前缀可以缓存。\n\n---\n\n## 怎么知道缓存有没有生效？\n\nAPI 返回的 `usage` 字段会告诉你：\n\n```json\n{\n  \"usage\": {\n    \"input_tokens\": 200,\n    \"cache_creation_input_tokens\": 5000,\n    \"cache_read_input_tokens\": 0\n  }\n}\n```\n\n- `cache_creation_input_tokens`：首次写入缓存的 token 数（多花 25%）\n- `cache_read_input_tokens`：命中缓存的 token 数（**省 90%**）\n- `input_tokens`：没有被缓存的 token 数（全价）\n\n理想状态：`cache_read_input_tokens` 占比越高越好。\n\n---\n\n## 总结\n\n| 要点 | 说明 |\n|------|------|\n| 缓存是什么 | 让模型记住已读过的内容，不重复计费 |\n| 省多少 | 命中缓存部分省 **90%** 输入费用 |\n| 怎么提高命中 | 不变的内容放最前面，避免开头插入变化内容 |\n| 最容易的优化 | 保持 system prompt 稳定 + 不频繁开新对话 |\n| 最常见的坑 | 在 prompt 开头放时间戳，导致整个缓存失效 |\n\n缓存不需要你做很多事情 —— 很多平台已经自动处理了。但理解它的原理，能帮你避免\"明明用了缓存但一直没命中\"的情况。","acl":"public","tags":["LLM","prompt","caching","AI","科普"],"category_id":2,"category":{"id":2,"name":"work","description":"","color":"#25292e","created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z"},"author":{"id":1,"username":"kiyor","email":"cai@kiyor.com"},"enable_variables":false,"created_at":"2026-03-05T06:49:27.489+08:00","updated_at":"2026-03-05T06:49:27.489+08:00"}}
