大模型的上下文缓存是什么?从底层原理到 GPT、Claude 的长对话策略

大模型的上下文缓存是什么?从底层原理到 GPT、Claude 的长对话策略

使用 GPT、Claude 等大模型进行编程时,经常会出现这样的情况:刚开始对话只有几千 Token,经过多轮代码分析、工具调用和报错修复后,上下文很快就增长到几万甚至几十万 Token。

表面上看,模型每一轮都需要重新读取完整历史:

1
2
3
4
5
6
7
8
9
系统提示词
项目规范
工具定义
用户第一轮问题
模型第一轮回答
用户第二轮问题
模型第二轮回答
……
当前问题

如果每次都按普通输入重新计算,长对话的费用和响应时间都会快速上升。Prompt Caching,也就是提示词缓存,正是为了解决这种重复计算问题。

不过,缓存并不意味着“历史对话免费”,也不意味着对话可以无限增长。理解它的底层逻辑后,才能真正用好长上下文。

缓存到底保存了什么?

大模型收到一段输入后,并不是简单地把文字读进内存。

输入文本首先会被分成 Token,然后经过模型每一层的注意力计算。在这个过程中,模型会为已经处理过的 Token 生成 Key 和 Value 等中间表示,通常统称为 KV Cache。

可以把它理解为:

1
2
3
4
5
6
7
8
9
原始输入
↓
Token 化
↓
模型逐层进行注意力计算
↓
生成 KV 中间结果
↓
继续预测后面的内容

当下一次请求仍然包含完全相同的前缀时,模型没有必要重新计算这一部分,可以直接加载之前生成的 KV 中间结果,然后只处理新增的内容。

OpenAI 对扩展缓存的官方说明也明确提到,缓存的是模型在 Prefill 阶段产生的 Key/Value 张量,而不是简单地把整段提示词当作普通文本文件保存下来。

例如第一次请求是:

1
2
3
4
固定系统提示词
+ 固定工具定义
+ 项目开发规范
+ 用户问题一

第二次请求变成:

1
2
3
4
5
6
固定系统提示词
+ 固定工具定义
+ 项目开发规范
+ 用户问题一
+ 模型回答一
+ 用户问题二

只要前面的内容完全一致,第二次请求就可以复用一部分已经计算好的结果。

因此,一轮长对话的输入费用可以拆成三部分:

1
2
3
4
5
6
7
本轮输入费用
=
旧上下文的缓存读取费用
+
本轮新增内容的普通输入费用
+
新增缓存内容的缓存写入费用

模型生成的新回答仍然属于输出 Token,缓存不会降低输出 Token 的价格。

这也解释了为什么缓存读取通常很便宜:平台省掉了最耗计算资源的重复 Prefill,只需要读取已经生成的中间结果。

而缓存创建可能比普通输入更贵,是因为平台除了要完成正常计算,还需要继续保留这些中间结果,并承担缓存索引、存储、路由和资源占用成本。

需要注意,缓存创建更贵并不是所有模型的通用规则。GPT-5.6 及之后的 OpenAI 模型,缓存写入按普通输入价格的 1.25 倍收费;GPT-5.6 之前的 OpenAI 模型没有额外的缓存写入费用。Claude 则一直把缓存写入、缓存读取和普通输入分开计费。

为什么持续对话通常更省,但不能无限聊?

假设一轮编程对话已经积累了 100000 个历史 Token,本轮只新增了 2000 个输入 Token。

没有缓存时,模型可能需要重新处理:

1
2
100000 个历史 Token
+ 2000 个新增 Token

命中缓存后,可以变成:

1
2
100000 个缓存读取 Token
+ 2000 个普通输入 Token

由于缓存读取价格通常远低于普通输入价格,这一轮的输入成本会明显下降,响应延迟通常也会降低。

因此,在同一个功能、同一个问题或同一个代码模块上持续对话,一般比频繁开启新对话、反复发送同一批项目说明更经济。

但这不代表对话越长,每轮费用越低。

历史上下文仍然会被计入请求,只是其中一部分按照缓存读取价格收费。随着历史从 10000 Token 增长到 100000 Token,再增长到 500000 Token,即使缓存读取很便宜,总费用仍然可能持续上涨。

长上下文还会带来另外几个问题:

  • 早期已经失效的信息仍然占用上下文;
  • 大量无关代码和日志可能分散模型注意力;
  • 文件修改后,历史中的旧代码可能和当前代码冲突;
  • 上下文达到模型限制后必须压缩或删除内容;
  • 修改较早的消息可能导致后续缓存失效;
  • 缓存过期后,部分内容需要重新计算和写入。

所以,缓存解决的是“重复计算成本”,不是“上下文管理问题”。

对于编程任务,更合理的做法是:

  1. 同一个功能继续使用同一个对话,例如登录模块从分析、修改、测试到 Code Review 都放在一个会话中。

  2. 切换到完全不同的功能时开启新对话,例如从登录系统转到支付系统,不要为了保留缓存强行共用一个超长会话。

  3. 不要把整个项目的所有文件永久放进上下文。优先让模型读取当前任务相关文件、Git Diff、测试结果和报错日志。

  4. 在完成一个阶段后生成简短的任务状态文档,记录目标、已完成内容、关键决策、修改文件和下一步工作。

  5. 上下文过长时进行压缩,用结构化摘要代替几十轮已经完成的过程。

一个适合编程任务的状态文件可以写成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 当前目标

完成用户登录接口重构。

# 已完成

- 修改用户认证服务
- 增加 JWT 校验
- 补充登录失败测试

# 关键决定

- 刷新令牌存入 Redis
- Access Token 有效期为两小时

# 修改文件

- AuthController.java
- UserService.java
- JwtUtils.java

# 当前问题

刷新令牌的过期时间测试失败。

# 下一步

检查 Redis Key 的 TTL 设置。

压缩上下文或开启新对话后,只需要重新提供:

1
2
3
4
5
稳定的项目规范
+ 任务状态摘要
+ 当前 Git Diff
+ 当前相关文件
+ 最新问题

这样通常比重新携带整个旧对话更干净,也更不容易让模型受到过时信息影响。

GPT 和 Claude 的缓存策略有什么区别?

GPT 和 Claude 的底层目标相同,都是复用已经处理过的相同前缀,但两家的控制方式、有效期和计费规则不同。

对比项 GPT Claude
基本匹配方式 相同前缀 相同前缀
是否自动使用 OpenAI API 默认自动检查 需要启用 cache_control
是否支持显式缓存点 GPT-5.6 及之后支持 支持
缓存写入 GPT-5.6 起为普通输入的1.25倍 5分钟1.25倍,1小时2倍
缓存读取 按缓存输入优惠价 普通输入价格的0.1倍
默认时效 GPT-5.6 至少30分钟 5分钟
可选长时效 旧模型部分支持最长24小时 可选择1小时
命中后续期 官方未按 Claude 方式明确说明 命中后刷新有效期

GPT 的缓存策略

OpenAI API 会自动对达到要求的长提示词进行缓存检查。官方目前规定,提示词至少达到 1024 Token 才能使用缓存;匹配依赖完全一致的前缀,因此固定内容应放在前面,动态内容放在后面。图片和工具定义同样需要保持一致。

GPT-5.6 及之后同时支持两种方式:

  • 隐式缓存:默认方式,由系统在最新消息处自动设置缓存点;
  • 显式缓存:开发者明确指定哪些稳定前缀需要缓存。

GPT-5.6 还可以通过稳定的 prompt_cache_key,帮助具有相同公共前缀的请求被路由到相同缓存。需要注意,这个 Key 只能提高路由和匹配稳定性,真正能否命中仍然取决于前缀内容是否完全一致。

GPT-5.6 的缓存写入按照普通输入价格的 1.25 倍计费,缓存读取按照更低的缓存输入价格计费。当前只支持 30m 这一种 TTL,含义是缓存前缀至少可以复用 30 分钟,OpenAI 可能继续保留更长时间,但没有承诺一个固定的最长命中时间。

GPT-5.6 之前的模型采用另一套保留策略:

  • 内存缓存通常在停止使用后维持 5~10 分钟;
  • 系统不繁忙时最长可能达到1小时;
  • 部分模型支持扩展缓存,最长可以达到24小时;
  • 旧模型的缓存写入没有额外费用。

因此,不能把 GPT 所有版本都简单概括成“缓存30分钟”或“缓存24小时”,需要根据具体模型家族判断。

Claude 的缓存策略

Claude 的缓存属于显式启用功能。开发者可以在请求顶层添加 cache_control 使用自动缓存,也可以在某个内容块后面放置缓存断点,精确决定缓存范围。

Claude 按照以下顺序构造可缓存前缀:

1
2
3
tools
→ system
→ messages

也就是说,工具定义、系统提示词以及断点之前的历史消息共同组成完整缓存前缀。任何靠前内容发生变化,都可能使后面的缓存无法继续命中。

Claude 的自动缓存非常适合多轮对话。随着对话增长,缓存点会自动向后移动:

1
2
3
4
5
6
7
8
9
10
第一轮:
系统提示词 + 用户1 + 回答1 + 用户2
全部写入缓存

第二轮:
系统提示词 + 用户1 + 回答1 + 用户2
从缓存读取

回答2 + 用户3
写入新的缓存部分

也就是说,它不会每一轮都重新写入完整历史,而是读取旧前缀,再缓存新增的对话部分。

Claude 默认缓存时间是5分钟,每次成功读取缓存后会刷新有效期。如果交互间隔比较长,也可以选择1小时缓存。

其计费比例非常明确:

1
2
3
4
普通输入:1倍
5分钟缓存写入:1.25倍
1小时缓存写入:2倍
缓存读取:0.1倍

因此,5分钟缓存只要成功读取一次,通常就可以抵消首次创建时多出的成本;1小时缓存通常需要至少两次读取,才会比每次都按普通输入处理更划算。

对于连续编程、频繁工具调用和短时间内不断修改代码的任务,5分钟缓存通常已经足够,因为每次命中都会续期。

对于以下情况,1小时缓存更合适:

  • 两轮操作之间经常暂停十几分钟;
  • 单次深度思考或工具执行时间很长;
  • 需要人工审核后再继续;
  • 批量任务排队时间可能超过5分钟;
  • 多步骤 Agent 工作流持续时间较长。

怎样让长对话更容易命中缓存?

缓存匹配的重点不是“意思相同”,而是“前缀完全相同”。

最推荐的输入顺序是:

1
2
3
4
5
6
7
8
固定工具定义
固定系统提示词
稳定的项目规范
稳定的背景材料
稳定的代码或文档
历史对话
当前变化的任务
时间、日志、用户输入等动态数据

不推荐的顺序是:

1
2
3
4
5
6
当前时间
随机请求编号
本轮用户信息
固定系统提示词
项目规范
工具定义

因为最前面的时间或随机值每轮都会变化,可能导致后面很长的一段稳定内容无法复用。

实际使用中,可以遵循以下原则:

  • 静态内容放前面,动态内容放最后;
  • 不要频繁调整工具顺序;
  • 不要在系统提示词开头插入当前时间;
  • 项目规范没有变化时不要重新生成;
  • 同一份文件尽量保持完全相同的格式和内容;
  • 新增代码放在已有稳定内容之后;
  • 同一功能在同一会话完成;
  • 无关功能开启新会话;
  • 定期把旧过程压缩成状态摘要;
  • 保留最近的错误信息、Git Diff 和关键决策,删除已经无用的调试过程。

缓存和上下文压缩并不是相互替代的功能。

缓存负责降低重复上下文的计算费用,压缩负责控制上下文长度和信息质量。Claude 官方也建议在压缩后把稳定的系统提示词和新的压缩摘要分别设置缓存点,这样系统提示词仍能继续命中,只需要重新缓存新的摘要。

最终可以把适合 Coding 的上下文结构概括为:

1
2
3
4
5
6
7
8
9
稳定系统提示词
↓
稳定工具和项目规则
↓
项目状态摘要
↓
当前相关代码与 Git Diff
↓
本轮任务和最新报错

大模型缓存真正节省的,不是“文字存储成本”,而是反复处理同一段上下文所需要的模型计算。

在长对话中,之前已经处理过的历史可以按较低的缓存读取价格复用,本轮新增内容正常计算,模型输出正常收费。因此,持续对话通常比反复重建相同上下文更省,但对话依然需要定期整理和压缩。

最理想的策略不是让一个对话无限增长,而是:

让稳定内容尽可能保持不变并持续命中缓存,让变化内容尽量靠后,让已经完成的历史及时压缩成清晰摘要。