最近 DeepSeek API 更新了一个很有意思的功能:Context Caching,也就是上下文缓存。
我第一反应是疑惑:
大模型推理本身不是有随机性吗?
如果命中缓存,那不就变成固定答案了吗?
那这个缓存还有什么意义?
后来查了 DeepSeek 官方文档后,我发现这里的"缓存"和我们后端开发里常说的 Redis 缓存、接口缓存、页面缓存,虽然思想类似,但缓存对象完全不一样。
一、结论先说
DeepSeek 的缓存不是缓存最终回答。
它缓存的是:
- 输入 prompt 的重复前缀
- 长上下文的中间计算结果
- 可以复用的上下文 KV 状态
也就是说,它缓存的是"模型读懂前面那一大段内容所做的计算",不是缓存"模型最后回答了什么"。
所以即使命中缓存,模型的输出仍然会重新生成,仍然可能受到 temperature、top_p 等参数影响,仍然可能有随机性。
DeepSeek 官方文档也明确说明:硬盘缓存只匹配用户输入的前缀部分,输出仍然通过计算和推理生成,并且会受到 temperature 等参数影响。参考:DeepSeek Context Caching 官方文档。
二、传统软件里的缓存是什么?
我们开发中常见的缓存一般是这样的:
java
String key = "user:1001";
User user = redis.get(key);
if (user == null) {
user = db.queryUser(1001);
redis.set(key, user);
}
return user;
这种缓存的特点是:
- key 一样
- value 一样
- 命中缓存后直接返回结果
- 不再执行原来的计算或查询
比如:
text
查询用户 ID = 1001
命中缓存后,直接返回用户数据。这叫"结果缓存",它缓存的是最终结果。
三、DeepSeek 的缓存不是结果缓存
DeepSeek 的 Context Caching 更像是:
text
我不缓存最终答案
我缓存模型处理重复上下文时产生的中间状态
比如你有一个固定 system prompt:
text
你是一个专业的财报分析师,请用严谨、结构化的方式回答问题。
再加上一份很长的财报:
text
这里是一份 10 万 token 的财报内容......
第一次请求:
text
system: 你是一个专业的财报分析师......
user: <财报全文> 请总结这份财报
第二次请求:
text
system: 你是一个专业的财报分析师......
user: <同一份财报全文> 请分析公司的盈利能力
这里前面的大部分内容都是重复的。如果每次都让模型重新处理整份财报,就很浪费。
DeepSeek 的缓存就是把这部分重复前缀的计算结果存下来。下次遇到相同前缀,就复用这部分计算。注意:最终回答仍然是重新生成的。
四、它和 Redis 缓存像不像?
像,但不完全像。
相同点
本质上都是为了避免重复计算。
- 传统缓存:
相同查询 -> 复用查询结果 - DeepSeek 缓存:
相同上下文前缀 -> 复用上下文计算结果
都是用空间换时间,用存储换成本。
不同点
| 对比项 | 传统软件缓存 | DeepSeek 上下文缓存 |
|---|---|---|
| 缓存对象 | 最终结果 | 输入前缀的计算状态 |
| 命中后是否直接返回 | 是 | 否 |
| 输出是否固定 | 通常固定 | 不固定 |
| 主要作用 | 减少数据库/接口压力 | 减少大模型重复推理成本 |
| 开发者是否需要手动控制 | 通常需要 | DeepSeek 默认自动开启 |
DeepSeek 官方文档说明,Context Caching 默认对所有用户开启,不需要修改代码。每次请求都会触发硬盘缓存构建,如果后续请求和之前请求存在重叠前缀,重叠部分就可以从缓存读取。参考:DeepSeek Context Caching 官方文档。
五、为什么缓存不会让答案固定?
因为大模型生成答案分两步看:
text
第一步:理解输入上下文
第二步:继续生成输出 token
DeepSeek 缓存的是第一步中重复上下文的计算结果。它没有缓存第二步生成出来的最终文本。
所以命中缓存后,相当于:
text
前面这段我已经读过并处理过了,不用重新算
但接下来怎么回答,还是重新生成
举个开发者能理解的类比:
你让一个人读一本 100 页的文档,然后回答问题。第一次,他读完文档,回答问题 A。第二次,你又问问题 B。如果他已经记住了前面文档的内容,他就不需要重新读 100 页。但他回答问题 B 的时候,答案还是现想的,不是把上一次答案复制出来。
DeepSeek 的缓存大概就是这个意思。
六、DeepSeek 缓存的命中规则
DeepSeek 官方文档里提到:缓存命中需要对应的前缀已经被持久化到磁盘缓存中。它不是任意相似都能命中,而是需要"前缀匹配"。
官方给了几个规则:
- 请求边界会持久化缓存前缀:比如用户输入结束处、模型输出结束处。
- 系统检测到多个请求有共同前缀时:会把共同前缀作为独立缓存单元持久化。
- 对于长输入或长输出:系统会按固定 token 间隔切分缓存单元,避免超长前缀因为没有到边界而完全无法缓存。
所以它更适合下面这些场景:
- 多轮对话
- 长文档问答
- 固定 system prompt
- 固定工具说明
- 固定 few-shot 示例
- Agent 场景下的大量历史上下文复用
七、怎样判断有没有命中缓存?
DeepSeek API 的 response usage 里有两个字段:
json
{
"prompt_cache_hit_tokens": 10000,
"prompt_cache_miss_tokens": 500
}
含义是:
prompt_cache_hit_tokens:本次输入中命中缓存的 token 数prompt_cache_miss_tokens:本次输入中没有命中缓存的 token 数
DeepSeek 官方 API 文档说明,prompt_tokens = prompt_cache_hit_tokens + prompt_cache_miss_tokens。这点对开发者很重要,因为你可以通过这两个字段观察自己的 prompt 结构是否适合缓存。
八、价格为什么会低很多?
因为命中缓存的输入 token 不需要完整重新计算,所以价格更低。
DeepSeek 官方价格页显示,输入 token 会区分 cache hit 和 cache miss 计费。比如 deepseek-v4-flash 当前 cache hit 的输入价格明显低于 cache miss 输入价格。这说明它的商业逻辑很清楚:
text
重复上下文越多,缓存命中越多,输入成本越低。
对于长上下文应用,这个差距会非常明显。
九、开发时怎样提高缓存命中率?
作为开发者,可以注意几个点。
1. 把稳定内容放在前面
比如:
text
system prompt
固定规则
工具说明
长文档
历史上下文
用户本轮问题
不要把每次变化的内容放在最前面。
错误示例:
text
当前时间:2026-xx-xx xx:xx
随机 requestId:abc123
system prompt
长文档
用户问题
这样每次最前面都不同,会破坏前缀匹配。
2. 不要频繁改 system prompt
如果你的 system prompt 每次都动态拼接不同内容,会影响缓存命中。可以把稳定规则和动态变量分开:
text
稳定规则放前面
动态变量放后面
3. 长文档问答要保持文档内容一致
比如同一份合同、财报、论文,多次提问时,文档原文尽量不要变。如果你每次都对文档做不同切分、不同摘要、不同排序,缓存命中率会下降。
4. Few-shot 示例适合放前面
如果你有固定示例:
text
示例1
示例2
示例3
它们适合放在前面,因为多次请求都能复用。
十、它的局限性
DeepSeek 官方文档也提到,缓存系统是 best-effort 的,不保证 100% 命中。另外,缓存构建需要几秒钟。如果缓存不再被使用,会自动清理,通常在几小时到几天内清除。
所以不能把它当成 Redis 那种由你完全控制生命周期的缓存。它更像是 DeepSeek 服务端自动做的推理优化。
十一、最终理解
我觉得可以这样总结:
text
传统缓存:缓存结果
DeepSeek 缓存:缓存上下文计算过程
传统软件缓存解决的是:
text
不要重复查数据库
不要重复调用接口
不要重复计算结果
DeepSeek 上下文缓存解决的是:
text
不要让模型重复处理同一段长上下文
所以它不会让模型答案固定。它只是让模型在处理重复输入时更省钱、更快。
对于开发者来说,真正要做的是:
text
设计更稳定的 prompt 结构
把重复内容放前面
把变化内容放后面
观察 prompt_cache_hit_tokens
这才是用好 DeepSeek 缓存的关键。