EZAT RouterDocs

提示缓存与会话

提示缓存如何在 EZAT Router 上帮你省钱、如何在两种 API 上使用,以及为什么一个对话会固定在同一个部署上。

何时阅读本页

你会多次发送同一段很长的前缀(系统提示词、文档、工具定义,或不断增长的对话),希望为它少付些费用。本页介绍缓存有效期、Anthropic 格式 API 上的 cache_control、OpenAI 兼容 API 上的自动缓存、会话,以及如何确认缓存生效。

缓存能省什么

提示缓存(prompt caching)保存的是提示词开头部分处理后的结果。当下一个请求以完全相同的内容开头时,模型会从缓存中读取这部分,而不是重新处理。

token 类型价格
输入模型的输入价格
缓存写入高于输入价格。1 小时缓存的写入价格高于 5 分钟缓存。
缓存读取远低于输入价格

各模型的价格见价格。缓存的前缀只要被读回一两次就划算了。写入后再也没被读取的前缀,比不缓存直接发送更贵。

  • 缓存匹配的是完全相同的前缀,顺序为 tools、system、messages,一直到标有 cache_control 的块为止。在这之前任何地方的改动(系统提示词中的时间戳、调整了顺序的工具)都会导致缓存未命中。
  • 短提示词不会被缓存。每个模型都有最小可缓存长度(几百到几千个 token)。低于这个长度时,请求照常执行,按普通输入计费,不会报错。
  • 每次读取都会刷新有效期,且不额外收费。

缓存有效期

有效期cache_control适用场景
5 分钟(默认){"type": "ephemeral"}请求之间相隔几分钟以内:聊天、agent 循环、连续执行的任务
1 小时{"type": "ephemeral", "ttl": "1h"}请求之间的间隔超过 5 分钟

有效期从写入或最近一次读取该缓存条目的请求开始时算起。模型页面的「能力」一列显示哪些模型提供 1 小时缓存。

在 Anthropic 格式的 API 上

把 cache_control 放在你想缓存的前缀的最后一个块上:tools 中的最后一个工具、system 中的一个块,或 messages 中的一个内容块。每个请求最多 4 个断点。

import os

import anthropic

client = anthropic.Anthropic(base_url="https://api.router.ezat.io", api_key=os.environ["EZAT_API_KEY"])

# A long, unchanging system prompt: the part worth caching.
system = [
    {
        "type": "text",
        "text": "You answer questions about the Example Ltd product manual. " + "Manual text. " * 400,
        "cache_control": {"type": "ephemeral"},
    }
]

for question in ["How do I reset my password?", "How do I change my email address?"]:
    message = client.messages.create(
        model="anthropic/claude-sonnet-5-5",
        max_tokens=64,
        system=system,
        messages=[{"role": "user", "content": question}],
        # Different questions are different sessions; one session id keeps them on one deployment.
        extra_headers={"x-ezat-session": "manual-assistant-v1"},
    )
    usage = message.usage
    print("write", usage.cache_creation_input_tokens, "read", usage.cache_read_input_tokens)

只要前缀超过模型的最小长度,第一个请求会写入缓存,第二个请求会读取缓存。

Anthropic 的顶层 cache_control(请求上的一个字段,由它替你放置断点)同样会透传,但 Claude Opus 4.6 会拒绝它。在块上设置的断点对所有支持缓存的模型都有效。

在 OpenAI 兼容 API 上

OpenAI 的格式无法标记断点,所以 EZAT 会自动缓存。对于自己没有设置断点、且发往支持缓存的模型的请求,EZAT 会放置两个 5 分钟的断点:

  1. 在稳定前缀之后:系统提示词,没有系统提示词时为最后一个工具;
  2. 在最新的消息上,供对话的下一轮读取。

这样,多轮对话中已经发送过的内容都会从缓存读取。

  • 关闭缓存:对单个请求使用请求头 x-ezat-cache: off。超过最小长度、且不会再次发送的一次性提示词应当这样做:否则它们要为一次永远不会被读取的缓存写入付费。
  • 自己放置断点:像 OpenRouter 一样,在内容部分(text、image_url、file,或系统消息的文本部分)或工具上加上 cache_control。这时 EZAT 不再添加断点。ttl 字段在这里同样有效。
  • prompt_cache_key 为会话命名,作用与 x-ezat-session 相同(见下文)。
{
  "model": "anthropic/claude-sonnet-5-5",
  "messages": [
    {
      "role": "system",
      "content": [
        { "type": "text", "text": "Long instructions…", "cache_control": { "type": "ephemeral", "ttl": "1h" } }
      ]
    },
    { "role": "user", "content": "First question" }
  ]
}

会话:一个对话,一个部署

EZAT 用多个部署提供每个模型,而提示缓存只存在于其中一个部署中。发往另一个部署的请求会错过缓存,并重新写入。所以 EZAT 会把同一个对话的请求保持在服务过它的部署上。

  • 会话由你的组织、系统提示词和第一条消息共同确定,忽略 cache_control(客户端每一轮都会移动断点)。正常对话的每一轮都属于同一个会话,你无需做任何事。
  • EZAT 会记住这个部署 1 小时,从会话最后一次成功请求算起。
  • 会话会在以下情况迁移:它的部署不健康、容量不足,或停止服务。之后的下一个请求会在新部署上重新写入缓存。

自动确定的会话不合适时,请自己为会话命名:

情况发送什么
对话过程中系统提示词或第一条消息会变化(系统提示词中带日期、历史记录被裁剪或摘要)每个对话一个 id
许多彼此独立的请求共享一段很长的前缀(同一个系统提示词、相同的文档)每个共享前缀一个 id,如上面的示例
其他情况什么都不用发
API方式
两者请求头 x-ezat-session: <id>。任意字符串;只有前 256 个字符有效。
OpenAI 兼容请求体字段 prompt_cache_key,最多 256 个字符(更长会以 400 拒绝)。两者都发送时以请求头为准。

会话 id 只在你自己组织的请求范围内分组。不要给所有流量都发送同一个 id:只要那个部署还有容量,所有流量都会发往它。

确认缓存生效

位置缓存写入缓存读取
Anthropic 格式的 usagecache_creation_input_tokens(提供方报告时,在 cache_creation 中按有效期细分)cache_read_input_tokens
OpenAI 格式的 usageprompt_tokens_details.cache_write_tokensprompt_tokens_details.cached_tokens
控制台「用量」页面缓存(写入 / 读取)缓存(写入 / 读取)

在 Anthropic 格式的 API 上,input_tokens 只统计未缓存的输入;输入总量为 input_tokens + cache_creation_input_tokens + cache_read_input_tokens。在 OpenAI 兼容 API 上,prompt_tokens 已经是总量。

如果缓存读取一直为零:

  • 前缀在请求之间发生了变化,或低于模型的最小长度;
  • 请求之间相隔超过 5 分钟(请使用 1 小时有效期);
  • 请求属于不同的会话(请发送 x-ezat-session);
  • 模型不支持缓存(见下一节)。

有特殊情况的模型

模型有何不同
capabilities.promptCaching 为 false 的模型(目前:Claude Fable 5)它们的缓存永远读不回来,所以在两种 API 上,EZAT 都会从发往它们的请求中移除 cache_control,并跳过自动缓存。你按普通输入付费,而不必为永远不会被读取的缓存写入付费。usage 中不显示缓存 token。
Claude Opus 4.6拒绝 Anthropic 的顶层 cache_control。请在块上设置断点。

每个模型的 capabilities 见 GET https://api.router.ezat.io/api/models(无需 key)。模型页面的「能力」一列会显示「提示缓存」和「1 小时缓存」。

Claude Code

Claude Code 会自己放置断点,而且它的系统提示词和第一条消息在整个对话中保持不变,所以缓存和会话无需任何设置即可生效。见 Claude Code。

给 AI agent 的说明

  1. Anthropic 格式 API:在稳定前缀的最后一个工具、系统块或消息块上放置 {"type": "ephemeral"}(或加上 "ttl": "1h")。最多 4 个断点。
  2. OpenAI 兼容 API:缓存是自动的;除非需要 1 小时有效期,否则不要添加断点。一次性提示词发送 x-ezat-cache: off。
  3. 对于一个对话,如果它的系统提示词或第一条消息会变化,每一轮都发送相同的 x-ezat-session(或 prompt_cache_key)。不要所有流量都用同一个值。
  4. 在第二个请求上检查 cache_read_input_tokens(Anthropic 格式)或 prompt_tokens_details.cached_tokens(OpenAI 格式),确认缓存读取生效。

本页内容