OpenRouter 提示缓存 + 粘性路由 2026:Agent 成本实测

2026 年 7 月 21 日,OpenRouter 发布了一篇题为《The Cheapest Token Is a Cached One: Prompt Caching + Sticky Routing》的教程。这是迄今为止关于他们的聚合层如何在 70+ 上游服务商之间处理缓存读取、缓存写入与会话锁定最清晰的公开拆解。对任何跑多轮 Agent 的人来说,这篇博客里的成本数学就是同一个工作负载从月费 $5,000 降到 $500 的关键。

本文把 OpenRouter 那篇博客提炼成生产环境最在意的几个部分:

  1. 逐服务商的缓存价格矩阵(Anthropic、GPT-5.6 前后的 OpenAI、Gemini、Grok、Moonshot、Groq、DeepSeek、阿里 Qwen、Z.AI)。
  2. 6 轮 × 10,000 token 的具体算例:粘性路由究竟省了多少。
  3. 四种导致缓存不命中的原因及对应的修复。
  4. session_id 参数,以及为什么它能把粘性从「有时 warm」变成「稳定 warm」。
  5. cached_tokenscache_discountcache_write_tokens 这三个确认缓存生效的字段。

所有价格事实都来自 2026-07-21 的 OpenRouter 博客及 OpenRouter 提示缓存文档(截至 2026 年 7 月底)。

OpenRouter 的提示缓存是什么?

OpenRouter 是一个统一 API,前接 300+ 模型、70+ 服务商。所谓「提示缓存」,是指 OpenRouter(或上游服务商)复用 prompt 的一部分,而不是每轮重新分词、重新计费完整的输入。复用部分通常是的部分 — 系统提示、工具定义、JSON schemas、护栏、检索到的文档、examples — 它们跨轮次保持不变。

两个成本要素要分开看:

  • 缓存写入 — 第一次请求把可复用的前缀存下来。有些服务商写入价比正常输入还贵(Anthropic 5 分钟 TTL 收 1.25x,1 小时 TTL 收 2.0x),有些免费(Gemini、Grok、Moonshot、GPT-5.6 之前的 OpenAI、Groq)。
  • 缓存读取 — 之后的每次请求复用存好的前缀。这是便宜的那部分:根据服务商不同,从正常输入价格的 0.1x 到 0.5x 不等。

0.1x 的缓存读取意味着:一个 10,000 token 的系统提示,在 Claude Sonnet 4.6 上正常输入花 $30,从缓存读取只要 $3。把它乘到长跑 Agent 的 6 轮,节省会快速累积。

逐服务商缓存价格矩阵(验证于 2026-07-21)

下表是 OpenRouter 公开的拆解,直接从博客抓取:

服务商缓存读取倍率缓存写入倍率启用方式
Anthropic Claude(5 分钟 TTL)0.1x input1.25x input自动或显式
Anthropic Claude(1 小时 TTL)0.1x input2.0x input显式(ttl: "1h"
OpenAI(GPT-5.6 之前)0.25x–0.50x input免费自动
OpenAI(GPT-5.6 之后)0.25x–0.50x input1.25x input自动或显式
Google Gemini(隐式)0.25x input免费自动
Grok(xAI)0.25x input免费自动
Moonshot AI0.25x input免费自动
Groq0.5x input免费自动(Kimi K2 模型)
DeepSeek0.1x input1.0x input自动
阿里 Qwen0.1x input1.25x input显式(cache_control
Z.AI~0.2x input免费自动

三种模式值得注意:

  • Anthropic、DeepSeek、阿里 Qwen 提供最低 0.1x 的缓存读取,但它们的写入都比正常输入贵。如果你的 Agent 复用前缀不足以摊薄写入,缓存反而烧钱
  • Google Gemini、Grok、Moonshot 写入免费、读取 0.25x。对偶尔命中缓存但很少需要多 hour TTL 的轻量 Agent 来说是最佳组合。
  • OpenAI 在 GPT-5.6 之后改成付费写入。GPT-5.6 之前的 OpenAI 写入免费;GPT-5.6+ 缓存写入收 1.25x。如果你在 2026 年 6 月迁到 GPT-5.6,缓存经济已经悄悄变了。

具体节省:6 轮 Agent × 10K 缓存 token

OpenRouter 那篇博客用同一个假想 Agent:6 轮、每轮都重复同一段 10,000 token(系统提示 + 工具定义 + schemas + 策略上下文)。输出 token 与变化的消息不计入。

场景第 1 轮第 2–6 轮相对 1 轮未缓存的总成本
不缓存完整输入每轮完整输入6.0x
Anthropic 5 分钟缓存 + 粘性路由1.25x 写入0.1x 读取1.75x
免费写入 + 0.25x 读取1.0x 输入/写入0.25x 读取2.25x
免费写入 + 0.5x 读取1.0x 输入/写入0.5x 读取3.5x

表格的含义:「对于同一段重复内容,6 轮的总 token 成本是单轮未缓存成本的 N 倍。」

Anthropic + 粘性路由比未缓存基线便宜 3.4x。免费写入 + 0.5x 读取(Groq)仍然比未缓存好 1.7x,但比 Anthropic 激进的缓存读取价格差一截。

轮次越多,节省越大。一个 20 轮深度研究 Agent 配 30K token 前缀,差距还会再拉宽。

为什么 warm cache 不一定有用?

这是多数工程师第一次踩到的坑:warm cache 只有当下一请求落到同一个持有缓存前缀的服务商端点时才管用。OpenRouter 前接 70+ 服务商;每轮路由器挑一个。第二轮可能路由到另一个服务商 — 即便「理论上应该有缓存」,你仍然按全价付费。

OpenRouter 的粘性路由就是为了修这个坑。某服务商上缓存请求成功后,OpenRouter 把同模型的后续请求钉回那个端点,当该服务商的缓存读取价格低于正常输入时这么做。如果粘性服务商挂了,OpenRouter 回退到下一个可用服务商,而不是直接报错。

钉靠的是 key。默认情况下,OpenRouter 对第一条 system 或 developer 消息 + 第一条非 system 消息做哈希。只要这两段不变,钉就稳。system prompt 跨轮变、或者把每次请求的时间戳塞进前缀,钉就废。

session_id 从第 1 轮就强制 warm

对 Agent 循环来说,默认的哈希 key 太脆弱。OpenRouter 建议为会话、工单、或 workflow run 传一个稳定的 session_id

  • 没有 session_id,粘性路由只在观察到一次缓存命中后才生效。第一轮没缓存,路由器没东西可钉。第二轮可能落到冷端点。
  • session_id,OpenRouter 直接拿 session ID 当粘性路由 key。第一次成功请求之后,粘性就生效 — 早于任何缓存命中。对多轮 Agent 而言,这是「从第 1 轮就稳定 warm」和「只是偶尔 warm」之间的差别。

在你的 OpenRouter 客户端里就一行:

import requests

response = requests.post(
    "https://openrouter.ai/api/v1/chat/completions",
    headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}"},
    json={
        "model": "anthropic/claude-sonnet-4.6",
        "messages": [...],   # 你的 Agent 完整消息历史
        "session_id": "support-ticket-9821",   # 每会话稳定的 key
        # 可选:在 Anthropic 1 小时模型上显式设缓存 TTL
        # "provider": {"cache_control": {"ttl": "1h"}}
    }
)

对工单风格的 workflow(用稳定标识符:工单 ID、thread ID、用户 ID、对话 ID),session_id 是最便宜的优化。

如何确认提示缓存是否生效?

OpenRouter 返回里有三个字段可以直接确认缓存是否生效:

  • usage.prompt_tokens_details.cached_tokens — 从缓存读取的输入 token 数。大于 0 即命中。
  • usage.prompt_tokens_details.cache_write_tokens — 写入那轮被存入的输入 token 数。
  • usage.cache_discount — 单次生成的成本影响(写入付费时为负,读取轮次为正)。

也可以看 OpenRouter Dashboard 的 Activity 详情页,或调用 /api/v1/generation 拿完整每轮遥测。

生产环境里一个实用的健康检查:

import requests

def cache_health_check(response_json):
    usage = response_json.get("usage", {})
    details = usage.get("prompt_tokens_details", {})
    cached = details.get("cached_tokens", 0)
    discount = usage.get("cache_discount", 0)
    return {
        "cached_tokens": cached,
        "cache_discount": discount,
        "cache_active": cached > 0,
    }

如果 cache_active 多轮都是 False,说明你踩到了下面四个常见 cache miss 原因之一。

为什么会 cache miss(以及每个原因的修复)

缓存看上去失效,几乎总逃不掉这四种情况:

  1. Prompt 太短。每个服务商都有最小可缓存 token 数 — Anthropic 要求 1,024,OpenAI 要求 1,024,Gemini 要求 4,096。Prompt 太短,无论多稳定都不会触发缓存。
  2. 缓存过期。Anthropic 默认 TTL 是 5 分钟;1 小时 TTL 需要显式 ttl: "1h"。如果一轮之间闲置超过 TTL,前缀就消失了。
  3. 开头内容变了。任何对 system prompt、工具定义、或第一条用户消息的修改都会让缓存前缀哈希失效。即使只是往系统消息里加了当前时间戳也会破坏缓存。
  4. 请求换到了别的服务商。没有 session_id,路由器可能把第二轮交给跟第一轮不同的服务商。缓存在服务商 A 上是 warm;你的请求落到服务商 B 上按全价付费。

修法都是机械的:

  • 针对 (1):合并短 prompt,或者接受短请求不吃缓存红利。
  • 针对 (2):挑一个匹配你 Agent 典型轮次节奏的 TTL。要 1 小时 Anthropic 缓存,设 provider: { cache_control: { ttl: "1h" } }
  • 针对 (3):把每次请求的动态数据(时间戳、request ID、用户 token)放到 prompt 末尾,不是开头。
  • 针对 (4):设 session_id。没有它,粘性路由只在缓存命中后才启动 — 对第 1 轮来说已经太晚。

缓存能跟 Auto Router 一起用吗?

可以。设了 session_id 后,Auto RouterPareto Router 同时锁定该会话的解析模型和服务商端点。不设 session_id,Auto Router 可以跨轮切换模型 — 这对探索没问题,但会让前一个模型持有的缓存失效。

如果你用 Auto Router 做成本优化,session_id 的价值更大。它告诉路由器「就用这次会话选的那个模型」。不设它,模型每轮切换,前缀每次都是新的,缓存写入成本会持续发生。

一个坑:如果自己设了 provider.order,你的显式顺序会覆盖粘性路由。要在多服务商模型上启用粘性路由,留空 provider.order,让 OpenRouter 自己挑。除非你有特定服务商顺序需求并愿意放弃缓存收益,否则不要用 provider 路由控制。

合在一起:Agent 循环 checklist

对任何每轮都重复同一段贵内容的 Agent:

  1. 稳定内容放最前 — system prompt、工具定义、JSON schemas、策略、长生命周期上下文。
  2. 变化内容放后面 — 用户消息、工具结果、时间戳、运行期元数据。
  3. 为会话、工单、或 workflow run 设一个稳定的 session_id
  4. 检查 response 里的 cached_tokenscache_discount,确认读取确实在发生。
  5. 要 1 小时 Anthropic TTL,设 provider: { cache_control: { ttl: "1h" } }
  6. 不要设 provider.order — 让粘性路由挑 warm 端点。

便宜 token 这件事是机械的,但顺序重要。如果修了缓存读取但开头前缀还在变,再多 session_id 也救不了。稳定前缀 + 稳定 session ID + 提供低读取价的服务商 — 这三件事一起把 6.0x 变 1.75x。

联盟推荐:FreeModel 多服务商 Agent 路由

如果你的多轮 Agent 跨多个模型服务商,FreeModel 提供一个统一 API 表面,聚合 OpenRouter 同类的多家服务商,并自带路由和缓存层。对不需要 OpenRouter 全部服务商、但又想要类似缓存路由行为的场景,FreeModel 是值得拿来跟当前方案做基准对比的轻量替代。

OpenRouter 自己的公开联盟计划在 openrouter.ai/affiliates — 如果你在自己的产品或 newsletter 里讲 OpenRouter,可以把读者链到该页面。

FAQ

OpenRouter 缓存读取价格是多少?

缓存读取价格为正常输入价格的 0.1x 到 0.5x,取决于服务商。Anthropic、DeepSeek、阿里 Qwen 可低至 0.1x。OpenAI 在 0.25x-0.50x。Gemini、Grok、Moonshot 为 0.25x。Groq 为 0.5x。写入价格在免费(Gemini、Grok、Moonshot、GPT-5.6 之前的 OpenAI、Groq)和 2.0x(Anthropic 1 小时 TTL)之间。以上来自 2026-07-21 OpenRouter 博客。

为什么通过 OpenRouter 的提示缓存没有生效?

常见原因:prompt 长度低于服务商最低要求(Anthropic 1024,Gemini 4096);缓存过期(Anthropic 默认 5 分钟 TTL);prompt 前缀不稳定(任何对第一条 system/developer 消息的修改都会破坏哈希);轮次间切换了服务商。设置稳定的 session_id,把每次请求的动态数据放到 prompt 末尾,inspect cached_tokens 确认命中。

OpenRouter 在所有服务商都支持提示缓存吗?

凡是实现了缓存机制的服务商和模型,OpenRouter 都透传支持。大多数服务商自动启用 — Anthropic 和阿里 Qwen 通过 cache_control 显式启用。详细模型清单见 OpenRouter 提示缓存文档;并非每个服务商上的每个模型都实现该功能。

如何确认缓存确实省了钱?

检查 usage.prompt_tokens_details.cached_tokens(缓存读取的 token 数)和 cache_write_tokens(缓存写入的 token 数)。cached_tokens 大于 0 即确认命中。也可以读 response 中的 cache_discount 字段看单次生成的成本影响。写入需要付费的服务商,写入那轮 discount 可能为负(写入比正常输入贵);后续读取轮次 discount 会转正。

什么是 OpenRouter 的粘性路由(Sticky Routing)?

粘性路由把后续请求锁定到持有 warm cache 的服务商。缓存请求在某个服务商成功后,OpenRouter 把同模型的下一轮请求路由回该服务商,当该服务商的缓存读取价格低于正常输入时这么做。如果粘性服务商不可用,OpenRouter 回退到下一个可用服务商,而不是直接报错。

缓存能跟 OpenRouter Auto Router 一起用吗?

可以。设置 session_id 后,Auto Router 和 Pareto Router 会同时锁定该会话的解析模型和服务商端点。没有 session_id 时,Auto Router 可能在不同轮次切换模型,让缓存失效。session_id + Auto Router 的组合提供成本最优模型选择 + 可靠缓存复用。

缓存读取最便宜应该选 Anthropic 还是 DeepSeek?

Anthropic 和 DeepSeek 都提供 OpenRouter 上最低的 0.1x 缓存读取。Anthropic 写入 1.25x(5 分钟 TTL)或 2.0x(1 小时)。DeepSeek 写入 1.0x。如果 agent 节奏密集(每隔几分钟一轮),Anthropic 较高的写入成本很快摊薄;会话稀疏时,DeepSeek 较低的写入成本可能更划算。

如果我自己设置了 provider.order 会怎样?

你显式的服务商顺序会覆盖粘性路由。请求总会落到你的第一个可用服务商,哪怕缓存前缀在另一个端点上。除非你有特定的服务商偏好且愿意放弃缓存收益,否则不要设 provider.order — 让粘性路由自己挑 warm 端点。

OpenRouter 缓存能持续多久?

取决于服务商和你设置的 TTL。Anthropic 默认 5 分钟;设 ttl: 1h 可延长到 1 小时。GPT-5.6 之前的 OpenAI 约 5-10 分钟。Gemini 隐式缓存 TTL 约 1 小时。阿里 Qwen 的 cache_control TTL 按请求配置。对长跑 agent,Anthropic 1 小时或 Gemini 1 小时隐式缓存覆盖大多数会话模式,无需每轮重新写入。

能关闭 OpenRouter 的提示缓存吗?

可以。在 Anthropic 或阿里 Qwen 上设 provider: { cache_control: { ttl: no-cache } } 即可关闭。其他隐式免费缓存的服务商没有单次请求级别的关闭开关 — 缓存自动发生,但只有真正命中读取时才省钱。如果完全不想用缓存,可以路由到不支持缓存的服务商(代价是每轮都付全价输入)。