OpenAI 提示词缓存怎么用:30 分钟有效期、缓存断点和未命中诊断
按 OpenAI 官方文章和文档整理 GPT-6 系列的新版提示词缓存:缓存读取最低是输入价的 5%,写入是 1.25 倍,有效期至少 30 分钟。讲清哪些改动会让缓存失效、显式断点怎么放、怎么用诊断工具查出未命中的原因。
本文根据公开分享整理,本站尚未实测。操作前请对照文末的原始来源。
这篇面向用 OpenAI API 做应用和智能体的开发者。OpenAI 在 9 月 22 日随 GPT-6 系列推出了新版的提示词缓存,同时给了监控面板和诊断工具。先说要点:
- 缓存默认开启,不用改代码就有。请求之间相同的开头部分会被复用,这部分输入按缓存价计费。
- 读取便宜,写入略贵。 GPT-5.6 及之后的模型,缓存读取是标准输入价的 0.1 倍,GPT-6.1 Sol 是 0.05 倍;缓存写入是 1.25 倍。
- 有效期至少 30 分钟,每次写入或复用都会重新计时。
- 前面任何一处变了,后面的缓存就用不上。 换模型、动工具、改推理强度、在开头放时间戳,都会让缓存失效。
- 未命中可以查原因了。 新的诊断功能会告诉你是哪一类改动导致的。
关于折扣幅度有一处出入:9 月 22 日的文章写“最高 90%”,现在的官方文档写“最高 95%”。95% 对应的是 9 月 29 日发布的 GPT-6.1 Sol,其他模型是 90%。
缓存是怎么回事
模型处理输入时要算出一批中间状态。缓存保存的是提示词开头不变的那一段(前缀)对应的状态。后面的请求如果开头完全一样,并且找到了匹配的缓存,就不用把这段重新算一遍,只处理新增的部分。
被缓存的是模型看到的完整上下文:OpenAI 提供的指令、开发者消息、工具定义、对话历史(文字、图片、文档和支持的音频)。
三个基本规则:
| 规则 | 说明 |
|---|---|
| 整段前缀必须一致 | 断点之前有任何内容或相关设置变了,从变的地方往后都匹配不上 |
| 有最小长度 | GPT-5.6 及之后的模型是 1,024 个 token(OpenAI 隐藏的系统内容不算在内) |
| 不跨组织 | 缓存不在不同组织之间共享,也不能跨数据处理区域复用 |
官方还回答了三个常见问题:缓存不影响模型怎么生成输出,相同的请求不保证得到相同的回答;不能手动清除缓存;缓存的输入 token 仍然计入每分钟 token 数的速率限制。
价格:什么时候划算
GPT-5.6 及之后的模型,输入 token 按三种费率之一计费(写入费不是额外叠加的):
| 类型 | 相对标准输入价 |
|---|---|
| 未缓存的输入 | 1 倍 |
| 缓存写入 | 1.25 倍 |
| 缓存读取 | 0.1 倍(GPT-6.1 Sol 是 0.05 倍) |
官方给的算例,按 0.1 倍的读取价:
| 情况 | 不用缓存 | 用缓存 |
|---|---|---|
| 同一段前缀用 2 次 | 2 倍 | 1.35 倍(写 1 次、读 1 次) |
| 同一段前缀用 10 次 | 10 倍 | 2.15 倍(写 1 次、读 9 次) |
也就是说,只要一段前缀会被复用哪怕一次,缓存就是划算的。反过来,写进去却没人再读的内容是亏的,多付了 0.25 倍。这就是后面“显式断点”要解决的问题。
GPT-5.5 及更早的模型规则不同:没有单独的缓存写入费,读取价因模型而异,也不支持显式断点。
有效期
GPT-5.6 及之后的模型,缓存在最近一次写入或复用之后至少 30 分钟内可以复用,OpenAI 可能保留得更久。控制它的参数是 prompt_cache_options.ttl,目前唯一支持的值 30m 也是默认值。复用会刷新有效期,不会再收一次写入费。
哪些改动会让缓存失效
官方文档列出了影响前缀的设置:
| 设置 | 为什么会影响 |
|---|---|
model |
不同的模型权重和缓存行为不同 |
tools |
工具的名字、描述、schema、顺序变了 |
parallel_tool_calls |
会改变关于一轮里调用多个工具的指令 |
text.format(结构化输出) |
加入了输出格式的指令和 schema |
reasoning.effort |
会改变模型侧的推理指令 |
text.verbosity |
会改变关于回答详略的指令 |
context_management(压缩) |
用压缩后的内容替换了前面的对话 |
再加上内容本身的变化:改了、调换了或删了前面的消息,或者指令开头有时间戳、请求 ID 这类每次都不同的东西。
四个做法
1. 稳定的放前面,会变的放后面,只追加不改写
- 固定的开发者指令和共享的参考资料放最前面。
- 时间戳、用户相关的内容等放到末尾,或者放进后面的对话消息里。
- 新消息往后追加,不要改写前面的轮次。摘要、压缩、截断上下文都会改变前缀。
官方文档提醒,多轮应用里,复用不断增长的对话历史省下的 token,可能比只缓存开头的指令更多。所以要保留前面的消息和工具结果。
压缩是个需要算账的地方:压缩后第一个请求的缓存复用会变少,但输入 token 总数也少了。官方的建议是比较压缩前后的总输入成本,命中率下降不等于更贵。
2. 调推理强度时用 configuration_update
直接改请求顶层的 reasoning.effort 会改写隐藏的系统指令,缓存就断了。在 GPT-6 及之后支持的模型上,做法是顶层的值保持不变,往 input 数组里追加一项:
{
"type": "configuration_update",
"reasoning": { "effort": "high" }
}
最新的一条配置更新决定后续回复的推理强度。这样难的任务可以调高,常规的后续任务可以调低,前面的缓存都还在。
3. 工具列表保持不变,改“哪些能调”
应用在不同的请求里需要的工具不一样时,不要增删工具定义,而是:
| 需求 | 做法 |
|---|---|
| 这次请求不用工具 | 把 tool_choice 设为 "none",不要移除工具定义 |
| 只允许其中几个 | 用 allowed_tools 限制可调用的工具,tools 列表不动 |
| 工具太多,一开始不想全带上 | 用工具搜索并设 defer_loading: true,找到的工具追加在上下文末尾 |
| 对话中途要加工具 | 用开发者角色的 additional_tools 输入项追加 |
同样的思路也适用于指令:要给新的指令,用一条新的开发者消息追加在靠后的位置来覆盖旧的,不要回头改前面的。
4. 需要快速响应时预热
对 GPT-5.6 及之后的模型,可以在用户提问之前先把已知的上下文准备好,缩短第一个 token 出来的时间。比如交互式应用在启动时预热共享的指令、工具定义或参考资料。
把 prompt_cache_options.prewarm 设为 true,这个请求只准备缓存、不生成输出:
{
"model": "gpt-6.1-sol",
"input": [
{
"role": "developer",
"content": "Your app's shared instructions and reference material..."
}
],
"prompt_cache_options": {
"prewarm": true
}
}
之后的正式请求用同样的开头,不带 prewarm。预热时写入缓存的 token 按标准的缓存写入价计费。
隐式和显式:断点放在哪里
“缓存断点”标记一段可以保存和复用的前缀的结尾。GPT-5.6 及之后的模型有两种放法。
隐式(默认):OpenAI 自动把断点放在最新一条符合条件的消息末尾。适合不断往后追加的多轮对话。
显式:你自己选断点。把 prompt_cache_options.mode 设为 explicit,并在要作为断点的内容块上加 prompt_cache_breakpoint。官方的例子:
{
"model": "gpt-6.1-sol",
"reasoning": { "effort": "low", "context": "all_turns" },
"text": { "verbosity": "medium" },
"prompt_cache_options": { "mode": "explicit" },
"input": [
{
"role": "developer",
"content": [
{
"type": "input_text",
"text": "Stable instructions and shared reference material...",
"prompt_cache_breakpoint": { "mode": "explicit" }
}
]
},
{
"role": "developer",
"content": "Dynamic developer instructions, such as user-specific content and timestamps..."
},
{
"role": "user",
"content": "The user's current question..."
}
]
}
显式模式的几条规则:
- 最后一个断点之后的内容按未缓存的输入价计费,不收写入费。所以可以避免把不太会被复用的、变化的内容写进缓存。
- 只开显式模式却一个断点都不放,这个请求就完全不用缓存。
- 每个请求最多产生四次缓存写入。可以放多个断点,分别保住变化快慢不同的几段前缀。
- 顶层的
instructions不能放断点。要标记可复用的开发者指令,把它放进开发者消息里的input_text块。 - 也可以在隐式模式下加显式断点,这时隐式断点占掉四个写入名额里的一个。
什么时候必须用显式断点
官方文档的“容易踩的坑”里有两个,都是隐式模式下开头相同却没命中:
一、固定指令加每次不同的提问。 每个请求都是“一段固定的开发者消息 + 一条不同的用户消息”。隐式断点在最新消息的末尾,第一个请求写入的是包含用户消息的整段。下一个请求的用户消息变了,匹配不上那段更长的前缀,而固定内容后面又没有单独的断点。办法是在固定内容末尾放一个显式断点。从旧模型迁移到 GPT-5.6 及之后时特别容易遇到,因为隐式缓存的行为变了。
二、往同一条消息里续写。 请求 1 的最后一条用户消息是“内容 A”,请求 2 把这条消息扩成“内容 A + 内容 B”。原来的断点现在落在了消息中间,匹配不上。办法是保留原消息、另外追加一条新消息;做不到的话,把可复用的文字放在单独的内容块里,两个请求都在它后面放显式断点。
监控和诊断
看整体:缓存面板和用量字段
平台的用量页面里有提示词缓存面板,显示输入中有多少来自缓存、命中率随时间的变化,以及已缓存和未缓存 token 的构成。适合用来发现命中率下降,或者评估一次改动的影响。
自己统计的话,看每个响应的这两个字段:
usage.input_tokens_details.cached_tokens:从缓存读取的 token 数;usage.input_tokens_details.cache_write_tokens:写入缓存的 token 数。
命中率的算法是缓存 token 总数除以输入 token 总数,可以按用户、工作区或天来汇总。
查单个请求:诊断功能
诊断功能在 Responses API 里提供,支持 GPT-5.6 及之后的模型。它把当前请求和之前的一个响应作比较,说明本该复用的前缀为什么没复用。用法是三步:
- 选一个基准响应:同一组织下最近完成的、你认为当前请求应该复用其前缀的响应,比如上一轮对话。
- 把
prompt_cache_options.comparison_response_id设为那个响应的id。 - 读当前响应里的
prompt_cache_diagnostics。
官方的示例:两个请求只差一个工具的名字。
from pathlib import Path
from openai import OpenAI
client = OpenAI()
policy = Path("support-policy.txt").read_text() # At least 1,024 tokens.
first = client.responses.create(
model="gpt-6-astra",
instructions=policy,
input="Reply with exactly OK.",
tools=[{"type": "function", "name": "get_time"}],
)
second = client.responses.create(
model="gpt-6-astra",
instructions=policy,
input="Reply with exactly OK.",
tools=[{"type": "function", "name": "get_date"}],
prompt_cache_options={"comparison_response_id": first.id},
)
diagnostics = second.prompt_cache_diagnostics
if diagnostics is not None and diagnostics.type == "cache_miss":
print(diagnostics.reason)
print(diagnostics.comparison_reusable_tokens)
print(diagnostics.cache_missed_tokens)
结果类似这样:
{
"prompt_cache_diagnostics": {
"type": "cache_miss",
"reason": "tools_changed",
"comparison_reusable_tokens": 5629,
"cache_missed_tokens": 5629
}
}
设置 comparison_response_id 只是请求诊断,不会加载之前的对话,也不改变缓存的行为。多轮对话里,把每个完成的响应的 id 存下来,下一次请求时传进去即可。
原因对照表
reason 字段的取值和对应的处理,按官方文档整理:
| reason | 什么变了 | 怎么办 |
|---|---|---|
model_changed |
换了模型,可能是路由、A/B 测试或回退造成的 | 检查有没有意外的切换 |
tools_changed |
工具被增删、调换顺序,或描述、schema 变了 | 保持工具定义和顺序,用 tool_choice 或 allowed_tools 控制 |
reasoning_effort_changed |
推理强度变了 | 用 configuration_update |
input_changed |
前面的输入变了,比如指令里有时间戳或请求 ID,或者消息被改、被删 | 把变化的内容挪到断点之后,只追加 |
context_compacted |
压缩替换了前面的对话 | 保持稳定的指令不变;比较总输入成本 |
text_format_changed |
输出格式或 schema 变了 | 结构没变时保持一致 |
verbosity_changed |
回答详略的设置变了 | 保持一致 |
service_tier_changed |
处理请求的服务档位变了 | 保持一致;注意返回的 service_tier 可能和请求的不同 |
prompt_cache_key_changed |
传入的 key 变了 | 不需要按客户分开统计缓存时,不传这个参数 |
用的时候注意几点:
- 诊断只报告第一个识别出的原因。修掉之后要再比较一次,看还有没有别的。
- 诊断记录过一段时间就过期,过期后会返回
comparison_response_not_found。 cache_hit的意思是这次比较没发现未命中,新增的输入仍然要处理。实际复用了多少,看cached_tokens。- 诊断本身不额外收费,不单独占速率限制,也不会让请求失败。为了做对比而多发的请求照常计费。
- 有些变化是有意的,比如换模型或压缩对话,可以选择保留。
改完之后的确认步骤:再发一个有代表性的请求和基准比较,然后在若干个请求上对比 cached_tokens、cache_write_tokens 和总成本。
从旧模型迁移时要留意
| 行为 | GPT-5.6 及之后 | 更早的模型 |
|---|---|---|
| 隐式断点的位置 | 最新一条符合条件的消息末尾 | 按固定的 token 间隔 |
| 显式断点 | 支持 | 不支持 |
| 最小可缓存长度 | 1,024 个可见输入 token | 随请求设置变化 |
| 缓存写入费 | 1.25 倍 | 不另外收 |
prompt_cache_key |
可选,只用于分开统计 | 用稳定的 key 来改善路由 |
| 有效期 | 至少 30 分钟 | 因模型和设置而异 |
两点提醒:在一个模型上够长的前缀,换一个模型可能就不够了,换模型后要重新确认;旧模型上靠 prompt_cache_key 优化路由的做法,在新模型上不再需要,OpenAI 会自动处理路由。
和 Claude 的提示词缓存对照
两家的缓存都是按前缀匹配,所以经验是相通的:不变的放前面,更新信息时追加消息而不是改提示词,会话中途不要增减工具。Claude Code 团队的做法见本站的 提示词缓存为什么这么重要。
OpenAI 这边多出来的是几个专门的开关:换推理强度有 configuration_update,限制工具有 allowed_tools,未命中有诊断字段可以直接看原因。
说明
- 本文按官方文章和文档整理,本站没有实际调用这些接口测试。
- 文中的费率是相对标准输入价的倍数,具体的美元价格见官方价格页;三个 GPT-6 模型的价格也可以看本站的 Codex 该选哪个模型。
- 9 月 22 日的文章里引用了 GitHub Copilot、Strawberry Browser 等公司的使用效果,那是客户自己的说法,本文没有采用。