L1 第四篇算过缓存的账:命中读价 0.1×(Fable 5.1 0.025×、DeepSeek 0.02×),写入 1.25×,读一次就回本,多轮对话的二次增长被乘上 0.1。这一篇讲怎样让它命中——这是一个排列问题:缓存的是逐字节相同的前缀,所以上下文里每一段的位置、每一个字节是否稳定、每一次修改发生在哪,直接决定命中率。Manus 把 KV-cache 命中率列为”生产 agent 最重要的单一指标”,理由是它同时决定成本与 TTFT,而 agent 的输入输出比是 100 : 1——几乎全部成本都在可缓存的那一侧。

前四篇讲的每一种上下文操作都与缓存有关:第一篇的分层决定断点放哪;第三篇的 schema 稳定性影响前缀;第四篇的清理与压缩必然让缓存失效一次。这一篇把它们收拢成一套排列规则、四家的机制差异、几条反直觉的做法,以及命中率的监控与排查。

本篇要回答的核心问题是:

四家的缓存各要什么条件,断点怎么分层放?1 哪些操作会破坏前缀,为什么”屏蔽工具”比”删除工具”好?2 命中率低于多少要查什么、怎么查?3

一、总览

1. 一条规则

前缀逐字节相同才命中;顺序是 tools → system → messages;改动从改动处起全部失效。 其余一切都是这条规则的推论。

2. 排列的原则

按变化频率排四层、断点放在每层末尾;system 开头一个时间戳或中途删一个工具,就让后面全部失效

┌──────────────────────────────────────────────┐
│ tools(工具定义)                               │ ← 静态:发布时变        断点 ①
├──────────────────────────────────────────────┤
│ system:指令 · 规则 · 格式(含 schema 注入)       │ ← 静态                 断点 ②
├──────────────────────────────────────────────┤
│ system 末尾 / 首条消息:few-shot · 用户画像 · 记忆  │ ← 半静态:会话间变       断点 ③
├──────────────────────────────────────────────┤
│ messages:历史(只追加)                          │ ← 每轮追加,前面的不变    断点 ④(最后一条消息)
├──────────────────────────────────────────────┤
│ 动态段:日期 · 环境状态 · 本轮检索 · 当前输入        │ ← 每轮变,放最后,不缓存
└──────────────────────────────────────────────┘

变化频率从上到下递增;断点放在每一层的末尾;动态内容放在所有断点之后。Anthropic 最多 4 个断点,正好对应四层;OpenAI 与 DeepSeek 自动缓存不需要你放断点,但排列规则相同——自动缓存也只缓存前缀。

3. 本文的章节安排

第二章四家的机制;第三章断点的分层与放置;第四章破坏前缀的操作清单与 Manus 的三条规则;第五章反直觉的做法(屏蔽工具、保留失败、不删历史);第六章命中率的监控与排查;第七章与清理、压缩、effort 的配合;第八章实践建议。

二、四家的机制

四家的 prompt cache 机制
  Anthropic OpenAI Google(Gemini) DeepSeek
声明 显式:cache_control: {type: ephemeral} 放在具体的内容块上;或顶层一个 cache_control 自动放到最后一个可缓存块 自动(无声明);prompt_cache_key 可选,帮路由到同一缓存 隐式(自动,短 TTL)+ 显式:创建缓存对象、指定 TTL、请求引用它 自动,磁盘级前缀缓存
最小长度 按模型 1,024 token 起,不足静默不缓存 1,024 token;按 128 的倍数向下取整 模型相关 64 token 粒度
断点 最多 4 个;20 块回看窗口(系统在最后一个断点之前 20 块内找最长命中)
  • GPT-5.6 起在最近一条可缓存消息末尾
  • GPT-5.5 按 2,048 token 间隔
  • 早期模型按模型相关间隔
显式缓存对象即断点 无需
TTL 5 分钟(命中免费刷新);1 小时(写 2×) 分钟级;部分模型支持延长保留 显式缓存按设定 TTL,按小时收存储费(3.8 Flash 引入价 0.50 美元 / 百万 token · 小时) 数小时到数天
价格 读 0.1×(Fable 5.1 0.025×);写 1.25× / 2× 读 0.1×;写 1.25×(GPT-5.6 起) 读 0.1× + 存储费 读约 0.02×;无写入费
用量字段 cache_read_input_tokens、cache_creation_input_tokens input_tokens_details.cached_tokens cached_content_token_count prompt_cache_hit_tokens、prompt_cache_miss_tokens
特点 控制最细;缓存键含顶层参数(effort、thinking 配置)——逐消息 effort(beta)是特意做成不失效的例外 最省事;prompt_cache_key 让同一用户 / 会话的请求落到同一缓存 显式缓存适合”一份长文档反复问”:存一次、按小时付存储、每次问只付读价 最便宜;粒度最细(64)

两个设计差别值得注意。显式 vs 自动:Anthropic 的显式断点让你精确控制”缓存到哪”,代价是要理解规则(最小长度、20 块回看)并在代码里维护;自动缓存零维护但你不知道它缓存了哪一段——只能从 usage 反推。存储费:Gemini 的显式缓存是唯一按时间收费的,它的模型是”你租一块 KV 存储”,适合长文档问答(把 500 页手册存一小时、问二十个问题),不适合高频短会话。

三、断点的分层

1. 四层四个断点

以 Anthropic 为例(自动缓存的供应商按同样顺序排列即可):

四层四个缓存断点
断点 放在 覆盖 变化频率 命中场景
① 最后一个工具定义 全部 tools 发布时 所有请求(任何用户、任何会话)
② system 的静态部分末尾 tools + 静态 system 发布时 所有请求
③ 半静态部分末尾(用户画像、few-shot、记忆) + 半静态 会话间 / 用户间 同一用户的所有请求
④ 最后一条历史消息 + 全部历史 每轮 同一会话的下一轮

四个断点的意义是分级命中:一个新用户的第一轮命中 ①②(跨用户共享的前缀),第二轮起命中 ④。20 块回看窗口的含义:系统从最后一个断点向前找最多 20 个内容块,寻找最长的已缓存前缀——所以断点 ③④ 之间不要隔太多块。

顶层自动断点(Anthropic 2026 年加的 cache_control 顶层写法)等价于只放断点 ④ 并让它随对话前移——多轮对话的最简写法,但失去了 ①②③ 的跨会话共享。两者可以同时用(自动断点占 4 个槽位之一)。

%% 图:四个断点的分级命中——新用户第一轮命中 ①②(所有请求共享的 tools + 静态 system);同一用户的新会话命中到 ③(加上用户画像与记忆);同一会话的下一轮命中到 ④(加上全部历史);请求越「熟」,命中的前缀越长
flowchart TB
    subgraph P["前缀(按变化频率排)"]
        direction LR
        T["tools"] --> S["静态 system"] --> U["半静态:画像 · few-shot · 记忆"] --> H["历史(只追加)"] --> D["动态段:日期 · 检索 · 当前输入"]
    end
    T -.- B1["断点 ①"]
    S -.- B2["断点 ②"]
    U -.- B3["断点 ③"]
    H -.- B4["断点 ④"]
    R1["新用户第一轮"] -->|"命中到 ②"| B2
    R2["老用户新会话"] -->|"命中到 ③"| B3
    R3["同一会话下一轮"] -->|"命中到 ④"| B4
    classDef pre fill:#eefaf0,stroke:#4d9a5c,color:#222
    classDef dyn fill:#fdecea,stroke:#c0392b,color:#222
    classDef bp fill:#eef6ff,stroke:#5b8fd6,color:#222
    classDef req fill:#fff7e0,stroke:#c98a00,color:#222
    class T,S,U,H pre
    class D dyn
    class B1,B2,B3,B4 bp
    class R1,R2,R3 req

2. 最小长度的陷阱

不足最小长度的块静默不缓存——没有报错,usage 里 cache_creation_input_tokens 是 0。一个 800 token 的 system prompt 永远不会被缓存;一个团队为此困惑了一周的情况并不少见。对策:把 tools 与 system 合起来看是否过线(Anthropic 的断点覆盖的是到该块为止的全部前缀,所以 ② 覆盖 tools + system 的总长);或者接受它——800 token 每轮全价也不贵,二次增长的主体在历史。

3. 半静态层的判断

用户画像、few-shot、长期记忆放在 ② 与 ③ 之间还是 ③ 之后,取决于它们多久变一次:用户画像会话间变、会话内不变——放 ③;每轮都在更新的记忆(Claude Code 的自动记忆在会话中会写)——它的更新会让 ③ 之后全部失效,要么放到动态段、要么批量更新(会话结束时写)。

四、破坏前缀的操作

1. 清单

从最常见到最隐蔽:

破坏缓存命中的操作清单
操作 后果 修法
system 开头写当前时间(精确到秒 / 分) 每次请求前缀不同,全部失效 日期放动态段末尾;精度到天
请求间隔超过 TTL 缓存过期,下一次全量写入 会话型产品用 1 小时 TTL(Anthropic);接受低频用户不命中
修改历史中间的内容(清理旧返回、换摘要、修正一条消息) 从修改处起失效
  • 只追加
  • 清理批量做
  • 压缩接受一次失效
工具集动态增删 tools 在最前,改它让一切失效 保留全部工具、用屏蔽控制可用(第五章)
序列化不确定 JSON 键序、浮点格式、空白不同 → 字节不同 固定键序(sort_keys)、固定格式
schema 动态生成 注入的 schema 文本每次不同 schema 稳定(第三篇)
顶层参数改动 Anthropic 缓存键含 effort / thinking 配置 会话内不改;用逐消息 effort(beta)
多实例路由到不同缓存 OpenAI 的缓存按前缀哈希路由,负载均衡可能把同一会话打到不同节点 传 prompt_cache_key(如会话 id)
半静态层每轮更新 ③ 之后失效 批量更新或移到动态段
检索结果放在历史前面 每轮检索不同 → 历史全部失效 检索结果放动态段(历史之后)

2. Manus 的三条规则

Manus 的博客把它们压成三句,值得原样记:

  1. 保持 prompt 前缀稳定。 由于自回归的性质,一个 token 的差异让其后的缓存全部失效——常见的错误是在 system 开头放精确时间戳。
  2. 上下文只追加。 不修改之前的动作与观察;序列化要确定性——很多框架不保证 JSON 键序,一次序列化的差异就静默地毁掉缓存。
  3. 需要时显式标记断点。 有些供应商或框架不做自动增量缓存,要手工插断点;放断点时至少要覆盖 system prompt 的末尾。

他们给的数字:Claude Sonnet 缓存与未缓存输入价差 10 倍(0.30 vs 3.00 美元 / 百万,2025 年价目),一个 50 步的 agent 任务里这个差异是成本的主体。

%% 图:一个 50 步 agent 任务的输入成本(美元,Sonnet 每百万输入 3 美元、缓存读 0.3 美元,2025 年价目;常驻 15K,每步 +3.3K)——前缀稳定时约 2 美元,system 开头有精确时间戳时每步全价重读,约 14 美元;差的那 12 美元全是被时间戳毁掉的缓存
%%{init: {"xyChart": {"width": 760, "height": 340, "plotReservedSpacePercent": 60}, "themeVariables": {"xyChart": {"plotColorPalette": "#c0392b, #5b8fd6, #4d9a5c"}}}}%%
xychart-beta
    title "50 步 agent 任务的累计输入成本(美元)"
    x-axis "步数" [10, 20, 30, 40, 50]
    y-axis "美元" 0 --> 16
    line [0.9, 2.8, 5.7, 9.5, 14.4]
    line [0.26, 0.56, 0.96, 1.46, 2.06]

红线是前缀每步都变(时间戳、不确定序列化),蓝线是前缀稳定、只追加。两条线的差就是 Manus 说的「成本的主体」。

五、反直觉的做法

1. 屏蔽工具,不删除工具

agent 在不同阶段可用的工具不同(规划阶段不该写文件、确认前不该付款)。直觉做法是按阶段增删 tools——这让最前面的前缀失效,且历史里引用了已删除工具的调用会让模型困惑甚至报错。Manus 的做法是保留全部工具定义,用屏蔽控制可用性:自托管时在解码阶段屏蔽不允许的工具名的 logits(工具名有统一前缀如 browser_、shell_,屏蔽一个前缀即屏蔽一类);用 API 时用 tool_choice 约束(auto / required / 指定 / none)。前缀不变,可用性变了。

OpenAI 与 Anthropic 的 tool search 是另一条路:工具定义不常驻、按需加载。它解决的是”工具太多占预算”(第一篇),代价是按需加载的定义在历史中间、不可缓存;对”阶段性可用”这个问题,屏蔽仍然更好。

%% 图:按阶段控制工具可用性的两种做法——按阶段增删 tools 让最前面的前缀失效、历史里引用了已删工具的调用会让模型困惑;保留全部定义、用 logits 屏蔽(自托管,按 browser_ / shell_ 前缀屏蔽一类)或 tool_choice 约束(API)控制可用性,前缀一个字节不变
flowchart TB
    subgraph A["增删 tools(直觉做法)"]
        direction LR
        A1["规划阶段:tools = [search, read]"] --> A2["执行阶段:tools = [search, read, write, shell]"] --> A3["确认前:tools = [search, read]"]
        A1 -.- AX["每次切换 → tools 变 → 前缀全部失效<br/>历史里的 write 调用引用了已删的工具"]
    end
    subgraph B["屏蔽(Manus 做法)"]
        direction LR
        B1["全部阶段:tools = [search, read, write, shell](不变)"] --> B2["规划阶段:屏蔽 write_ / shell_ 前缀的 logits<br/>或 tool_choice 限定"] --> B3["执行阶段:全部放开"]
        B1 -.- BX["前缀逐字节不变 → 命中"]
    end
    A ~~~ B
    classDef bad fill:#fdecea,stroke:#c0392b,color:#222
    classDef ok fill:#eefaf0,stroke:#4d9a5c,color:#222
    classDef n fill:#fff7e0,stroke:#c98a00,color:#222
    class A1,A2,A3 n
    class AX bad
    class B1,B2,B3,BX ok

2. 不删失败

第四篇已讲:失败的工具返回、错误信息保留在上下文里是模型调整行为的证据。从缓存角度它还有一个理由:删除就是修改历史,让前缀失效。两个理由指向同一个做法。

3. 不”整理”历史

工程师的本能是让上下文整洁:合并连续的用户消息、把冗长的工具返回换成一句总结、纠正模型上一轮的一个错字。每一项都是修改历史、都让前缀失效、都可能破坏推理状态(L1 第三篇)。整洁的正确时机只有一个:压缩(第四篇),一次做完、接受一次失效。

4. 长 TTL 的账

Anthropic 的 1 小时 TTL 写入价 2×(vs 5 分钟的 1.25×),L1 第四篇算过要读两次才回本。什么场景值得:用户思考时间长的会话(客服里用户查资料再回来、编程助手里用户去改代码再回来)——5 分钟内没有下一轮,缓存过期,下一轮全量重写;1 小时 TTL 让这类会话的第二轮起仍然命中。判断方法:从 trace 里统计轮次间隔的分布,间隔 5–60 分钟的轮次占比高于两成就值得。

六、命中率的监控与排查

1. 定义与目标

\[\text{命中率} = \frac{n_{\text{hit}}}{n_{\text{in}} + n_{\text{hit}} + n_{\text{write}}}\]

按请求类型分开看:多轮对话与 agent 的稳态命中率应在 70–90%;单轮问答(每次不同用户、无历史)只有 ①② 能命中,命中率 = 静态前缀 / 总输入,通常 30–60%,这是结构决定的不是问题。把命中率与未缓存 token 的绝对数一起看——第一篇自测 5 的例子:渐进披露让命中率下降但总成本下降。

2. 排查表

缓存命中率的排查表
症状 最可能的原因 检查
命中率长期为 0 前缀不足最小长度;或 Anthropic 没放断点 看 cache_creation_input_tokens 是否为 0;数 tools + system 的 token
每轮都是写入、没有读取 前缀每次不同:时间戳、随机 id、不确定序列化 diff 连续两次请求体的前 N 个字节
命中率随会话长度下降 半静态层在会话中更新;或检索结果放在历史前 检查 ③ 之后的内容是否每轮变
某些用户命中、某些不命中 请求间隔超 TTL 统计轮次间隔分布
命中率在某天骤降 部署改了 system / tools / schema;或模型升级换了 tokenizer 对照部署时间线;换模型会一次性失效(正常)
多实例部署命中率低于单实例 路由到不同缓存节点 OpenAI 传 prompt_cache_key
清理 / 压缩后命中率归零 预期行为 频率是否过高

3. 从 trace 计算

L1 第二篇的中间层记录了四类 token;每个请求算命中率,按会话、按请求类型、按模型聚合;再加两个派生指标——未缓存输入 token / 请求(绝对量)与缓存节省的美元 / 天(\(n_{\text{hit}} \times (p_{\text{in}} - p_{\text{hit}})\) 减去写入加价)。第二个数字通常是让团队认真对待排列规则的那一个。

%% 图:命中率排查的决策树——先看 cache_creation 是否为 0(前缀不足最小长度或没放断点),再看是否每轮都在写入(前缀每次不同:时间戳、随机 id、序列化不确定),再看是否随会话长度下降(半静态层每轮更新或检索结果放在历史前),再看是否只有部分用户不命中(间隔超 TTL),最后看是否某天骤降(部署改了前缀或换了模型)
flowchart TB
    Q["命中率低于预期"] --> A{"cache_creation 也是 0?"}
    A -->|"是"| A1["前缀不足最小长度(1,024)<br/>或 Anthropic 没放断点"]
    A -->|"否"| B{"每轮都是写入、没有读取?"}
    B -->|"是"| B1["前缀每次不同:<br/>时间戳 · 随机 id · JSON 键序不定<br/>→ diff 连续两次请求体的前 N 字节"]
    B -->|"否"| C{"随会话长度下降?"}
    C -->|"是"| C1["半静态层每轮更新<br/>或检索结果放在历史前面"]
    C -->|"否"| D{"只有部分用户不命中?"}
    D -->|"是"| D1["请求间隔超 TTL<br/>→ 统计间隔分布,考虑 1 小时 TTL"]
    D -->|"否"| E{"某天骤降?"}
    E -->|"是"| E1["部署改了 system / tools / schema<br/>或换模型(一次性失效,正常)"]
    E -->|"否 · 多实例"| F1["路由到不同缓存节点<br/>→ 传 prompt_cache_key"]
    classDef dec fill:#eef6ff,stroke:#5b8fd6,color:#222
    classDef fix fill:#fff7e0,stroke:#c98a00,color:#222
    class A,B,C,D,E dec
    class A1,B1,C1,D1,E1,F1 fix

七、与其他操作的配合

  • 清理(第四篇):修改历史 → 失效。批量做、在清理线做,让两次清理之间的前缀稳定;服务端清理(Anthropic 的 context editing)由供应商处理缓存关系,优于客户端自己删。
  • 压缩:必然失效一次,之后以短前缀重建。压缩线离硬线要有距离,否则每几步一次压缩,缓存永远在重建。
  • effort:Anthropic 的缓存键含顶层 effort,会话中改 effort 会失效——逐消息 effort(beta 头 mid-conversation-output-config-2026-07-01)是不失效的例外;OpenAI 的 Responses 每次请求独立设 reasoning.effort,服务端状态下不影响前缀。
  • 子 agent:子 agent 有自己的上下文与前缀;它的 system 与工具定义也应稳定,多个子 agent 共享同一前缀时能互相命中(同一时间窗口内)。
  • 多供应商 fallback(L1 第六篇):切到另一家,缓存从零开始;fallback 的成本要把这一项算进去。

八、实践建议

  1. diff 两次连续请求体:取同一会话相邻两轮的完整请求体,找第一个不同的字节在哪——它就是你的有效前缀边界。多数团队第一次做会在 system 前几百字节里找到一个时间戳或 id。
  2. 按四层放断点(Anthropic)或按四层排列(其他家),动态段一律放最后。
  3. 固定序列化:sort_keys、固定浮点格式、去掉无意义空白;把”序列化确定性”写进代码审查清单。
  4. 工具全量保留 + 屏蔽,不按阶段增删。
  5. 统计轮次间隔,决定 TTL;多实例部署传 prompt_cache_key。
  6. 命中率、未缓存 token / 请求、缓存节省美元 / 天三个指标上仪表盘,按请求类型分开;命中率骤降接部署时间线告警。

九、本文小结

  • 一条规则:前缀逐字节相同才命中,顺序 tools → system → messages,改动处起失效;排列按变化频率分四层——静态工具、静态 system、半静态(画像 / few-shot / 记忆)、只追加的历史——动态段放最后。
  • 四家:Anthropic 显式断点(最多 4 个、20 块回看、1,024 起静默不缓存、5 分钟 / 1 小时)或顶层自动;OpenAI 自动(1,024、128 倍数、prompt_cache_key 路由);Gemini 隐式 + 显式缓存对象(按小时存储费,适合长文档反复问);DeepSeek 自动 64 粒度。
  • 破坏前缀的十种操作:时间戳、超 TTL、改历史、增删工具、不确定序列化、动态 schema、改顶层参数、多实例路由、半静态层每轮更新、检索放历史前。Manus 三条:前缀稳定、只追加、显式断点。
  • 反直觉:屏蔽工具不删除(logits 或 tool_choice)、不删失败、不整理历史(整理只在压缩时)、用户思考时间长的会话用 1 小时 TTL。
  • 命中率 = hit / 总输入,多轮与 agent 稳态 70–90%,单轮 30–60% 是结构决定;与未缓存 token 绝对量、节省美元一起看;排查表按症状定位。

十、自测

  1. 一个客服应用的请求体:system 开头是”当前时间:2026-10-10 14:23:07”,之后是 3,000 token 的规则、工具定义、历史。命中率接近 0。为什么?改法?

    答案

    时间戳精确到秒放在最前面,每次请求前缀的第一行就不同,其后全部失效。改法:把时间移到动态段末尾(历史之后、当前输入之前),精度到天;tools 与 system 按静态 → 半静态排列并放断点。详见第四章。

  2. Anthropic 上一个 700 token 的 system prompt 加 200 token 的工具定义,放了断点,cache_creation_input_tokens 始终为 0。原因?值得修吗?

    答案

    到断点为止的前缀 900 token 不足该模型的最小可缓存长度(1,024 起),静默不缓存。修的价值取决于历史长度:常驻层每轮全价 900 token 不贵,二次增长的主体在历史——在最后一条历史消息上放断点让历史命中才是收益所在;若想让常驻层也命中,可把 few-shot 等半静态内容一起纳入使前缀过线。详见第三章。

  3. agent 分”规划 / 执行 / 确认”三阶段,团队按阶段传不同的 tools 列表。命中率与错误率各会怎样?Manus 的做法是什么?

    答案

    tools 在前缀最前,换列表让每个阶段切换时缓存全部失效;且历史里有对已删工具的调用记录,模型会困惑、API 可能因引用不存在的工具而报错。Manus:全部工具始终在定义里,用 logits 屏蔽(自托管,按工具名前缀屏蔽一类)或 tool_choice 约束(API)控制当前阶段可用的工具——前缀不变。详见第五章。

  4. 一个会话第 2–10 轮命中率 85%,第 11 轮起变成每轮都是写入。看 trace 发现第 11 轮起每轮请求体里历史之前多了一段”用户最近浏览记录”,每轮不同。诊断与修法?

    答案

    半静态 / 动态内容被放在历史之前,每轮变化让其后的全部历史失效。修法:把浏览记录移到动态段(历史之后、当前输入之前);若它确需在 system 里,批量更新(会话开始时一次)而不是每轮。详见第三章、第六章。

  5. 从 trace 统计得到轮次间隔:60% 在 5 分钟内,30% 在 5–60 分钟,10% 超过 1 小时。Anthropic 上该用哪个 TTL?算一下 30% 那部分的账(写 1.25× vs 2×,读 0.1×,假设每个间隔后至少还有 3 轮)。

    答案

    30% 的轮次在 5 分钟 TTL 下会过期、下一轮全量重写;1 小时 TTL 覆盖它们。账:5 分钟 TTL 下这些前缀被写两次(间隔前后各一次 1.25×)加后续读;1 小时 TTL 写一次 2× 加读。间隔后有 3 轮时:5 分钟方案 1.25 + 0.3 + 1.25 + 0.3 = 3.1×,1 小时方案 2 + 0.6 = 2.6×——1 小时更省,且 TTFT 更好;占比三成足以让 1 小时 TTL 划算。超过 1 小时的 10% 两种方案都不命中。详见第五章。

  1. 条件都是前缀逐字节相同、顺序 tools → system → messages。Anthropic:显式 cache_control 放在内容块上(最多 4 个断点、20 块回看窗口、按模型 1,024 token 起不足静默不缓存、5 分钟或 1 小时 TTL、缓存键含顶层参数)或顶层一个自动断点;OpenAI:自动(1,024 起、128 倍数、GPT-5.6 起断点在最近可缓存消息末尾),prompt_cache_key 帮多实例路由;Gemini:隐式自动 + 显式缓存对象(按小时收存储费,适合长文档反复问);DeepSeek:自动磁盘缓存、64 token 粒度、无写入费。断点按变化频率分四层:① 工具定义末尾(跨用户共享)、② 静态 system 末尾、③ 半静态(画像 / few-shot / 记忆)末尾(同一用户共享)、④ 最后一条历史消息(同一会话下一轮命中);动态段(日期、环境、本轮检索、当前输入)放所有断点之后。详见第二章、第三章。 ↩

  2. 破坏前缀:system 开头的精确时间戳、请求间隔超 TTL、修改历史中间内容(清理、换摘要、纠错)、工具集动态增删(tools 在最前,一切失效)、不确定的序列化(JSON 键序 / 浮点格式)、动态生成的 schema、会话中改顶层参数(Anthropic 缓存键含 effort)、多实例路由到不同缓存节点、半静态层每轮更新、检索结果放在历史前。Manus 三条:前缀稳定、只追加(序列化确定性)、显式断点。屏蔽优于删除:按阶段增删 tools 让最前面的前缀失效且历史里引用了已删工具会让模型困惑或报错;保留全部定义、用 logits 屏蔽(自托管,按名字前缀屏蔽一类)或 tool_choice(API)控制可用性,前缀不变。同理不删失败、不整理历史——整理只在压缩时一次做完。详见第四章、第五章。 ↩

  3. 命中率 = \(n_{\text{hit}} / (n_{\text{in}} + n_{\text{hit}} + n_{\text{write}})\),多轮与 agent 稳态应 70–90%,单轮问答 30–60% 是结构决定;要与未缓存 token 绝对量、节省美元一起看。排查:长期为 0 → 前缀不足最小长度或没放断点(看 cache_creation_input_tokens);每轮都写没有读 → 前缀每次不同(diff 连续两次请求体前 N 字节找时间戳 / 随机 id / 序列化差异);随会话长度下降 → 半静态层会话中更新或检索放在历史前;部分用户不命中 → 间隔超 TTL(统计间隔分布,三成在 5–60 分钟就换 1 小时 TTL);某天骤降 → 部署改了 system / tools / schema 或换模型;多实例低于单实例 → 传 prompt_cache_key;清理 / 压缩后归零 → 预期,看频率。详见第六章。 ↩

这篇对你有用?

本文由 arganzheng 创作,采用 CC BY 4.0 许可协议。在保留原文作者、署名以及完整原文链接(https://arganzheng.life/prompt-caching-and-context-layout.html)的前提下,欢迎各种形式的转载、翻译或商业引用。


COMMENTS

评论存放在 GitHub Discussions, 用 GitHub 账号登录即可发表,支持 Markdown。 想针对正文某句话说?选中那段文字,点浮出的「评论」即可划线评论;觉得哪里写错了,发表时勾上「同时提交 Issue」。 有人回复你时 GitHub 会按你的通知设置发邮件,不用守在这里。

×