Claude Code 缓存机制与请求架构
核心问题
大语言模型的 API 是**无状态**的------服务端不会记住你之前说了什么。每次请求都必须把完整上下文(系统提示 + 工具定义 + 全部历史消息)重新发送。
这意味着在多轮对话中,**大量相同的内容被反复发送、反复计算**:
-
第 1 轮:计算 1 万 token 的 KV
-
第 2 轮:重新计算同样的 1 万 + 新增的 1 千 token 的 KV
-
第 3 轮:重新计算同样的 1.1 万 + 新增的 1 千 token 的 KV
-
...
对话越长,重复计算越多,**GPU 算力和用户费用双重浪费**。
**Prompt Cache 解决的就是这个问题**:把已经计算过的 KV Cache 持久化存储,后续请求直接复用,跳过重复的计算。结果是服务端 GPU 计算量减少、响应延迟降低、用户费用降到 1/10。
一、整体请求架构
```
┌─────────────────────────────────────────────────┐
│ 本地电脑 │
│ │
│ Claude Code (CLI) │
│ ├─ 组装请求:system prompt + tools + 历史消息 │
│ ├─ 标记 cache_control │
│ └─ 发送 HTTPS 请求 → Anthropic API │
└──────────────────────┬──────────────────────────┘
│ HTTPS (streaming SSE)
▼
┌─────────────────────────────────────────────────┐
│ Anthropic API Gateway │
│ ├─ 认证 (API Key) │
│ ├─ 限流 / 计费 │
│ └─ 路由到对应模型集群 │
└──────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 模型推理集群 (GPU Servers) │
│ │
│ 1. 检查 Prompt Cache │
│ ├─ 命中 → 直接加载已有 KV Cache │
│ └─ 未命中 → 从 token 计算 KV,写入缓存 │
│ │
│ 2. Prefill 阶段 │
│ └─ 处理新增 token,计算它们的 KV │
│ │
│ 3. Decode 阶段(逐 token 生成) │
│ ├─ Q × K → 注意力分数 │
│ ├─ 加权 V → 输出 token │
│ ├─ 新 token 的 KV 追加到缓存 │
│ └─ 每生成一个 token 就流式返回 │
└──────────────────────┬──────────────────────────┘
│ streaming tokens
▼
┌─────────────────────────────────────────────────┐
│ Claude Code (CLI) │
│ ├─ 流式接收 token,实时显示 │
│ ├─ 如果返回 tool_use → 本地执行工具 │
│ │ (Read/Edit/Bash/Grep 等都在本地电脑上跑) │
│ ├─ 把工具结果拼入消息,发起下一轮请求 │
│ └─ 循环直到模型不再调用工具 │
└─────────────────────────────────────────────────┘
```
**关键点**:
-
模型计算全在服务端,本地电脑只负责发请求和执行工具
-
Read、Bash、Grep 等工具都是 Claude Code 在本地执行的
-
一个问题可能触发多轮请求(模型调用工具 → 本地执行 → 结果发回 → 模型继续)
服务端组成
服务端不只是大语言模型,而是一整套基础设施:
| 组件 | 职责 |
|------|------|
| API Gateway | 认证、限流、计费、路由(不涉及模型计算) |
| 推理引擎 | 管理 KV Cache、调度请求、批处理优化 |
| 大语言模型 | 跑在 GPU 上的 Transformer 权重,负责实际 token 生成 |
二、Transformer 中的 KV 计算
注意力机制(Attention)
Attention 要解决的问题:模型在生成每个 token 时,需要"回顾"之前所有 token,决定该关注哪些内容。
每个 token 被变换成三个向量:
| 向量 | 角色 | 类比 |
|------|------|------|
| **Q** (Query) | "我在找什么" | 当前的问题 |
| **K** (Key) | "我是什么" | 每段历史内容的标签 |
| **V** (Value) | "我的内容" | 每段历史内容的实际信息 |
计算过程:
-
用当前 token 的 **Q** 和所有历史 token 的 **K** 做点积 → 得到注意力分数(该关注谁)
-
用注意力分数对所有 **V** 加权求和 → 得到当前 token 的输出
为什么 KV 可以缓存
模型逐个生成 token 时,**之前 token 的 K 和 V 不会变**。不缓存的话,生成第 N 个 token 要重新计算前面 N-1 个 token 的 KV,浪费大量算力。
核心原则:**历史不变,算一次就够了。**
三、KV Cache 与 Prompt Cache 的关系
两者是同一个东西的不同作用范围:
| | KV Cache | Prompt Cache |
|--|----------|-------------|
| **是什么** | 注意力层算出的 K、V 向量 | 同样是 K、V 向量 |
| **范围** | 单次请求内复用 | 跨请求复用 |
| **谁管的** | 推理引擎自动做 | Anthropic 额外做的持久化 |
| **生命周期** | 请求结束就释放 | 保留 5 分钟,命中续期 |
**Prompt Cache 的底层实现就是 KV Cache 的持久化存储**------"算好的 KV 别扔,留着下次用"。
跨请求流程示例
-
**第 1 次请求** --- 算出所有 token 的 KV → 生成回复 → KV 不丢弃,存下来(Prompt Cache 写入)
-
**第 2 次请求** --- 前缀相同的部分直接加载之前存的 KV(Prompt Cache 命中)→ 只算新增 token 的 KV → 生成回复
四、Prompt Cache 机制详解
什么内容会被缓存
按 API 处理顺序,以下内容构成缓存前缀:
-
**工具定义(tools)** --- Claude Code 的内置工具 + MCP 工具
-
**系统提示(system prompt)** --- 核心指令、CLAUDE.md、Memory 等
-
**消息历史(messages)** --- 所有之前的 user/assistant 轮次
缓存遵循严格的层级关系:`tools → system → messages`。某一层变化,该层及之后的缓存全部失效。
触发方式
全自动,无需配置。Claude Code 在 API 请求中设置 `cache_control: {"type": "ephemeral"}`,服务端自动处理。
费用对比(以 Opus 模型为例)
| 操作 | 价格 | 与基础价格的倍率 |
|------|------|-----------------|
| 普通输入 token | $15/MTok | 1x(基准) |
| 缓存写入 | $18.75/MTok | 1.25x |
| **缓存命中** | **$1.50/MTok** | **0.1x** |
**缓存命中时,输入 token 费用降到原来的 1/10。** 10 轮对话大约节省 ~78% 的输入 token 费用。
缓存失效场景
| 场景 | 影响 |
|------|------|
| `/compact` 压缩对话 | 消息历史改变,缓存重建 |
| 切换模型 | 工具/系统提示可能变化,全部失效 |
| MCP 工具动态加载 | tools 数组变化,全部失效 |
| 超过 5 分钟没交互 | 缓存自然过期 |