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

上下文缓存(Context API)(已下线)

本文介绍如何使用Context API 实现 Session 缓存以及前缀缓存能力。

开通功能#

  • 访问 开通管理,在 推理(缓存)定价列开通缓存。

模型及API#

API:

模型支持: 上下文缓存能力

调用方式及对比#

平台提供了两种 API 来实现显式缓存功能。由于每个模型最多只支持一种缓存 API,因此当您选定模型后,能够使用的 API 也就随之确定。

下表简要对比了这两种 API 的调用方式与核心区别。您可以查阅相应的 API 教程,获取详细的调用说明和代码示例。

API Context Create API Responses API
模型支持 上下文缓存能力 上下文缓存能力
使用流程
  1. 缓存信息:使用 创建上下文缓存 API 创建缓存信息,并指定创建的缓存类型(Session 缓存、前缀缓存)。在返回信息中获取缓存 ID 值。
  2. 使用缓存:通过 上下文缓存对话 API 配置 "context_id":"<ID>",本轮对话使用缓存信息。
    • Session 缓存:每次使用缓存时,更新本轮信息至缓存中。不生成新 ID。下一轮您继续使用原缓存 ID 即可。
    • 前缀缓存:每次使用固定的缓存信息。
  1. 缓存信息:在对话时配置 "caching": {"type": "enabled" } 创建 Session 缓存,配置 "caching":{"type": "enabled", "prefix": true} 创建前缀缓存,存储当前对话内容到缓存中。在返回信息中获取 ID 值。
  2. 使用缓存:在对话时配置 "previous_response_id":"<ID>",本轮对话使用缓存信息。
    • Session 缓存:每次使用缓存同时配置 "caching": {"type": "enabled" },将本轮信息也更新到缓存中,并生成新 ID。下一轮调用获取本轮调用返回的 ID。
    • 前缀缓存:无需配置 "caching": {"type": "enabled" },previous_response_id 仅配置为固定缓存 ID 即可。

更多使用说明,请参见 显式缓存。
保留初始信息 是
不可控制,一旦写入,不可更改。
是
可灵活控制,即您可删除任意一轮传入的缓存信息,来控制初始信息内容。
缓存收费项 存储缓存费用以及输入命中缓存费用(折扣)。 存储缓存费用以及输入命中缓存费用(折扣)。
可缓存的类型 仅支持文本缓存。 支持对多模态(文字、图片、视频等)输入进行缓存,支持缓存工具调用信息。
变更缓存内容 Session 缓存:支持更新缓存信息,缓存 ID 保持不变。
前缀缓存:不支持,且无需更新。
Session 缓存:支持更新缓存信息,缓存 ID 会新生成一个缓存 ID。
前缀缓存:不支持,且无需更新。
调用往期缓存信息 Session 缓存:不支持,创建缓存后 ID 不变,更新内容,往期的内容会被覆盖。
前缀缓存:不涉及,内容不可变。
支持,使用往期缓存 ID。
手动删除缓存信息 不支持
过期自动删除。
支持
可删除任意 ID 的缓存信息。
缓存保留时间 支持配置 TTL。
创建缓存时可配置,最大 168 小时。
支持配置过期时刻。
UTC Unix 时间戳(单位:秒),最大当前时间 + 604800(秒),即创建起保留 7 天。
过期机制 配置的是保存时长,计算公式:当前时刻 - 最近使用缓存时刻。
缓存在 TTL 周期内未使用过,则过期。使用后重新激活缓存,生命周期重置。
配置的是过期时刻,即缓存到对应时间点即过期。不随着缓存 / 存储的使用而重置生命周期。
存储及缓存过期后,需通过 Responses API 重新创建存储 / 缓存内容。
最大缓存长度 有
最大上下文窗口 - 最大输出长度。
有
最大上下文窗口。
触发最大缓存长度 创建时超出最大缓存长度,会报错。
其中 Session 缓存在更新时超出长度限制,会自动删除历史消息。
创建时超出最大缓存长度,会报错。
其中 Session 缓存在更新时超出长度限制会报错。

Session 缓存教程#

快速跳转至 前缀缓存教程

Session 缓存为会话级别的缓存,将会话的上下文(Context)进行缓存,并在对话进行中不断更新缓存内容,保障每轮对话能获取历史轮次的对话信息,维持对话内容延续,适合角色扮演、情景聊天等长轮数对话场景。开通 Session 缓存后,通过缓存输入的内容会有较大的折扣,可有效降低使用成本。

因使用缓存来存储您的上下文信息,您开通并使用了该功能后,会产生相应的存储费用。

快速开始#

1.创建 Session 缓存#

使用 Session 缓存前,您需要调用 创建上下文缓存 API 创建缓存,并写入初始信息。后续只要在 上下文缓存对话 API 中引入此缓存,即可让方舟为您管理历史对话以及享受使用缓存带来的费用降低。

Bash
curl https://ark.cn-beijing.volces.com/api/v3/context/create \
-H "Authorization: Bearer $ARK_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
    "model":"<YOUR_ENDPOINT_ID>",
    "messages":[
        {"role":"system","content":"你是李雷,你只会说“我是李雷”"}
     ],
     "ttl":3600,
     "mode": "session"
}'

其中:

  • $ARK_API_KEY替换为您的 API Key,或者配置 API Key 至环境变量。
  • <YOUR_ENDPOINT_ID>替换为您的推理接入点 ID。
  • "ttl":3600代表 Session 缓存 TTL,当前为 3600 秒。
  • "mode": "session"代表使用的 Session 缓存模式。

模型回复预览。

{
    "id": "<YOUR_CONTEXT_ID>",
    "model": "<YOUR_ENDPOINT_ID>",
    "ttl": "3600",
    "mode": "session",
    "truncation_strategy": {
            "type": "last_history_tokens",
            "last_history_tokens": 4096
            },
    "usage": {
        "prompt_tokens": 18,
        "completion_tokens": 0,
        "total_tokens": 18,
        "prompt_tokens_details": {
            "cached_tokens": 0
        }
    }
}

返回的创建的 Session 缓存的基本信息,其中<YOUR_CONTEXT_ID>是创建的 Session 缓存的 ID,格式类似ctx-****,需要记录并在后面请求模型推理服务时使用。

2.使用 Session 缓存进行对话#

我们使用接口 创建上下文缓存 API ,来进行使用 Session 缓存的对话。

Bash
curl https://ark.cn-beijing.volces.com/api/v3/context/chat/completions \
-H "Authorization: Bearer $ARK_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
    "context_id": "<YOUR_CONTEXT_ID>",
    "model": "<YOUR_ENDPOINT_ID>",
    "messages":[
        {
            "role":"user",
            "content": "你好"
        }
    ]
}'

其中。

  • $ARK_API_KEY替换为您的 API Key,或者配置 API Key 至环境变量。
  • <YOUR_CONTEXT_ID>替换为之前创建的 Session 缓存 ID。
  • <YOUR_ENDPOINT_ID>替换为您的推理接入点 ID。

模型回复预览。

{
  "id": "****",
  "object": "chat.completion",
  "created": 167765****,
  "model": "<YOUR_ENDPOINT_ID>",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "我是李雷"
    },
    "finish_reason": "stop"
  }],
 "usage": {
    "prompt_tokens": 28,
    "completion_tokens": 4,
    "total_tokens": 32,
    "prompt_tokens_details": {
      "cached_tokens": 18
    }
  }
}
Python
import datetime
import os
# Install SDK:  pip install 'volcengine-python-sdk[ark]'
from volcenginesdkarkruntime import Ark

# Create your endpoint: https://ark.volcengine.com/region:cn-beijing/endpoint
model = "<YOUR_ENDPOINT_ID>"

client = Ark(
    api_key=os.environ.get("ARK_API_KEY"),
    base_url="https://ark.cn-beijing.volces.com/api/v3",
)

if __name__ == "__main__":
    print("----- create context -----")
    response = client.context.create(
        model=model,
        # Specify the mode as Session cache
        mode="session",
        messages=[
            {"role": "system", "content": "你是李雷"},
        ],
        # Set cache retention to 60 minutes
        ttl=datetime.timedelta(minutes=60),
    )
    print(response)

    print("----- chat round 1 (non-stream) -----")
    chat_response = client.context.completions.create(
        context_id=response.id, # Specify context id
        model=model,
        messages=[
            {"role": "user", "content": "我是方方"},
        ],
        stream=False,
    )
    print(chat_response.choices[0].message.content)
    print(chat_response.usage)

    print("----- chat round 2 (streaming) -----")
    stream = client.context.completions.create(
        context_id=response.id, # Specify context id
        model=model,
        messages=[
            {"role": "user", "content": "你是谁,我是谁?"},
        ],
        stream=True,
        stream_options={
            "include_usage": True,
        },
    )
    for chunk in stream:
        if chunk.usage:
            print(chunk.usage)
        if not chunk.choices:
            continue
        print(chunk.choices[0].delta.content, end="")
Go
package main

import (
    "context"
    "fmt"
    "io"
    "os"
    "github.com/volcengine/volcengine-go-sdk/service/arkruntime"
    "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
    "github.com/volcengine/volcengine-go-sdk/volcengine"
)

// main 函数是程序的入口点
func main() {
    // 替换为您的推理接入点ID(https://ark.volcengine.com/region:cn-beijing/docs/online-inference-standard#1739de09)
    const Model = "<YOUR_ENDPOINT_ID>"
    // 使用 API Key创建一个客户端,从环境变量中获取 API Key(https://ark.volcengine.com/region:cn-beijing/docs/manage-api-keys)
    client := arkruntime.NewClientWithApiKey(
        os.Getenv("ARK_API_KEY"),
        arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
        )
    // 创建一个新的上下文
    goCtx := context.Background()

    // 打印创建上下文的提示信息
    fmt.Println("----- create context -----")
    // 创建一个新的上下文请求
    createCtxReq := model.CreateContextRequest{
        // 设置模型为常量 Model
        Model: Model,
        // 设置模式为会话模式
        Mode: model.ContextModeSession,
        // 设置初始消息
        Messages: []*model.ChatCompletionMessage{
            {
                // 设置角色为系统
                Role: model.ChatMessageRoleSystem,
                // 设置内容为系统消息
                Content: &model.ChatCompletionMessageContent{
                    // 设置字符串值为系统消息内容
                    StringValue: volcengine.String("你是李雷"),
                },
            },
        },
        // 设置 TTL 为 3600 秒
        TTL: volcengine.Int(3600),
    }

    // 发送创建上下文请求并获取响应
    createCtxRsp, err := client.CreateContext(goCtx, createCtxReq)
    // 如果发生错误,打印错误信息并返回
    if err != nil {
        fmt.Printf("create context error: %v\n", err)
        return
    }
    // 打印创建上下文的响应
    fmt.Printf("create context response: %v\n", createCtxRsp)

    // 打印非流式聊天的提示信息
    fmt.Println("----- chat round 1 (non-stream) -----")
    // 创建一个新的聊天请求
    req := model.ContextChatCompletionRequest{
        // 设置上下文ID为创建上下文的响应ID
        ContextID: createCtxRsp.ID,
        // 设置模型为常量 Model
        Model: Model,
        // 设置消息
        Messages: []*model.ChatCompletionMessage{
            {
                // 设置角色为用户
                Role: model.ChatMessageRoleUser,
                // 设置内容为用户消息
                Content: &model.ChatCompletionMessageContent{
                    // 设置字符串值为用户消息内容
                    StringValue: volcengine.String("我的名字是方方"),
                },
            },
        },
    }

    // 发送聊天请求并获取响应
    resp, err := client.CreateContextChatCompletion(goCtx, req)
    // 如果发生错误,打印错误信息并返回
    if err != nil {
        fmt.Printf("non-stream chat error: %v\n", err)
        return
    }
    // 打印聊天响应的内容
    fmt.Println(*resp.Choices[0].Message.Content.StringValue)

    // 打印流式聊天的提示信息
    fmt.Println("----- chat round 2 (stream) -----")
    // 创建一个新的聊天请求
    req = model.ContextChatCompletionRequest{
        // 设置上下文ID为创建上下文的响应ID
        ContextID: createCtxRsp.ID,
        // 设置模型为常量 Model
        Model: Model,
        // 设置消息
        Messages: []*model.ChatCompletionMessage{
            {
                // 设置角色为用户
                Role: model.ChatMessageRoleUser,
                // 设置内容为用户消息
                Content: &model.ChatCompletionMessageContent{
                    // 设置字符串值为用户消息内容
                    StringValue: volcengine.String("你是谁,我是谁?"),
                },
            },
        },
    }
    // 发送聊天请求并获取流式响应
    stream, err := client.CreateContextChatCompletionStream(goCtx, req)
    // 如果发生错误,打印错误信息并返回
    if err != nil {
        fmt.Printf("stream chat error: %v\n", err)
        return
    }
    // 延迟关闭流式响应
    defer stream.Close()

    // 循环接收流式响应
    for {
        // 接收流式响应
        recv, err := stream.Recv()
        // 如果接收到 EOF,返回
        if err == io.EOF {
            return
        }
        // 如果发生错误,打印错误信息并返回
        if err != nil {
            fmt.Printf("Stream chat error: %v\n", err)
            return
        }
        // 如果接收到的响应中有选择项
        if len(recv.Choices) > 0 {
            // 打印选择项的内容
            fmt.Print(recv.Choices[0].Delta.Content)
        }
    }
}
Java
package com.example;

import com.volcengine.ark.runtime.model.completion.chat.ChatCompletionResult;
import com.volcengine.ark.runtime.model.completion.chat.ChatMessage;
import com.volcengine.ark.runtime.model.completion.chat.ChatMessageRole;
import com.volcengine.ark.runtime.model.context.CreateContextRequest;
import com.volcengine.ark.runtime.model.context.CreateContextResult;
import com.volcengine.ark.runtime.model.context.chat.ContextChatCompletionRequest;
import com.volcengine.ark.runtime.service.ArkService;
import com.volcengine.ark.runtime.Const;
import java.util.Collections;

public class ContextChatCompletionsExample {

    /**
     * 主方法,程序入口
     * @param args 命令行参数
     */
    public static void main(String[] args) {

        // 从环境变量中获取 API Key(https://ark.volcengine.com/region:cn-beijing/docs/manage-api-keys)
        String apiKey = System.getenv("ARK_API_KEY");
        // 替换为您的推理接入点ID(https://ark.volcengine.com/region:cn-beijing/docs/online-inference-standard#1739de09)
        String model = "<YOUR_ENDPOINT_ID>";
        // 创建 ArkService 实例
        ArkService service = ArkService.builder().apiKey(apiKey).baseUrl("https://ark.cn-beijing.volces.com/api/v3").build();

        // 打印创建上下文的提示信息
        System.out.println("\n----- create context -----");
        // 创建上下文请求
        CreateContextRequest createContextRequest = CreateContextRequest.builder()
                // 设置模型
                .model(model)
                // 设置上下文模式为会话模式
                .mode(Const.CONTEXT_MODE_SESSION)
                // 设置系统消息
                .messages(Collections.singletonList(ChatMessage.builder().role(ChatMessageRole.SYSTEM)
                        .content("你是李雷").build()))
                // 设置上下文的生存时间(秒)
                .ttl(3600)
                .build();

        // 发送创建上下文请求并获取结果
        CreateContextResult createContextResult = service.createContext(createContextRequest);
        // 打印创建的上下文 ID
        System.out.println("created context, id = " + createContextResult.getId());

        // 打印第一轮聊天(非流式)的提示信息
        System.out.println("\n----- chat round 1 (non-stream) -----");
        // 创建聊天补全请求
        ContextChatCompletionRequest chatCompletionRequest = ContextChatCompletionRequest.builder()
                // 设置上下文 ID
                .contextId(createContextResult.getId())
                // 设置模型
                .model(model)
                // 设置用户消息
                .messages(Collections.singletonList(ChatMessage.builder().role(ChatMessageRole.USER)
                        .content("我是方方").build()))
                .build();

        // 发送聊天补全请求并获取结果
        ChatCompletionResult chatCompletionResult = service.createContextChatCompletion(chatCompletionRequest);
        // 遍历并打印聊天补全结果中的消息内容
        chatCompletionResult.getChoices()
                .forEach(choice -> System.out.println(choice.getMessage().getContent()));

        // 打印第二轮聊天(流式)的提示信息
        System.out.println("\n----- chat round 2 (stream) -----");
        // 创建流式聊天补全请求
        ContextChatCompletionRequest streamChatCompletionRequest = ContextChatCompletionRequest.builder()
                // 设置上下文 ID
                .contextId(createContextResult.getId())
                // 设置模型
                .model(model)
                // 设置用户消息
                .messages(Collections.singletonList(ChatMessage.builder().role(ChatMessageRole.USER)
                        .content("你是谁,我是谁?").build()))
                .build();

        // 发送流式聊天补全请求并处理结果
        service.streamContextChatCompletion(streamChatCompletionRequest)
                // 处理错误
                .doOnError(Throwable::printStackTrace)
                // 阻塞式遍历结果
                .blockingForEach(
                        choice -> {
                            // 如果结果不为空,则打印消息内容
                            if (!choice.getChoices().isEmpty()) {
                                System.out.print(choice.getChoices().get(0).getMessage()
                                        .getContent());
                            }
                        });

        // 关闭服务
        service.shutdownExecutor();
    }
}

典型使用#

手动管理 Session 缓存#

使用了支持 rolling_tokens 模式 的模型,可以通过下面方法创建 Session 缓存,并可以根据需要来启用自动管理和手动管理 Session 缓存。

Bash
curl https://ark.cn-beijing.volces.com/api/v3/context/create \
-H "Authorization: Bearer $ARK_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
    "model":"<YOUR_ENDPOINT_ID>",
    "messages":[
        {"role":"system","content":"你是李雷,你只会说“我是李雷”"}
     ],
     "ttl":3600,
     "mode": "session",
     "truncation_strategy":{
         "type":"rolling_tokens",
         "rolling_tokens": false
     }
}'

设置truncation_strategy.type为rolling_tokens,即可创建 Session 缓存。

  • 当您希望自行控制触发 Session 缓存上限时的处理,可以将truncation_strategy.rolling_tokens设置为false,在历史消息长度超过上下文长度时模型会停止输出,并在返回信息中返回finish_reason为length,您获取此信息后可以根据您按需进行后续处理。

当您希望自动控制触发 Session 缓存上限时的处理,可以将truncation_strategy.rolling_tokens设置为true,这是会按照默认的方式进行处理,处理方式请参见 rolling_tokens 模式。

使用说明#

  • 了解 Session 缓存与前缀缓存,参见 缓存类型。

  • Session 缓存 API 为有状态 API,不支持并发调用同一 Session 缓存,即不能在同一时刻发起多个带相同 Session 缓存 ID(context_id)的请求。

  • 不支持 Partial 模式(又称 Prefill Response),调用 创建上下文缓存 API 时,提交的messages数组的最后一条消息role可以是user或system,而不能为assistant。

    正例:

    JSON
    "messages":[
    {"role":"user","content":"你是谁"},
    {"role":"assistant","content":"我是李雷"},
    {"role":"user","content":"今天天气如何"}
     ],
    

    反例:

    JSON
    "messages":[
        {"role":"user","content":"你是谁"},
        {"role":"assistant","content":"我是李雷"}
    ],
    
  • Session 缓存当前只支持在线推理,不支持与批量推理一起使用。

缓存上限说明#

随着对话轮数增加,内容会达到 Session 缓存的上限。按照处理方式的不同,可以分为 2 种模式。

last_history_tokens 模式#

触发上限则滚动信息窗口,遵循先进先出的策略。触发缓存上限时,先删除最早的缓存对话记录(初始消息不会删除,即在创建 session 缓存时写入缓存的信息不会删除),再存入新的对话信息。这个模式下,滚动数据不会产生额外的计算成本。

rolling_tokens 模式#

触发上限则定量删除信息并重新计算,使用固定长度 A 来限制 Session 缓存上限,固定长度 B 来控制删除上下文的长度。当 Session 缓存达到上限时,即达到 A 长度时,会进行下面两个动作:

  1. 清除 Session 缓存 B 长度的陈旧消息(初始消息不会删除,即在创建 session 缓存时写入的信息不会删除),为后续新的消息腾挪存储空间。
  2. 重新计算缓存中历史信息,以确保模型回复与历史交互的连贯性。

具体计算逻辑供您了解:以Doubao-pro-32k模型为例,Session 缓存的 token 量达到 32k(最大上下文长度)-4k(最大输出长度)时,会删除 4k 陈旧的历史信息(除了初始消息),然后重新计算和存储保留的缓存内容。

触发 Session 缓存上限引起重新计算的轮次,会将保留的历史信息和新消息一样进行计算和存储。表现为此轮 Session 缓存的 token 比例降低为 0,该轮无缓存输入 token,后续又会趋于正常。

过期时间#

Session 缓存未被使用时会开始计时,达到 TTL(Time To Live),会被删除;如果中途被使用,那么此缓存 TTL 会被重置,并继续保留。

举例:您在 8:00 创建了 Session 缓存 A、B,并设置 TTL 为 2 小时。10:00 时,缓存 A 没被使用,缓存 B 在 9:00 被使用。那么,缓存 A 会被删除,B TTL 还有 1 小时。

计费说明#

计费单价请参见:模型价格。

与未使用 Session 缓存相比,模型服务费用会产生在:

  • 输入(元/千 token):新的请求中您无需重新发送历史对话,输入 token 仅代表添加到正在进行的对话中的新文本。

    在 rolling_tokens 模式下触发重新计算时,会将保存的历史对话重新计算和缓存,与新输入内容一样计费。

  • 缓存输入(元/千 token):缓存中使用的内容。方舟自动处理历史记录,并输入给模型,这部分内容即缓存输入内容,他们也会产生费用,但是减少了计算和存储开销,计费费率会显著低于新输入内容。

  • 存储(元/千 token/小时):历史对话存储在 Session 缓存中,会产生存储费用。计算方式根据每个自然小时使用缓存的最大量乘以单价进行累加。

    举例说明(单价仅为举例使用):单价为 0.000017 元/千 token/小时,第 1 小时 Session 缓存最大的缓存用量 10k token,第 2 小时 Session 缓存最大的缓存用量 15k token,那么存储费用为:

    0.000017*10+0.000017*15 = 0.000425 元

  • 输出(元/千 token):模型根据输入信息生成的内容。计费方式与未使用 Session 缓存的调用方式一致。

下面介绍对于使用特定缓存的请求(创建缓存后特定的某个 Context ID)的计费逻辑。
每次调用计费用量可在返回的usage结构体看到。

JSON
"usage": {
    "prompt_tokens": 20,
    "completion_tokens": 8,
    "total_tokens": 28,
    "prompt_tokens_details": {
      "cached_tokens": 10
    }
}

其中,各个字段含义:

  • prompt_tokens:此次请求输入给模型处理的总的 token 数。
  • completion_tokens:此次请求模型回复的 token 数,即输出的 token 量,对应的计费项为输出。
  • total_tokens:此次请求,总共使用的 token 数。
  • prompt_tokens_details.cached_tokens:此次请求,缓存输入的 token 量,对应计费项缓存输入。

其中费用计算重要数据。

  • 输入 token 量:可以通过prompt_tokens- cached_tokens来获得。
  • 存储费用:每个自然小时计算,该小时内cached_tokens的最大值。如果该小时未请求及变更缓存,则取上个小时cached_tokens的最大值,以此类推。

某缓存涉及的费用分为请求产生的 token 费用和存储费用,计算逻辑如下:

Bash
使用缓存的请求费用
= 使用缓存的所有请求费用累加
= 请求1(输入费用 + 缓存输入费用 + 输出费用) +请求2(输入费用 + 缓存输入费用 + 输出费用) +...+请求N(输入费用 + 缓存输入费用 + 输出费用)
= 请求1((prompt_tokens-cached_tokens) * 输入单价 + cached_tokens * 缓存输入单价 + completion_tokens * 输出单价 )
+ 请求2((prompt_tokens-cached_tokens) * 输入单价 + cached_tokens * 缓存输入单价 + completion_tokens * 输出单价 )
+...
+ 请求N ((prompt_tokens-cached_tokens) * 输入单价 + cached_tokens * 缓存输入单价 + completion_tokens * 输出单价 )
Bash
缓存存储花费
= 缓存生命周期内各个自然小时缓存存储费用之和
= 第1小时内缓存 token 最大值 * 存储单价 + 第2小时内缓存 token 最大值 * 存储单价 + ... + 第N小时内缓存 token 最大值 * 存储单价
  • 自然小时:以实际整点为准,举例缓存存储从 13:59开始,到14:00 时,会被计算为第 1小时。

工作原理#

请参见 Session 缓存。

前缀缓存教程#

快速跳转至 上下文缓存(Context API)

使用前缀缓存可以降低模型调用的成本。您可以预先存储常用信息如角色、背景等信息作为初始化信息,后续调用模型时无需重复发送此信息给模型,降低开销和成本,尤其适用于具有重复提示或标准化开头文本的应用。

快速开始#

1.创建前缀缓存#

使用前缀缓存前,您需要调用 创建上下文缓存 API 创建缓存,并写入初始信息。后续只要在 上下文缓存对话 API 中引入此缓存,即可让方舟为您保留初始信息以及享受使用缓存带来的费用折扣。

Bash
curl https://ark.cn-beijing.volces.com/api/v3/context/create \
-H "Authorization: Bearer $ARK_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
    "model":"<YOUR_ENDPOINT_ID>",
    "messages":[
        {"role":"system","content":"你是李雷,你只会说“我是李雷”"}
     ],
     "ttl":3600,
     "mode": "common_prefix"
}'

其中:

  • $ARK_API_KEY替换为您的 API Key,或者配置 API Key 至环境变量。
  • <YOUR_ENDPOINT_ID>替换为您的推理接入点 ID。
  • "ttl":3600代表前缀缓存 TTL,当前为 3600 秒。
  • "mode": "common_prefix"代表使用的前缀缓存模式。

模型回复预览:

Bash
{
    "id": "<YOUR_CONTEXT_ID>",
    "model": "<YOUR_ENDPOINT_ID>",
    "ttl": "259200",
    "mode": "common_prefix",
    "usage": {
        "prompt_tokens": 18,
        "completion_tokens": 0,
        "total_tokens": 18,
        "prompt_tokens_details": {
            "cached_tokens": 0
        }
    }
}

返回的创建的前缀缓存的基本信息,其中<YOUR_CONTEXT_ID>是创建的前缀缓存的 ID,格式类似ctx-****,需要记录并在后面请求模型推理服务时使用。

2.使用前缀缓存进行对话#

我们使用接口,来进行使用前缀缓存的对话。

Bash
curl https://ark.cn-beijing.volces.com/api/v3/context/chat/completions \
-H "Authorization: Bearer \$ARK_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
    "context_id": "<YOUR_CONTEXT_ID>",
    "model": "<YOUR_ENDPOINT_ID>",
    "messages":[
        {
            "role":"user",
            "content": "你好"
        }
    ]
}'

其中。

  • $ARK_API_KEY替换为您的 API Key,或者配置 API Key 至环境变量。
  • <YOUR_CONTEXT_ID>替换为之前创建的缓存 ID。
  • <YOUR_ENDPOINT_ID>替换为您的推理接入点 ID。

模型回复预览。

{
  "id": "****",
  "object": "chat.completion",
  "created": 167765****,
  "model": "<YOUR_ENDPOINT_ID>",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "我是李雷。"
    },
    "logprobs": null,
    "finish_reason": "stop"
  }],
 "usage": {
    "prompt_tokens": 28,
    "completion_tokens": 5,
    "total_tokens": 33,
    "prompt_tokens_details": {
      "cached_tokens": 18
    }
  }
}
Python
# 导入datetime模块,用于处理日期和时间
import datetime
# 导入os模块,用于获取环境变量
import os
# 需升级方舟 Python SDK到1.0.116版本或以上,pip install --upgrade 'volcengine-python-sdk[ark]'
from volcenginesdkarkruntime import Ark

# 从环境变量中获取 API Key(https://ark.volcengine.com/region:cn-beijing/docs/manage-api-keys)
api_key = os.environ.get("ARK_API_KEY")
# 替换为您的推理接入点ID(https://ark.volcengine.com/region:cn-beijing/docs/online-inference-standard#1739de09)
model = "<YOUR_ENDPOINT_ID>"

# 创建一个Ark客户端实例,传入API密钥
client = Ark(
    api_key=api_key,
    base_url="https://ark.cn-beijing.volces.com/api/v3",
    )

# 如果当前脚本是作为主程序运行
if __name__ == "__main__":
    # 打印创建上下文的提示信息
    print("----- create context -----")
    # 调用client的context.create方法创建一个会话上下文
    response = client.context.create(
        # 指定模型
        model=model,
        # 指定模式为会话
        mode="common_prefix",
        # 设置消息列表,包含一个系统角色的消息
        messages=[
            {"role": "system", "content": "你是李雷"},
        ],
        # 设置会话的生存时间为60分钟
        ttl=datetime.timedelta(minutes=60),
    )
    # 打印创建上下文的响应结果
    print(response)

    # 打印第一轮非流式聊天的提示信息
    print("----- chat round 1 (non-stream) -----")
    # 调用client的context.completions.create方法进行非流式聊天
    chat_response = client.context.completions.create(
        # 指定上下文ID
        context_id=response.id,
        # 指定模型
        model=model,
        # 设置消息列表,包含一个用户角色的消息
        messages=[
            {"role": "user", "content": "我是方方"},
        ],
        # 设置为非流式
        stream=False,
    )
    # 打印聊天响应的消息内容
    print(chat_response.choices[0].message.content)
    # 打印聊天响应的使用情况
    print(chat_response.usage)

    # 打印第二轮流式聊天的提示信息
    print("----- chat round 2 (streaming) -----")
    # 调用client的context.completions.create方法进行流式聊天
    stream = client.context.completions.create(
        # 指定上下文ID
        context_id=response.id,
        # 指定模型
        model=model,
        # 设置消息列表,包含一个用户角色的消息
        messages=[
            {"role": "user", "content": "你是谁,我是谁?"},
        ],
        # 设置流式选项,包含使用情况
        stream_options={
            "include_usage": True,
        },
        # 设置为流式
        stream=True,
    )
    # 遍历流式响应的每个块
    for chunk in stream:
        # 如果块中包含使用情况信息,则打印使用情况
        if chunk.usage:
            print(chunk.usage)
        # 如果块中不包含选择信息,则继续下一个块
        if not chunk.choices:
            continue
        print(chunk.choices[0].delta.content, end="")
Go
package main

import (
    "context"
    "fmt"
    "io"
    "os"

    "github.com/volcengine/volcengine-go-sdk/service/arkruntime"
    "github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
    "github.com/volcengine/volcengine-go-sdk/volcengine"
)

// main 函数是程序的入口点
func main() {
    // 替换为您的推理接入点ID(https://ark.volcengine.com/region:cn-beijing/docs/online-inference-standard#1739de09)
    const Model = "<YOUR_ENDPOINT_ID>"
    // 使用 API Key创建一个客户端,从环境变量中获取 API Key(https://ark.volcengine.com/region:cn-beijing/docs/manage-api-keys)
    client := arkruntime.NewClientWithApiKey(
        os.Getenv("ARK_API_KEY"),
        arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
        )
    // 创建一个新的上下文
    goCtx := context.Background()

    // 打印创建上下文的提示信息
    fmt.Println("----- create context -----")
    // 创建一个新的上下文请求
    createCtxReq := model.CreateContextRequest{
        // 设置模型为常量 Model
        Model: Model,
        // 设置模式为会话模式
        Mode: model.ContextModeCommonPrefix,
        // 设置初始消息
        Messages: []*model.ChatCompletionMessage{
            {
                // 设置角色为系统
                Role: model.ChatMessageRoleSystem,
                // 设置内容为系统消息
                Content: &model.ChatCompletionMessageContent{
                    // 设置字符串值为系统消息内容
                    StringValue: volcengine.String("你是李雷"),
                },
            },
        },
        // 设置 TTL 为 3600 秒
        TTL: volcengine.Int(3600),
    }

    // 发送创建上下文请求并获取响应
    createCtxRsp, err := client.CreateContext(goCtx, createCtxReq)
    // 如果发生错误,打印错误信息并返回
    if err != nil {
        fmt.Printf("create context error: %v\n", err)
        return
    }
    // 打印创建上下文的响应
    fmt.Printf("create context response: %v\n", createCtxRsp)

    // 打印非流式聊天的提示信息
    fmt.Println("----- chat round 1 (non-stream) -----")
    // 创建一个新的聊天请求
    req := model.ContextChatCompletionRequest{
        // 设置上下文ID为创建上下文的响应ID
        ContextID: createCtxRsp.ID,
        // 设置模型为常量 Model
        Model: Model,
        // 设置消息
        Messages: []*model.ChatCompletionMessage{
            {
                // 设置角色为用户
                Role: model.ChatMessageRoleUser,
                // 设置内容为用户消息
                Content: &model.ChatCompletionMessageContent{
                    // 设置字符串值为用户消息内容
                    StringValue: volcengine.String("我的名字是方方"),
                },
            },
        },
    }

    // 发送聊天请求并获取响应
    resp, err := client.CreateContextChatCompletion(goCtx, req)
    // 如果发生错误,打印错误信息并返回
    if err != nil {
        fmt.Printf("non-stream chat error: %v\n", err)
        return
    }
    // 打印聊天响应的内容
    fmt.Println(*resp.Choices[0].Message.Content.StringValue)

    // 打印流式聊天的提示信息
    fmt.Println("----- chat round 2 (stream) -----")
    // 创建一个新的聊天请求
    req = model.ContextChatCompletionRequest{
        // 设置上下文ID为创建上下文的响应ID
        ContextID: createCtxRsp.ID,
        // 设置模型为常量 Model
        Model: Model,
        // 设置消息
        Messages: []*model.ChatCompletionMessage{
            {
                // 设置角色为用户
                Role: model.ChatMessageRoleUser,
                // 设置内容为用户消息
                Content: &model.ChatCompletionMessageContent{
                    // 设置字符串值为用户消息内容
                    StringValue: volcengine.String("你是谁,我是谁?"),
                },
            },
        },
    }
    // 发送聊天请求并获取流式响应
    stream, err := client.CreateContextChatCompletionStream(goCtx, req)
    // 如果发生错误,打印错误信息并返回
    if err != nil {
        fmt.Printf("stream chat error: %v\n", err)
        return
    }
    // 延迟关闭流式响应
    defer stream.Close()

    // 循环接收流式响应
    for {
        // 接收流式响应
        recv, err := stream.Recv()
        // 如果接收到 EOF,返回
        if err == io.EOF {
            return
        }
        // 如果发生错误,打印错误信息并返回
        if err != nil {
            fmt.Printf("Stream chat error: %v\n", err)
            return
        }
        // 如果接收到的响应中有选择项
        if len(recv.Choices) > 0 {
            // 打印选择项的内容
            fmt.Print(recv.Choices[0].Delta.Content)
        }
    }
}
Java
package com.example;

import com.volcengine.ark.runtime.model.completion.chat.ChatCompletionResult;
import com.volcengine.ark.runtime.model.completion.chat.ChatMessage;
import com.volcengine.ark.runtime.model.completion.chat.ChatMessageRole;
import com.volcengine.ark.runtime.model.context.CreateContextRequest;
import com.volcengine.ark.runtime.model.context.CreateContextResult;
import com.volcengine.ark.runtime.model.context.chat.ContextChatCompletionRequest;
import com.volcengine.ark.runtime.service.ArkService;
import com.volcengine.ark.runtime.Const;
import java.util.Collections;

public class ContextChatCompletionsExample {

    /**
     * 主方法,程序入口
     * @param args 命令行参数
     */
    public static void main(String[] args) {

        // 从环境变量中获取 API Key(https://ark.volcengine.com/region:cn-beijing/docs/manage-api-keys)
        String apiKey = System.getenv("ARK_API_KEY");
        // 替换为您的推理接入点ID(https://ark.volcengine.com/region:cn-beijing/docs/online-inference-standard#1739de09)
        String model = "<YOUR_ENDPOINT_ID>";
        // 创建 ArkService 实例
        ArkService service = ArkService.builder().apiKey(apiKey).baseUrl("https://ark.cn-beijing.volces.com/api/v3").build();

        // 打印创建上下文的提示信息
        System.out.println("\n----- create context -----");
        // 创建上下文请求
        CreateContextRequest createContextRequest = CreateContextRequest.builder()
                // 设置模型
                .model(model)
                // 设置上下文模式为会话模式
                .mode(Const.CONTEXT_MODE_COMMON_PREFIX)
                // 设置系统消息
                .messages(Collections.singletonList(ChatMessage.builder().role(ChatMessageRole.SYSTEM)
                        .content("你是李雷").build()))
                // 设置上下文的生存时间(秒)
                .ttl(3600)
                .build();

        // 发送创建上下文请求并获取结果
        CreateContextResult createContextResult = service.createContext(createContextRequest);
        // 打印创建的上下文 ID
        System.out.println("created context, id = " + createContextResult.getId());

        // 打印第一轮聊天(非流式)的提示信息
        System.out.println("\n----- chat round 1 (non-stream) -----");
        // 创建聊天补全请求
        ContextChatCompletionRequest chatCompletionRequest = ContextChatCompletionRequest.builder()
                // 设置上下文 ID
                .contextId(createContextResult.getId())
                // 设置模型
                .model(model)
                // 设置用户消息
                .messages(Collections.singletonList(ChatMessage.builder().role(ChatMessageRole.USER)
                        .content("我是方方").build()))
                .build();

        // 发送聊天补全请求并获取结果
        ChatCompletionResult chatCompletionResult = service.createContextChatCompletion(chatCompletionRequest);
        // 遍历并打印聊天补全结果中的消息内容
        chatCompletionResult.getChoices()
                .forEach(choice -> System.out.println(choice.getMessage().getContent()));

        // 打印第二轮聊天(流式)的提示信息
        System.out.println("\n----- chat round 2 (stream) -----");
        // 创建流式聊天补全请求
        ContextChatCompletionRequest streamChatCompletionRequest = ContextChatCompletionRequest.builder()
                // 设置上下文 ID
                .contextId(createContextResult.getId())
                // 设置模型
                .model(model)
                // 设置用户消息
                .messages(Collections.singletonList(ChatMessage.builder().role(ChatMessageRole.USER)
                        .content("你是谁,我是谁?").build()))
                .build();

        // 发送流式聊天补全请求并处理结果
        service.streamContextChatCompletion(streamChatCompletionRequest)
                // 处理错误
                .doOnError(Throwable::printStackTrace)
                // 阻塞式遍历结果
                .blockingForEach(
                        choice -> {
                            // 如果结果不为空,则打印消息内容
                            if (!choice.getChoices().isEmpty()) {
                                System.out.print(choice.getChoices().get(0).getMessage()
                                        .getContent());
                            }
                        });

        // 关闭服务
        service.shutdownExecutor();
    }
}

支持的模型#

请参见文档 上下文缓存。

使用限制#

  • 与 Session 缓存不同,前缀缓存支持并发调用。

  • 不支持 Partial 模式(又称 Prefill Response),调用 上下文缓存对话 API 时,messages数组的最后一条消息的role字段取值可以是user或system,而不能为assistant。

    正例:

    JSON
    "messages":[
        {"role":"user","content":"你是谁"},
        {"role":"assistant","content":"我是李雷"},
        {"role":"user","content":"今天天气如何"}
     ],
    
  • 当前前缀缓存过期时长可配置范围在1小时到7天,即ttl字段设置范围(单位:秒)为 [3600, 604800]。具体字段配置说明,请参见 请求体。

  • 前缀缓存当前只支持在线推理,不支持与批量推理一起使用。

如果您需要使用Java来调用模型推理服务,您可以使用方舟的 Java SDK 来很方便使用前缀缓存。

计费说明#

计费单价请参见:后付费(按tokens使用量付费)。

与 Session 缓存类似,前缀缓存采用透明的计费方式,基于以下四个关键因素:

  • 输入(元/千 token):输入 tokens 代表发送给大语言模型的新文本,不包括缓存的前缀。
  • 输出(元/千 token):输出标记代表大语言模型生成的文本。输出标记的计费与标准大语言模型使用方式一致。
  • 缓存输入(元/千 token):每次从前缀缓存中检索标记都会产生缓存输入费用。此费用通常低于输入标记费用。
  • 存储(元/千 token/小时):存储费用按小时计费,基于每个自然小时内所有前缀存储的最大标记数。存储费用将持续到前缀的生存时间 (TTL) 到期。

您发起带前缀缓存的请求时,会因为下面行为产生费用:

  • 新的输入。
  • 缓存输入。
  • 模型生成的输出。
  • 初始信息存储费用(按存储信息大小以及小时计费)。

每次调用模型服务的用量,除了存储初始信息的费用,均可在返回的usage结构体看到。

JSON
{
    ...
    "usage": {
        "prompt_tokens": 20,
        "completion_tokens": 8,
        "total_tokens": 28,
        "prompt_tokens_details": {
            "cached_tokens": 10
        }
    }
}

其中,各个字段含义:

  • prompt_tokens:此次请求输入给模型处理的总的 token 数。
  • completion_tokens:此次请求模型回复的 token 数,即输出的 token 量,对应的计费项为输出。
  • total_tokens:此次请求,总共使用的 token 数。
  • prompt_tokens_details.cached_tokens:此次请求模型的缓存输入的 token 量,对应计费项缓存输入。

其中费用计算重要数据。

  • 输入 token 量:可通过prompt_tokens- cached_tokens来获得。
  • 存储费用:每个自然小时计算cached_tokens的值。

某缓存涉及的费用分为请求产生的 token 费用和存储费用,计算逻辑如下:

Bash
使用缓存的请求费用
= 使用缓存的所有请求费用累加
= 请求1(输入费用 + 缓存输入费用 + 输出费用) +请求2(输入费用 + 缓存输入费用 + 输出费用) +...+请求N(输入费用 + 缓存输入费用 + 输出费用)
= 请求1((prompt_tokens-cached_tokens) * 输入单价 + cached_tokens * 缓存输入单价 + completion_tokens * 输出单价 )
+ 请求2((prompt_tokens-cached_tokens) * 输入单价 + cached_tokens * 缓存输入单价 + completion_tokens * 输出单价 )
+...
+ 请求N ((prompt_tokens-cached_tokens) * 输入单价 + cached_tokens * 缓存输入单价 + completion_tokens * 输出单价 )
Bash
缓存存储花费
= 缓存生命周期内各个自然小时缓存存储费用之和
= 缓存 token 量 * 存储自然小时数 * 存储单价
  • 存储自然小时数:和存储时长可能存在差异,以实际整点为准,举例缓存存储从 13:59开始,到14:00 时,会被计算为 1 小时。
  • 成本优化技巧:分析初始信息的使用频率,合理配置缓存生存时间(ttl),平衡缓存收益和存储支出。

工作原理#

请参见文档 前缀缓存。