首页
/ caveman cacheengine:cache-replay 活体回放协议详解——从零网络 Preflight 到 Provider 级命中证据

caveman cacheengine:cache-replay 活体回放协议详解——从零网络 Preflight 到 Provider 级命中证据

2026-09-03 18:57:47作者:房伟宁

本文基于 caveman 仓库中的 REPLAY_PROTOCOL.md 协议文档与 cache-replaycachebench 源码实现,完整讲解 cacheengine 的活体(live)回放协议:trace v3 数据格式与三类 timing basis、零 provider 调用的 preflight 验证、带成本接受的真实执行流程、外部任务验证器的 wire 契约、以及保留证据目录的原子化布局。读完后你可以独立构建一条“可计费、可审计、可复算”的 provider 级缓存命中证据链,而不仅仅停留在本地模拟结果。

为什么活体回放被设计得“比模拟更难”

cache-replay 的目标是把一份 cachebench trace 转化为 retained provider 证据与任务质量证据。协议开篇即明确其设计立场:活体推理有真实成本,trace 内容可能暴露给 provider,而且“靠掩盖弱时间戳或用估算 token 预算无法让证据在科学上成立”(见 REPLAY_PROTOCOL.md)。

这与 cachebench 的模拟基准形成明确分工:README 中本地 golden path 报告 benchmark_simulated | publishable: false,零 provider 调用;而 cache-replay 的 evidence basis 是 provider_observed,两者永远不混合(types.goBasisSimulated/BasisObserved 两个常量即体现这一隔离)。

证据流水线:从 trace 到 exact-population 报告

协议定义了完整的证据管线:

trace.v3
  -> exact NativeRequest reconstruction
  -> cacheengine metadata-only optimization
  -> model-visible equivalence check
  -> authenticated provider request
  -> retained full response + provider usage extraction
  -> external task verifier
  -> observation.v3 + replay-evidence.v1
  -> exact-population 97% report

管线有两个关键不变式,源码中都有直接对应:

1. 请求一旦开始绝不重试。 连接失败是模糊的:即使客户端没看到响应,provider 可能已经处理并计费该请求;自动重试会造成双重计费并破坏缓存时间线(corrupt cache chronology)。在 replay_http.go 中,Send 方法注释即声明 "sends one bounded provider request without retry",且 http.Client 通过 CheckRedirect 返回 http.ErrUseLastResponse 拒绝一切重定向。

2. 绝对时间调度,而非相对间隔。 runner 在第一次 provider 调用之前先优化 trace 中所有请求,并逐一证明 wire body 与捕获 body 在模型可见意义上等价;调度使用相对第一条 trace 时间戳的绝对偏移,因此 provider 延迟不会被累加到历史的 start-to-start 间隔上。实现见 replay.goRun 方法:先 prepare(对全量 records 做 Engine.Optimize + ModelVisibleEquivalent 检查,任一失败以 model_visible_mismatch 失败码终止),再按 anchorTrace/anchorReal 锚点计算 scheduled = anchorReal.Add(offset) 分派。

-max-concurrency 约束的是完整的请求生命周期(provider 调用到任务验证完成)的并发数,取值 1~1024,默认 1(main.goflag.Int("max-concurrency", 1, ...))。每个 worker 在传输前立即测量漂移(drift);grounded 回放中漂移超过 -max-schedule-drift(默认 250ms)时,该请求不发送任何内容,直接产出 schedule_drift 失败码证据。证据中保留目标时间、实测漂移和容差。协议要求:并发度按真实的全局重叠度和 verifier 延迟配置,容量不足时让证据失败,而不是悄悄拉伸时间表。

Trace v3:绑定原始 body 与全部优化器输入

生成的 trace 记录绑定原始 body 和每一个优化器输入,字段清单如下(与 types.goTraceRecord 结构一一对应):

  • 请求 ID、provider、模型、region、endpoint、scope、epoch、partition key;
  • 预期 requests/minute 与缓存 TTL 内预期调用数;
  • runtime/auth mode;
  • 原始 body 及其 SHA-256;
  • 用于规划的声明式可缓存前缀 token 数;
  • 优化后 wire 输入 token 的声明上限(ceiling)、精确的 provider 原生最大输出 token、以及调用方提供的 token 计数依据;
  • 时间戳与 timing basis。

为什么活体回放强制 v3? 读者保留 trace.v1 仅用于旧 observation join、trace.v2 用于精确优化器重建;但 v2 缺少总输入/输出 ceiling,其旧的 prefix-token 上限无法约束实际计费量。v3 会把输出 ceiling 与 provider 请求 body 交叉校验(trace.gorequestBudgetMatchesBody):拒绝流式请求(stream: true)、模型不匹配、OpenAI 歧义的 ceiling 字段(max_tokens/max_completion_tokens/max_output_tokens 三字段只允许出现其一且与 endpoint 匹配)、缺失 ceiling 和非正值。

三种 timing basis

basis 含义
grounded_global_timestamps 真实全局请求排序;live 执行默认要求
per_partition_timestamps_only LMCache 公开语料只提供会话内间隔,无全局时间线
synthetic_schedule 确定性生成的合成负载

(常量定义见 types.goTimingGrounded/TimingPerPartition/TimingSynthetic。)

token 计数依据与计费 ceiling

声称 provider 计数的输入使用 token basis provider_counted_input_tokens;声明的总输入必须覆盖优化后 wire body,而不是原始可缓存前缀。runner 把该声明绑定进 trace 哈希,但无法独立证明调用方此前的计数操作——本地 tokenizer 或 fixture 计数需要显式降级 flag,且不能确立 provider 级输入 ceiling。

preflight 会把所有请求的声明总输入加上精确最大输出求和,得到 declared_billed_token_ceiling,并拒绝超过操作员上限的总体。provider 响应中任一 total input 或 output 超过请求声明值,该请求之后整个 run 失败replay.goprovider_input_budget_exceeded / provider_output_budget_exceeded 两个失败码)。完整 usage 提取要求 provider 原生 input/output 计数器;缺失、歧义、小数、负数或溢出的计数器全部 fail closed。注意边界:声明 ceiling 不是实际 token 上限、美元上限或 provider 发票,因为 provider 处理发生在响应计数器可被检查之前。

Preflight:零 provider 调用

每次运行都要求硬性限制:请求数、声明计费 token、trace 大小、响应大小、并发度、漂移、请求间隔。不带 -execute 时,命令只做校验并打印 trace digest 与调度表:

cd public
go run ./cacheengine/cmd/cache-replay \
  -trace /secure/grounded-trace.jsonl \
  -max-requests 500 \
  -max-declared-billed-tokens 5000000

合成/公开 trace 无法满足 grounded 默认要求。机制测试可以不发流量、选择较弱证据:

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

preflight 输出(schema caveman.cachebench.replay-preflight.v1)包含:声明总输入、声明最大输出、declared_billed_token_ceilingtiming_groundedinput_budget_claimed_provider_countedmax_concurrency——flag 永远不会重写证据 basis

从源码结构看,preflight 核心是 replay.go 中的 ValidateReplay:它强制所有 limits 为正数且并发 ≤1024、检查 request ID 去重、时间顺序单调、v3 schema 与 NativeRequest() 可重建性、每请求预算与 body 一致、declared_billed_token_ceiling 不超过 -max-declared-billed-tokens、每个 scaled gap 不超过 -max-gap(默认 10 分钟)。CLI 层的硬边界(main.go 顶部常量):

  • trace 路径必须为绝对路径;CLI 接受最多 512 MiB trace 输入与 100,000 个付费请求(maxReplayTraceBytes / maxReplayRequests);
  • 库级 trace 解码默认 96 MiB/行、100,000 条记录、64 MiB 请求 body;observation 解码默认 8 MiB/条、100,000 条;ReadTraceJSONLWithLimitsReadObservationJSONLWithLimits 允许嵌入调用方收紧边界;
  • verifier 输入与聚合保留工件以有界流方式序列化,而非全量内存 buffer。

活体执行:显式成本接受 + 私有输出目录 + 凭据 + 验证器

live 执行额外要求显式成本接受、全新私有输出目录、provider 凭据和任务验证器:

go run ./cacheengine/cmd/cache-replay \
  -trace /secure/grounded-trace.jsonl \
  -max-requests 500 \
  -max-declared-billed-tokens 5000000 \
  -max-concurrency 8 \
  -provider-timeout 2m \
  -execute \
  -accept-live-cost \
  -output /secure/cache-replay-2026-08-10 \
  -verifier-command /absolute/path/to/task-grader \
  -verifier-arg --suite \
  -verifier-arg swebench-pinned

源码中 -execute 强制要求 -accept-live-cost、绝对路径 -output-verifier-command(必须是已存在的常规文件),并预先校验 trace 涉及的所有 provider 凭据存在(validateProviderCredentials)。

内置 HTTP transport 支持的 provider

Provider 凭据环境变量 Endpoint
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

transport 安全细节(replay_http.goNewHTTPReplayTransport/authorize,均与文档逐条对应):

  • 拒绝重定向;默认 client Proxy: nil 忽略环境代理;
  • 强制 TLS 1.2+(tls.Config{MinVersion: tls.VersionTLS12}),逐请求硬超时(1s~1h,默认 2 分钟);
  • 响应大小有界(默认 16 MiB/请求,-max-response-bytes 可配);凭据绝不写入证据或错误信息;
  • 出站 body 默认超过 64 MiB 被拒绝(HTTPReplayConfig.MaxRequestBytes 可收紧或最高提到 256 MiB);
  • 自定义 base URL 需要 -allow-custom-base-url;明文 HTTP 仅对显式 loopback 且带专门测试 flag 允许;
  • 聚合的 response + verifier buffer 跨 worker 不得超过 1 GiB(CLI 启动时按 (max-response-bytes + max-verifier-output-bytes) × max-concurrency 校验)。这约束的是保留 buffer,不约束 provider SDK、内核、JSON 解码器或 verifier 进程内存。

发送前,runner 还会用原始与引擎 eligible 的总体校验目标样本下限;已知的非可缓存引擎决策在网络之前失败,唯一例外是 provider 最低长度 miss——它们保留为报告中的诚实 inelig 样本。调用方自定义 transport 与 verifier 需自行负责连接池、进程资源与更强策略。

任务验证器:stdin/stdout 的严格 wire 契约

verifier 被直接执行,从不经过 shell。每个 provider 响应通过 stdin 送入一个 JSON 对象:

{
  "schema": "caveman.cachebench.verification.v1",
  "request_id": "openai/session/0002",
  "provider": "openai",
  "model": "gpt-5.6",
  "trace_body_sha256": "<sha256>",
  "wire_body_sha256": "<sha256>",
  "original_request": {},
  "optimized_request": {},
  "provider_response": {}
}

verifier 必须恰好输出一个对象:

{
  "schema": "caveman.cachebench.verification.v1",
  "request_id": "openai/session/0002",
  "passed": true,
  "verifier": "swebench-harness@immutable-revision",
  "evidence": {"instance_id":"fixture","resolved":true}
}

以下情况全部使回放失败:未知字段、重复 JSON key、request ID 不匹配、控制字符、空 verifier identity/evidence、输出超限、超时、非零退出。解析逻辑见 replay.goParseVerificationCommandOutputDisallowUnknownFields + 尾随 JSON 检查 + schema/ID 一致性校验)。

环境隔离是源码中值得注意的一点:默认只向 verifier 暴露 PATH、locale(LANG/LC_ALL)与临时目录(TMPDIR);provider 凭据变量(OPENAI_API_KEYANTHROPIC_API_KEYGEMINI_API_KEYAWS_BEARER_TOKEN_BEDROCKAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_SESSION_TOKEN)在 blocked 集合中被硬编码禁止,即使试图通过 -verifier-env 请求也会被拒绝(main.goverifierEnvironment)。stdout/stderr 由 boundedBuffer 限制(stdout 上限 -max-verifier-output-bytes,stderr 64 KiB)。

协议最后一句定调:任务验证器拥有任务语义。请求等价性本身无法替代结果质量。

保留输出:原子写入与部分运行语义

输出目录必须不存在;命令以 mode 0700 创建它,文件原子写入、fsync、mode 0600(实现见 main.gocreateEvidenceDirectory/atomicWriteStream:临时文件 → Chmod 0600 → Sync → Rename → 目录 Sync):

manifest.json
report.json
replay-summary.json
evidence.jsonl
observations.jsonl
responses/<sha256(request_id)>.json
quality/<sha256(request_id)>.json
evidence/<sha256(request_id)>.json
observations/<sha256(request_id)>.json

每个请求的文件名从不暴露 request IDmain.goreplaySink.emitdigest([]byte(requestID)) 命名)。摘要哈希绑定 trace body、精确 wire body、保留的 provider 响应、usage 对象与 grader 工件。

replay-summary.json(schema caveman.cachebench.replay-summary.v1)在同一保留证据总体上报告 overall/per-provider 的 p50、p95、p99 与最大延迟(nearest-rank 算法见 SummarizeReplayEvidence),外加绑定到保留 usage 对象的 provider 报告 input/output token 总和。observation.v3 把低于 provider 最低长度的请求保留在总体中,但从 eligible 命中分母中排除。

中断/失败目录仍然是证据。 它绝不会被 resume 到同一总体中:墙钟时间与 provider 缓存状态都已改变。正确做法是开新的运行目录并重放完整总体。部分运行保留失败 manifest 定稿前写出的每请求工件与聚合 JSONL;completed_requests 计数发出的证据记录(含失败项);部分 observation 无法通过精确 trace join。退出码语义:manifest.json 记录 status: running/failed,gate 失败进程退出 1(见 main.go 末尾 os.Exit(1)),配置错误 2、运行时错误 3。

证据边界:publishable 恒为 false

协议以一句硬边界收尾:provider 观察到的通过只证明所提供的总体(population)。它仍不证明生产环境流行度、被省略的尾部、发票支出或 caveman 的已验证节省。因此 publishable 保持 false——main.go 中 manifest 的 EvidenceBasis 常量即写明 "provider_observed; retained responses and external task-verifier artifacts; never verified savings"。这与 cachebench README 的立场一致:模拟基准与活体证据始终是两份独立报告,provider-observed pass 之后的“生产普遍性/发票支出/已验证节省”属于 managed-ledger 工作范畴。

小结

阶段 命令/入口 证据强度
本地模拟 go run ./cacheengine/cmd/cachebench benchmark_simulated,零 provider 调用
Preflight(零流量) cache-replay 不带 -execute 仅校验 + trace digest + 调度摘要
活体回放 cache-replay -execute -accept-live-cost ... provider_observed,保留全量响应与 grader 工件,但 publishable: false

cache-replay 的整套设计可以概括为一句话:宁可让证据失败(fail closed),也不让证据变弱。绝对时间调度、无重试、发送前全量等价校验、声明计费 ceiling 与 provider 计数器双向核对、verifier 环境沙箱、哈希绑定的保留工件——每一环都在把“缓存命中率 97%”从一张嘴说出的数字,变成可复算、可审计、且明确知道自己边界在哪的 provider 级证据。相关实现可继续深入 replay.go(runner 与证据校验)、replay_http.go(transport)、trace.go(v3 读写与预算校验)以及测试 replay_test.go(如 TestReplayRunnerProducesBoundObservedPopulation 验证的绑定不变式)。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384