首页
/ Caveman Engine 压缩引擎深度解析:四操作 API、fail-closed 流水线与可恢复压缩设计

Caveman Engine 压缩引擎深度解析:四操作 API、fail-closed 流水线与可恢复压缩设计

2026-09-05 13:48:33作者:温艾琴Wonderful

Caveman Engine 是 caveman 项目中的本地 Go 压缩引擎,负责在字节到达模型之前检测输入形态、路由到带安全分级的压缩器、计算 token 缩减并保留原始数据以便恢复。本篇基于 技术文档 与仓库源码展开,读者读完后可掌握 Engine 的 Compress/Retrieve/Detect/Stats 核心 API、其 fail-closed 流水线设计、内容自动检测规则、15 个压缩器注册表与恢复存储(CCR)的配置方式,并能在本地构建、测试和扩展 Engine。

引擎定位与许可

Caveman Engine 是一个本地的 Go 库和命令行运行时,目标是削减模型上下文。它检测输入形态(shape),选择匹配的压缩器,当有损变换(lossy transform)需要恢复时先存储精确的原始字节,并且只有当结果通过大小与安全校验时才输出压缩产物。

许可方面需要注意:Engine 源码采用 Business Source License 1.1(BSL 1.1),而接口与采纳(adoption)包使用各自的包级许可。详见 LICENSING.md。从 engine/README.md 可确认补充事实:Engine 源码是 source-available,在 2030-06-21 的 Change Date 之前不是 OSI 开源许可;Agent SDK、轻量 CLI、契约与 evals 等采纳面是 MIT。

核心 API:四个稳定操作

Engine 的公开 API 由四个操作构成,这是 proxy、SDK、CLI、MCP 服务器和浏览器构建共同依赖的稳定契约(见 engine/engine.go 的包注释):

操作 用途
Compress 在给定模式与策略下检测并压缩输入
Retrieve 根据 handle 恢复精确的原始字节
Detect 只分类输入形态,不改变它
Stats 读取本地压缩统计

Simulate 是辅助操作:它评估某个变换会带来什么效果,但不产生任何常规运行时副作用。从源码看(engine/engine.go),SimulateCompress 走完全相同的 detect → route → compress → count 流水线,但不写 CCR 存储、不发任何网络请求,是一个纯 dry-run。它唯一刻意的差异是:对于无存储后端时的有损(S4)压缩器,Compress 会 fail-closed 直通,而 Simulate 仍会报告理论缩减量并以 Recoverable=false 标记"必须先配置 CCR 才能真正输出该压缩"。

一个重要约定:调用方必须把"返回原始字节"视为一个合法结果——它意味着没有任何合格变换通过全部闸门(gates)。这在 Result 结构上有专门辅助:PassedThrough() 通过检查 RecoveryHandle == "" && Method == "" && Ratio == 0 来判定本次调用是否直通(见 engine/result.go)。

Compress 的完整决策链

阅读 engine/engine.go 中的 Compress 实现,可以确认文档中"fail-closed"描述的具体顺序:

  1. record 模式永不变换,直接返回原始字节(Mode 为空或未知值时都归一化为 record,即未知枚举值也直通);
  2. 按内容类型查注册表,找不到压缩器 → 直通;
  3. 查安全类表(safety.Lookup),未知安全类 → 直通;
  4. S4(有损)压缩器要求能存储原始字节:没有恢复存储且未声明 ExternalRecovery 时 → 直通;
  5. 压缩器报告解析问题 → 直通;
  6. 压缩后 token 数不小于压缩前,或输出与输入字节相同 → 直通,"claim nothing";
  7. 需要 CCR 时先 store.Put 写入原始字节,写入失败则整体结果回退为直通并返回错误——调用方永远拿不到"没有持久化 handle 的变换字节";
  8. 只有 CCR 成功之后,才把变换后的 OutputTokensAfterRatio 等字段发布到结果上。

Options 结构(engine/result.go)中还有两个值得注意的字段:Type 用于强制内容类型(空值表示自动检测),Query 允许实现了 QueryAwareCompressor 接口的压缩器按相关性查询偏置保留内容(确定性 BM25,不用 embedding);未实现该接口的压缩器会完全忽略它。

压缩流水线

文档给出的流水线为:

flowchart LR
    A["Input bytes"] --> B["Detect shape"]
    B --> C["Select compressor"]
    C --> D["Transform"]
    D --> E{"Smaller and valid?"}
    E -- No --> A
    E -- Yes --> F{"Recovery required?"}
    F -- No --> G["Emit compact bytes"]
    F -- Yes --> H["Store exact original"]
    H --> I{"Store succeeded?"}
    I -- No --> A
    I -- Yes --> G

对应到代码,"Smaller and valid?" 闸门就是 engine/engine.go 中的 after >= before || bytes.Equal(out, input) 检查;"Store succeeded?" 闸门对应 e.store.Put 的失败处理(engine/engine.go)。

因此完整的直通条件清单是:

  • 没有匹配的内容类型压缩器;
  • 压缩器解析失败(malformed input、不支持的结构);
  • 结果不比原输入小;
  • 有损结果无法建立恢复存储;
  • record 模式(总是直通);
  • 未知模式(同样直通)。

自动检测规则

自动检测可识别以下十类输入:JSON、终端输出、diff、HTML、表格数据(tabular)、源代码、日志、搜索结果、配置文本、通用文本。

engine/detect.goDetect 实现看,检测顺序是刻意设计的,且是确定性的、fail-open 的(不确定的输入一律落入 text,路由到保守的压缩器):

  1. 严格 JSON:去空白后首字符为 {[ 且通过 json.Valid
  2. 终端输出:优先于 diff/code/log 判断,因为原始 ANSI/CSI 转义序列是"结论性信号"——没有日志或代码会合法地嵌入它;次要信号是连续裸 \r(进度条原地重绘),CRLF 行尾被排除以免误伤普通文本;
  3. diff:需要至少 4 个匹配行(diff --git@@---/+++ 等)且至少存在一个结构性标记;
  4. HTML<!doctype html><html 前缀是结论性的;否则要求容器标签 + 足够标签密度,且必须像源代码(避免把 JSX/TSX 组件误判为 HTML);
  5. tabular:委托 compressors.LooksTabular
  6. 源代码:要求至少 3 个关键词 + 结构性信号(花括号、Python suite、缩进块),且先排除日志——注释里明确说这是"价值最高的误路由修复",避免消息中包含 return/class 的日志被路由到 code 压缩器;
  7. 日志:至少 4 行,且级别词/时间戳匹配行 matched >= 3 && matched*2 >= len(lines)(占绝对多数);
  8. 搜索结果path:line:line? 或 URL 形式的行占多数;
  9. 配置文本compressors.LooksConfig
  10. 兜底 text

此外,Detect 会先 unwrapInput 剥离 agent 文件标签和行号边栏(line-number gutters)再分类——注释说明"分类文件是什么,而不是 agent 如何打印它"。

文档特别强调:部分压缩器仅限显式调用,因为自动选择会产生歧义或不安全。源码印证了这一点——engine/compressors/compressor.goDefault() 注释写明:tool-schema、lossless tool-schema、TOON、accessibility-tree(a11y)和 repetition 永远不会被自动检测命中,只能通过 Options.Type 强制路由。

压缩器注册表

默认注册表包含 15 个压缩器,与 engine/compressors/compressor.goDefault() 的实际注册顺序完全一致:

  1. JSON
  2. log
  3. code
  4. diff
  5. search result
  6. text
  7. HTML
  8. tabular
  9. configuration
  10. tool schema
  11. tool-schema annotations
  12. TOON
  13. accessibility tree(a11y)
  14. repetition
  15. terminal output

两点实现细节值得展开:

  • code 压缩器按构建时选择实现:启用 cgo 时用 tree-sitter 支持版本,否则用纯 Go 的 go/ast 版本(对应 code_cgo.gocode_nocgo.go)。
  • JSON 策略变体是实验性的engine/engine.go 中,只有环境变量 CAVE_ENGINE_TOON=best-of 时才会额外注册 NewJSONStrategy

每个压缩器都声明一个安全类(safety class)并自行实现解析与输出规则。安全类定义在 engine/safety/safety.go 的 S0–S4 阶梯中:

语义 ByteSafe RequiresCCR
S0 字节安全(元数据、记账)
S1 provider 原生提示(cache、路由)
S2 需要 SDK 配合的结构变更
S3 行为变更(路由、推理),Cloud 中由 eval 门控
S4 有损结构压缩:改变模型可见字节,必须 opt-in、必须可恢复(CCR)、必须披露丢弃内容

文档强调:当前所有注册压缩器都属于有损类 S4——即使某个特定输入在结构上可以往返(round-trip)也不改变这一点。调用方绝不能从压缩器名称推断字节安全。S4 类的 RequiresCCR: true 正是 Compress 第 4 步闸门(无存储则直通)的依据。

Compressor 接口本身(engine/compressors/compressor.go)只有三个方法:ContentType()SafetyClass()Compress()。包注释明确约束压缩器是纯字节变换:不数 token、不存恢复、不触网——这些全由引擎核心在它外围完成。这正是"新增一个压缩器只需一个新文件 + 测试"的原因。

恢复要求(CCR)

有损结果只有在能精确恢复原始字节时才能输出。这与 S4 安全类的 RequiresCCR 约束直接对应:

  • 原生调用方提供恢复存储(主机平台使用 SQLite,见 engine/ccr/store_sqlite.go);
  • 浏览器/WASM 构建使用内存存储(engine/ccr/store_wasm.go,因为 modernc.org/sqlite 不能为 js/wasm 构建);
  • 没有恢复存储时,Engine 保留原始输入(直通),绝不输出不可恢复的有损字节。

engine/ccr/store.go 的包注释可补充两个设计细节:

  • handle 是内容寻址的——原始字节的 sha256。这使引擎幂等:同一负载压缩两次得到同一 handle,只存储一次;
  • 存储"从不猜测恢复":未知 handle 是显式的 miss(ErrNotFound),而不是近似匹配。

Retrieve 本身还处理了双 ID 空间问题(engine/engine.go):压缩产生 blob handle(ccr_…,走 store.Get),而原生运行时的工具输出掩码产生 typed OBJECT id(展示为 ccr://<id>,走 store.GetObject)。查不到 blob 表时会先落到 object 表再失败,确保"恢复永远不会指向另一个死指针"。handle 的详细语义、上限与对象指针见 上下文恢复文档

Token 计量

Engine 使用离线的 o200k_base 计数器(如果可用),否则退回基于字符的估算。从 engine/tokens/tokens.go 的实现看:

  • 默认计数器是真正的 BPE tokenizer(OpenAI o200k_base,GPT-4o 家族编码),词表内嵌在二进制中,计数完全离线且确定;
  • 计数器接口(Counter)允许按 provider 替换 tokenizer 而无需改动任何压缩器;
  • BPE 计数失败时降级到确定性的 approx-chars/4 估算(~4 字符/token),保证计数错误绝不打断压缩。

因此本地计数一律使用 inferred 口径(Result.Basis,常量 BasisInferred),除非 provider 提供自己的 usage。文档明确其用途边界:这些数字适合比较与容量规划,不适合与 provider 账单对账。

输入限制

  • CLI 压缩输入上限 64 MiB:读取 stdin 的命令在超过时失败并报 cave_input_too_large(见 engine/README.md);
  • 恢复存储有独立配置容量,默认 payload 上限 512 MiB(engine/ccr/store_sqlite.goDefaultMaxStorageBytes = 512 << 20)。启动前设置环境变量 CAVEMAN_CCR_MAX_BYTES 为正字节数可改该上限;
  • 达到上限时,新的有损变换 fail-closed 为原始字节直通;已有 handle 从不被驱逐,保持可恢复(预算超限返回显式的 ErrBudgetExceeded);
  • 超限输入按调用方契约被拒绝或直通,永远不会被静默截断

构建与运行:原生与 WebAssembly

Engine 有三种运行形态:

  • Go 库;
  • 原生 CLI(供 JavaScript launcher 调用);
  • 浏览器兼容消费者的 WebAssembly 模块(WASM 使用内存恢复,因为浏览器运行时无法直接暴露主机 SQLite 存储)。

从仓库根目录构建原生 Engine 与运行测试:

go build ./engine/cmd/caveman-engine
go test ./engine/...

构建好的 CLI 提供如下子命令(见 engine/README.md):

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]

其中 evals run 的调用方 fixtures 被限制在指定目录内,路径穿越与逃逸 symlink 会 fail-closed。

如何新增一个压缩器

文档给出六条要求,与源码结构一一对应:

  1. 无歧义的名称与声明的安全类——对应 Compressor 接口的 ContentType()SafetyClass();安全类未知时 safety.Lookup 返回 ok=false,引擎 fail-closed;
  2. 有界解析与确定性输出——包注释要求每个压缩器 structural、deterministic、idempotent、fail-closed;
  3. 无效输入时回退原始字节——Compress 返回 ok=false 时调用方必须原样转发;
  4. 有损输出集成恢复——S4 必须能写入 CCR,否则引擎根本不会运行它;
  5. fixtures 覆盖合法、非法、更大输出与边界用例——仓库中的 zz_adversarial_probe*_test.go(如 engine/compressors/zz_adversarial_probe_test.go)展示了这类对抗性测试的形态;
  6. 只有当自动或显式选择行为明确时才注册进默认注册表——即 Default() 中的 Register 调用(engine/compressors/compressor.go)。

公开的 Performance 声明必须有已提交的 fixtures、方法说明、token 口径与质量检查作为支撑。详见 测试与基准文档

小结

Caveman Engine 的核心设计哲学可以用一句话概括:任何不能保证"更小、有效、可恢复"的变换都不输出。四个操作(Compress/Retrieve/Detect/Stats)加一个 Simulate dry-run 构成跨所有部署面(proxy、SDK、CLI、MCP、WASM)的稳定契约;15 个压缩器按内容类型注册,全部位于 S4 有损安全类,因此每一条压缩路径都强制经过 CCR 恢复闸门;本地 token 计数以 o200k_base BPE 离线估算为准,inferred 口径用于比较与容量规划。理解这套 fail-closed 机制后,你既可以把它作为库嵌入自己的网关,也可以按"六条要求"为新的内容形态添加压缩器。

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

项目优选

收起
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.78 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
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384