目标说明
读完你应能独立完成四件事:
-
交代清楚, 关于两种启用的方式, 这两种方式分别是顶层的那种, 还有块级的那种, 另外, 还要说明前缀顺序, 也就是 `tools → → ` 这样的顺序。
-
依照官方定价详表去核对, 5分钟的写入倍率以及1小时的写入倍率, 还有缓存读取倍率, 并且从`usage`当中读取出来。
` / `ens`。
-
说出为什么在会话处在进行当中的时候, 在工具方面进行增加或者删减的操作, 将工具原本有的顺序弄乱,把时间戳放置到静态之中, 这类情况都会致使缓存体系被突破呢。
-
在自己构建的 Agent 当中, 去复现 Code 的三条约束, 其中一条是 Plan Mode 运用工具进行建模, 还有一条是以``实现代替删除工具, 另外一条是复用父会话的前缀。
规格先钉死(均来自官方文档):
可启用的方式分两种, 首先请求放在最顶层的"",这样做的话断点会自动落在最后一个能够进行缓存的块上, 并且会随着多轮的操作而向前移动;其次是将""放置在具体的block之上。这两种方式是可以组合着使用的, 不过会占用最多4个断点名额当中的一个。
- **前缀顺序**: 有个叫 `tools` 的, 然后 空的两个位置状态, 是这样的。要是在它之前的任何一层出现了变化, 那么就会导致跟在它后面的每层和原来的情况不匹配啦。
, **TTL**: 默认时长为5分钟, 使用期间会自行刷新且进行使用可不追加费用;存在可选 **ttl**: `"1h"`, 写入流程时将依据更高倍率进行计算。TTL自针对其相应规定的写入/读取请求起始伊始启动累计计费计算, 流式生成时所耗费时间也会计入窗口期之内。
定价倍率, 相对 base input, 5 分钟 cache write 对应的是 1.25×, 1 小时 write 对应的是 2×, cache read 通常为 0.1×。官方定价表对Fable 5.1 / 5.1的cache hits记为0.025×, 比如base input为10 / MTok 时, hits为0.25 / MTok。本文聚焦,不展开型号发布细节。
- **可观测字段**:`
进行书写的那个, ens用于读取的那个, 断点之后出现的未缓存的部分。它们的总量大致等同于这三者加起来的总和。
适用场景与边界
适合认真做缓存
请问你提供的内容是否准确完整呢? 感觉这段表述不太清晰, 不太能依照其精准改写。请你检查或进一步明确你希望改写的内容。
- 有着长度较长的静态指令, 存在仓库约定情况(比如是 .md哈), 还具备共享知识库这点, 之后紧接着的是变化着的用户问题。
MCP 具备好些工具 , 却并不想要于每一轮之际 , 皆带上完备者 ;倾向选用短桩加以 tool , 而绝非于中途之时 , 自 `tools` 里头将其摘除。
处在需要Plan Mode的状况下, 进行只读探索时, 当存在切换行为的需求, 所依靠的并非是更换一整个完整系列进行定义的多种工具, 而是运用某个状态工具从而达成切换行为的目的。
后续聊的话上下文将满, 所以需要摘要, 调用的时候条件是必须要与那个父会话共享同样一个前缀。
不该指望缓存单独搞定
前缀比模型最小能够缓存的长度短, 要是达不到那个门槛, 那就不写缓存, 并且也不报错, 通过查看usage是不是双零来进行判断。
时而改变 `tools` 的定义, 之后更改其顺序, 接着变换名称, 并且有的时候还会在过程中增加或删减工具。
- 把变化的时间戳、会话 ID、当前文件列表塞进「静态」。
用不同种类的模型, 持续应用同一个超长的会话: 缓存根据模型的类型进行隔离;为了节省费用更改到更小的模型, 或者需要对整个段落, 展开重新构建前缀字符, 这样一来, 费用反而会偏高。
另外开启一条「专门摘要」的申请, 采用不一样的、不带有 tools: 前缀的方式, 从首个 token 开始就进行分叉, 按照全额未缓存输入来计算费用。
风险提示
字节大小的缓存要求保持一致, 断点数量最多为4个, 窗口大小约为20个block, 断点本身无需进行收费, 读取与写入的token才是真正需要付费的部分按照 (也包括平台对应的隔离边界)为区间来实施隔离。缓存并不支持跨组织共享且不发生改变地保持输出采样方式。
并发场景当中还要留意, 缓存条目必须要等到第一次响应开始之后, 才能够被后续的请求读取到。要是刚开始就并行发起多条具有相同前缀的请求, 那么有可能全部会未命中、全部进行写入操作。采用预热或者串行首请求的方式会更加稳妥。官方同样提供了 `: 0` 的预热写法, 在共享静态段上进行显式断点, 占位 user 使用非空字符串就可以, 加以确认。
放真实流量要在之后, 预热请求的配置, 要和正式流量一样, 不然写入的前缀, 和正式流量就对不上了。
步骤:按 Code 思路搭一套缓存优先
- 先开 ,再按需加
多轮的场景之中, 顶层的东西, 通常情况下就已经是够用的啦: 系统会把那个断点放置在最后一个能够进行缓存的块那里, 然后随着对话的向前推进而移动。要是静态段跟动态后缀它们的变化频率是不一样的, 那么就在静态段的末尾又钉上一个断点, 以此来避免断点落在「每一轮都会发生变化」的块上面。
Code的布局方面的经验能够直接拿来用: 有关全局稳定的内容, 以及tools要放在最前面;项目级的约定, 像.md这种, 要放在其次;会话上下文要放在再其次的位置;真正的轮次, 才放在最后。如此这般, 跨会话、跨用户时也能够尽可能地共享处于最前面的前缀。一旦其顺序被「临时插入动态字段」给破坏掉了, 命中率便会出现断崖式的下跌。
官方文档之中的常见反例为, 系统上下文(块1至5)之后跟着带有时间戳的用户块(块6), 然而却将 `` 放置于块6处。每一轮的哈希均不相同, 并且也寻觅不到更早的写入点, 其结果便是每一轮都在进行写入操作、几乎不进行读取操作。修正的方法为, 将断点固定在最后一块跨请求保持不变的内容之上。所要找寻的是「此前请求在断点位置所写下的条目」, 并非「替你自动缓存断点之前看起来稳定的内容」。
- 示例 A: 多轮 + 读 usage
```
= .()
= (
不要存档你还没读过的内容, 不要提交你尚未阅读的文件, 但不要提交你未曾读过的内容。
TOOLS =
"name": "",
"": 从该处在 . 中读取一个文本文件。""。
"": {
"type": "",
"": {"path": {"type": ""}},
"": ,
},
},
"name": "",
"": {"type": "", "": {}},
},
"name": "",
"": {
"type": "",
"": {"plan": {"type": ""}},
"": ,
},
可选:把 tools 前缀钉住()
"": {"type": ""},
},
def turn(, *, ttl=None):
= {"type": ""}
if ttl:
= ttl # 例如 "1h"
resp = ..(
model="-opus-5",
=1024,
=, #
=
"type": "text",
"text": ,
"": {"type": ""},
tools=TOOLS,
=,
u = resp.usage
print(
"": u.,
"": u.ens,
"input": u.,
"": u.,
resp
=
第 1 步, 仅仅提前阅读以整理 src/auth 的登录进口处。第 2 步, 只进行读这一动作去梳理 src/auth 的登录入口处。
r1 = turn()
.({"role": "", "": r1.})
.({"角色": "用户""":"接着进行, 详尽罗列出与之相关的测试文件所对应的路径" })
r2 = turn() # 期望 明显上升
```
第一轮常见形态是 `
同前缀的后续轮次, 应看到 `ens` 上升, 0`;若读写全是 0, 先查最小可缓存长度, 与断点是否落在变化块上。
- Plan Mode:用工具建模,而不是换工具集
"一进入那个Plan Mode, 就会换成只读工具来看", 这看起来是挺干净的, 然而, 它却会把tools的前缀改掉, 会导致整段会话缓存全都作废。Code的做法是这样的: 工具定义一直都会在现场;那两个反引号夹着的斜线本身就是工具。人进入之后, 要靠系统那边注入的说明, 或者是下一条user里的标签来约束"只进行探索、不能改文件", 等退出的时候再把计划交上去。
附带额外的收益, 该模型能够自行调用, 去处理那些棘手的难题, 而无需宿主去修改请求体, 这是其一。其二是, 在进行自建操作的时候, 要将「模式」打造成工具或者消息标签, 并且一定不要把它做成「另一份 tools 数组」, 就是这样。
如下这种同类模式, 还能够推广至「只读审查」「发布冻结」等状态: 其运用进入/退出工具去表达状态机, 将执行策略书写在消息当中, 工具清单维持恒定, 以此方式, 产品功能能够增添, 缓存前缀无需每次都跟着改变。
- ``:短桩代替删除 MCP 工具
MCP数量一旦增多, 每一轮携带完整的就会变得很贵;在中途删掉工具又会导致打穿前缀。Code使用冒号: 在请求里始终放置同一批短桩(通常先给出名称, 并标注为: true), 在需要的时候再通过工具拉取完整定义。短桩的集合以及顺序保持不变, 缓存前缀就能稳定。
表明, (针对字段名而言, 其具体应以你所运用的SDK或者平台文档作为依据;此处所传达的意思是一种限制, 并非是第二套官方的API规范)。
```
=
"name": "",
为空, "Jira(通过工具完整列出)"。
"": {"type": "", "": {}},
"": True,
},
"name": "",
"": "Fetch a pull by .",
"": {"type": "", "": {}},
"": True,
},
核心工具 + 短桩:顺序固定,会话中途不增删、不重排
tools = TOOLS +
```
处在需要用到某工具的情形下, 是由 tool 将完整的内容在后续消息里进行注入, 并非去修改顶层工具数组 `tools` , 此外官方还给出了能够经 API 使用的 tool 能力, 用于把这一层予以简化。
验收之际要盯紧两件事情, 其一, 短桩集合在整个会话的生命周期当中, 是不是字节级别的那种一致;其二, 真正完全加载好的时候, 是不是仅通过消息或者工具结果通道进入的, 并且并没有回头去修改 `tools`。前者是确保前缀, 后者是保障行为。
- :复用父会话前缀,提示放在最后一条 user
当上下文快要满的时候, 需要先把长历史送给模型去做摘要。要是另外开启请求, 更换一套「请摘要」, 并且还不带有tools的时候, 前缀从第一个token开始就会与父会话产生分叉, 长历史按照全额未缓存输入来计费。会话的时间越长, 这次「为了省上下文」的调用就会越贵。
Code 的 cache-safe :
-
用上跟父会话一模一样的, user/的, tool定义。
-
前置父会话 ;
-
把 提示**追加为新的 user 消息**;
-
预留 ,给摘要提示和摘要输出留 token。
```
= (
" the for . Keep goals, , "
def ():
同一 / TOOLS / ,不要另起「摘要专用」
= list() +
{"role": "user", "": }
turn()
```
就 API 的角度看去, 此次请求差不多等同于「在父会话的上一轮里再多出来一条 user」, 所以能够获取到已有的前缀缓存。官方在后续也将该能力径直做到了 API 当中;在进行自建的时候依旧需要明白这个前缀约束。
- 动态信息走 ,不改静态
日期, 以及当前打开的文件, 还有用户刚刚修改的配置, 要是写进静态之中, 那就等同于每一轮都要重写前缀啦。Code的习惯是这样子的: 在于下一条user消息或者里增添标签进行更新操作。静态段要维持着可缓存的状态;变化的部分则落在后缀那里。
同样的情况, 不要于会话进行当中更换模型去继续同一个长前缀。要是非得进行更换, 那么优先选用子Agent, 让当前模型书写一份, 接着开启新的会话。Code的这类子Agent通常会使用更小的模型, 就是基于这样的思路。
常见坑
-
断点被钉在所变化的那块之上, 时间戳, 请求ID, 还有本轮用户自己的原文要是恰巧处于断点所在的那块区域内, 那就没办法找到稳定的写入位置。断点, 应该被钉在那「跨请求没有变化」的处于最后的那块。
-
工具层处于前缀最靠前 的位置那种情况,中途进行增删或者重排tools,任何细微更改会致使tools//整链失配。Plan Mode以及MCP这二者,都应当避开那个「对tools数组做出改动」的行为。
-
针对于被称作静态里塞深度时间戳之物进行相关考量, 在Code复盘这个行为当中, 有特地对这类回归予以点名陈述,具体情况为, 只要是出现一次被形容为「看起来无害」的时间注入这种情况, 便能够达成让全局缓存失效此番结果。
-
与之不同的是, 关于空tools的那种摘要调用, 是按照未缓存全量来进行计费的, 而且必须要复用父前缀, 还要把提示放置在末尾的user那里。
-
中途通过换模型以达到省钱 , 缓存依照模型进行彼此隔离 , 当十万token级的会话切换至小模型时 , 有可能是需要重新构建全部的前缀 , 而账单却不一定会更低。
-
只关注延迟, 而不理会命中率, Code 将缓存命中率当作可用性指标紧盯不放, 一旦这个数值低下便开启 SEV 模式, 自建的 Agent 最少要依会话进行聚合操作, 也就是用( + + input)来处理, 在回归阶段还要予以对比甄别。
-
**TTL遭误判的情况很特别哎**, 它 默认是按照从发起请求开始计算时长为5分钟, 那种长流式的响应居然是会把窗口给消耗掉的。对于频率非常低但复用时间间隔是以小时来计算的前缀, 那就得再去考虑设置 `ttl:"1h"` 这种情况啦(写入的时候得两份哦)。
-
**JSON的键序并非保持着稳定性。**具体而言, 存在一些语言, 当对诸如 ``此类结构去实施序列化操作的时候, 那么就会扰乱键的既有排列顺序, 此时跟随发生变化的便是前缀哈希。而要是序列化层肩负起固定键序的职责, 那么在联调之时, 就得选用原始请求体去开展字节层面的对比。
-
将``拿来当作总输入。官方给出的说明是, ``主要指的是断点之后的未被缓存的段落。总成本以及限流应当按照`++input`这样去理解, 不然的话就会产生「usage很小然而账单却不小」的那种错觉。
小结
这是前缀匹配的问题, 并非那种差不多相同就能够命中的情况。官方所规定的路径是, 针对管多轮增长, 要钉住异频静态段。计费的标准是, 按照5分钟写1.25×, 1小时写2×, 读通常为0.1×(Fable 5.1 / 5.1的hits在官方表中是0.025×)。与 Code 相关的工程含义更具刚性: 工具的顺序方面存在集合冻结的情况, 动态信息会进行流转, 在 Plan Mode 中运用 Enter/Exit 工具, 于 MCP 里采用 `` 短桩, 复用父会话并结合多工具, 还将把摘要提示附加作为使用者。
落地之际, 使用三条验收线便足矣: 自同一会话第二轮起, `ens` 能否稳定逐步提高;于切换 Plan Mode / 加载 MCP 之际, `tools` 数组是否依旧字节保持一致;所请求的 +tools 与父会话是否相同。将缓存命中率视作与同等级别的指标, 才会由文档参数转变为账单以及 TTFT 上可稳定测得的演进, 最终得以改善。
参考:
