Caveman Engine 深度解析:可恢复的本地上下文压缩引擎,15 个压缩器、S0–S4 安全阶梯与 CCR 恢复存储
Caveman Engine 是 caveman 项目中的核心压缩引擎,负责在 AI Agent 的每次模型调用前,把对话历史与工具结果中的大日志、JSON、表格、代码清单、diff 和终端输出压缩成更小的上下文,并保证压缩可精确恢复。本文基于 engine/README.md 及其配套源码,完整讲解其四调用核心 API、fail-closed 字节安全契约、内容寻址的 CCR 恢复存储、caveman-engine CLI 的每条子命令与限制参数,帮助你在现有 Agent 中接入该引擎,或将其作为库与 CLI 直接嵌入自己的工具链。
引擎要解决的问题
Agent 在单个任务中会反复把对话历史和工具结果重新发给模型。大日志、JSON、表格、代码清单、diff 和终端输出往往被同一任务重复读取多次,这些内容构成了上下文中可压缩的"噪音"。Caveman Engine 的思路是:检测内容形态 → 路由到匹配压缩器 → 在发出有损结果之前先把原始字节存到本地 → 把更小的上下文和恢复句柄交给 Agent。
由于压缩改变的是模型实际看到的内容,Engine 的设计前提是"任何压缩都必须可恢复":
- 输入无法解析时,原样透传,不声明任何节省;
- 恢复存储不可用时,有损压缩器拒绝运行;
- 压缩结果在 token 计数下不比原文更小时,同样原样透传。
Engine 完全本地运行,不需要 Caveman 账号。所有 token 减少量是本地 o200k_base 分词器给出的估计值,结果中标注 inferred 基线——它们是本地估计,不是供应商账单,也不是经过验证的节省金额。
快速开始:在现有 Agent 中使用
终端用户安装薄 CLI 后,通过本地运行时启动受支持的 Agent:
npm i -g @caveman-ai/cli
caveman claude
Claude Code、Codex、Gemini、Aider、Hermes、OpenClaw 和 opencode 均已有注册 profile。具体行为取决于 Agent 协议和可用的恢复路径;当安全的压缩路径不可用时,运行时要么收窄变换范围,要么带显式警告直接启动。
工作流程与核心 API
README 中给出的核心流水线如下:
agent context
↓
detect content type
↓
matching compressor
↓
store original locally before lossy replacement
↓
smaller context + recovery handle
Compress、Retrieve、Detect、Stats 构成稳定核心 API,被 proxy、CLI、SDK、MCP server 和 WASM 构建共同使用。在 engine/engine.go 中可以确认这一契约:
// Engine is the compression core. Construct it with New.
type Engine struct {
registry *compressors.Registry
counter tokens.Counter
store *ccr.Store
}
构造函数 New(store, counter) 有一个关键的 fail-closed 语义(见 engine.go#L36-L41):store 可以为 nil,此时引擎仍然能检测并做透传,但永远不会运行有损压缩器——因为"没有恢复路径的有损结果"违反了可逆性契约。
除四调用 API 外,源码中还有一个补充接口 Simulate:它与 Compress 走完全相同的 detect → route → compress → count 流水线,但不写入任何 CCR 记录、不发起网络调用,纯干跑报告一次真实压缩将会达到的 token 缩减。它的唯一刻意差异是:对没有 store 支撑的有损压缩器,Compress 会 fail-closed 透传,而 Simulate 仍然报告"将会发生的缩减"并以 Recoverable=false 标记,提示调用方必须先配置 CCR 才能真的发出该变换。
15 个默认压缩器
默认注册表包含 15 个压缩器,在 compressors/compressor.go 的 Default() 中逐一注册:
| # | 内容类型 | 说明 |
|---|---|---|
| 1 | json | 结构化 JSON 压缩 |
| 2 | log | 日志行压缩 |
| 3 | code | 代码清单压缩,构建时选择:启用 cgo 用 tree-sitter,否则用纯 Go go/ast 实现 |
| 4 | diff | diff 压缩 |
| 5 | search-result | 搜索结果(文件:行号)压缩 |
| 6 | text | 保守的通用文本压缩 |
| 7 | html | HTML 文档/片段 |
| 8 | tabular | 表格数据 |
| 9 | config | 配置文件 |
| 10 | tool-schema | 工具 schema |
| 11 | tool-schema-annotations | 工具 schema 注解 |
| 12 | toon | JSON 到 TOON 的无损往返编码 |
| 13 | a11y(accessibility tree) | 无障碍树 |
| 14 | repetition | 重复内容去重 |
| 15 | terminal | 终端输出 |
其中 tool-schema、tool-schema-annotations、TOON、a11y、repetition 五类永远不会被自动检测命中,只能通过强制 Options.Type 到达。每个压缩器都是纯字节变换:不数 token、不写恢复、不联网——这些职责由引擎核心在压缩器外围承担。这也是"新增压缩器只需一个文件加其测试"的原因。
压缩器接口定义同样在 compressor.go:
type Compressor interface {
ContentType() string
SafetyClass() safety.Class
Compress(input []byte) (out []byte, ok bool)
}
Compress 在任何解析问题下返回 ok=false,调用方必须原样转发原始字节。可选的 QueryAwareCompressor 能力允许压缩器在设置查询时(确定性 BM25,无 embedding)把保留内容偏向查询相关项;空查询行为与历史行为完全一致。
record 模式与失败方向
Mode 只有两个取值,定义在 result.go:
const (
ModeRecord Mode = "record" // 永远透传,默认
ModeCompress Mode = "compress"
)
record 模式绝不变换字节,输出与输入逐字节一致,也不写恢复存储。Mode.normalized() 的实现显示:空值或任何未知模式都回落到 record——未知枚举值 fail-closed。同理,evals 中未知 grader 返回 passed: false。
安全阶梯:S0–S4 分类
每个压缩器声明自己所在的安全等级,该等级内生于压缩方法本身,而非用户选项。等级注册表在 safety/safety.go 中,它回答两个"诚实问题":该类是否改变模型可见字节?是否必须先存可恢复记录(CCR)才允许运行?
| 等级 | 语义 | ByteSafe | RequiresCCR | Reversible |
|---|---|---|---|---|
| S0 | 字节安全行为(元数据、记账) | true | false | true |
| S1 | 供应商原生提示(缓存、路由) | true | false | true |
| S2 | 需要 SDK 配合的结构变换 | false | false | true |
| S3 | 行为变换(路由、推理),云端经 eval 门控 | false | false | false |
| S4 | 有损结构压缩:改变模型可见字节,必须可逆(CCR),并披露丢弃了什么 | false | true | false |
只有 S4 强制 RequiresCCR:没有恢复存储,S4 压缩器根本不允许运行。Lookup 对未知等级返回 false,调用方一律按"不可安全运行"处理。
Compress 的 fail-closed 契约
engine.go#L68-L140 中的 Compress 是引擎的心脏,其透传条件完整覆盖了 README 的承诺:
record模式 → 直接透传;- 注册表中没有匹配的内容类型压缩器 → 透传;
- 安全等级未知 → 透传;
- S4 压缩器但没有本地 store(且调用方未声明
ExternalRecovery)→ 透传; - 压缩器报告解析问题(
ok=false)→ 透传; - 压缩后 token 数不小于原文,或字节逐字节相同 → 透传,不声明任何节省;
- 有损结果的 CCR 写入失败 → 所有结果字段保持透传值,调用方永远不会拿到"没有持久句柄的变换字节"。
结果结构体 Result 的 JSON 字段如下(result.go#L47-L69):
{
"content_type": "json",
"tokens_before": 1200,
"tokens_after": 780,
"token_count_basis": "o200k_base",
"ratio": 0.35,
"basis": "inferred",
"recovery_handle": "ccr_...",
"method": "json",
"lossless_to_model": true
}
PassedThrough() 通过 recovery_handle == "" && method == "" && ratio == 0 判定本次调用是否原样透传,供上层(如 CLI 的边界检查)使用。
CCR:内容寻址的恢复存储
CCR(Caveman Context Recovery)包负责保存每次有损压缩的原始字节,engine/ccr/store.go 头部注释说明了其不变式:retrieve(handle) 必须逐字节返回原文,引擎压缩的内容绝不被销毁。
关键设计(ccr/store.go):
func Handle(original []byte) string {
sum := sha256.Sum256(original)
return "ccr_" + hex.EncodeToString(sum[:16])
}
句柄是原文的 SHA-256 前 16 字节的十六进制,前缀 ccr_。内容寻址使引擎天然幂等:同一载荷压缩两次得到同一句柄,只存储一次。
存储实现按平台分裂:宿主平台用本地 SQLite 库(ccr/store_sqlite.go),js/wasm 构建用纯 Go 内存 map(ccr/store_wasm.go,因为 modernc.org/sqlite 不支持 js/wasm 构建)。两者暴露相同类型与方法,引擎对差异无感知。CLI 默认在 ~/.caveman/ccr.db 打开该存储,路径解析在 cmd/caveman-engine/main.go#L741-L746:
CAVEMAN_CCR_DB可覆盖数据库路径;CAVEMAN_HOME可覆盖~/.caveman目录。
另外,CCR store 还持有第二个 ID 空间:原生运行时工具输出掩码铸造的 typed OBJECT id(显示为 ccr://<id>)。因此 Retrieve 在 blob 表未命中时会先回落到 object 表再报错(engine.go#L247-L266)——解析不了句柄会诱发反复重试取回,恢复路径必须解析运行时能发出的所有引用。
512 MiB 存储预算与 CAVEMAN_CCR_MAX_BYTES
持久 CCR 存储默认保留至多 512 MiB 载荷。在启动前设置 CAVEMAN_CCR_MAX_BYTES 为正整数字节数可更改上限,环境变量解析见 store_sqlite.go#L129-L132,非法值直接报错拒绝启动。达到上限后的行为同样 fail-closed:
- 新的有损变换回落为原始字节透传,CLI 报告
cave_ccr_budget_exceeded(存储不可用则为cave_ccr_unavailable); - 已存在的句柄永不被逐出,保持可恢复。
Engine CLI:caveman-engine
caveman-engine 是独立压缩引擎二进制,CLI 与其他工具通过 shell 调用它。完整子命令集:
caveman-engine compress < input
caveman-engine detect < input
caveman-engine retrieve <handle>
caveman-engine stats
caveman-engine registry
caveman-engine toon encode|decode
caveman-engine pixel render|simulate
caveman-engine evals run [--fixtures DIR]
结合 main.go 的源码,各命令的实际行为如下:
compress
compress 从 stdin 读取载荷,输出压缩后的字节到 stdout,JSON 报告写 stderr(保证 stdout 是干净的载荷字节)。支持 --type <content-type> 强制内容类型,--type auto 等价于自动检测:
caveman-engine compress --type json < payload.json
当 CCR 写入失败时,CLI 边界层 emitCompressResult 会做一致性校验:只要输出不是原始字节、或结果不满足透传条件,就直接 fatal,而不是猜测着继续——这是对引擎字节安全契约在进程边界上的最后一道守卫。
detect
detect 打印 stdin 载荷的检测到的内容类型,不需要打开恢复存储。检测算法在 detect.go 中是确定性的、fail-open 的:不确定一律归为 text(路由到保守压缩器)。严格顺序为:strict-JSON → terminal → diff → HTML → tabular → code → log → search-result → config → text。其中有几个值得注意的判断:
- terminal 放在 diff/code/log 之前,因为裸 ANSI/CSI 转义序列是决定性信号(只有终端输出会内嵌它),低置信时还要求 3 个以上裸回车(排除普通 CRLF);
- 以
#!/开头、关键词 ≥ 3 且带结构信号(花括号、缩进块等)判为代码;被日志行主导的载荷即使含代码关键词也路由到 log 压缩器; - HTML 检测会先排除携带 JSX/TSX 代码结构的载荷,避免把源码里的标记字符串当文档抽成垃圾文本。
caveman-engine detect < some.log # 输出 log
retrieve
retrieve <handle> 打印该 CCR 句柄对应的原始字节;带第二个参数(查询)时走与 MCP caveman_retrieve 工具相同的 BM25 路径,只返回最相关的章节:
caveman-engine retrieve ccr_8f3a... # 字节精确原文
caveman-engine retrieve ccr_8f3a... "timeout" # 与 timeout 最相关的段落
stats / registry
stats 从恢复存储聚合压缩记账,输出含 totals、by_content_type、storage_bytes、max_storage_bytes、storage_full 的 JSON;registry 输出 transform-capability manifest JSON——一份稳定的能力 ABI 清单,列出每个变换的 ID、安全等级、确定性标志、恢复方式与 conformance 摘要(见 compressor.go#L152-L200)。
toon encode|decode
无状态、不走 CCR 的 JSON⇄TOON 转换器:让 Agent 发出紧凑的 TOON 形式(下游再恢复 JSON),或读取大 JSON 的 TOON 渲染,而无需在自己的上下文里写入/读出冗长 JSON。编码 fail-closed:不是合法 JSON 或形状超出已证明可往返的子集时返回错误而非有损近似,调用方保留原 JSON。
pixel render|simulate
pixel render 把文本文件渲染为 <file>.pxN.png 分页 PNG 加 JSONL 指标,默认 reflow 换行,支持 --cols N、--multicol N、--density conservative|balanced|max、--dense、--no-reflow;pixel simulate 嗅探请求 JSON 格式(input → OpenAI Responses,contents → Gemini,messages → Anthropic Messages),无网络地输出像素变换的 TransformInfo JSON,--model 可覆盖或补全模型字段。
evals run
evals run [--fixtures DIR] 运行内嵌或调用方提供的评估夹具,全部通过才返回 0,任何 fixture/grader 失败均以非零退出(fail-closed)。调用方提供的夹具目录被约束在 DIR 内,路径穿越与逃逸符号链接 fail-closed。
64 MiB stdin 上限
所有读取 stdin 的命令强制 64 MiB 输入上限(maxStdinBytes = 64 << 20,main.go#L40),超限失败并报 cave_input_too_large:
if int64(len(input)) > maxBytes {
return nil, fmt.Errorf("cave_input_too_large: stdin exceeds %d bytes", maxBytes)
}
许可与构建
Engine 源码以 BSL 1.1 发布:它是 source-available,在 Change Date 2030-06-21 之前不属于 OSI 定义的开源软件;第一方自托管生产使用是被允许的。Agent SDK、薄 CLI、contracts、evals 与其他采纳面为 MIT。
在本仓库内构建与测试:
make product-build PRODUCT=engine
make product-test PRODUCT=engine
注册表 ID 为 engine。
小结
Caveman Engine 的工程价值在于把"压缩上下文"从一个有损黑盒变成了一个可审计、可恢复、处处 fail-closed 的确定性管线:15 个内容感知压缩器由注册表路由,S0–S4 安全等级把"是否允许改变模型可见字节"变成静态事实,内容寻址的 CCR 存储保证任何有损结果都能逐字节取回原文,512 MiB 预算与 64 MiB 输入上限让本地资源消耗有界可预测。所有 token 数字以 inferred 基线诚实标注,透传时不声明任何节省——这套契约使得 proxy、CLI、SDK、MCP server 与 WASM 构建可以共享同一个四调用 API,而不必各自重新实现安全性。
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 StartedRust0622
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