caveman cachebench:面向 Agent 提示词缓存引擎的命中率基准测试与实时回放体系
本文围绕 cachebench 展开,讲解 caveman 项目如何用一个确定性基准回答“cacheengine 能否在持续增长的工具调用 Agent 上维持至少 97% 的缓存命中”,覆盖黄金路径模拟基准、指标契约、公开 Agent 语料回放、真实 Provider 实时重放(cache-replay)与失败演练。读完你将掌握一套“零网络模拟 + 付费实测分层、证据严格隔离”的缓存命中率评测方法,以及可直接复制运行的命令、参数与源码级验证依据。
解决的核心问题:一个严格的问题陈述
cachebench 回答单一问题:能否在持续增长的工具使用型 Agent 上维持 ≥97% 缓存命中,同时不掩盖冷启动、压缩(compaction)、语义变更、无效样本或不支持的 Provider 行为?
从源码结构看,整个包通过 schema 常量把证据类型硬编码隔离(types.go):
- 模拟证据基准:
benchmark_simulated/benchmark_public_corpus_simulated; - 观测证据基准:
provider_observed; - 质量基准:
model_visible_request_equivalence(模型可见请求等价)或external_task_verifier(外部任务验证器); - trace 与 observation 均有版本化 schema(
caveman.cachebench.trace.v3、caveman.cachebench.observation.v3),模拟与观测证据永不混合。
报告结构 Report 强制携带 publishable: false 与 evidence_limitations 字段——基准结果自始就声明自己不是生产证据或已验证的节省证明。
黄金路径:零参数模拟基准
运行方式
原文档给出的命令面向 public 模块布局书写;当前仓库布局中 go.mod 位于仓库根目录,因此在仓库根目录直接执行即可:
go run ./cacheengine/cmd/cachebench
零参数运行会对 Anthropic、OpenAI、Bedrock、Gemini 四个 Provider 各跑 128 个请求。工作负载携带 8,192 个声明的稳定 system/tool 前缀 token、持续增长的用户消息、assistant 工具调用与工具结果历史,并每 64 轮计划一次压缩(compaction)。压缩开启新 epoch,其冷写入仍计入分母——这正是“不掩盖冷启动”的体现。
内置默认场景与 Provider 配置定义在 types.go 中,可直接对照:
// DefaultScenario 返回 128 轮 Agent 工作负载
Scenario{
Name: "tool-using-agent-128-turn", Turns: 128, CompactionEvery: 64,
StaticTokens: 8_192, UserTokens: 64, AssistantTokens: 96,
ToolResultTokens: 256, SummaryTokens: 512,
Step: 3 * time.Second, AssumedTTL: 5 * time.Minute,
}
四条内置 Provider 泳道(模型与端点均以仓库当前内容为准):
| Provider | 模型 | 端点 |
|---|---|---|
| anthropic | claude-sonnet-4-6 | /v1/messages |
| openai | gpt-5.6 | /v1/chat/completions |
| bedrock | global.anthropic.claude-sonnet-4-6(us-east-1) | converse |
| gemini | gemini-2.5-pro | generateContent |
预期读值
CACHEBENCH agent-cache evaluation: PASS
Evidence: benchmark_simulated | publishable: false | quality: model_visible_request_equivalence
Target: request hits >= 97.00%, eligible-token hits >= 97.00%, >= 100 eligible requests/provider
provider mode rolling eligible inelig req-hit token-hit attributed cold invalid safety gate
anthropic explicit true 128 0 99.22% 97.79% 97.79% 1 0 0 PASS
openai explicit true 128 0 99.22% 97.79% 97.79% 1 0 0 PASS
bedrock explicit true 128 0 99.22% 97.79% 97.79% 1 0 0 PASS
gemini implicit true 128 0 99.22% 97.79% 0.00% 1 0 0 PASS
这证明的是本地机制,对象是确定性的 Provider 缓存模拟:零 Provider 调用,且永远不构成生产或已验证节省的证据。注意 128 个请求中恰好 1 个冷写入(epoch 首请求),127/128 = 99.22% 的请求命中率;Gemini 的 attributed 为 0.00%,因为其隐式缓存命中是“自然(organic)”的,见下文归因说明。
完整 CLI 参数
main.go 中定义了全部标志,按功能分组如下:
工作负载形状(模拟模式)
| 参数 | 默认值 | 说明 |
|---|---|---|
-providers |
all |
逗号分隔:anthropic,openai,bedrock,gemini,all |
-turns |
128 | 每个 Provider 的 Agent 请求数 |
-compaction-every |
64 | 每 N 轮开启新缓存 epoch;0 禁用 |
-step |
3s | Agent 请求之间的时间间隔 |
-assumed-ttl |
5m | Provider profile 无显式 TTL 时的模拟 TTL |
-static-tokens |
8192 | 声明的稳定 system/tool 前缀 token 数 |
-user-tokens |
64 | 每用户轮声明 token |
-assistant-tokens |
96 | 每 assistant 工具调用声明 token |
-tool-result-tokens |
256 | 每工具结果声明 token |
-summary-tokens |
512 | 每次压缩摘要的声明 token |
门槛与输出
| 参数 | 默认值 | 说明 |
|---|---|---|
-target |
0.97 | 要求的请求与合格 token 命中率(比例值) |
-min-requests |
100 | 每 Provider 最少合格请求数 |
-format |
text | text 或 json |
-include-requests |
false | JSON 中是否包含逐请求行 |
模式切换(互斥,由 main.go 强制)
| 参数 | 说明 |
|---|---|
-observations |
提供 Provider 观测 JSONL,从模拟切换到观测回放(必须搭配 -trace-in,且不能与 -corpus 同用) |
-trace-in |
观测模式必需的请求 trace JSONL |
-trace-out |
将生成的 Provider 原生请求 trace 写为 JSONL(供 cache-replay 使用) |
-corpus |
LMCache Agent 语料文件,- 表示 stdin |
-corpus-format |
lmcache-jsonl 或 hf-rows |
-corpus-name / -corpus-license / -corpus-revision |
记录进报告的语料名称、许可、不可变 revision |
-corpus-max-rows / -corpus-max-sessions |
行数/会话数 fail-closed 上限(默认 100,000 / 10,000) |
-corpus-max-bytes |
保留语料字节上限,默认 1 GiB(1<<30) |
指标契约:冷写入必须留在分母里
主门槛(primary gates)显式包含冷写入(cold writes):
request_hit_rate = requests with cache_read_tokens > 0
/ all cache-eligible requests
token_hit_rate = sum(cache_read_tokens)
/ sum(cache-eligible prompt-prefix tokens)
两者都必须 ≥ 目标值,每个 Provider 都必须满足最小样本数,质量通过率必须等于 100%,模型可见等价失败必须为零,无效样本必须为零。速率从不排除计划内冷启动、TTL 过期、压缩或前缀失效——这与 simulate.go 的 finalizeProvider 逐项对应:任一条件未满足即追加 blocking_reasons,全部清零才 gate_passed。聚合层 aggregateProviders 还会把最小样本数乘以 Provider 数量再校验 overall。
三个容易被误读的概念:
opportunity_*_capture_rate(机会捕获率)是诊断量,永不替代门槛。它用“同一证据支持分区内此前已见过的、完全可复用前缀”除以实际读取,忽略 TTL。作用是把引擎/Provider 的实现能力与冷启动、全新后缀 token 区分开,同时保持原始 97% 目标不变。inelig(不合格)不是 miss 也不是无效:短于 Provider 最小前缀的请求只报告为不合格样本;而未知 Provider、畸形请求体、静默稳定前缀漂移、不安全变换或缺失 usage 仍是无效样本,直接令门槛失败。attributed_token_hit_rate独立统计:Gemini 的隐式命中保持“自然”归因——它改善缓存表现,但永不变成引擎因果(engine-causal)证据。
模拟缓存模型的内部实现
理解读值为何如此,需要看模拟器的三个核心机制(simulate.go):
- 前缀段与最长公共前缀:每个请求的前缀建模为
PrefixSegment(ID + token 数)数组,命中读取量取同一分组内与当前请求的最长公共前缀,且要求达到MinPrefixTokens才算读取。cold_write判定为read == 0;已有分组但读取为零记为invalidated(前缀失效)。 - TTL 过期与分区隔离:缓存前缀带
expiresAt = request.At + TTL(profile 无 TTL 时回落到场景AssumedTTL)。simulatedStateKey 用 provider/model/scope 加 partition key 构造状态键,注释明确写道“公开语料通常只提供 per-session 间隔而无全局时间线,绝不推断不相干会话在 Provider TTL 内重叠”——这是语料回放不跨会话计复用的源码级依据。 - 机会状态双轨:模拟器维护两组前缀状态——一组带 TTL 过期(真实命中),一组不过期(opportunity 机会集),从而同一遍遍历同时产出严格命中率与诊断性捕获率。
此外,OpenAI 显式模式下的查找前缀边界由 simulatedLookupPrefix 决定:解析请求体,定位含 prompt_cache_breakpoint 的消息(Responses 端点用 input 序列而非 messages),无断点且引擎走亲和回退(affinity fallback)时退化为最后一条 user/tool 消息。这保证了模拟读取量与引擎真实注入断点的位置一致。
公开 Agent 语料:LMCache Agentic Traces 回放
LMCache Agentic Traces 数据集记录了 SWE-bench、GAIA 与 WildClaw 的 Agent 请求历史,许可 CC-BY-4.0。获取脚本固定不可变 revision 并逐分片校验 SHA-256(脚本内五个 parquet 分片的字节数与摘要均硬编码),已有文件先验大小再验摘要,失败即退出:
sh cacheengine/cachebench/scripts/fetch_lmcache_agentic_traces.sh /tmp/lmcache-agentic-traces
python3 -m pip install pyarrow
五个分片按顺序流式转换为 JSONL,再作为一个连续语料整体回放。完整归一化输入约 2.43 GB,因此命令显式上调默认 1 GiB 的保留输入护栏:
cachebench_data_dir=/tmp/lmcache-agentic-traces
for cachebench_shard in "$cachebench_data_dir"/*.parquet; do
python3 cacheengine/cachebench/scripts/lmcache_parquet_to_jsonl.py "$cachebench_shard"
done | GOMEMLIMIT=12GiB go run ./cacheengine/cmd/cachebench \
-corpus - \
-corpus-name lmcache-agentic-traces/full-train \
-corpus-license CC-BY-4.0 \
-corpus-revision hf:6e043b9e89865df3aec19fd5679286b683bfd70e \
-corpus-max-bytes 3221225472 \
-providers openai \
-target .97 \
-format json
转换脚本 用 pyarrow 按批(默认 32 行,上限 4096)流式输出 JSONL,支持 --expected-sha256 在输出前校验分片摘要,BrokenPipeError 视为正常结束(配合管道使用)。
解码护栏(fail closed)
语料解码对源字节、保留字节、行数、会话数、行大小、消息数与消息大小都设上限(CorpusLimits):默认每请求最多 4,096 条消息、单行 64 MiB、单消息 16 MiB、源/保留字节各 1 GiB;而 normalizedCorpusLimits 对一切显式配置设置了硬顶——任何一项超过 16 GiB 或行数/会话数超过 1,000,000 直接报错,显式上限过高同样 fail closed。
数据集只暴露归一化消息、模型标签、输出长度和 per-session 间隔——不含完整原始 Provider 信封或工具 schema。
机器可读的固定结果
仓库内置 固定结果文件(2026-08-10 测量):
- 24,880 个请求、767 个会话(来源 swebench 665 / gaia 94 / wildclaw 8);
- 24,706 个 OpenAI 缓存合格请求、174 个低于 Provider 下限;
- 请求命中率 96.89%,估计 token 命中率 95.76%;零无效样本、零模型可见等价失败;
- 两个指标上严格门槛均为 FAIL。原因在数据本身:语料无全局时间线,基准隔离会话、不记任何跨会话复用;每会话一次冷 epoch 在其它任何损失之前就把请求命中率封顶在 96.92%((24,880−767)/24,880)——没有单独测量的跨会话复用,97% 对该总体不可达。
- 引擎捕获了 100% 的会话内可复用机会:23,938 个请求、665,558,422 个估计 token。机会捕获是诊断量,不会把失败的原始门槛变成通过。结果仍是使用 o200k 估计的确定性模拟,不是 Provider 计数器,更不是可发布的节省证据。
跨 Provider 分片回放:固定分片 train-00000-of-00005 上每个内置适配器各跑 4,976 个请求(固定结果中的 shard 区块):
| Provider | 请求命中率 | 估计 token 命中率 | 机会捕获(请求/token) |
|---|---|---|---|
| Anthropic | 97.01% | 95.69% | 99.36% / 99.23% |
| OpenAI | 97.63% | 96.43% | 100% / 100% |
| Bedrock | 97.01% | 95.69% | 99.36% / 99.23% |
| Gemini | 97.01% | 95.69% | 99.36% / 99.23% |
所有严格门槛均因 token 率失败;零无效样本、零等价失败。OpenAI 捕获 100% 可复用请求/token 机会;其余三家因 5 分钟 TTL 假设使 31 个本可复用请求过期而分别停在 99.36%/99.23%。OpenAI 受益于保证的 30 分钟 GPT-5.6 保留期;直连 Anthropic 与选定 Bedrock profile 使用保证的 5 分钟保留期;Gemini 的取值是带 5 分钟回退的自然模拟,永不作为引擎因果归因。
给语料命令附加 -trace-out /tmp/lmcache-openai-trace.jsonl 可将同一批 Provider 原生请求以 trace v3 导出。由于公开语料的时间戳仍是会话局部、token 数仍是 o200k 估计,实时运行器默认拒绝它们;显式降级标志只是在预检中保留这些限制,并不制造任何“有据可依”的证据。语料模拟与实时证据始终是两份独立报告。
真实 Provider 回放:cache-replay
生成可回放的请求体
go run ./cacheengine/cmd/cachebench \
-providers openai \
-trace-out /tmp/cachebench-openai.jsonl
生成的 trace v3 记录(TraceRecord)绑定原始请求体与全部优化器输入:请求 ID、Provider、模型、region、端点、scope、epoch、分区键;预期 RPM 与 TTL 内预期调用数;运行/鉴权模式;原始体与 SHA-256;用于规划的声明可缓存前缀 token;优化后 wire 总输入上限、Provider 原生最大输出 token 与调用方 token 计数基准;时间戳与时序基准。
零网络预检
cache-replay 执行精确的优化器重建、认证 Provider 调用、保留响应捕获、外部任务验证和有界绝对时间并发调度,且整条 trace 在第一次实时调用前必须全部通过优化与模型可见等价。先从零网络预检开始:
go run ./cacheengine/cmd/cache-replay \
-trace /tmp/cachebench-openai.jsonl \
-max-requests 128 \
-max-declared-billed-tokens 5000000 \
-allow-ungrounded-timing \
-allow-estimated-token-budget
有据可依(grounded)的生产 trace 会省略降级标志。预检打印声明总输入、声明最大输出、declared_billed_token_ceiling、timing_grounded、input_budget_claimed_provider_counted 与 max_concurrency;标志从不改写证据基准。
实时执行
实时模式额外要求 -execute -accept-live-cost -output <新私有目录> -verifier-command <path>。REPLAY_PROTOCOL.md 给出了完整证据管线:
trace.v3
-> 精确 NativeRequest 重建
-> cacheengine 仅元数据优化
-> 模型可见等价检查
-> 认证 Provider 请求
-> 保留完整响应 + Provider usage 提取
-> 外部任务验证器
-> observation.v3 + replay-evidence.v1
-> 精确总体 97% 报告
关键执行约束:
- 请求开始之后绝不重试:连接失败可能是歧义的(Provider 可能已处理并已计费),自动重试会导致双重计费并破坏缓存时序。
- 绝对时间调度:排程使用首个 trace 时间戳的绝对偏移,Provider 延迟不叠加到历史 start-to-start 间隔上;
-max-concurrency约束 1..1024(默认 1)个活跃回放生命周期,每个 worker 在发送前立即测量漂移,漂移超过-max-schedule-drift时发出schedule_drift并不发送该请求。并发容量不足会使证据失败,而不是静默拉伸排程。 - 预算校验:预检把所有请求的声明总输入加精确最大输出求和为
declared_billed_token_ceiling,超过运营上限即拒绝;Provider 响应总输入/输出超过请求声明即在该请求后失败运行。完整 usage 提取要求 Provider 原生输入与输出计数器——缺失、歧义、小数、负数或溢出全部 fail closed。 - CLI 硬上限:trace 输入 512 MiB、合计配置的响应/验证器缓冲 1 GiB、付费 trace 总体 100,000 请求、Provider 请求受可配置
-provider-timeout(默认 2 分钟)约束;trace、输出与验证器路径必须为绝对路径。库层 trace 解码默认每 JSONL 记录 96 MiB、100,000 条记录、64 MiB 请求体;观测解码默认每记录 8 MiB、100,000 条记录,ReadTraceJSONLWithLimits/ReadObservationJSONLWithLimits允许嵌入方收紧边界。
内置 HTTP 传输支持四家 Provider(凭据均走环境变量,重定向一律拒绝,默认客户端忽略环境代理、要求 TLS 1.2+、限制响应大小、绝不把凭据写入证据或错误信息):
| Provider | 凭据环境变量 | 端点 |
|---|---|---|
| OpenAI | OPENAI_API_KEY |
Chat Completions 或 Responses |
| Anthropic | ANTHROPIC_API_KEY |
Messages |
| Gemini | GEMINI_API_KEY |
generateContent |
| Bedrock | AWS_BEARER_TOKEN_BEDROCK,或 access key + secret + 可选 session token |
Converse;使用 IAM 时走 SigV4 |
observation v3 与评估
运行器发出 observation v3 记录(结构见 ObservationRecord):
{"schema":"caveman.cachebench.observation.v3","request_id":"request-001","request_body_sha256":"<sha256-from-trace>","provider_evidence_sha256":"<sha256-of-retained-provider-response>","provider":"openai","epoch":"agent-epoch-1","eligible_input_tokens":10000,"cache_eligible":true,"applied":true,"engine_decision":"apply","engine_reason":"applied","profile_id":"openai-gpt-5.6-explicit-v1","attribution":"causal","optimizer_ids":["openai-prompt-cache-key","cave-cache-openai-explicit-v1"],"quality_passed":true,"quality_verifier":"swebench-harness@pinned-revision","quality_evidence_sha256":"<sha256-of-retained-grader-artifact>","usage":{"input_tokens_details":{"cached_tokens":9900,"cache_write_tokens":100}}}
评估 Provider 计数器:
go run ./cacheengine/cmd/cachebench \
-observations /tmp/cachebench-observations.jsonl \
-trace-in /tmp/cachebench-openai.jsonl
嵌入代码仍可用 NewObservationRecord 构造记录:它绑定真实 NativeResult 归因与优化器 ID,以及保留的 Provider 响应、验证器身份与质量工件的 SHA-256。观测评估器拒绝重复 ID/JSON 键、未知字段、畸形/负计数器、超过合格输入的计数器、无精确优化器证据的因果标签、任何失败的任务验证与低于要求数量的样本。回放层的失败也使用稳定失败码(validReplayFailureCode):schedule_drift、model_visible_mismatch、engine_not_cacheable、clock_regression、transport_error、provider_response_invalid、provider_http_status、provider_usage_invalid、provider_input_budget_exceeded、provider_output_budget_exceeded、quality_verifier_error、quality_provenance_missing。
保留输出目录必须不存在;命令以 0700 创建,文件原子写入、同步、0600,逐请求文件名以 SHA-256 派生、绝不暴露请求 ID(布局见 REPLAY_PROTOCOL.md:manifest.json、report.json、replay-summary.json、evidence.jsonl、observations.jsonl 与四个逐请求子目录)。被中断/失败的目录本身仍是证据,且永不续入同一总体——墙上时间与 Provider 缓存状态都已改变。
Provider 观测层面的通过仍不证明被省略的尾部流量、流量普遍性、Provider 账单支出或已验证节省——那些属于受管账本工作,因此 publishable 恒为 false。
失败演练与退出码
失败演练验证门槛确实会拒绝坏场景。让所有 Anthropic 请求都超出 5 分钟 TTL(默认间隔 3 秒改为 6 分钟):
go run ./cacheengine/cmd/cachebench \
-providers anthropic \
-turns 128 \
-step 6m \
-target 0.97
预期 FAIL。程序退出码语义固定(见 main.go 的出口逻辑):门槛失败退出 1;无效输入/配置退出 2;仅当每个选中的 Provider 都通过门槛时退出 0。
证据边界小结
cachebench 的设计可以浓缩为四条边界:
- 模拟证明机制:黄金路径与语料回放都是确定性 Provider 缓存模拟,零网络调用,永远不是生产或节省证据(
benchmark_simulated/benchmark_public_corpus_simulated)。 - 观测证明总体:实时回放用 Provider 原生计数器评估,但只证明所供应的精确总体,不证明流量普遍性与账单节省(
provider_observed,publishable: false)。 - 门槛从不放水:冷启动、TTL 过期、压缩、前缀失效全部留在分母;低于下限的请求是
inelig而非 miss;无效样本与等价失败一票否决。 - 诊断与门槛分离:
opportunity_*捕获率解释“引擎实现得有多好”,97% 原始门槛回答“这个数字对用户成立吗”——两者不可互相替代。
自定义 Provider 的接入路径同样明确:构造 Trace 值并调用 EvaluateTrace,与内置场景共用同一套缓存与安全门槛;cacheengine 中同一套 Profile/Driver 仍是缓存语义的唯一来源。
相关文件索引:cacheengine/cachebench/README.md、cacheengine/cachebench/REPLAY_PROTOCOL.md、cacheengine/cachebench/types.go、cacheengine/cachebench/simulate.go、cacheengine/cachebench/corpus.go、cacheengine/cachebench/replay.go、cacheengine/cmd/cachebench/main.go、cacheengine/cmd/cache-replay/main.go、cacheengine/cachebench/scripts/fetch_lmcache_agentic_traces.sh、cacheengine/cachebench/results/lmcache-agentic-traces-2026-08-10.json。
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