缓存与幂等

提示词缓存与响应缓存的命中条件、请求头与计费,以及用 Idempotency-Key 避免重试重复扣费。

更新于 2026-10-23阅读约 10 分钟

查看 .md

SciRouter 上有两层缓存,作用不同:

项目上游提示词缓存SciRouter 响应缓存
缓存位置模型厂商SciRouter 网关
命中条件请求开头一段与之前的请求相同整个请求与之前的请求完全相同
计费命中部分按较低的缓存命中价计费,模型仍会推理不调用模型,不计费
调用方操作多数情况无需操作;单轮批量调用见单轮批量调用对话将 temperature 设为 0 即自动缓存;其他情况带请求头

上游提示词缓存

#

同一段长前缀(系统提示、参考文档、工具定义)重复发送时,上游会缓存该前缀。命中部分按模型详情页的缓存命中价计费,通常远低于输入价。

部分上游首次写入缓存时按缓存写入价计费,略高于输入价,之后几轮的命中可抵消这部分差价。Anthropic 协议的 1 小时缓存写入单独计价(cache_write_1h_input_tokens,通常更贵):模型未单独定价时按缓存写入价计,缓存写入价也未设置时按输入价计。模型未单独设置缓存价时,对应部分按输入价计费。

网关在以下两种上游上自动处理缓存:

上游类型网关行为
需要显式标记缓存位置输入较长(约 1000 token 起)且为多轮对话(system 之外至少两条消息)时,在工具定义、系统提示、上一轮末尾与最后一块内容上加标记
支持缓存分组键计算分组键后发送。默认按 API Key 隔离;请求带 prompt_cache_key 时使用该键,仍按账号隔离

用量与计费

#
位置内容
usage.prompt_tokens_details.cached_tokens命中缓存的输入 token 数
usage.prompt_tokens_details.cache_write_tokens写入缓存的输入 token 数,仅在有写入时返回
响应体 scirouter.prompt_cache单次调用的缓存明细,见可观测性
控制台用量页的「上游缓存命中」一栏一段时间内的命中率、命中与写入的 token 数、命中节省的配额

走 OpenAI 兼容入口时,usage 由上游原样返回;个别兼容服务只在顶层返回 prompt_cache_hit_tokens,两种写法均按其计费。

命中部分按缓存命中价计费,写入部分按写入价计费,均不免费。用量页的命中率 = 命中 ÷ 全部输入 token(普通输入、命中与两种写入之和)。

单轮批量调用

#

一批请求共用同一段很长的系统提示、但每条只有一个问题时,网关不自动加标记。在需要缓存的内容段上加 cache_control:

json
{
  "model": "YOUR_MODEL_ID",
  "messages": [
    {
      "role": "system",
      "content": [
        { "type": "text", "text": "(很长的评测规则与参考资料…)", "cache_control": { "type": "ephemeral" } }
      ]
    },
    { "role": "user", "content": "第 17 题:…" }
  ]
}
  • 同一请求最多保留 4 个标记,超出时保留最后 4 个。
  • 需要标记的线路按标记缓存;其他线路按线路画像保留或去掉该字段(自动缓存的上游一律去掉,不影响缓存效果)。
  • Anthropic 格式的请求经转换时,断点同样按画像带或不带,见 Messages「请求的转发方式」。
  • 上游明确不支持该字段时,平台去掉后重发(响应头 X-SciRouter-Param-Actions 中为 healed=cache_control),调用方不会收到 400,见参数如何被处理。

提高命中率

#

将不变的内容放在前面,变化的内容放在后面。系统提示开头写入当前时间、随机 id 等内容时,每次请求的前缀都不同,无法命中。

SciRouter 响应缓存

#

对话的 temperature 显式为 0 时自动缓存:1 小时内完全相同的请求,第二次起直接返回上一次的结果,无需带请求头。对话接口的缓存规则:

条件缓存行为
temperature 显式为 0,未带 X-SciRouter-Cache缓存
带 X-SciRouter-Cache: enabled缓存,不论 temperature 取值
带 X-SciRouter-Cache: disabled本次不缓存
未写 temperature(或不为 0),且未带请求头不涉及缓存

向量接口(/embeddings)默认缓存,且按条缓存,见向量按条缓存。不需要时带 X-SciRouter-Cache: disabled。

以下示例带请求头,不依赖 temperature:

bash
curl https://api.scirouter.cn/v1/chat/completions \
  -H "Authorization: Bearer $SCIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-SciRouter-Cache: enabled" \
  -d '{
    "model": "YOUR_MODEL_ID",
    "temperature": 0,
    "messages": [{"role": "user", "content": "列出这段序列里所有的跨膜区段"}]
  }'
python
import os

from openai import OpenAI

client = OpenAI(api_key=os.environ["SCIROUTER_API_KEY"], base_url="https://api.scirouter.cn/v1")

response = client.chat.completions.create(
    model="YOUR_MODEL_ID",
    temperature=0,
    messages=[{"role": "user", "content": "列出这段序列里所有的跨膜区段"}],
    extra_headers={"X-SciRouter-Cache": "enabled"},
)

适用场景:批量评测反复运行同一套题、向量化含重复文本的批次、开发时反复调试同一请求。要求每次重新生成的场景不适用:命中时返回的回答与首次完全相同。

带 X-SciRouter-Cache: enabled 时不论 temperature 取值都缓存;温度不为 0 时,之后每次得到的都是首次那一份随机结果。

请求头

#
请求头取值说明
X-SciRouter-Cacheenabled / disabled开启或关闭本次请求的缓存。对话 temperature 显式为 0 时不带该请求头也开启
X-SciRouter-Cache-TTL正整数(秒)本次结果的保存时长。对话默认 1 小时、向量默认 24 小时;超过上限(默认 24 小时)时按上限计
Cache-Controlno-cache不读取旧结果,调用模型后用新结果覆盖缓存
Cache-Controlno-store本次既不读取也不写入

X-SciRouter-Cache 不是上述两个取值、X-SciRouter-Cache-TTL 不是正整数时,请求直接返回 400。Cache-Control 的其他取值(如 max-age)被忽略。

命中状态

#

响应头 X-SciRouter-Cache 的取值:

取值含义
HIT来自缓存,未调用模型,不扣配额
MISS未命中,正常调用并计费;成功的结果会被缓存
PARTIAL仅向量接口:一批输入中部分命中,只有未命中的条目发给模型并计费,见向量按条缓存
BYPASS本应走缓存,但调用方要求不读取(Cache-Control: no-cache / no-store,或对向量、temperature 为 0 的对话带 X-SciRouter-Cache: disabled),或请求形状不可缓存,见不缓存的情况
无该响应头本次不涉及缓存。例如对话未带 X-SciRouter-Cache: enabled,且 temperature 不是显式的 0(对话只带 Cache-Control 或 disabled 不会开启缓存)

其他相关的响应头与响应体字段:

响应头 / 字段说明
X-SciRouter-Cache-Age命中时返回:该结果产生至今的秒数
X-SciRouter-Cache-TTL结果将被保存时返回:实际保存的秒数。请求的时长超过上限时为截断后的值
scirouter.cache、scirouter.cache_age_s、scirouter.cache_ttl_s与上述响应头的结论相同,取值小写
scirouter.saved_quota命中时返回:按当前倍率计算的节省配额
scirouter.cache_items仅向量接口:{"hit": 命中条数, "total": 总条数}

流式响应在 data: [DONE] 之前、choices 为空的扩展分片中也带这些字段。排队较久、心跳已先发出响应头时,响应头中不带这些信息,以扩展分片或请求日志为准。

命中时响应中的 usage 是首次调用的真实用量(与无缓存时相同),但本次不扣费;本次消耗见 scirouter.cost_quota(命中时为 0)。控制台请求日志中命中的记录带「缓存」标记,token 与费用记为 0;用量页的请求数、Tokens、配额均不含命中,命中次数单独列在「缓存命中」。

相同请求的判定

#

按规范化之后的请求体比较:

  • 键的顺序、空白、1 与 1.0 等写法差异不影响比较;
  • stream、stream_options、user、safety_identifier、metadata、prompt_cache_key、store 不参与比较。流式与非流式共用一份缓存,非流式保存的结果也可以流式回放;
  • max_completion_tokens 与 max_tokens 视为同一参数,n: 1 与不写相同;
  • 其余任何字段不同(更换模型、修改一个标点、temperature 不同)均视为另一个请求。

向量按条缓存

#

向量接口按每一条输入分别缓存,不按整批缓存:同一段文字无论出现在哪一批、排在第几条,都能命中。一批中只有部分命中时(PARTIAL),网关只将未命中的条目发给模型,再按原顺序拼回。计费只计发给模型的条目,命中部分记为节省的配额(scirouter.saved_quota);响应中的 usage 仍为整批输入的 token 数。

不缓存的情况

#
  • 要求多个候选(n 大于 1)或逐 token 概率(logprobs / top_logprobs)的对话:回放无法提供这些内容,响应头为 BYPASS;
  • 带平台托管工具的请求(结果随网页内容变化);
  • 失败的调用,以及未正常结束的回答:只保存 finish_reason 为 stop / tool_calls 且有内容的结果,被截断(length)、被过滤(content_filter)或内容为空的均不保存;
  • 过大的结果:对话单条超过 256 KB,或向量中单条输入的结果超过 2 MB(按压缩前的大小计);
  • 缓存服务自身故障时,一律按未命中处理,调用照常进行。

隔离与计费

#
  • 缓存按 API Key 隔离:同一账号下的不同 Key 互相看不到对方的缓存。
  • 命中计入每分钟请求数(RPM),不计入每分钟 token 数与并发;命中不扣配额,因此不受 Key 月度上限、项目预算与套餐每日上限的约束。
  • 平台更换该模型背后的上游,或管理员清空该模型的缓存后,旧结果不再命中。
  • 响应缓存只作用于 /v1 的调用;Playground 与 Chat 不使用响应缓存。

幂等

#

网络抖动时客户端可能重试。为避免一次重试产生两次计费,在请求中带调用方自行生成的 Idempotency-Key:

python
import os
import uuid

from openai import OpenAI

client = OpenAI(api_key=os.environ["SCIROUTER_API_KEY"], base_url="https://api.scirouter.cn/v1")

key = str(uuid.uuid4())  # 同一个逻辑请求的所有重试用同一个值
response = client.chat.completions.create(
    model="YOUR_MODEL_ID",
    messages=[{"role": "user", "content": "帮我判断这段序列的二级结构倾向"}],
    extra_headers={"Idempotency-Key": key},
)

/chat/completions、/messages、/responses 与 /embeddings 支持该请求头。键最长 128 字节,按 API Key 隔离,各接口各自独立,保留 24 小时。下表与下方说明适用于这四个接口;图片接口同样接受该请求头,但按规整后的请求判定,b64_json 与流式的结果不保存,规则见图片「幂等」。

情况结果
同一个键、同一个请求体直接返回首次的响应体,带响应头 Idempotent-Replayed: true,不再调用模型、不再计费
首次请求尚未处理完,同一个键再次请求409 idempotency_conflict
同一个键、不同的请求体409 idempotency_conflict,通常是代码将键复用到了其他请求
首次请求失败(非 2xx)不保存,可用同一个键修正后重试
首次请求被客户端先断开服务端取消该次调用、不保存结果;之后同一个键的重试会重新调用模型并照常计费
流式请求流未结束时,同一个键的重试返回 409;流结束后,同一个键重新执行一次并照常计费。流式结果无法重放
  • 请求体按原始字节比较。重试时发送完全相同的字节(SDK 的自动重试即如此);重新拼接 JSON 导致键的顺序变化时返回 409。
  • 重放的响应体中 scirouter.request_id 仍为首次的编号,按它查询日志。X-SciRouter-* 与 x-ratelimit-* 响应头不重放,重放也不产生新的日志记录。
  • 两种 409 的 code 相同,只有 message 不同。确定是重试同一个请求时,退避等待后再试,并设次数上限。
  • 客户端超时应长于模型的正常耗时;回答可能很长时使用流式。
  • 幂等存储自身故障时平台放行请求,不拒绝;极少数情况下可能重复执行一次。

幂等与响应缓存相互独立:幂等只识别调用方提供的键,保证同一个请求只计费一次;响应缓存按请求内容识别,使不同时间发出的相同请求共用结果。两者可同时使用。

  • 开始

  • 接入编程工具

  • 接口

  • 专题

  • 网页端指南

↑ ↓ 选择 · Enter 打开 · Esc 关闭

请验证你的身份

为了保护你的账户,这个操作需要再验证一次。

验证码已发送到你绑定的手机号。5 分钟内的其他敏感操作不会再问。

Cookie 偏好

您可以随时回到这里修改。页脚有常驻入口。

必要

维持登录状态、记住主题与语言偏好。没有它们本站无法正常工作,因此无法关闭。

统计

了解哪些功能有人用、用户在哪一步流失。只收集维度(页面、操作类型、计数),不收集您输入的任何内容。

本站不加载任何第三方统计脚本,也不做会话录屏。完整说明