OpenRouter 讲解 Prompt Caching 与 Sticky Routing 如何降低 Agent 多轮 token 成本
The Cheapest Token Is a Cached One: Prompt Caching + Sticky Routing
OpenRouter 发布教程,讲解如何用提示词缓存加粘性路由降低 Agent 多轮会话的 token 成本。
原文给出各提供商缓存读写倍率表和 session_id 粘性路由用法,Agent 多轮场景可直接照做省钱。
你的 agent 每一轮都会发送相同的系统提示、工具定义、schema 和策略指令。在一个 6 轮的会话中,同一个开头块可能会被计费 6 次,即使唯一变化的是用户的最新消息或 agent 的最新工具结果。
提示缓存解决了这个问题。提供商从缓存中读取提示中重复的部分,而不是每次都按全价收费。粘性路由通过将同一会话发回持有热缓存的同一提供商,让这一机制在多个轮次间持续生效。
本文讨论成本方面:缓存 token 的价格、为什么缓存读取和写入定价不同、session_id 如何让 agent 的会话从第一轮起就保持热缓存,以及如何确认缓存确实在生效。
太长不看
- 缓存读取的成本是全新输入 token 的 0.1 倍到 0.5 倍,具体取决于提供商。在 Claude Sonnet 4.6 上,缓存读取为 $0.30/M,而输入为 $3.00/M,正好是 0.1 倍。
- 第一个请求需要支付缓存写入费用。Anthropic 的写入成本是输入的 1.25 倍(5 分钟 TTL)或 2.0 倍(1 小时 TTL),因此一次未被复用的写入比完全不缓存还要贵。
- 热缓存只有在你的下一个请求落到同一提供商端点时才有用。在 70 多家提供商中,第二轮可能命中冷端点,于是你就要付全价。
- 我们的粘性路由会将后续请求固定到持有热缓存的提供商,而
session_id会在第一个成功请求时就强制做到这一点,此时还没有发生任何缓存命中。 - 缓存未命中来自 4 种原因:提示太短、缓存过期、开头块不断变化,或请求被转移到其他提供商。检查 usage 响应中的
cached_tokens以确认命中。
提示缓存能为你节省多少 token 成本?
缓存读取的成本是正常输入定价的 0.1 倍到 0.5 倍,具体取决于提供商。正是这个区间让缓存能大幅降低 agent 循环的成本。
重复的部分通常也是昂贵的部分:冗长的系统提示、工具定义、JSON schema、护栏、检索到的文档,或用于保持模型一致性的示例。没有缓存时,每一轮都要为所有这些再次支付全价。有了缓存,第一个请求会将其写入缓存,后续请求则以更便宜的价格读回。
以下是提供商层面的概览:
| 提供商 | 缓存读取 | 缓存写入 | 如何启用 |
|---|---|---|---|
| Anthropic Claude(5 分钟 TTL) | 0.1 倍输入 | 1.25 倍输入 | 自动或显式 |
| Anthropic Claude(1 小时 TTL) | 0.1 倍输入 | 2.0 倍输入 | 显式(ttl: "1h") |
| OpenAI(GPT-5.6 之前) | 0.25 倍-0.50 倍输入 | 免费 | 自动 |
| OpenAI(GPT-5.6 及之后) | 0.25 倍-0.50 倍输入 | 1.25 倍输入 | 自动或显式 |
| Google Gemini(隐式) | 0.25 倍输入 | 免费 | 自动 |
| Grok(xAI) | 0.25 倍输入 | 免费 | 自动 |
| Moonshot AI | 0.25 倍输入 | 免费 | 自动 |
| Groq | 0.5 倍输入 | 免费 | 自动(Kimi K2 模型) |
| DeepSeek | 0.1 倍输入 | 1.0 倍输入 | 自动 |
| Alibaba Qwen | 0.1 倍输入 | 1.25 倍输入 | 显式(cache_control) |
| Z.AI | 约 0.2 倍输入 | 免费 | 自动 |
提示缓存文档中有完整明细。具体金额仍取决于模型和提供商路由;倍数告诉你的是对于该提供商,缓存输入与正常输入相比如何。
对于 agent 构建者来说,模式很简单:第一轮可能需要为建立缓存付费,但只要复用同一个开头块,之后每一轮都会便宜得多。
成本花在哪里:缓存写入与缓存读取?
提示缓存有两种成本:写入和读取。
写入发生在提供商存储提示中可复用部分时。读取发生在后续请求复用该已存储内容时。当同一内容被读取足够多次以覆盖写入成本时,你就开始获益。
在某些提供商上,写入的成本高于普通输入。Anthropic 缓存写入在默认 5 分钟 TTL 下成本为输入的 1.25 倍,在 1 小时 TTL 下为输入的 2.0 倍。一次从未被复用的 Anthropic 缓存写入,其成本高于不启用缓存发送同一提示。
对于一次性请求,缓存可能没有帮助。对于多轮智能体,重复是常态:智能体在整个会话中携带相同的指令、工具、模式和策略上下文。因此写入会在几轮之后收回成本。
对于下一轮很快到来的短时突发场景,使用 5 分钟缓存生命周期(TTL)。当会话可能暂停足够久、导致默认缓存过期,但内容仍值得保留时,使用 1 小时的。
为什么热缓存并不总能在下一次请求中帮上忙?
热缓存只有在下一个请求落到持有它的提供商端点时才有帮助。
当请求可能路由到多个提供商时,第一轮可能在一个提供商上写入缓存,而第二轮落到别处。第二个提供商没有热缓存可读取。请求仍然能工作,但你要支付全价,且 cached_tokens 保持低位或为零。
这就是我们把粘性路由与提示缓存配对使用的原因。在缓存请求之后,当某个提供商的缓存读取定价比普通输入更便宜时,我们会把同一模型的后续请求路由回同一个提供商端点。如果该粘性提供商不可用,OpenRouter 会回退到下一个可用提供商,而不是让请求失败。
默认情况下,OpenRouter 通过对其第一条 system 或 developer 消息以及第一条非 system 消息进行哈希来识别对话。当这些开头消息保持不变时,这种方式有效。
智能体经常会打破这一点。有些会在总结状态、重排工具上下文或添加新的运行元数据时重写其开头消息。当开头消息变化时,哈希就会变化,对话可能落到不同的提供商上。解决办法是显式设置 session_id。

使用 session_id 从第一轮起强制热缓存
对于智能体循环,设置 session_id。当你传入它时,OpenRouter 会直接将其用作粘性路由键,而不是从开头消息派生键。
使用 session_id 时,粘性路由会在第一次成功请求之后、任何缓存命中发生之前就生效。不使用它时,粘性只有在检测到缓存命中之后才开始。对于多轮智能体,这就是从第一轮起就可靠的缓存与只是偶尔变热的缓存之间的区别。
你可以将 session_id 作为顶层请求体字段发送,或通过 x-session-id 请求头发送。在对话或智能体运行期间保持它稳定,并将其保持在 256 个字符以内。
curl https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4.6",
"session_id": "my-agent-session-abc123",
"messages": [{"role": "system", "content": "..."}]
}'from openrouter import OpenRouter
client = OpenRouter()
resp = client.chat.send(
model="anthropic/claude-sonnet-4.6",
session_id="my-agent-session-abc123",
messages=[{"role": "system", "content": "..."}],
)import { OpenRouter } from '@openrouter/sdk';
const openRouter = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY });
const response = await openRouter.chat.send({
model: 'anthropic/claude-sonnet-4.6',
session_id: 'my-agent-session-abc123',
messages: [{ role: 'system', content: '...' }],
});使用与工作单元匹配的值:一个聊天线程、工单、工作流运行或智能体任务。不要为每一轮创建新的 session_id,否则请求将不再落到持有缓存的提供商上。
如果你使用 Auto Router 或 Pareto Router 等路由器模型,会话粘性还会在尽力而为的基础上复用路由器所选的模型,而不仅仅是提供商:在后续轮次中,只要所记住的模型仍在路由器当前候选范围内,就会优先使用它,因此当任务或你的设置发生变化时,路由器仍可能选择不同的模型。
我如何确认提示缓存确实在起作用?
最快的检查方法是检查用量。
在响应中,usage.prompt_tokens_details.cached_tokens 显示从缓存中读取了多少 token。如果大于零,说明该请求命中了缓存。cache_write_tokens 显示在缓存写入请求期间写入了多少 token。
{
"usage": {
"prompt_tokens": 10339,
"completion_tokens": 60,
"total_tokens": 10399,
"prompt_tokens_details": {
"cached_tokens": 10318,
"cache_write_tokens": 0
}
}
}在这个示例中,大部分提示词 token 来自缓存,而这一轮没有写入新的缓存条目。
你可以在三个地方检查缓存行为:Activity 页面上的详情视图、/api/v1/generation API,以及随 API 响应返回的 usage.prompt_tokens_details 对象。
使用 cache_discount 查看某次生成节省了多少。在写入需付费的提供商上,写入轮次可能会看到负折扣,因为缓存写入的成本高于普通输入。在后续的缓存读取轮次中,折扣应变为正值。

为什么你的缓存未命中,又该如何避免?
当缓存看起来失效时,通常归结为以下 4 种情况之一:提示词太短、缓存已过期、开头内容发生了变化,或者请求被路由到了不同的提供商。
提示词低于提供商的最低要求
每个提供商都有最低提示词大小,低于该值就不会缓存任何内容。在 Anthropic 上,Claude Opus 4.5 到 4.8 以及 Claude Haiku 4.5 需要 4,096 个 token;Claude Haiku 3.5 需要 2,048 个;Claude Sonnet 4、4.5 和 4.6(以及 Opus 4 / 4.1)需要 1,024 个。OpenAI 需要 1,024 个。Gemini 2.5 Pro 需要 4,096 个;Gemini 2.5 Flash 需要 1,024 个。
如果你的可复用内容低于该最低值,缓存就不会启动。不要为了强行达到要求而用填充文本塞满请求。在你已经有大量可复用内容的地方使用缓存:工具、schema、检索到的文档、示例或策略文本。
轮次之间缓存已过期
缓存不会存活太久。Anthropic 的默认值是 5 分钟,对于较长的会话可选择 1 小时。Gemini 的隐式缓存持续约 3-5 分钟,并且在你读取它时不会重置。一旦缓存过期,下一个请求就必须写入新的缓存。
如果你的用户在轮次之间经常暂停,请在支持的情况下使用更长的 TTL,或者将 agent 设计为在空闲期后接受新的写入。
提示词的开头不断变化
当提示词的开头保持不变时,自动缓存和隐式缓存效果最好。把稳定的内容放在前面:系统指令、工具、schema 和固定的参考资料。把变化的内容放在后面:用户问题、时间戳、临时状态、工具输出和短期元数据。
这里的细节很重要。第一条系统消息中的时间戳会让提示词在每一轮看起来都是新的。如果它不需要成为缓存内容的一部分,就把它移到后面的用户消息或工具消息中。
请求漂移到了不同的提供商
缓存存在于它被写入的地方。如果后续请求路由到不同的提供商端点,该端点无法读取先前的缓存。
对于 agent 工作流,设置 session_id 并让粘性路由将会话保持在热提供商上。有一个注意点:如果你自己设置了 provider.order,你的顺序会优先于粘性路由。如果你需要特定的提供商顺序,请使用提供商路由控制。
将缓存和粘性路由结合用于 agent 循环
如果你的 agent 每一轮都发送相同的内容,以下是检查清单:
- 把稳定的内容放在前面:系统提示词、工具定义、schema、策略和长期上下文。
- 把变化的内容放在后面:用户消息、工具结果、时间戳和运行特定的状态。
- 为需要显式
cache_control的提供商启用提示词缓存。 - 为对话或工作流运行设置一个稳定的
session_id。 - 检查
cached_tokens和cache_discount以确认读取正在发生。
大致来说,想象一个智能体在 6 轮对话中重复发送同样的 10,000 个 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 |
此示例仅涵盖重复内容。它忽略了较小的变化消息和模型的输出 token。节省量随轮数增加而增长。

何时使用哪种方式:
- 对于多轮对话,当复用内容随对话增长时,使用自动缓存。
- 当您确切知道哪些大块内容应被缓存时,使用显式缓存断点:检索到的文档、长参考文件、角色卡、CSV 数据或策略文本。
- 对于智能体会话、支持工单、聊天线程、工作流运行,以及任何开场消息可能在轮次之间发生变化的对话,使用
session_id。 - 对于较长的 Anthropic 会话,使用 1 小时缓存,因为默认的 5 分钟缓存可能在轮次之间过期。对于简短、密集的来回对话,使用默认缓存。
当您的智能体反复发送同样昂贵的内容时,缓存读取和粘性路由可防止它成为循环中最昂贵的部分。缓存降低了您发送的 token 的价格。关于从根本上降低每 token 价格的路由设置,请参阅如何在 OpenRouter 上获得最低成本的 LLM 推理。
常见问题
OpenRouter 支持提示缓存吗?
支持。OpenRouter 在支持的提供商和模型上支持提示缓存。大多数提供商会自动启用它,而 Anthropic 和阿里云 Qwen 使用 cache_control 进行显式缓存。缓存读取的费用为正常输入定价的 0.1x 到 0.5x,具体取决于提供商,因此复用的前缀在首次请求后会便宜得多。
在 OpenRouter 上,缓存的 token 费用是多少?
缓存读取的费用为正常输入定价的 0.1x 到 0.5x,具体取决于提供商。Anthropic、DeepSeek 和阿里云 Qwen 可以以 0.1x 读取。OpenAI 以 0.25x 到 0.50x 读取。Gemini、Grok 和 Moonshot 以 0.25x 读取。Groq 以 0.5x 读取。
为什么通过 OpenRouter 的提示缓存不工作?
常见原因包括提示低于提供商的 token 最低要求、缓存过期、提示前缀不稳定,或轮次之间提供商漂移。对于智能体工作流,首先设置一个稳定的 session_id,然后检查使用响应中的 cached_tokens,任何大于零的值都确认缓存命中。
如何在智能体的轮次之间保持缓存热?
为对话、工单或工作流运行传递一个稳定的 session_id。OpenRouter 将其用作粘性路由键,因此后续请求会路由回持有热缓存的同一提供商端点。设置 session_id 后,粘性会在首次成功请求后激活,早于观察到任何缓存命中。
如何检查缓存是否节省了费用?
检查 usage.prompt_tokens_details.cached_tokens 中的缓存读取和 cache_write_tokens 中的缓存写入;cached_tokens 值大于零即确认命中。您还可以读取响应中的 cache_discount 以查看每次生成的费用影响,或打开活动页面或 /api/v1/generation API 上的详情视图。
缓存与 Auto Router 兼容吗?
兼容。设置 session_id 后,Auto Router 和 Pareto Router 等路由器模型会为对话固定解析后的模型和提供商,因此后续轮次会持续命中同一个热缓存。
来源:OpenRouter:Announcements(RSS) · openrouter.ai