caveman 双轨优化体系解析:Prompt 缓存规划器(Cache Planner)与轨迹重写器(Trajectory Rewriter)
本文以 docs/technical/cache-and-rewriter.md 为主线,深入 caveman 仓库中两套彼此独立、互不替代的上下文优化系统:为请求注入供应商原生提示缓存控制的 cache planner,以及对老旧低价值 agent 轨迹进行受控压缩的 trajectory rewriter。读完后你会理解两套系统各自的安全门槛、fail-safe 语义、证据分离原则,以及从源码层面验证它们行为边界的具体路径,从而在代理系统中正确选型并评估其可验证性。
总览:两套系统,零响应缓存
caveman 的 token 优化不依赖"缓存模型响应"。技术文档开宗明义:
- cache planner(缓存规划器):在供应商原生提示缓存可用、且经济与请求形态支持的前提下,向请求添加 provider 原生的缓存控制标记(breakpoint / cache hint);
- trajectory rewriter(轨迹重写器):调用一个 agent 看不到的外部模型,对 agent 转录(transcript)中较老、较低价值的区段做压缩,且输出必须先通过确定性结构检查才被接受。
文档特别强调一句关键事实:"Neither system caches model responses."(两者都不缓存模型响应)。这是理解整个体系产品边界的起点:cache planner 是元数据级请求变换,provider 仍负责跑模型并报告缓存计数器;它与"重放存储输出的响应缓存"或"自托管 KV 缓存"是不同产品、不同正确性边界(见 cacheengine/README.md 的 "Product boundary" 一节)。
两套系统各自落在独立的 Go 包中:cacheengine/(缓存规划器)与 rewriter/(轨迹重写器),均随源码提供,许可为 Business Source License 1.1(见 rewriter/README.md 与 cacheengine/README.md)。
Cache Planner:本地规划,零网络调用
工作机制
Cache planner 检查一个请求,识别稳定前缀边界(stable prefix boundaries),然后添加 provider 原生的缓存提示。规划与请求优化过程不发起任何网络调用——这一点在 cacheengine/engine.go 的 Engine 结构中可以印证:它持有的是 cacheguard 守卫、前缀安全检查缓存(prefixSafety)、key 分片上限与 resolver/driver 回调,没有任何 HTTP 客户端字段;README 也明确 "Optimize makes no provider call"。
内置 planner 覆盖 Anthropic、OpenAI、Amazon Bedrock 与 Google Gemini 四类 provider;未知 provider 原样透传(pass-through),不做任何字节改动。
内置 provider 的原生行为矩阵
cacheengine/README.md 给出的 native bridge 策略表是实操选型时的核心参考:
| Surface | Behavior | Attribution ceiling |
|---|---|---|
| Anthropic | 复用既有稳定 tool/system breakpoint;追加滚动式顶层自动缓存(rolling top-level automatic caching) | 因果型 provider 观测;standalone 金额保持为零 |
| OpenAI GPT-5.6 家族 | 作用域 affinity key + 1 个稳定 breakpoint 与最近 3 个显式 breakpoint;无安全可标记块时退化为仅 affinity | 因果型 provider 观测;仓库级已验证账本扩展尚未构建 |
| 更早的 OpenAI | 基于 provider 自动缓存的作用域 affinity key | 仅 affinity |
| Bedrock Anthropic Claude | 复用 catalog 门控的稳定点 + 滚动消息 checkpoint | 因果型 provider 观测;standalone 金额保持为零 |
| Gemini | 观测隐式的 provider 托管缓存,不改写 body | 自然发生(organic),永不归属到 engine |
| Unknown | 精确透传(exact pass-through) | 不可用 |
这张表的工程含义是:planner 对不同 provider 的"归因上限"(attribution ceiling)是分档声明的,而非一刀切。也就是说,caveman 不会把所有 provider 的缓存收益都算到自己头上——Gemini 的隐式缓存被标记为 organic、不归属 engine;这正是文档中"provider observations retain their own evidence basis"的底层设计。
Fail-safe cases:planner 何时保持原请求
技术文档 列出 planner 保持原始请求不动(保持字节不变)的七类情形:
- record mode(记录模式);
- 不支持的计费档位(unsupported billing tier);
- 畸形或含糊的 JSON(malformed or ambiguous JSON);
- 重复的 JSON key;
- 不支持的 provider 行为;
- 候选边界处存在易变内容(volatile content at a candidate boundary);
- provider 语义偏离了已注册的能力数据(capability drift)。
源码侧与之对应:cacheengine/README.md 补充了更多触发 fail-safe 的条件——body/metadata 模型不匹配、非 PAYG 模式、前缀漂移(prefix drift)都会保留原始字节;"callers cache fields always win"(调用方自带的缓存字段优先)。而 cacheengine/engine.go 中 New 与 NewChecked 的构造逻辑体现了 fail-closed 原则:配置错误不会在构造时 panic 逃逸,而是被存储进 configErr,之后每一次 Plan/Optimize 调用都会返回该错误——"legacy New also stores configuration errors and makes every operation fail closed"。
此外,引擎在 engine.go 中定义了资源边界:
const (
maxConfiguredKeyShards = 1_000_000
defaultInputByteLimit = 64 << 20 // 64 MiB 默认
maxConfiguredByteLimit = 1 << 30 // 1 GiB 上限
)
provider 原生 body 与框架化稳定前缀默认各受 64 MiB 独立限制(可通过 MaxRequestBytes / MaxStablePrefixBytes 配置),且在复制或拼接之前就拒绝超限请求;显式配置的限额必须落在 1 字节到 1 GiB 之间。请求标识、分段名、profile ID 与路由元数据均有长度上限并拒绝控制字符。这些约束保证 planner 在任何路径上都不会"悄悄改坏"请求。
文档还强调一个重要的经济学纪律:planner 使用期望调用次数与 provider 计费单位做通用规划,"does not invent dollar savings when provider pricing evidence is absent"(在缺少 provider 定价证据时不虚构美元节省)。cacheengine/README.md 的通用 planner 示例印证了这一点——Economics 以 WriteMultiplier/ReadMultiplier 等输入费率单位表达(如写 1.25 倍、读 0.10 倍),"Economics use input-rate units, never guessed dollars"。
Provider observations:缓存提示不等于缓存命中
这是文档中最容易被误读的一点:一个 cache hint 不能证明发生了 cache hit。是否发生 cache read 或 cache write,由 provider 响应中的 usage 字段决定。本地规划记录始终是推断(inferred),而 provider 观测则保留其独立的证据基础。
源码中对应两个观测入口(见 cacheengine/README.md):
Observe:接受归一化后的 provider usage,区分 hit / write / miss / unavailable;ObserveRawCacheUsage:额外映射官方原始缓存计数器,包括 OpenAI 的cache_write_tokens。
两条路径都"never mint verified savings"(不凭空铸造已验证的节省)。也就是说,caveman 在证据链上严格区分三类数字:planner 的本地推断、provider 报告的真实 usage、下游模型质量——后文"选择系统"一节会说明为什么它们不可互相替代。
通用 planner 与自定义 provider
核心 planner 只认识能力(capabilities),不认识 provider 名字。给它有序的 stable segments 加缓存经济学参数,它会选择正盈亏平衡的前缀点、守卫 epoch 字节、检测易变数据与漂移,并生成 tenant 不透明、按负载分片的 affinity key。最小示例(摘自 cacheengine/README.md):
engine := cacheengine.New(cacheengine.Config{})
plan, err := engine.Plan(cacheengine.PlanRequest{
Scope: "org/project",
Epoch: "conversation-42",
ExpectedCalls: 8,
Profile: cacheengine.Profile{
ID: "provider-cache-v1", Mode: cacheengine.ModeExplicit,
MinPrefixTokens: 1024, MaxBreakpoints: 4,
EconomicsKnown: true,
WriteMultiplier: 1.25, ReadMultiplier: 0.10,
RoutingKey: true,
},
Segments: []cacheengine.Segment{
{Name: "tools", Content: toolBytes, Tokens: 1800, Stable: true, Cacheable: true},
{Name: "live", Content: userBytes, Stable: false},
},
})
其中 ExpectedCalls 的语义有明确前提:预期在 provider 缓存条目保持温热期间共享该前缀的调用次数——不要喂入跨越缓存过期间隙的终身总调用数。token 数未知时,安全变换仍然可用,但经济学报告为 unavailable。
接入新 provider 只需提供能力 profile 加一个 Driver,planner 本体不变;native profile 必须显式绑定 Provider,Driver 在无法安全编译时必须返回无优化器 ID 的原始字节(fail-safe 的最后一道防线)。
Trajectory Rewriter:受控的"只压缩、不删除"
定位与目标
Trajectory rewriter 瞄准 agent 转录中较老、较低价值的区段(older, lower-value zones)。当前支持显式配置的 Anthropic 与 OpenAI 重写模型——这一点在 rewriter/rewriter.go 的 New 构造函数中得到验证:Provider 只能是 anthropic 或 openai,未知 provider 直接报错而非静默走默认路径,"a silently mis-routed rewriter would produce bytes no gate was designed for"。rewriter/provider.go 固定了两个端点:Anthropic 的 /v1/messages(anthropic-version: 2023-06-01)与 OpenAI 的 /v1/chat/completions。
该包的头部注释(rewriter/rewriter.go)披露了机制来源:AgentDiet(arXiv 2509.23586),"parameter for parameter" 地移植进 wire proxy 而非 agent loop——一个 agent 永远看不到的外部模型、a=2 步滞后加 b=1 步额外上下文、双 θ=500 门(低于 θ 跳过、节省未过 θ 则不应用)、以及 rewrite-never-delete(被移除的文本用一句短小的 takeaway 替换,而不是直接丢弃)。
重写生效的六个前置条件
技术文档 给出的 rewrite path 六条件,与源码一一对应:
-
输入超过配置的阈值,且在任何 provider 调用之前判定。对应 Rewrite 入口:
originalTokens := countTokens(req.StepBytes) if originalTokens <= c.theta { return Result{RejectReason: reasonBelowTheta}, nil // 根本不发 API 请求 }这是"invocation gate":θ 以下直接跳过,连重写模型的开销都省了。θ 由
Config.Theta配置,默认DefaultTheta = 500(rewriter/rewriter.go),注释明确指出它是"replay-tunable parameter, not a constant of nature"——正是它让整个机制在小块语料上自我禁用、在仓库级语料上触发,实现语料自适应。 -
模型与凭据必须显式配置。
New要求Model与APIKey非空,Theta不得为负(rewriter/rewriter.go)。 -
响应必须通过结构性接受检查。这是
Accept纯函数(见下文"确定性接受门")。 -
失败信号与计数必须保留,连同 exit code 和引用。对应 rewriter/gate.go 中的 fidelity 检查族:
failure_signal、failure_detail、count、exit_code、reference。 -
输出的节省量必须超过配置的大小阈值("output beats configured size threshold")。
Accept中先于 fidelity 检查执行:if countTokens(original)-countTokens(rewritten) <= theta { return reasonTokens }即"application gate":节省量本身也要过 θ。检查顺序是"shape → saving → fidelity",文档与源码一致。
-
原始字节必须存储在恢复指针(recovery pointer)背后。见下文"恢复与信任"一节。
此外还有两个结构性检查(fail-closed 的一部分):完成被输出上限截断(gate_failure_truncated,依据 provider 自己的 stop reason:Anthropic 的 max_tokens 与 OpenAI 的 length,见 rewriter/provider.go)、模型回显 <step> 包装或 markdown 围栏(gate_failure_step_wrapper / gate_failure_fence)。输出上限由 outputCeiling 计算:以原 step 自身 token 数为诚实上限,夹在 minOutputTokens = 256 与 maxOutputTokens = 16384 之间(rewriter/rewriter.go);触顶即拒绝(reasonTruncated),绝不静默截断——因为被裁掉的尾部恰恰是"被保留的失败"所在。
被拒绝的重写永远不会插入 agent 上下文。但注意文档后半句:被拒绝尝试的 provider usage 仍然会被上报,"because spend occurred"(花费已经发生)。源码中 Result.InputTokens/OutputTokens 是重写模型自己的 provider 计量,且在拒绝调用上同样报告,好让"宣称的节省"能与"重写本身的花费"相抵(见 Result 结构注释,rewriter/rewriter.go)。
确定性接受门:比长度更严格的检查
Accept(original, rewritten, theta) 是一个纯函数——无时钟、无网络、无状态(rewriter/gate.go),离线回放网格(replay grid)用同一个函数重新打分捕获的语料,"an offline verdict and a live verdict cannot diverge"。完整拒绝原因枚举(Result.RejectReason,rewriter/rewriter.go):
below_theta / api_error / gate_failure_truncated / gate_failure_step_wrapper / gate_failure_fence / gate_failure_empty / gate_failure_tokens / gate_failure_failure_signal / gate_failure_failure_detail / gate_failure_count / gate_failure_exit_code / gate_failure_reference。
fidelity 检查的具体规则(rewriter/gate.go):
- 失败信号词(
failureNeedles,大小写不敏感):fail、error、exception、traceback、panic、fatal、warn、err!、not found、no such file、timed out、timeout、segmentation、core dumped、killed、aborted、conflict、denied、refused、unable to、cannot等。原文出现而重写文本缺失任何一个 → 拒绝。注释明确:过匹配只会让门更严。 - 失败计数必须带数字原样存活(
countPattern):把 "5 failed" 改成 "1 failed" 即使信号词全在,也属于对问题规模的撒谎;匹配带词边界,防止 "1 failed" 被 "21 failed" 满足。 - 非零 exit code 必须存活(
exitStatusPattern):exit code/status (\d+),只保护非零值("exit code 0" 不承载任何需要恢复的失败),且exit code 1不能被exit code 12满足。 - 源码位置无条件保护(
sourceLocationPatterns):path.ext:line[:col]、无扩展名Makefile:12形式、tsc/MSVC 的path.ext(line,col)、CPython traceback 的File "…", line N——出现在原文任何位置都要字节级存活,不允许意译。注释解释:go build、tsc、gcc、rustc、eslint 都把位置单独成行且该行没有失败信号词,若只在失败行范围内保护会漏掉它们。 - 失败行上的裸路径与行:列坐标受保护(非失败行上的引用可以省略——省略
ok pkg/foo 0.01s正是本组件存在的全部意义)。 - 失败细节行必须逐行存活(忽略周边水平空白):一个通用的 "error" 词不能顶替多个不同的失败;重写文本允许在诊断前加上保留的源码位置(后缀匹配),但更短的错误不能被更长的错误满足。
门的设计哲学写在 gate.go 的注释里:唯一不安全的方向是接受了丢掉一个 agent 仍需处理的失败——那会把 token 节省变成一次静默的错误转向。代价是刻意的:一个 200 条警告的编译器日志会因"源码位置无条件保护"而永久不可翻转(permanently unflippable),源码选择接受这个代价而不是开豁免,"A carve-out is only defensible once the replay grid prices that block class as material."
提示词设计与 PromptVersion
反思提示词(rewriter/prompt.go)复刻 AgentDiet 的四段结构:任务描述、<step id="…"> 包裹的 I/O 格式、三类浪费的示例、防信息丢失的准则。三类浪费(three kinds of waste):
- USELESS——从未对任何人重要的内容:构建闲聊、进度条、文件列表里的
__pycache__/.git条目、依赖解析噪声、重复的 banner 行; - REDUNDANT——窗口他处已存在的内容:编辑器回显刚写入的文本、两步前取过的文件内容、逐文件重复的警告;
- EXPIRED——产生时重要、现在已不重要的内容:agent 已把候选缩到一个文件的 grep 完整列表、已经得出结论的环境探测。
准则部分强制"condense, never delete":删除处必须留一句 takeaway(如 "individual test lines omitted; 214 PASSED");所有失败逐字保留;标识符原样保留;agent 尚未处理的内容(未应用的 diff、未读取的输出、未回答的问题)一律保留;无可安全压缩时原样返回——"That is a correct answer; a lossy rewrite is not."
输入窗口渲染(buildUserMessage,rewriter/prompt.go)按论文的 s / s-1 / s-2 记法从最新 step 向前编号,良构的 a=2 窗口目标即 "s-2"。一个重要的防御:若 StepBytes 不在 WindowBytes 中,窗口被丢弃、目标单独呈现——调用方不匹配绝不能导致模块改写了交给它的 step 之外的内容。
PromptVersion(当前为 "1",rewriter/prompt.go)承载一个强约定:内容寻址的重写存储必须以 SHA-256(stepBytes) || PromptVersion 为键,绝不能只用 step 哈希——否则编辑过的提示词会持续重放旧版本生成的重写,存储会静默混入两套回放网格分别定价的机制输出。任何对 systemPrompt 或 user-message 渲染的改动都必须 bump 该版本号。
为什么保留失败信号
技术文档 对此有一节专门解释:"Why failures are preserved"——过去的失败往往解释了 agent 为何选择当前路径;删掉一个 exit code、错误类或失败命令,会让后续步骤显得"没有动机"(unmotivated)。因此接受检查远不止检查输出长度。上文 gate.go 的五类 fidelity 检查正是这段论述的实现:每一个检查都是 survival check——证明原文中存在的东西在重写中仍然存在。
包注释还诚实地声明了一个非保证(named non-guarantee):加法式捏造(additive fabrication)不会被捕获。所有门检查都是存活性检查,没有任何约束限制模型添加什么——一句捏造的安抚("the remaining failures are unrelated")会通过所有检查。对此的防线是 live Δturns 上的 harm tripwire,而不是这个门本身(rewriter/rewriter.go)。
恢复与信任:CCR 指针
被接受的重写是模型生成的文本,不是无损编码。技术文档 的 "Recovery and trust" 一节指出:CCR 提供了访问原始轨迹的途径,但调用方在宣称"质量等价"之前仍需要做任务级评估。
实现上,被接受的重写末尾自动追加一行恢复指针(rewriter/rewriter.go):
func recoveryPointer(original []byte) string {
return "\n[condensed by caveman; original recoverable via <<ccr:" + ccr.Handle(original) + ">>]"
}
该标记语法与 proxy 压缩路径发出的 appendCCRMarker 完全相同,并被 retrieve 工具消费;句柄即 ccr.Handle(StepBytes)(engine/ccr/store.go 中的 Handle 函数)。这里有一个调用方必须承担的契约:
调用方必须在把一个被接受的重写放到 wire 上之前,把原始字节持久化到该句柄背后——即 CCR store 的 retrieve 路径可达的位置。一个句柄解析不到任何东西的标记比没有重写更糟:agent 被告知恢复存在而它并不存在。
由于恢复指针随块同行上 wire,节省量是在实际离线的字节(含指针)上测量的(rewriter/rewriter.go),所以宣称的节省永远不是虚数。
如何在两套系统之间选择
技术文档 的选型表完整继承如下:
| Need | Use |
|---|---|
| 复用精确稳定的 provider 前缀 | Cache planner |
| 缩短旧对话历史 | Trajectory rewriter |
| 避免任何模型可见的字节变化 | Record mode;仅在请求契约把缓存提示视为元数据时使用 cache hints |
| 离线工作 | Cache planner 或 Engine;不是 trajectory rewriter |
选型背后的差异可以浓缩为一句话:cache planner 是纯本地、零成本、fail-safe 的元数据变换;trajectory rewriter 是要花真实 token 钱、靠确定性门兜底、且产物需要 CCR 恢复保障的有损压缩。
两套系统可以共存,但文档划定了明确的证据边界:"Systems can coexist, but evidence must remain separate."(系统可以共存,但证据必须分离。)四类度量彼此独立、不可混同:
- cache usage(provider 报告的缓存读写);
- Engine 的 token 估算(本地推断);
- rewrite-model 的花费(重写模型自己的 provider usage);
- 下游模型质量(任务级评估)。
这与 rewriter 侧"Meter honestly"的性质声明互相印证:凡本包用自己的 token 计数器测得的都是 inferred 估计,永远不呈现为 provider 计数(rewriter/rewriter.go)。
基准边界与验证方式
技术文档 的最后一段划定了基准的负空间:
Repository cache corpus tests measure safety gates and planner behavior. They do not establish a universal provider savings rate or market ranking. Provider features and prices change; recheck capability data and public documentation before publishing current claims.
即:仓库中的缓存语料测试度量的是安全门与 planner 行为,而不是通用的 provider 节省率或市场排名。cacheengine/README.md 进一步给出验证入口与诚实声明:
go test -race ./cacheengine/...、go vet、BenchmarkOptimizeOpenAIExplicit基准、go run ./cacheengine/cmd/cache-experiment(零 provider 调用;fixture token 计数与盈亏平衡输出是建模证据,不是 live cache-hit 证据);cachebench在合成与公开 agent trace 上施加严格的 97% request-hit 与 eligible-token-hit 门,覆盖计划压缩、provider 特定 wire 变换、模型可见请求等价性、TTL 失败演练与 provider 观测 JSONL 回放;其回放协议见 cachebench/REPLAY_PROTOCOL.md——"Simulation and provider-observed reports never blend"(仿真报告与 provider 观测报告永不混合);- 内置能力行为在 2026-08-10 对照过四家 provider 的官方文档核验,README 同时声明当前公开语料工件是保守仿真,且严格 97% 门尚不通过,"No best-in-market claim exists yet."
对 rewriter 侧,其门函数纯函数化(无时钟/网络/状态)的设计直接服务于这个基准边界:离线回放与 live 判定用同一个 Accept 函数,两者不可能分歧,从而使"安全门"本身成为可回放、可审计的对象。
小结
caveman 的这套双轨优化体系可以概括为四条工程纪律:
- 规划本地化:cache planner 不做网络调用、不发明美元节省、未知 provider 精确透传、边界异常一律 fail-safe 保留原始字节(cacheengine/engine.go);
- 压缩受控化:trajectory rewriter 双 θ 门(invocation 与 application)+ 五类 fidelity 检查 + 截断即拒,保证"token 节省"永不变成"静默错误转向"(rewriter/gate.go);
- 证据分离:推断、provider 观测、重写花费、下游质量是四类不可混同的度量(cacheengine/README.md 与 rewriter/rewriter.go);
- 恢复可达:被接受的重写附带 CCR 恢复指针,原始字节必须先落盘再上线,
SHA-256(stepBytes) || PromptVersion作为内容寻址键(rewriter/prompt.go)。
对需要把长轨迹 agent 接入代理网关的读者,这套体系的参考价值不在于任何单一压缩率数字,而在于它把"何时不动请求""压缩产物凭什么可信""节省数字从哪里来"三个问题都落实到了可回放、可审计的源码与测试上——这正是 docs/technical/cache-and-rewriter.md 所定义的基准边界。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00