面向 AI 助手:本页的 Markdown 版本位于 https://ark.volcengine.com/region:cn-beijing/docs/context-cache.md,全部文档索引见 https://ark.volcengine.com/llms.txt。

上下文缓存

当应用在多轮对话、工具调用或固定提示词中反复发送相同的长前缀时,重复输入会持续消耗计算与成本。上下文缓存复用这些前缀的计算结果,隐式缓存对支持的模型自动启用,显式缓存由开发者主动开启并管理生命周期。两者不能同时生效,实际命中以缓存 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 判断命中。

Bash
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
Python
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.")
Go
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.")
}
Java
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。

优化缓存命中#

建议按以下顺序使用:

  1. 准备稳定前缀:将稳定角色设定、背景知识、工具定义或长文本放在请求前部,将动态问题放在后部,并保持工具定义、工具顺序和结构化输出方案稳定。
  2. 发起请求:使用支持隐式缓存的模型。Chat、Responses 和 Messages 都会自动启用隐式缓存;Chat 和 Responses 可选设置 prompt_cache_key 增强相同前缀请求的路由亲和,Messages 支持通过 metadata.user_id.session_id 传递会话标识实现缓存亲和,没有直接 prompt_cache_key 入参。Batch 对支持模型自动使用隐式缓存,但 cached_tokens 固定为 0,不能据此计算命中率。
  3. 验证命中:使用相同公共前缀发起有限次重复请求;完整判定步骤见 观测与计费。

prompt_cache_key 配置#

prompt_cache_key 用于提升相同前缀请求的路由亲和,不改变缓存是否启用。快速开始的 Chat 与 Responses 示例已经包含该参数,可按以下约束设置:

  • 会话隔离:上下文连续的请求使用同一取值;不同用户或并行会话使用不同取值。
  • 流量控制:同一取值的总流量建议控制在每分钟 15 个请求以内;超过后部分请求可能回退到常规路由。
  • 敏感信息:使用不可读的内部会话 ID,不传入手机号、邮箱、访问凭证或提示词原文。
  • 重路由边界:服务扩缩容、发布、故障切换、映射过期或保护策略触发时可能重新路由,无法保证每次命中。

观测与计费#

先按 优化缓存命中 准备稳定前缀,再按以下顺序验证:

  1. 首次请求:使用相同前缀完成首次请求,使输入达到对应模型门槛。
  2. 重复请求:再次发送相同前缀的请求,读取 Chat 的 usage.prompt_tokens_details.cached_tokens、Responses 的 usage.input_tokens_details.cached_tokens 或 Messages 的 usage.cache_read_input_tokens。
  3. 判断命中:缓存 Token 大于 0 表示本次命中;等于 0 时检查输入门槛、前缀稳定性和路由条件,再发起有界验证。
  4. 排除间接信号:不把 HTTP 200、响应 ID 或单次延迟变化当作命中证据;命中影响因素见 缓存长度门槛。

一次请求的费用构成:

text
普通输入费用 = (input_tokens - cached_tokens) × 输入单价
缓存输入费用 = cached_tokens × 缓存输入单价
输出费用 = output_tokens × 输出单价
请求费用 = 普通输入费用 + 缓存输入费用 + 输出费用

隐式缓存当前不单独收取缓存存储费。输入单价、缓存输入单价和输出单价以 模型价格 为准。

显式缓存#

显式缓存仅通过 Responses API 使用。使用前需要 开通推理(缓存)定价。使用或继续写入显式缓存时,每次请求都必须设置 caching.type=enabled;store 默认为 true,但响应存储不等于开启显式缓存。显式缓存与隐式缓存不能同时生效。

Session 缓存接入#

Session 缓存按轮次续接,适合多轮对话和连续工具调用:

  1. 首轮开启:首轮设置 caching.type=enabled,保持 store 实际为 true,并保存响应返回的 id。
  2. 下一轮续接:下一轮设置 caching.type=enabled,并通过 previous_response_id 传入紧邻上一轮的响应 id。
  3. 连续轮次:后续每轮重复第 2 步;连续多轮请求之间建议加入约 100 毫秒延迟,间隔过短可能导致调用失败。
  4. 前置轮缺失:任一前置轮未写入缓存时,后续轮次无法补写该轮上下文;此时从仍有效的响应或新请求重新建立 Session 链。

Session 缓存原理#

Session 缓存未使用与使用时的计算存储对比原理图
  1. 未使用缓存:未使用 Session 缓存时,初始信息、前 N 轮对话和第 N+1 轮问题都需要重新计算并生成 Tokens。
  2. 使用缓存:使用 Session 缓存时,初始信息和前 N 轮对话以缓存 Tokens 直接复用;第 N+1 轮问题单独计算。
  3. 写入下一轮:模型基于缓存 Tokens 与本轮输入生成第 N+1 轮回复,并将本轮内容加入 Session 缓存,供下一轮继续复用。

代码示例#

以下示例展示首轮创建、续接和连续轮次的完整调用。

步骤 1:创建首轮响应并保存响应 ID。

Bash
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 续接。

Bash
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 续接。

Bash
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>"
}'
Python
# 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())
Go
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),
            },
        ),
    }
}
Java
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();
    }
}
Python
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:

  1. 创建固定前缀:准备至少 256 Tokens 的固定前缀;创建请求设置 caching.type=enabled 和 caching.prefix=true,且不能设置 stream=true。
  2. 保存响应 ID:创建请求只写入前缀、不生成模型输出,output_tokens 为 0;保存创建响应返回的 id。
  3. 后续复用:多个后续请求设置 caching.type=enabled,并使用同一个 previous_response_id 引用创建响应 ID;固定前缀本身不随每轮更新。连续多轮请求之间建议加入约 100 毫秒延迟。
  4. 创建失败:未保存或传错创建响应 ID 时无法复用该前缀缓存;创建失败时先检查 Token 门槛、stream、store、caching 和组合限制。

Prefix 缓存原理#

Prefix 缓存未使用与使用时的计算存储对比原理图
  1. 未使用缓存:未使用 Prefix 缓存时,初始信息、前 N 轮历史和第 N+1 轮输入都参与计算。
  2. 使用缓存:使用 Prefix 缓存时,初始信息作为固定前缀直接复用缓存 Tokens,不再重复计算;前 N 轮历史和第 N+1 轮输入继续计算。
  3. 生成回复:模型基于固定前缀缓存、历史 Tokens 和本轮输入生成回复;固定前缀本身不随轮次更新。

代码示例#

以下示例展示创建固定前缀、首次复用和再次复用的完整调用。

步骤 1:创建固定前缀缓存。

Bash
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:首次复用该前缀缓存。

Bash
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:再次复用同一前缀缓存。

Bash
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>"
}'
Python
# 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)
Go
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),
            },
        ),
    }
}
Java
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();
    }
}
Python
# 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 秒时间戳,对响应存储和显式缓存同时生效。使用缓存不会重置生命周期;默认值和取值范围见 支持模型与使用限制 的“自定义过期”行。

Bash
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
Python
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())
Go
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),
            },
        ),
    }
}
Java
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();
    }
}
Python
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 表示删除成功。

Bash
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"
Python
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)
Go
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)
}
Java
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();
    }
}
Python
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 轮信息如何重新计算并重新缓存。

删除第3轮缓存后第4到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 增长;可使用 上下文编辑 控制后续携带的思维链与工具内容。

显式缓存的费用由两部分组成:

  1. 请求费用:普通输入、缓存输入和输出,公式与隐式缓存相同。
  2. 存储费用:开启显式缓存后,新增缓存 Token 按自然小时账期累加存储费。

存储费用与轮次#

多轮请求中命中缓存与新增缓存的轮次示意图

图中橙色表示命中的缓存。第 2 轮复用第 1 轮的系统提示词和问题;第 3 轮继续复用前两轮已缓存内容,并只新增本轮内容。每轮新增的缓存内容才计入当小时存储费用。

缓存存储计费按自然小时累加:

text
缓存存储费用 = Σ(每个自然小时账期内新增缓存 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 的固定零值边界。
  • 上下文编辑:控制后续请求携带的思维链和工具内容。