当应用在多轮对话、工具调用或固定提示词中反复发送相同的长前缀时,重复输入会持续消耗计算与成本。上下文缓存复用这些前缀的计算结果,隐式缓存对支持的模型自动启用,显式缓存由开发者主动开启并管理生命周期。两者不能同时生效,实际命中以缓存 Token 大于 0 为准,HTTP 200 或配置回显不能单独证明命中。若只调用一次且没有稳定重复前缀,通常不需要缓存。本文覆盖选型、接入、观测、计费与管理。
快速开始#
上下文缓存让支持的模型复用重复输入前缀。你不需要先选择模式,也不需要额外的缓存开关:第一轮请求预热缓存,第二轮保持相同长前缀、只替换末尾问题,对比两轮 usage 就能直接看到缓存收益。下面的示例使用 Responses API 的隐式缓存。
| 输入 | 输出预览(usage) | 说明 |
|---|---|---|
| 首次请求:固定前缀(重复 30 次)+ 第一轮问题 | {"usage": {"input_tokens_details": {"cached_tokens": 0}}} |
首次请求没有可复用缓存输入,全部输入按普通输入单价计费,并写入缓存。 |
| 第二次请求:相同固定前缀 + 第二轮问题 | {"usage": {"input_tokens_details": {"cached_tokens": 1336}}} |
命中前缀按缓存输入单价计费,重复前缀不再计算;相比不使用缓存,可降低模型使用成本,并在前缀较长且稳定命中时提升回复速度。 |
表中数值来自实际运行(doubao-seed-2-1-pro-260915,固定前缀重复 30 次);实际 Token 数取决于输入,速度收益取决于模型、前缀长度和服务负载。
cached_tokens 大于 0 表示本次命中;完整命中判定、零值排查和计费口径见 隐式缓存 > 观测与计费。
最小示例#
以下页签均调用 Responses API。示例将固定前缀重复 30 次以超过默认门槛;实际使用时按对应模型门槛调整,并通过第二轮 usage 判断命中。
PREFIX_CHUNK='You are a literary analysis assistant. Answer concisely and clearly. Analyze the story The Gift of the Magi by O. Henry. Della and Jim each sell a treasured possession to buy a Christmas gift for the other. '
LONG_PREFIX=""
for _ in {1..30}; do
LONG_PREFIX="${LONG_PREFIX}${PREFIX_CHUNK}"
done
echo "First request: warm the cache"
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ARK_API_KEY" \
-d @- <<JSON
{
"model": "doubao-seed-2-1-pro-260915",
"input": "${LONG_PREFIX} User question: summarize the core plot in five bullet points.",
"prompt_cache_key": "shared-workflow-v1"
}
JSON
echo "Second request: reuse the same prefix and observe usage"
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ARK_API_KEY" \
-d @- <<JSON
{
"model": "doubao-seed-2-1-pro-260915",
"input": "${LONG_PREFIX} User question: describe the main characters in three bullet points.",
"prompt_cache_key": "shared-workflow-v1"
}
JSON
import os
from arkruntime import Ark
client = Ark(
api_key=os.environ.get("ARK_API_KEY"),
base_url="https://ark.cn-beijing.volces.com/api/v3",
)
PREFIX_CHUNK = "You are a literary analysis assistant. Answer concisely and clearly. Analyze the story The Gift of the Magi by O. Henry. Della and Jim each sell a treasured possession to buy a Christmas gift for the other. "
LONG_PREFIX = PREFIX_CHUNK * 30
def ask(question: str):
response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
input=f"{LONG_PREFIX} User question: {question}",
prompt_cache_key="shared-workflow-v1",
)
print(response.usage.model_dump_json())
return response
ask("summarize the core plot in five bullet points.")
ask("describe the main characters in three bullet points.")
package main
import (
"context"
"fmt"
"os"
"strings"
"github.com/volcengine/ark-runtime-go/arkruntime"
"github.com/volcengine/ark-runtime-go/arkruntime/model/responses"
)
func main() {
client := arkruntime.NewClientWithApiKey(
os.Getenv("ARK_API_KEY"),
arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
)
ctx := context.Background()
prefixChunk := "You are a literary analysis assistant. Answer concisely and clearly. Analyze the story The Gift of the Magi by O. Henry. Della and Jim each sell a treasured possession to buy a Christmas gift for the other. "
longPrefix := strings.Repeat(prefixChunk, 30)
ask := func(question string) {
resp, err := client.CreateResponses(ctx, &responses.ResponsesRequest{
Model: "doubao-seed-2-1-pro-260915",
Input: responses.NewStringResponsesInput(longPrefix + " User question: " + question),
PromptCacheKey: responses.NewOptString("shared-workflow-v1"),
})
if err != nil {
fmt.Printf("response error: %v\n", err)
return
}
fmt.Println(resp)
}
ask("summarize the core plot in five bullet points.")
ask("describe the main characters in three bullet points.")
}
package com.ark.example;
import com.volcengine.ark.runtime.models.responses.Response;
import com.volcengine.ark.runtime.models.responses.ResponsesInput;
import com.volcengine.ark.runtime.models.responses.ResponsesRequest;
import com.volcengine.ark.runtime.service.ArkService;
class ContextCachePromptCacheKey {
public static void main(String[] args) {
String apiKey = System.getenv("ARK_API_KEY");
ArkService arkService = ArkService.builder()
.apiKey(apiKey)
.baseUrl("https://ark.cn-beijing.volces.com/api/v3")
.build();
String prefixChunk = "You are a literary analysis assistant. Answer concisely and clearly. Analyze the story The Gift of the Magi by O. Henry. Della and Jim each sell a treasured possession to buy a Christmas gift for the other. ";
String longPrefix = prefixChunk.repeat(30);
Response first = arkService.createResponse(ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.input(ResponsesInput.ofString(longPrefix + " User question: summarize the core plot in five bullet points."))
.promptCacheKey("shared-workflow-v1")
.build());
System.out.println(first);
Response second = arkService.createResponse(ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.input(ResponsesInput.ofString(longPrefix + " User question: describe the main characters in three bullet points."))
.promptCacheKey("shared-workflow-v1")
.build());
System.out.println(second);
arkService.shutdownExecutor();
}
}
选择缓存模式#
隐式缓存与显式缓存的核心区别在于是否需要主动控制。隐式缓存由系统自动启用,无需额外配置,也不产生缓存存储费用;显式缓存需要主动开启,通过响应 ID 续接或固定前缀复用,并按缓存存储时长计费。需要主动控制复用方式、生命周期或删除时机时选择显式缓存;只需要降低重复前缀成本时优先使用隐式缓存。
决策流程图#
先判断是否需要主动控制缓存,再结合模型支持范围和是否存在稳定重复前缀确定路径。隐式缓存即使没有稳定前缀也会自动启用,但命中概率不稳定,无法保证命中率。
模式对比#
| 对比项 | 隐式缓存 | 显式缓存 |
|---|---|---|
| 适用 API | Chat、Responses、Messages、Batch(以模型列表为准) | Responses |
| 启用与复用方式 | 对支持模型自动启用,无需缓存字段;Chat 与 Responses 可选路由标识,Messages 通过 metadata.user_id.session_id 传递会话标识优化亲和 |
每次请求主动开启;Session 用响应 ID 逐轮续接,Prefix 创建固定前缀后由多个请求复用 |
| 适用场景 | 多轮对话、工具调用、固定长系统提示词 | 需要主动控制复用方式、生命周期或删除时机的场景 |
| 管理与计费 | 无需管理缓存生命周期;当前不单独收取缓存存储费 | 可设置过期时间,新增缓存 Token 按自然小时账期收取存储费 |
隐式缓存#
隐式缓存对支持的模型自动开启,无法关闭。发起普通请求即可使用,不需要缓存开关或特殊配置;prompt_cache_key 只承担可选的路由亲和优化,省略后隐式缓存仍然生效。请求成功只表示调用成功,不等于命中缓存。
支持模型与适用场景#
隐式缓存的具体模型范围以 模型列表 的“上下文缓存能力”章节和“隐式缓存”列为准。
| API | 自动启用 | 可选优化 | 命中观测 | 边界 |
|---|---|---|---|---|
| Chat API | 对支持模型自动启用,不可关闭 | prompt_cache_key 为可选字符串,默认未设置;省略后仍启用隐式缓存,只是不使用该标识的路由亲和优化 |
usage.prompt_tokens_details.cached_tokens |
缓存 Token 大于 0 表示本次命中;等于 0 表示本次未命中,不代表能力关闭 |
| Responses API | 对支持模型自动启用,不可关闭 | prompt_cache_key 为可选字符串,默认未设置;省略后仍启用隐式缓存 |
usage.input_tokens_details.cached_tokens |
启用显式缓存时,不要再按隐式缓存解释同一响应 |
| Messages API | 对支持模型自动启用 | 支持通过 metadata.user_id.session_id 传递会话标识,实现与 prompt_cache_key 类似的缓存亲和;没有直接 prompt_cache_key 入参,metadata 中的 hidden 字段不是公共配置 |
usage.cache_read_input_tokens |
usage.cache_creation_input_tokens 当前返回 0;该字段表示隐式缓存读取量,不表示存在可配置的显式缓存入口 |
| Batch API | 对支持模型自动使用 | 当前公开 Batch Chat 请求不要求额外缓存字段 | usage.prompt_tokens_details.cached_tokens 当前固定返回 0 |
固定 0 是当前 API 的观测边界,不是缓存未命中信号 |
具体 Model ID 和缓存长度门槛见 使用方式 > 缓存长度门槛。
使用方式#
隐式缓存不需要开启步骤,发起普通请求即可。
缓存长度门槛#
- 缓存创建:输入达到对应模型的门槛后,系统会自动缓存本次请求中可复用的前缀片段,不需要用户显式声明公共前缀。缓存创建后,后续请求的输入会自动与已有缓存匹配。
- 缓存命中:当后续请求的输入开头与已有缓存存在相同前缀时,系统会尝试复用对应的缓存结果,从而减少重复计算。
- 不保证命中:缓存容量、淘汰策略、分布式路由、前缀稳定性和模型门槛都会影响实际命中,系统无法保证每次命中。
- 不保证最长前缀命中:系统会综合考虑可用资源与命中收益,命中长度可能不是全局最长命中的那一段。
输入达到对应模型的最小 Token 数后才会写入缓存,后续相同前缀才可能命中;低于门槛时能力仍然启用,只是本次不写入。不同模型的门槛如下:
- 128 Tokens:
deepseek-v4-1-flash-261017、deepseek-v4-1-flash-economy-261017。 - 256 Tokens:
deepseek-v4-1-flash-260910、kimi-k3-1-pro-261024。 - 默认门槛:至少 1024 Tokens。
- 2048 Tokens:
doubao-seed-2-1-turbo-260628、glm-5-2-260617、deepseek-v4-pro-ga-260813、deepseek-v4-flash-ga-260731、deepseek-v4-pro-260425。 - 8960 Tokens:
glm-5-3-flash-260828。
优化缓存命中#
建议按以下顺序使用:
- 准备稳定前缀:将稳定角色设定、背景知识、工具定义或长文本放在请求前部,将动态问题放在后部,并保持工具定义、工具顺序和结构化输出方案稳定。
- 发起请求:使用支持隐式缓存的模型。Chat、Responses 和 Messages 都会自动启用隐式缓存;Chat 和 Responses 可选设置
prompt_cache_key增强相同前缀请求的路由亲和,Messages 支持通过metadata.user_id.session_id传递会话标识实现缓存亲和,没有直接prompt_cache_key入参。Batch 对支持模型自动使用隐式缓存,但cached_tokens固定为 0,不能据此计算命中率。 - 验证命中:使用相同公共前缀发起有限次重复请求;完整判定步骤见 观测与计费。
prompt_cache_key 配置#
prompt_cache_key 用于提升相同前缀请求的路由亲和,不改变缓存是否启用。快速开始的 Chat 与 Responses 示例已经包含该参数,可按以下约束设置:
- 会话隔离:上下文连续的请求使用同一取值;不同用户或并行会话使用不同取值。
- 流量控制:同一取值的总流量建议控制在每分钟 15 个请求以内;超过后部分请求可能回退到常规路由。
- 敏感信息:使用不可读的内部会话 ID,不传入手机号、邮箱、访问凭证或提示词原文。
- 重路由边界:服务扩缩容、发布、故障切换、映射过期或保护策略触发时可能重新路由,无法保证每次命中。
观测与计费#
先按 优化缓存命中 准备稳定前缀,再按以下顺序验证:
- 首次请求:使用相同前缀完成首次请求,使输入达到对应模型门槛。
- 重复请求:再次发送相同前缀的请求,读取 Chat 的
usage.prompt_tokens_details.cached_tokens、Responses 的usage.input_tokens_details.cached_tokens或 Messages 的usage.cache_read_input_tokens。 - 判断命中:缓存 Token 大于 0 表示本次命中;等于 0 时检查输入门槛、前缀稳定性和路由条件,再发起有界验证。
- 排除间接信号:不把 HTTP 200、响应 ID 或单次延迟变化当作命中证据;命中影响因素见 缓存长度门槛。
一次请求的费用构成:
普通输入费用 = (input_tokens - cached_tokens) × 输入单价
缓存输入费用 = cached_tokens × 缓存输入单价
输出费用 = output_tokens × 输出单价
请求费用 = 普通输入费用 + 缓存输入费用 + 输出费用
隐式缓存当前不单独收取缓存存储费。输入单价、缓存输入单价和输出单价以 模型价格 为准。
显式缓存#
显式缓存仅通过 Responses API 使用。使用前需要 开通推理(缓存)定价。使用或继续写入显式缓存时,每次请求都必须设置 caching.type=enabled;store 默认为 true,但响应存储不等于开启显式缓存。显式缓存与隐式缓存不能同时生效。
Session 缓存接入#
Session 缓存按轮次续接,适合多轮对话和连续工具调用:
- 首轮开启:首轮设置
caching.type=enabled,保持store实际为true,并保存响应返回的id。 - 下一轮续接:下一轮设置
caching.type=enabled,并通过previous_response_id传入紧邻上一轮的响应id。 - 连续轮次:后续每轮重复第 2 步;连续多轮请求之间建议加入约 100 毫秒延迟,间隔过短可能导致调用失败。
- 前置轮缺失:任一前置轮未写入缓存时,后续轮次无法补写该轮上下文;此时从仍有效的响应或新请求重新建立 Session 链。
Session 缓存原理#
- 未使用缓存:未使用 Session 缓存时,初始信息、前 N 轮对话和第 N+1 轮问题都需要重新计算并生成 Tokens。
- 使用缓存:使用 Session 缓存时,初始信息和前 N 轮对话以缓存 Tokens 直接复用;第 N+1 轮问题单独计算。
- 写入下一轮:模型基于缓存 Tokens 与本轮输入生成第 N+1 轮回复,并将本轮内容加入 Session 缓存,供下一轮继续复用。
代码示例#
以下示例展示首轮创建、续接和连续轮次的完整调用。
步骤 1:创建首轮响应并保存响应 ID。
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seed-2-1-pro-260915",
"input":[
{
"role":"system",
"content":"你是一名文学分析助手,回答需简洁明了,请根据下面内容分析《麦琪的礼物》相关问题。<麦琪的礼物小说内容>"
},
{
"role": "user",
"content":"用5个简短的要点总结核心情节。"
}
],
"caching":{
"type":"enabled"
},
"thinking": {
"type": "disabled"
}
}'
步骤 2:使用上一轮响应 ID 续接。
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seed-2-1-pro-260915",
"input": "以 Della 的视角写一篇日记,描述其卖掉长发前的心情。",
"caching":{
"type":"enabled"
},
"thinking": {
"type": "disabled"
},
"previous_response_id": "<THE_ID_FROM_FIRST_CALL>"
}'
步骤 3:继续使用上一轮响应 ID 续接。
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seed-2-1-pro-260915",
"input": "根据原文节选和 Della 刚写的日记,想象 Jame 读到这篇日记时会有怎样的感受。",
"caching":{
"type":"enabled"
},
"thinking": {
"type": "disabled"
},
"previous_response_id": "<THE_ID_FROM_SECOND_CALL>"
}'
# encoding=utf-8
import os
from arkruntime import Ark
client = Ark(
base_url='https://ark.cn-beijing.volces.com/api/v3',
api_key=os.getenv('ARK_API_KEY'),
)
input_text = "你是一名文学分析助手,回答需简洁明了,请根据下面内容分析《麦琪的礼物》相关问题。<麦琪的礼物小说内容>"
response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
input=[
{
"role": "system",
"content": input_text
},
{
"role": "user",
"content":"用5个简短的要点总结核心情节。"
}
],
caching={"type": "enabled"},
thinking={"type": "disabled"},
)
print(response)
print(response.usage.model_dump_json())
# 在后续请求中输入缓存信息
second_response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
previous_response_id=response.id,
input=[{"role": "user", "content": "以 Della 的视角写一篇日记,描述其卖掉长发前的心情。"}],
caching={"type": "enabled"},
thinking={"type": "disabled"},
)
print(second_response)
print(second_response.usage.model_dump_json())
third_response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
previous_response_id=second_response.id,
input=[{"role": "user", "content": "根据原文节选和 Della 刚写的日记,想象 Jame 读到这篇日记时会有怎样的感受。"}],
caching={"type": "enabled"},
thinking={"type": "disabled"},
)
print(third_response)
print(third_response.usage.model_dump_json())
package main
import (
"context"
"fmt"
"os"
"github.com/volcengine/ark-runtime-go/arkruntime"
"github.com/volcengine/ark-runtime-go/arkruntime/model/responses"
)
func main() {
client := arkruntime.NewClientWithApiKey(
// Get API Key: https://ark.volcengine.com/region:cn-beijing/apikey
os.Getenv("ARK_API_KEY"),
arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
)
ctx := context.Background()
input := "你是一名文学分析助手,回答需简洁明了,请根据下面内容分析《麦琪的礼物》相关问题。<麦琪的礼物小说内容>"
resp, err := client.CreateResponses(ctx, &responses.ResponsesRequest{
Model: "doubao-seed-2-1-pro-260915",
Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
easyMessage(responses.MessageRoleSystem, input),
easyMessage(responses.MessageRoleUser, "用5个简短的要点总结核心情节。"),
}),
Caching: responses.NewOptResponseCaching(responses.ResponseCaching{Type: responses.NewOptCacheMode(responses.CacheModeEnabled)}),
Thinking: responses.NewOptThinking(responses.Thinking{Type: responses.NewOptThinkingMode(responses.ThinkingModeDisabled)}),
})
if err != nil {
fmt.Printf("response error: %v", err)
return
}
fmt.Println(resp)
fmt.Println(resp.GetUsage())
second_resp, second_err := client.CreateResponses(ctx, &responses.ResponsesRequest{
Model: "doubao-seed-2-1-pro-260915",
PreviousResponseID: responses.NewOptString(resp.ID),
Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
easyMessage(responses.MessageRoleUser, "以 Della 的视角写一篇日记,描述其卖掉长发前的心情。"),
}),
Caching: responses.NewOptResponseCaching(responses.ResponseCaching{Type: responses.NewOptCacheMode(responses.CacheModeEnabled)}),
Thinking: responses.NewOptThinking(responses.Thinking{Type: responses.NewOptThinkingMode(responses.ThinkingModeDisabled)}),
})
if second_err != nil {
fmt.Printf("second response error: %v", second_err)
return
}
fmt.Println(second_resp)
fmt.Println(second_resp.GetUsage())
third_resp, third_err := client.CreateResponses(ctx, &responses.ResponsesRequest{
Model: "doubao-seed-2-1-pro-260915",
PreviousResponseID: responses.NewOptString(second_resp.ID),
Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
easyMessage(responses.MessageRoleUser, "根据原文节选和 Della 刚写的日记,想象 Jame 读到这篇日记时会有怎样的感受。"),
}),
Caching: responses.NewOptResponseCaching(responses.ResponseCaching{Type: responses.NewOptCacheMode(responses.CacheModeEnabled)}),
Thinking: responses.NewOptThinking(responses.Thinking{Type: responses.NewOptThinkingMode(responses.ThinkingModeDisabled)}),
})
if third_err != nil {
fmt.Printf("third response error: %v", third_err)
return
}
fmt.Println(third_resp)
fmt.Println(third_resp.GetUsage())
}
func easyMessage(role responses.MessageRole, content string) responses.InputItem {
return responses.InputItem{
OneOf: responses.NewItemEasyMessageInputItemSum(
responses.ItemEasyMessage{
Role: responses.NewOptMessageRole(role),
Content: responses.NewStringMessageContent(content),
},
),
}
}
package com.ark.sample;
import com.volcengine.ark.runtime.service.ArkService;
import com.volcengine.ark.runtime.models.responses.*;
public class demo {
public static void main(String[] args) {
String apiKey = System.getenv("ARK_API_KEY");
// The base URL for model invocation
ArkService arkService = ArkService.builder().apiKey(apiKey).baseUrl("https://ark.cn-beijing.volces.com/api/v3").build();
String input = "你是一名文学分析助手,回答需简洁明了,请根据下面内容分析《麦琪的礼物》相关问题。<麦琪的礼物小说内容>";
ResponsesRequest request = ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.input(ResponsesInput.ofList(java.util.Arrays.asList(ItemEasyMessage.builder().role(MessageRole.SYSTEM).content(
MessageContent.ofString(input)
).build(), ItemEasyMessage.builder().role(MessageRole.USER).content(
MessageContent.ofString("用5个简短的要点总结核心情节。")
).build())))
.caching(ResponseCaching.builder().type(CacheMode.ENABLED).build())
.thinking(Thinking.builder().type(ThinkingMode.DISABLED).build())
.build();
Response resp = arkService.createResponse(request);
System.out.println(resp);
System.out.println(resp.getUsage());
System.out.println("---------------------");
ResponsesRequest request2 = ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.previousResponseId(resp.getId())
.input(ResponsesInput.ofList(java.util.Arrays.asList(ItemEasyMessage.builder().role(MessageRole.USER).content(
MessageContent.ofString("以Della的视角写一篇日记,描述其卖掉长发前的心情。")
).build())))
.caching(ResponseCaching.builder().type(CacheMode.ENABLED).build())
.thinking(Thinking.builder().type(ThinkingMode.DISABLED).build())
.build();
Response resp2 = arkService.createResponse(request2);
System.out.println(resp2.getOutput());
System.out.println(resp2.getUsage());
System.out.println("---------------------");
ResponsesRequest request3 = ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.previousResponseId(resp2.getId())
.input(ResponsesInput.ofList(java.util.Arrays.asList(ItemEasyMessage.builder().role(MessageRole.USER).content(
MessageContent.ofString("根据原文节选和 Della 刚写的日记,想象 Jame 读到这篇日记时会有怎样的感受。")
).build())))
.caching(ResponseCaching.builder().type(CacheMode.ENABLED).build())
.thinking(Thinking.builder().type(ThinkingMode.DISABLED).build())
.build();
Response resp3 = arkService.createResponse(request3);
System.out.println(resp3.getOutput());
System.out.println(resp3.getUsage());
arkService.shutdownExecutor();
}
}
import os
from openai import OpenAI
client = OpenAI(
base_url='https://ark.cn-beijing.volces.com/api/v3',
api_key=os.getenv('ARK_API_KEY'),
)
input_text = "你是一名文学分析助手,回答需简洁明了,请根据下面内容分析《麦琪的礼物》相关问题。<麦琪的礼物小说内容>"
response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
input=[
{
"role": "system",
"content": input_text
},
{
"role": "user",
"content":"用5个简短的要点总结核心情节。"
}
],
extra_body={
"caching": {"type": "enabled"},
"thinking":{"type":"disabled"}
}
)
print(response)
second_response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
previous_response_id=response.id,
input=[{"role": "user", "content": "以 Della 的视角写一篇日记,描述其卖掉长发前的心情。"}],
extra_body={
"caching": {"type": "enabled"},
"thinking":{"type":"disabled"}
}
)
print(second_response)
third_response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
previous_response_id=second_response.id,
input=[{"role": "user", "content": "根据原文节选和 Della 刚写的日记,想象 Jame 读到这篇日记时会有怎样的感受。"}],
extra_body={
"caching": {"type": "enabled"},
"thinking":{"type":"disabled"}
}
)
print(third_response)
Prefix 缓存接入#
Prefix 缓存将固定长前缀写入缓存,再由多个请求复用,适合固定知识、固定角色设定或固定工具定义。以下页签均调用 Responses API,固定前缀至少 256 Tokens:
- 创建固定前缀:准备至少 256 Tokens 的固定前缀;创建请求设置
caching.type=enabled和caching.prefix=true,且不能设置stream=true。 - 保存响应 ID:创建请求只写入前缀、不生成模型输出,
output_tokens为 0;保存创建响应返回的id。 - 后续复用:多个后续请求设置
caching.type=enabled,并使用同一个previous_response_id引用创建响应 ID;固定前缀本身不随每轮更新。连续多轮请求之间建议加入约 100 毫秒延迟。 - 创建失败:未保存或传错创建响应 ID 时无法复用该前缀缓存;创建失败时先检查 Token 门槛、
stream、store、caching和组合限制。
Prefix 缓存原理#
- 未使用缓存:未使用 Prefix 缓存时,初始信息、前 N 轮历史和第 N+1 轮输入都参与计算。
- 使用缓存:使用 Prefix 缓存时,初始信息作为固定前缀直接复用缓存 Tokens,不再重复计算;前 N 轮历史和第 N+1 轮输入继续计算。
- 生成回复:模型基于固定前缀缓存、历史 Tokens 和本轮输入生成回复;固定前缀本身不随轮次更新。
代码示例#
以下示例展示创建固定前缀、首次复用和再次复用的完整调用。
步骤 1:创建固定前缀缓存。
PREFIX_CHUNK='《麦琪的礼物》讲述德拉与吉姆在拮据生活中各自卖掉珍贵物品,为对方准备圣诞礼物的故事。'
LONG_PREFIX=""
for _ in {1..20}; do
LONG_PREFIX="${LONG_PREFIX}${PREFIX_CHUNK}"
done
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json;charset=utf-8" \
-d @- <<EOF
{
"model": "doubao-seed-2-1-pro-260915",
"input":[
{
"role":"system",
"content":"你是一名文学分析助手,请根据以下固定前缀回答问题:${LONG_PREFIX}"
}
],
"caching":{
"type":"enabled",
"prefix": true
},
"thinking": {
"type": "disabled"
}
}
EOF
步骤 2:首次复用该前缀缓存。
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seed-2-1-pro-260915",
"input": "以 Della 的视角写一篇日记,描述其卖掉长发前的心境。",
"caching":{
"type":"enabled"
},
"thinking": {
"type": "disabled"
},
"previous_response_id":"<THE_ID_FROM_FIRST_CALL>"
}'
步骤 3:再次复用同一前缀缓存。
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seed-2-1-pro-260915",
"input": "分析 O. Henry 在该故事片段中反讽手法的运用,给出简明阐释。",
"caching":{
"type":"enabled"
},
"thinking": {
"type": "disabled"
},
"previous_response_id":"<THE_ID_FROM_FIRST_CALL>"
}'
# coding=utf-8
import os
from arkruntime import Ark
client = Ark(
base_url='https://ark.cn-beijing.volces.com/api/v3',
api_key=os.getenv('ARK_API_KEY'),
)
prefix_chunk = "《麦琪的礼物》讲述德拉与吉姆在拮据生活中各自卖掉珍贵物品,为对方准备圣诞礼物的故事。"
# Repeat stable content so the prefix is safely above the 256-token minimum.
long_prefix = prefix_chunk * 20
response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
input=[
{
"role": "system",
"content": long_prefix,
}
],
caching={"type": "enabled", "prefix": True},
thinking={"type": "disabled"},
)
print(response)
second_response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
previous_response_id=response.id,
input=[{"role": "user", "content": "以 Della 的视角写一篇日记,描述其卖掉长发前的心情。"}],
caching={"type": "enabled"},
thinking={"type": "disabled"},
)
print(second_response)
third_response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
previous_response_id=response.id,
input=[{"role": "user", "content": "分析 O. Henry 在该故事片段中反讽手法的运用,给出简明阐释。"}],
caching={"type": "enabled"},
thinking={"type": "disabled"},
)
print(third_response)
package main
import (
"context"
"fmt"
"os"
"strings"
"github.com/volcengine/ark-runtime-go/arkruntime"
"github.com/volcengine/ark-runtime-go/arkruntime/model/responses"
)
func main() {
client := arkruntime.NewClientWithApiKey(
// Get API Key: https://ark.volcengine.com/region:cn-beijing/apikey
os.Getenv("ARK_API_KEY"),
arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
)
ctx := context.Background()
prefix := true
prefixChunk := "《麦琪的礼物》讲述德拉与吉姆在拮据生活中各自卖掉珍贵物品,为对方准备圣诞礼物的故事。"
// Repeat stable content so the prefix is safely above the 256-token minimum.
longPrefix := strings.Repeat(prefixChunk, 20)
resp, err := client.CreateResponses(ctx, &responses.ResponsesRequest{
Model: "doubao-seed-2-1-pro-260915",
Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
easyMessage(responses.MessageRoleSystem, longPrefix),
}),
Caching: responses.NewOptResponseCaching(responses.ResponseCaching{Type: responses.NewOptCacheMode(responses.CacheModeEnabled), Prefix: responses.NewOptBool(prefix)}),
Thinking: responses.NewOptThinking(responses.Thinking{Type: responses.NewOptThinkingMode(responses.ThinkingModeDisabled)}),
})
if err != nil {
fmt.Printf("response error: %v", err)
return
}
fmt.Println(resp)
second_resp, second_err := client.CreateResponses(ctx, &responses.ResponsesRequest{
Model: "doubao-seed-2-1-pro-260915",
PreviousResponseID: responses.NewOptString(resp.ID),
Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
easyMessage(responses.MessageRoleUser, "以 Della 的视角写一篇日记,描述其卖掉长发前的心情。"),
}),
Caching: responses.NewOptResponseCaching(responses.ResponseCaching{Type: responses.NewOptCacheMode(responses.CacheModeEnabled)}),
Thinking: responses.NewOptThinking(responses.Thinking{Type: responses.NewOptThinkingMode(responses.ThinkingModeDisabled)}),
})
if second_err != nil {
fmt.Printf("second response error: %v", second_err)
return
}
fmt.Println(second_resp)
third_resp, third_err := client.CreateResponses(ctx, &responses.ResponsesRequest{
Model: "doubao-seed-2-1-pro-260915",
PreviousResponseID: responses.NewOptString(resp.ID),
Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
easyMessage(responses.MessageRoleUser, "分析 O. Henry 在该故事片段中反讽手法的运用,给出简明阐释。"),
}),
Caching: responses.NewOptResponseCaching(responses.ResponseCaching{Type: responses.NewOptCacheMode(responses.CacheModeEnabled)}),
Thinking: responses.NewOptThinking(responses.Thinking{Type: responses.NewOptThinkingMode(responses.ThinkingModeDisabled)}),
})
if third_err != nil {
fmt.Printf("second response error: %v", third_err)
return
}
fmt.Println(third_resp)
}
func easyMessage(role responses.MessageRole, content string) responses.InputItem {
return responses.InputItem{
OneOf: responses.NewItemEasyMessageInputItemSum(
responses.ItemEasyMessage{
Role: responses.NewOptMessageRole(role),
Content: responses.NewStringMessageContent(content),
},
),
}
}
package com.ark.sample;
import com.volcengine.ark.runtime.service.ArkService;
import com.volcengine.ark.runtime.models.responses.*;
public class demo {
public static void main(String[] args) {
String apiKey = System.getenv("ARK_API_KEY");
// The base URL for model invocation
ArkService arkService = ArkService.builder().apiKey(apiKey).baseUrl("https://ark.cn-beijing.volces.com/api/v3").build();
String prefixChunk = "《麦琪的礼物》讲述德拉与吉姆在拮据生活中各自卖掉珍贵物品,为对方准备圣诞礼物的故事。";
// Repeat stable content so the prefix is safely above the 256-token minimum.
String longPrefix = String.join("", java.util.Collections.nCopies(20, prefixChunk));
ResponsesRequest request = ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.input(ResponsesInput.ofList(java.util.Arrays.asList(ItemEasyMessage.builder().role(MessageRole.SYSTEM).content(
MessageContent.ofString(longPrefix)
).build())))
.caching(ResponseCaching.builder().type(CacheMode.ENABLED).prefix(true).build())
.thinking(Thinking.builder().type(ThinkingMode.DISABLED).build())
.build();
Response resp = arkService.createResponse(request);
System.out.println(resp);
System.out.println("---------------------");
ResponsesRequest request2 = ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.previousResponseId(resp.getId())
.input(ResponsesInput.ofList(java.util.Arrays.asList(ItemEasyMessage.builder().role(MessageRole.USER).content(
MessageContent.ofString("以 Della 的视角写一篇日记,描述其卖掉长发前的心情。")
).build())))
.caching(ResponseCaching.builder().type(CacheMode.ENABLED).build())
.thinking(Thinking.builder().type(ThinkingMode.DISABLED).build())
.build();
Response resp2 = arkService.createResponse(request2);
System.out.println(resp2);
System.out.println("---------------------");
ResponsesRequest request3 = ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.previousResponseId(resp.getId())
.input(ResponsesInput.ofList(java.util.Arrays.asList(ItemEasyMessage.builder().role(MessageRole.USER).content(
MessageContent.ofString("分析 O. Henry 在该故事片段中反讽手法的运用,给出简明阐释。")
).build())))
.caching(ResponseCaching.builder().type(CacheMode.ENABLED).build())
.thinking(Thinking.builder().type(ThinkingMode.DISABLED).build())
.build();
Response resp3 = arkService.createResponse(request3);
System.out.println(resp3);
arkService.shutdownExecutor();
}
}
# coding=utf-8
import os
from openai import OpenAI
client = OpenAI(
base_url='https://ark.cn-beijing.volces.com/api/v3',
api_key=os.getenv('ARK_API_KEY'),
)
prefix_chunk = "《麦琪的礼物》讲述德拉与吉姆在拮据生活中各自卖掉珍贵物品,为对方准备圣诞礼物的故事。"
# Repeat stable content so the prefix is safely above the 256-token minimum.
long_prefix = prefix_chunk * 20
response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
input=[
{
"role": "system",
"content": long_prefix,
}
],
extra_body={
"caching": {"type": "enabled", "prefix": True},
"thinking":{"type":"disabled"}
}
)
print(response)
second_response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
previous_response_id=response.id,
input=[{"role": "user", "content": "以 Della 的视角写一篇日记,描述其卖掉长发前的心境。"}],
extra_body={
"caching": {"type": "enabled"},
"thinking":{"type":"disabled"}
}
)
print(second_response)
third_response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
previous_response_id=response.id,
input=[{"role": "user", "content": "分析 O. Henry 在该故事片段中反讽手法的运用,给出简明阐释。"}],
extra_body={
"caching": {"type": "enabled"},
"thinking":{"type":"disabled"}
}
)
print(third_response)
管理与清理#
设置生命周期#
expire_at 是可选 UTC Unix 秒时间戳,对响应存储和显式缓存同时生效。使用缓存不会重置生命周期;默认值和取值范围见 支持模型与使用限制 的“自定义过期”行。
EXPIRE_AT=$(($(date +%s) + 3600))
curl https://ark.cn-beijing.volces.com/api/v3/responses \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<JSON
{
"model": "doubao-seed-2-1-pro-260915",
"input": [
{
"role": "system",
"content": "Hello"
}
],
"expire_at": ${EXPIRE_AT},
"caching": {
"type": "enabled"
},
"thinking": {
"type": "disabled"
}
}
JSON
import os
from arkruntime import Ark
import time
# Get API Key: https://ark.volcengine.com/region:cn-beijing/apikey
api_key = os.getenv('ARK_API_KEY')
client = Ark(
base_url='https://ark.cn-beijing.volces.com/api/v3',
api_key=api_key,
)
response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
input=[
{
"role": "system",
"content": "Hello"
}
],
caching={"type": "enabled"},
thinking={"type": "disabled"},
expire_at=int(time.time()) + 3600,
)
print(response.model_dump_json())
package main
import (
"context"
"fmt"
"os"
"time"
"github.com/volcengine/ark-runtime-go/arkruntime"
"github.com/volcengine/ark-runtime-go/arkruntime/model/responses"
)
func main() {
client := arkruntime.NewClientWithApiKey(
os.Getenv("ARK_API_KEY"),
arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
)
ctx := context.Background()
resp, err := client.CreateResponses(ctx, &responses.ResponsesRequest{
Model: "doubao-seed-2-1-pro-260915",
Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
easyMessage(responses.MessageRoleSystem, "Hello"),
}),
Caching: responses.NewOptResponseCaching(responses.ResponseCaching{Type: responses.NewOptCacheMode(responses.CacheModeEnabled)}),
Thinking: responses.NewOptThinking(responses.Thinking{Type: responses.NewOptThinkingMode(responses.ThinkingModeDisabled)}),
ExpireAt: responses.NewOptInt64(time.Now().Unix() + 3600),
})
if err != nil {
fmt.Printf("response error: %v", err)
return
}
fmt.Println(resp)
fmt.Println(resp.GetUsage())
}
func easyMessage(role responses.MessageRole, content string) responses.InputItem {
return responses.InputItem{
OneOf: responses.NewItemEasyMessageInputItemSum(
responses.ItemEasyMessage{
Role: responses.NewOptMessageRole(role),
Content: responses.NewStringMessageContent(content),
},
),
}
}
package com.ark.sample;
import com.volcengine.ark.runtime.service.ArkService;
import com.volcengine.ark.runtime.models.responses.*;
import java.time.Instant;
public class demo {
public static void main(String[] args) {
String apiKey = System.getenv("ARK_API_KEY");
// The base URL for model invocation
ArkService arkService = ArkService.builder().apiKey(apiKey).baseUrl("https://ark.cn-beijing.volces.com/api/v3").build();
ResponsesRequest request = ResponsesRequest.builder()
.model("doubao-seed-2-1-pro-260915")
.input(ResponsesInput.ofList(java.util.Arrays.asList(ItemEasyMessage.builder().role(MessageRole.SYSTEM).content(
MessageContent.ofString("Hello")
).build())))
.caching(ResponseCaching.builder().type(CacheMode.ENABLED).build())
.thinking(Thinking.builder().type(ThinkingMode.DISABLED).build())
.expireAt(Instant.now().getEpochSecond() + 3600)
.build();
Response resp = arkService.createResponse(request);
System.out.println(resp);
System.out.println(resp.getUsage());
arkService.shutdownExecutor();
}
}
import os
from openai import OpenAI
import time
# Get API Key: https://ark.volcengine.com/region:cn-beijing/apikey
api_key = os.getenv('ARK_API_KEY')
client = OpenAI(
base_url='https://ark.cn-beijing.volces.com/api/v3',
api_key=api_key,
)
response = client.responses.create(
model="doubao-seed-2-1-pro-260915",
input=[
{
"role": "system",
"content": "Hello"
}
],
extra_body={
"thinking":{"type":"disabled"},
"caching":{"type":"enabled"},
"expire_at": int(time.time()) + 3600 # The expiration time for storage and cache is 1 hour from the current time.
}
)
print(response.model_dump_json())
示例保持 store 默认 true;创建后核对响应返回的 expire_at。参数超出范围时修正时间戳后重试;缓存到期后停止引用旧响应 ID,并创建新的缓存链路。
删除缓存#
调用 DELETE /responses/{responseId} 删除指定响应及其缓存资源。删除结果通过返回的 id、object=response 和 deleted 判断;deleted=true 表示删除成功。
curl "https://ark.cn-beijing.volces.com/api/v3/responses/<RESPONSE_ID>" \
-X DELETE \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json"
import os
from arkruntime import Ark
api_key = os.getenv('ARK_API_KEY')
client = Ark(
base_url='https://ark.cn-beijing.volces.com/api/v3',
api_key=api_key,
)
response = client.responses.delete("<RESPONSE_ID>")
print(response)
package main
import (
"context"
"fmt"
"os"
"github.com/volcengine/ark-runtime-go/arkruntime"
"github.com/volcengine/ark-runtime-go/arkruntime/model/responses"
)
func main() {
client := arkruntime.NewClientWithApiKey(
os.Getenv("ARK_API_KEY"),
arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
)
ctx := context.Background()
resp := client.DeleteResponse(ctx, &responses.ResponsesRemoveParams{ResponseId: "<RESPONSE_ID>"})
fmt.Println()
fmt.Println(resp)
}
package com.ark.sample;
import com.volcengine.ark.runtime.service.ArkService;
import com.volcengine.ark.runtime.models.responses.*;
public class demo {
public static void main(String[] args) {
String apiKey = System.getenv("ARK_API_KEY");
// The base URL for model invocation
ArkService arkService = ArkService.builder().apiKey(apiKey)
.baseUrl("https://ark.cn-beijing.volces.com/api/v3")
.build();
DeleteResponseResponse deleteResult = arkService.deleteResponse(
DeleteResponseRequest.builder().responseId("<RESPONSE_ID>").build()
);
System.out.println(deleteResult);
arkService.shutdownExecutor();
}
}
from openai import OpenAI
import os
api_key = os.getenv('ARK_API_KEY')
client = OpenAI(
base_url='https://ark.cn-beijing.volces.com/api/v3',
api_key=api_key,
)
response = client.responses.delete("<RESPONSE_ID>")
print(response)
下图展示删除第 3 轮缓存后,第 4、5、6 轮信息如何重新计算并重新缓存。
删除后核对返回的 id、object=response 和 deleted,并在后续请求核对 cached_tokens 与重算输入。删除后的状态变化:
- 删除成功:返回相同
id、object=response和deleted=true,该响应及其缓存资源不再可用。 - 删除中间轮:后续轮次在下一次请求重新计算并写入新缓存,重算内容按普通输入计费。
- 删除失败:
deleted=false或请求报错时,先核对responseId和错误信息,不要直接重建链路。
观测与计费#
实际命中以 usage.input_tokens_details.cached_tokens 大于 0 为准;caching.type=enabled 回显只表示本轮配置生效。
跨轮续接时,上一轮回复、思维链和工具内容可能进入后续请求输入,使 input_tokens 增长;可使用 上下文编辑 控制后续携带的思维链与工具内容。
显式缓存的费用由两部分组成:
- 请求费用:普通输入、缓存输入和输出,公式与隐式缓存相同。
- 存储费用:开启显式缓存后,新增缓存 Token 按自然小时账期累加存储费。
存储费用与轮次#
图中橙色表示命中的缓存。第 2 轮复用第 1 轮的系统提示词和问题;第 3 轮继续复用前两轮已缓存内容,并只新增本轮内容。每轮新增的缓存内容才计入当小时存储费用。
缓存存储计费按自然小时累加:
缓存存储费用 = Σ(每个自然小时账期内新增缓存 Token 数 × 存储单价)
缓存有效期跨越多个账期时,每个账期分别计算;不足 1 小时的账期按 1 小时计。例如缓存于 10:57 创建、12:27 过期,会计入 10:00-11:00、11:00-12:00 和 12:00-13:00 三个账期。存储单价和缓存输入单价以 模型价格 为准。
支持模型与使用限制#
显式缓存仅适用于模型列表标注支持显式缓存的 Responses 模型,默认值为 disabled,允许值为 Session 缓存和前缀缓存。下表按任务汇总必要配置、缺省行为、直接验证和关键边界。
| 任务 | 必要配置 | 缺省行为 | 直接验证 | 关键边界 |
|---|---|---|---|---|
| 适用 API | 使用 Responses API;model 传支持显式缓存的 Model ID,或对应模型的推理接入点 ID(Endpoint ID) |
Chat、Messages、Batch 不提供本页的显式缓存接入契约 | 请求返回 Responses 响应 ID | 显式缓存仅适用于 Responses API |
| 开启 Session 缓存 | caching.type=enabled;store 实际为 true |
caching.type 默认 disabled,不开启也不写入显式缓存;store 默认 true,但只表示存储响应 |
响应回显 caching.type=enabled,且 usage.input_tokens_details.cached_tokens 大于 0 |
enabled 是必要条件,不是命中已有缓存的充分条件 |
| 关闭显式缓存 | caching.type=disabled |
当前轮不读取也不写入显式缓存;后续轮次无法补写本轮上下文 | 响应不启用显式缓存 | 默认值即关闭;不表示请求失败 |
| Session 续接 | 每轮 caching.type=enabled;previous_response_id 传紧邻上一轮响应 ID |
开始新的响应链,不引用已创建的显式缓存 | 每轮响应 ID 链完整,cached_tokens 大于 0 |
任一前置轮未写入时无法补写;连续多轮建议间隔约 100 毫秒;Session 缓存没有独立的最少输入 Token 门槛 |
| Prefix 创建 | caching.type=enabled;caching.prefix=true;输入至少 256 Tokens;stream 不能为 true |
caching.prefix 默认 false,正常生成响应并写入 Session 缓存 |
创建响应返回 id,output_tokens 为 0 |
创建成功不代表后续请求自动命中 |
| Prefix 复用 | caching.type=enabled;previous_response_id 传创建响应 ID |
未保存或传错 ID 时无法复用该前缀缓存 | 后续请求 cached_tokens 大于 0 |
固定前缀不随每轮更新;多个请求引用同一创建 ID |
| 自定义过期 | expire_at 为 UTC Unix 秒,晚于创建时刻且不晚于创建时刻加 604800 秒 |
使用默认过期时刻:创建时刻加 259200 秒 | 响应返回实际 expire_at |
expire_at 对 store 与 caching 同时生效;使用缓存不重置生命周期 |
| 组合使用 | 多轮 thinking 与首轮一致;tools 只在首轮设置 |
配置不一致时无法使用或写入缓存 | 请求成功且 cached_tokens 大于 0 |
caching.type=enabled 与 instructions 冲突;前置轮启用缓存时不支持 json_schema,可改用 json_object;后续轮重复设置 tools 会冲突 |
以下组合会失败或不生效:
- 前缀过短:Prefix 创建请求的固定前缀少于 256 Tokens。
- 过期时间越界:
expire_at不晚于创建时刻,或晚于创建时刻加 604800 秒。 - thinking 配置变化:多轮请求中的
thinking必须与首轮保持一致;首轮设置或未设置的字段,后续轮次不要改变。 - 流式创建:Prefix 创建请求设置
stream=true;此时可先以非流式请求创建前缀缓存。 - instructions 冲突:
caching.type=enabled与instructions同时使用。 - json_schema 冲突:已启用缓存的前置轮次使用
json_schema;此时可改用json_object。 - tools 重复设置:后续轮次重复设置
tools。 - 未开通缓存定价:使用显式缓存前未开通推理(缓存)定价,请求会报错。
请求失败通常来自模型或接入点配置错误、未开通推理(缓存)定价,或字段组合不符合上文限制。当前没有已确认会被静默忽略的缓存字段;已知冲突会报错,或表现为 cached_tokens=0。
请求失败时以实际返回的 error.code 和 error.message 为准,错误码说明见 错误码。
工作原理#
客户端负责组织稳定前缀、保存响应 ID 并在后续请求中续接;方舟服务负责写入缓存、按前缀匹配并返回缓存 Token 用量。响应存储只是显式缓存的前提,store=true 不等于开启显式缓存;caching.type=enabled 回显只表示配置生效,实际命中仍以缓存 Token 大于 0 为准。
整体协作#
隐式缓存没有单独的对比原理图;显式缓存的 Session 与 Prefix 详细原理图分别见 Session 缓存接入 和 Prefix 缓存接入。本节只说明共同协作顺序。
阶段说明:
- 组织输入:客户端将稳定内容放在前部,将动态问题放在后部;Session 模式保留响应链,Prefix 模式准备固定前缀。
- 创建或写入:隐式缓存自动处理;显式缓存要求
caching.type=enabled且store实际为true,并返回响应 ID。 - 复用与观测:隐式模式依赖输入中可复用的相同前缀,由系统自动匹配;显式模式在后续请求中保持
caching.type=enabled并传入正确的previous_response_id,再通过缓存 Token 判断命中。 - 终止:响应返回、缓存到期或删除、请求失败时结束当前链路。
命中与失效#
- 命中:缓存 Token 大于 0,表示本次请求复用了部分缓存输入。
- 未命中:缓存 Token 为 0,表示本次没有可计量的缓存读取;不代表缓存能力永久失效。
- 过期或删除:到期后不能继续引用旧响应 ID;删除中间轮后,后续内容会在下一次请求重新计算和存储。
- 模型版本切换:切换窗口内可能暂时无法命中,但请求仍可能写入缓存并产生存储费用;完成切换后再验证历史轮次。
不同 API 的命中字段和模式特有条件分别见 隐式缓存 和 显式缓存。
常见问题#
显式缓存和隐式缓存能否同时生效?#
不能。请求使用显式缓存时,隐式缓存不生效;先按主动控制需求选择一种模式,再按该模式的字段和观测契约解释结果。
前缀缓存创建失败常见原因有哪些?#
检查固定前缀是否达到 256 Tokens、创建请求是否错误设置 stream=true、caching.type 是否为 enabled、store 是否实际为 true,以及 instructions、tools、json_schema 等组合限制。
隐式缓存未传 prompt_cache_key 时是否仍会启用?#
会。隐式缓存自动开启,prompt_cache_key 仅用于优化路由亲和,不是开关。
为什么隐式缓存请求成功但 cached_tokens 为 0?#
检查输入是否达到对应模型门槛、公共前缀是否稳定,以及可选 prompt_cache_key 的 Endpoint、模型和取值是否保持一致;不要转而检查显式缓存字段。
为什么传入 previous_response_id 后显式缓存仍未命中?#
确认首轮和每个后续请求都设置 caching.type=enabled,store 实际为 true,previous_response_id 对应正确的前缀创建 ID 或 Session 上一轮 ID,并检查缓存生命周期和版本切换窗口。判定口径见 命中与失效。
删除中间轮缓存后为什么费用上升?#
后续轮次会在下一次请求重新计算并存储,被重算内容先按普通输入计费,并产生新的缓存存储费用。
相关文档#
- 快速入门:完成 API Key 配置和首次模型调用。
- 模型列表:核对当前 Model ID 是否支持隐式或显式缓存。
- Chat API:查看
prompt_cache_key和cached_tokens字段。 - Responses API:查看显式缓存字段、响应 ID 和生命周期参数。
- Messages API:查看
cache_read_input_tokens字段。 - Batch API:查看 Batch 当前
cached_tokens的固定零值边界。 - 上下文编辑:控制后续请求携带的思维链和工具内容。