CAG 是什么
CAG(Cache-Augmented Generation,缓存增强生成)由 Chan 等人在 WWW '25 论文 "Don't Do RAG: When Cache-Augmented Generation is All You Need"(arXiv:2412.15605)中提出。它的思路简单到让人怀疑——
把整个知识库一次性放进 LLM 的上下文窗口,预计算并缓存 KV(Key-Value)状态,之后每次查询直接复用缓存——跳过运行时检索。
对比一下三种范式的"知识进入模型"的方式:
| 范式 | 知识在哪里 | 每次查询做什么 | 核心成本 |
|---|---|---|---|
| 微调 | 烧进模型权重 | 什么都不做(直接生成) | 训练成本高、知识更新难 |
| RAG | 外部向量库 | Embedding → 检索 → 注入 prompt | 每次都要检索 + 重复送 chunk |
| CAG | 预热的上下文窗口 | 读取缓存 KV → 直接生成 | 一次性 KV 计算 + 缓存命中费 |
KV Cache:CAG 的物理基础
要理解 CAG 为什么能成立,必须先理解 Transformer 推理的一个关键事实:同样的前缀 token,每次推理时算出来的 KV 张量是完全一样的。
┌─────────────────────────────────────────────────┐
│ Transformer 推理过程(自回归生成) │
│ │
│ prompt = [SYSTEM_PROMPT] + [文档库] + [用户问题] │
│ ↓ │
│ 逐层 Attention │
│ ↓ │
│ 每个 token 算出 K, V 张量(QKV 中的 K 和 V) │
│ ↓ │
│ 生成下一个 token │
│ │
│ 关键:[SYSTEM_PROMPT] + [文档库] 这部分的 │
│ K, V 张量与"用户问什么"完全无关。 │
│ → 算一次,存下来,之后所有查询直接复用。 │
└─────────────────────────────────────────────────┘
这就是 Prompt Caching(提示词缓存)。Anthropic、OpenAI、Google、DeepSeek 在 2024 年陆续上线了这个能力。Anthropic API 教程第 6 章 完整覆盖了 Anthropic 的 Caching API,本章只取其中和 CAG 最相关的部分。
Prompt Caching 实战(Anthropic)
from anthropic import Anthropic
client = Anthropic()
# 假设我们有一份 50 页的产品手册,约 80K tokens
with open("product_manual.txt") as f:
manual = f.read()
def cag_query(question: str) -> str:
"""CAG 风格查询:整本手册放进 system,缓存它"""
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": "你是产品客服,根据下面的完整手册回答问题。",
},
{
"type": "text",
"text": manual,
# 关键:标记这一段为可缓存
"cache_control": {"type": "ephemeral"},
},
],
messages=[{"role": "user", "content": question}],
)
return response.content[0].text
# 第一次调用:写入缓存(写入比正常贵 25%)
cag_query("产品的保修期是多久?")
# 后续 5 分钟内的所有调用:缓存命中,输入 token 价格 1/10
cag_query("如何重置出厂设置?")
cag_query("支持哪些蓝牙协议?")
cag_query("国际版和国行版有什么区别?")
cache_control: {"type": "ephemeral"}:标记此 block 之前的所有内容(含此 block)需要缓存。
命中条件:从开头到 cache breakpoint 必须逐字节匹配。换一个空格、换个标点都会 miss。
TTL:默认 5 分钟,写入比未缓存调用贵 25%,命中价格降到 1/10(Sonnet)或 1/12(Opus)。
CAG vs RAG:成本回本公式
"用了 CAG 真的更便宜吗?" 这个问题的答案完全取决于查询频率。我们把它写成可计算的公式。
设:
- K = 知识库 token 数(如 80,000)
- Q = 单次查询的问题 token 数(如 50)
- A = 单次答案 token 数(如 300)
- p_in = 输入价格(如 Sonnet 4.6 = $3 / 1M tokens)
- p_cache_write = 写缓存价格(1.25 × p_in)
- p_cache_read = 命中价格(0.1 × p_in)
- p_out = 输出价格
- N = TTL 内的查询次数
纯 RAG 成本(N 次查询):每次送 5 个 chunk × 200 token = 1000 token 上下文:
cost_RAG = N × (1000 × p_in + A × p_out)
≈ N × (1000 × $3/1M + 300 × $15/1M)
≈ N × $0.0075
CAG 成本(N 次查询,命中同一缓存):
cost_CAG = K × p_cache_write # 一次性写入
+ N × (K × p_cache_read # 每次缓存命中
+ Q × p_in # 用户问题(小)
+ A × p_out) # 输出
≈ 80000 × $3.75/1M # = $0.30 写入费
+ N × (80000 × $0.30/1M # = $0.024 / 次命中
+ 50 × $3/1M # ≈ $0.00015
+ 300 × $15/1M) # ≈ $0.0045
≈ $0.30 + N × $0.0287
对比上面两个公式可以发现:CAG 在大多数 RAG 场景下反而更贵,因为它每次都要把整个知识库的 KV 重新计算一遍(虽然命中价格只有 1/10,但量级是 80K 而不是 1K)。
CAG 真正比 RAG 便宜的场景是:
① 知识库很小(< 5K tokens),KV 重读成本可忽略
② 检索召回率很差需要送 K=20+ 个 chunk,RAG 的输入 token 已经接近全量
③ 不算钱,只算"省得检索错"——CAG 能保证模型看到了完整知识,没有召回漏失
那么 CAG 真正的价值是什么
论文给出的实验结果在 SQuAD 和 HotpotQA 上 CAG 准确率高于 RAG。但仔细看会发现:CAG 真正的杀手锏不是省钱,是"消除检索错误"。
RAG 的隐性失败
RAG 存在不可见的召回失败:用户问题与文档表述差距太大、关键 chunk 在 Top-K 之外、Reranker 把对的排到了第 11 位。这些错误看不到——你只看到答案错了。
CAG 的"全知"承诺
CAG 把整个文档塞进窗口,模型在 attention 层面"看过"全部内容。理论上不会有"找不到"的问题——只剩下"模型理解力够不够"的问题,这是更可调试的失败模式。
延迟优势
RAG 的检索阶段(Embedding + 向量查询 + Rerank)通常 200–500ms。CAG 命中缓存后这一段消失了——首 token 延迟可以从 800ms 降到 300ms。
架构简化
CAG 不需要:向量数据库、Embedding 模型、分块策略、Reranker、混合检索逻辑。整个 RAG 教程前 9 章里的工程代码,CAG 都不需要。
长上下文:从 CAG 推到极致
CAG 的天花板就是上下文窗口本身。2025 年初的窗口现状:
| 模型 | 上下文窗口 | 大致可塞内容 | Caching 支持 |
|---|---|---|---|
| Claude Sonnet 4.6 / Opus 4.7 | 200K(Beta 1M) | 一本书 / 50 篇论文 | ✅ ephemeral 5 分钟 / 1 小时 |
| Gemini 1.5 / 2.5 Pro | 1M(实验 2M) | 整套技术文档 / 11 小时音频 | ✅ Context Caching API |
| GPT-4o / 4.1 | 128K / 1M | 一本中等长度书 | ✅ 自动 prefix caching |
| DeepSeek V3 | 128K | 一份大型技术报告 | ✅ 默认开启 |
不要因为窗口能塞 1M 就真的塞 1M。多项研究(Liu et al. 2023, NIAH 测试)发现:LLM 对窗口中部内容的注意力显著弱于开头和结尾。塞了 800K token 进去,模型可能就是"看不到"中段那个关键事实。
实践经验:把最重要的内容放在 system 末尾或用户消息之前,并在每次查询时用一句话提示模型"请仔细查看以下文档的全部内容"。
CAG 的工程实践要点
1. 缓存命中调试
# Anthropic 响应里会返回 usage 字段
response = client.messages.create(...)
print(response.usage)
# Usage(
# input_tokens=50, ← 用户问题部分
# cache_creation_input_tokens=0, ← 没有写缓存
# cache_read_input_tokens=80000, ← 命中了 80K 缓存
# output_tokens=300
# )
# 如果 cache_read 一直是 0,检查:
# ① system 内容是否完全字节匹配(甚至空格、换行符)
# ② cache_control 是否打在了正确的 block
# ③ 距离上次命中是否超过 5 分钟 TTL
2. 多层 Cache Breakpoints
# 不同变化频率的内容分层缓存,最大化命中率
system = [
{"type": "text", "text": STABLE_INSTRUCTIONS}, # 永不变
{"type": "text", "text": PRODUCT_MANUAL,
"cache_control": {"type": "ephemeral"}}, # breakpoint 1
{"type": "text", "text": USER_PROFILE,
"cache_control": {"type": "ephemeral"}}, # breakpoint 2
]
# 用户切换时:profile 缓存 miss,但 manual 缓存仍命中
3. CAG + RAG 混合架构
实际生产系统经常这样组合:"核心稳定知识用 CAG,海量长尾知识用 RAG"——
def hybrid_query(question: str) -> str:
# Step 1: 用关键词路由判断走哪条路
if is_core_product_question(question):
# 核心产品手册(80K)直接 CAG,命中率高
return cag_query(question, manual=PRODUCT_MANUAL)
# Step 2: 长尾知识走 RAG
chunks = vector_store.search(question, top_k=5)
return rag_query(question, chunks)
CAG 不适合什么场景
① 知识库 > 上下文窗口的 50%。就算技术上塞得下,"Lost in the Middle"会让答案质量崩溃。这种情况老实用 RAG。
② 知识库高频更新。更新意味着缓存失效,意味着每次都要付一次"写入缓存"的 1.25× 费用。如果文档每小时更新一次而 TTL 只有 5 分钟,CAG 就退化成"每次都额外付 25%"。
③ 多租户 / 个性化。CAG 的核心是"前缀字节匹配",如果每个用户看到的内容不一样(按权限过滤、按偏好排序),缓存命中率会非常低。
本章小结
- CAG 不是"取代 RAG",而是"知识库装得下时跳过检索"——前提是知识库 < 上下文窗口 50%
- 物理基础是 KV Cache:Transformer 对相同前缀算出的 K/V 张量完全相同,可缓存
- Prompt Caching 命中价格降到 1/10,但读 KV 仍按 token 计费——大知识库未必比 RAG 便宜
- CAG 最大价值:消除检索失败、降低延迟、架构简化,不一定是省钱
- 实践:用
cache_control多层 breakpoint、监控cache_read_input_tokens、与 RAG 混合使用 - 不适合:高频更新的知识库、多租户个性化、超大语料
本章只覆盖了 Prompt Caching 中和 CAG 直接相关的部分。完整的 Caching API(多层 breakpoint、TTL 选择、缓存命中调试、Tool-heavy Agent 场景),见 Anthropic API 教程第 6 章 Prompt Caching。原始论文:arXiv:2412.15605 - Don't Do RAG: When Cache-Augmented Generation is All You Need。