首页
/ Caveman Engine 深度解析:内容感知压缩、S0–S4 安全分级与 CCR 无损恢复

Caveman Engine 深度解析:内容感知压缩、S0–S4 安全分级与 CCR 无损恢复

2026-09-04 14:25:26作者:柯茵沙

Caveman Engine 是 caveman 项目(一个以“用更少 token 完成同样的事”为目标的 Claude Code skill 生态)的核心压缩引擎:它检测一段载荷的内容类型,路由到按安全等级划分的压缩器,统计 token 缩减比例,并把原始字节存入 CCR 存储以备恢复。本文基于仓库文档 engine/CLAUDE.md 与对应源码,完整拆解其稳定四调用 API(Compress/Retrieve/Detect/Stats)的工作方式、S0–S4 安全分级的含义、15 个默认压缩器的注册机制,以及“fail-closed、只报 inferred、绝不宣称 verified”这套诚实性不变量背后的工程实现。

引擎核心:detect → route → compress → 计数 → CCR

引擎的入口类型是 engine.go 中的 Engine 结构体,由三部分构成:压缩器注册表(compressors.Registry)、token 计数器(tokens.Counter)和 CCR 存储(*ccr.Store)。New(store, counter) 构造时若传入 nil counter 会使用默认离线 BPE 计数器,store 允许为 nil——但此时引擎仍然能检测和无损压缩,只是永远不运行有损压缩器,因为有损结果若无法恢复就违背了可逆性契约(见 engine.go#L31-L41)。

Compress(input, opts) 的完整流水线与文档描述完全一致,且五个直通(pass-through)条件都能在源码中逐条对应:

  1. record 模式直通record 模式下输出与输入逐字节相同,不做任何转换,也不存储恢复记录;
  2. 无匹配压缩器直通:内容类型在注册表中找不到对应压缩器时原样返回;
  3. 解析失败直通:压缩器报 parse 问题(ok=false)时转发原始字节;
  4. 结果不更小直通after >= before || bytes.Equal(out, input) 时原样放行、不声称任何缩减(见 engine.go#L110-L112);
  5. 有损但不可恢复直通:S4(有损)压缩器只有在原始字节能先存入 CCR 后才可运行;没有 store 时 fail closed 到直通。

只有 CCR Put 成功之后,res.Output 才会被替换为压缩结果——这意味着调用方绝不可能收到“没有持久化 handle 的转换后字节”(见 engine.go#L116-L139)。Result 结构(result.go)还带有 Basis 字段:token 数字的计量基础永远是 inferred(本地估算),因为压缩发生在计费前,拿不到 provider 的 usage。PassedThrough() 用“无 handle、无 method、ratio 为 0”三条件判定本次是否直通。

Simulate 是 API 之外的一个补充:与 Compress 完全同管道但不做任何存储、不发起网络调用。它有意与 Compress 保留一处差异——S4 压缩器在无 store 时,Compress 直通,而 Simulate 会照报“将会实现的缩减”并标记 Recoverable=false,让调用方知道需要先配好 CCR 才能真正发出这个转换。

Mode 与 Options:未知值一律 fail closed

result.go 中只有两个模式:ModeRecord(默认,直通)和 ModeCompress(运行路由到的压缩器)。Mode.normalized() 对空值和任何未知模式都回落到 record——文档里“unknown mode fails closed to record”在实现里就是一行 default: return ModeRecord

Options 的其余三个字段各有明确边界:

  • Type:强制指定内容类型,留空则自动检测;
  • Query:非空时让实现了 QueryAwareCompressor 的压缩器用确定性 BM25(无 embedding)偏向保留相关内容;不实现该接口的压缩器完全忽略它,且查询永远不会让输出比无查询结果更大;
  • ExternalRecovery:允许嵌入式网关在引擎本地 CCR 之外提供字节级恢复,前提是调用方在转发压缩字节前已自行存好原始字节。

另外,engine.go#L55-L61 显示环境变量 CAVE_ENGINE_TOON=best-of 会向注册表额外注册 JSONStrategy 压缩器——这是默认 15 个之外唯一的注册表变动入口。

内容路由器:12 种类型,低置信度一律落到 text

detect.go 定义了引擎的内容类型常量:jsonlogcodediffsearch-resulttexttoonhtmla11yterminaltabularconfigDetect 是纯确定性函数,判断顺序与源码一致:严格 JSON → 终端 → diff → HTML → 表格 → 代码 → 日志 → 搜索结果 → 配置 → text,任何低置信度情形都开放到下一个检测器,最终兜底为 text(对应“low confidence → text”的文档声明)。

源码中有几处值得注意的误路由防护:

  • 终端检测放在 diff/code/log 之前,因为裸 ANSI/CSI 转义序列是唯一性信号——只有真实终端输出才会携带它,不会抢走任何 log/code/JSON 流量;次要信号是密集裸 \r(进度条原地重绘),且排除了普通 CRLF;
  • looksLikeCode 先问“是不是日志”:由日志级别/时间戳行主导的载荷即使消息里含 returnclass 等关键词,也路由到日志压缩器。注释称之为“最高价值的误路由修复”(见 detect.go#L106-L112);
  • HTML 检测会让位给源码:JSX/TSX 组件或内嵌标记字符串字面量的源文件,因为有 =>className=import 等代码结构信号,会路由到代码压缩器而不是被按 HTML 抽成文本;
  • 代码判定要求“至少 3 个关键词 + 一个结构性信号”,避免一段恰好用了 class 一词的散文被误判。

行号边栏(listing)剥离:agent 发来的是“文件清单”不是文件

listing.go 解决一个很具体的问题:编码 agent 交给引擎的不是文件本身,而是其 read 工具打印的结果——每行都带行号边栏(1\t{2\t "unit")。这层边栏是展示格式,会直接击穿 Detect:JSON 文档不再以 { 开头,json.Valid 失败;源码变得不可解析。两者统统落到 text,压缩率约 0%。

因此解包放在引擎层而不是压缩器内部——边栏与内容类型正交,它可能包裹 JSON、源码、日志、CSV、配置中的任何一种,每种都必须按其真实类型路由。恢复规则有两条讲究:

  • 保留每行原始的编号:压缩后存活的行仍带它在原文件中的行号,编号继续指向 agent 读到的那份文件;
  • 对“重构式”转换整体放弃恢复:重编码的 JSON(如 TOON 输出)没有任何一行能被原编号所描述,此时直接返回裸压缩体。源码注释说得很直白:“描述不了任何东西的行号,比没有行号更糟。”

S0–S4 安全分级:类的固有属性,不是用户选项

safety/safety.go 是分级注册表,是一个无 engine 依赖的叶子包,让引擎核心和压缩器都能引用而不产生循环依赖。每个压缩器声明自己的类,注册表是回答两个诚实性问题的唯一地点:这个类是否改动模型可见字节?它是否必须先有 CCR 恢复记录才可运行?

级别 ByteSafe RequiresCCR Reversible 含义
S0 true false true 字节安全行为(元数据、计量),模型可见字节不变
S1 true false true provider 原生提示(缓存、路由),模型可见字节不变
S2 false false true 需要 SDK 配合的结构性改动
S3 false false false 行为性改动(路由、推理),Cloud 侧由 eval 门控
S4 false true false 有损结构性压缩:改动模型可见字节,必须可逆(CCR)且披露丢了什么

Lookup 对未知类返回 ok=false,调用方 fail closed——文档中“Unknown class → fail closed”正对应 engine.go#L96-L99。文档特别提示:只把 byte-safe 一词留给 safety.Info.ByteSafe == true 的类(即 S0/S1),其余不要误称。

压缩器注册表:Default() 注册 15 个,forced-only 不进 Detect

compressors/compressor.goCompressor 接口只有三个方法:ContentType()SafetyClass()Compress()。接口的包注释定义了核心纪律:压缩器是纯字节转换——它永远不数 token、不触碰 CCR、不访问网络,引擎核心在周围做这些事。这正是每个压缩器可以保持自包含、可单测、加一个压缩器只需“新文件 + 测试”的原因。

Default() 注册的 15 个压缩器为:JSON、log、code、diff、search-result、text、HTML、tabular、config、tool-schema、tool-schema-annotations、TOON、accessibility-tree(a11y)、repetition、terminal。其中后五个(tool-schema、tool-schema-annotations、TOON、a11y、repetition)从不被自动检测,只能通过 Options.Type 强制到达——这与文档“forced-only … must not be added to Detect”的约定一致。

两个可选能力接口值得了解:

  • MetadataCompressor:压缩器可报告本次实际使用的方法(Method)与 LosslessToModel 等元数据;
  • QueryAwareCompressor:实现 CompressQuery 的压缩器支持查询导向压缩,引擎仅在 Options.Query 非空时类型断言并调用。

为什么 toolschema-annotations 被刻意排除在能力清单之外

manifestExcludedcompressors/compressor.go#L139-L148)把 toolschema-annotations 排除在 Cave Compiler 消费的 transform-capability ABI 之外。原因是:advertise 一个压缩器会旋转 RegistrySHA256,而每个已构建的 Cave Build lock 都钉着旧值,失配的 lock 会在运行时让 agent 失败。既然没有编译后的计划会路由到它,把它加进 ABI 换来的只是一行永远用不到的能力,代价却是使所有现场 lock 失效。

toolschema 转换目前是纯客户端侧的

文档专门用一段约定说明了这一点,值得逐条消化:

  • 注册它只是让引擎调用方可以 force;并不让它从托管网关流量可达;
  • provider 适配器刻意把 tool 数组保留在冻结的 prompt-cache 前缀里,没有任何计费路由调用这个压缩器;
  • 引擎 API/CLI 调用方可以在本地 force 它,caveman-shrink 是它专门的产品面;这些缩减保持本地且 inferred
  • 托管网关另有一条独立的 S2 tool-search/deferral 路径,不要把它与压缩混为一谈;
  • 未来若要为它开通网关路由,需要先做缓存对 schema 的成本算术、保证字节级稳定的前缀输出、并通过 eval 门。

重复剔除的正确性护栏:keepNonRedundant

compressors/redundancy.go 实现了文档“elide repetition, never a document”这条正确性规则。所有会丢单元的压缩器(text、log、json、tabular、config、searchresult、terminal)在输出前都调用 keepNonRedundant:一个即将被丢的单元,若没有已存活的单元与它相似,则被保留,其后的副本再对它折叠。

实现细节上有几个精心设计的常量:

  • 词汇画像 + 数字掩码unitVocabulary 把单元降为词汇集合,数字串折叠为 #——这让只差时间戳的两行日志互相认得,同时保留原词,使只共享字段名的两条书目条目不会被误判为同类;
  • 无词单元不掩码redundancyMinWordTokens = 1。像 6 ['1973.', 251] 这样的单元掩码后只剩 #,会让整张数据表看起来全同并整体被剔除——所以带至少一个词的单元才掩码,否则按原数字比较,因为那里的数字本身就是内容;
  • 掩码与未掩码画像不可互比representedByclass.masked != dropped.masked 直接跳过,防止“形状相同、数字不同”被读成“文本相同、数字相同”;
  • 90% 包含阈值:单个存活单元必须已携带待丢单元 90% 的词汇才算“已被代表”,宁严勿松——漏剔除损失的是缩减,错剔除改变的是答案;
  • 128 次晋升上限:一个载荷里“首次出现即保留”的单元超过 128 个,说明它不是重复流而是文档,其余单元全部保留,最终由“输出不更小”检查让它整体直通、不声称任何缩减。注释特意提到计数陷阱:若把种子和晋升一起计数,任何超过 64 个存活单元的大数组都会静默变成直通。

文档引用了一个实测代价:若 agent 必须枚举的文档被剔除,agent bench 上会花掉基线 5.5 倍的开销(2026-08-06)。这条规则被明确定位为“正确性规则,不是调参旋钮”。

省略标记只陈述它验证过的事实:invariants

compressors/invariants.go(全文 806 行)为 log/tabular/JSON 的省略标记附加从被替换单元精确计算出的事实,且只陈述验证过的内容。源码头部注释与文档的 Gotchas 段落逐条对应,可归纳为:

标记可携带的三类事实

  1. 常量:某字段在被省略的每一个单元中字节级相同,记为 name=value(如 all state=charged);
  2. 枚举:可变字段在少量短值上变化时,完整列出并带精确计数,各桶之和恒等于被省略数;缺失该字段的单元进 absent×N 桶(如 status: fulfilled×15 shipped×3 processing×2);
  3. 数值范围:用原始极值字符串打印(range amount=5.00..199.99),从不做重格式化或舍入,因此边界绝不可能比实际更宽。

整体扣留的条件(永不截断,因为部分清单会被读成完整清单)

  • 凭证形状的字段名;
  • 含空格或超过 24 字节的值(源码常量 invariantMaxValueBytes = 40 用于更长的单值拒绝,标记预算则另有上限);
  • 超过 5 个不同取值;
  • 任何单元解析不出字段的 run——整个 run 的摘要禁用,标记退化为与裸 … N rows elided (caveman) … 字节级相同。

标识符列表的 coverage 表达:每个桶恰好一个单元的枚举不是类不变量而是标识符列表,标记改为陈述其覆盖度——order_id: 25 distinct, ord-1000..ord-1024:精确不同计数 + 按字节序最小/最大原始值串,不声称中间无缺口。这是唯一能回答“ord-1043 是在被省略的行里,还是根本不在账本中”这一事实。若 run 恰好稠密(共享前缀、定宽数字、max-min+1 等于计数值),则改说 wh-5000..wh-5059 all 60 present——一个经核实的成员性答案;稠密是数出来的,绝不从端点推断,混合后缀宽度、两个前缀或单个缺口都退回计数形式。

预算与裁剪顺序:摘要上限 160 字节且不超过其替换字节数的四分之一(下限 64,永不超过一半);超预算时按顺序整体丢弃条目:标识符式枚举 → 范围 → 有界 coverage → 常量 → 稠密 coverage,类枚举最后;每条载荷最多追加一行尾部契约行,且仅当实际丢掉的字节 ≥4 KB 时。

小于 3 个单元的 run 根本不省略(除非折叠后既能摘要又使自身字节减半)——单个单元标记加恢复 handle 的成本约等于单元本身,且读起来像个洞。

文档给出的代价是刻意为之的:这套机制在 toolwork 语料上让 ratio 降了约 4 个点(0.83 → 0.79)。换来的是什么:2026-08-08 一次 bench 中,无法分辨被省略内容的 agent 连发 46 次 caveman_retrieve,开销是无压缩对照组的 3.3 倍;只陈述常量和范围的第一版修复让风暴停在 11–27 次(因为它对任务真正依赖的那个混合值字段保持沉默);第二轮仍在标识符枚举、无事实小类和 {"__caveman_elided__":1} 单例上漏恢复调用。

恢复视图由完整单元构成:retrieve_query

engine/retrieve_query.go 把 CCR 恢复收窄到匹配查询的单元,而“单元”是自定界的整体:完整 JSON 记录(不是字段行——"status": "unfulfilled", 是片段)、从原始字节切出的完整 CSV 记录(表头只显示一次)、完整 log/NDJSON 行、或非 JSON 散文的完整段落。关键规则:

  • JSON 字段名从不是出处证据messages[].contenttextsystem 永远附着在其完整对象记录上,因为工具输出可以与 provider 请求使用相同字段名;
  • 独立 JSON 数组之间有显式 gap 哨兵;被省略的信封/标量字节有首尾标记;
  • 无法分解的内容(没有记录数组的 JSON)整体返回而非按行切开;收窄视图若不更小也返回完整原文——过度返回是安全的,片段不是
  • 视图中的任何间隙(两个返回单元之间、首单元之前、末单元之后)都打印 … [caveman: non-adjacent] …,保证视图中的相邻性从不暗示原始中的相邻性。

这段代码注释里的事故记录与文档一致:2026-08-08,query 模式返回了某 pretty-printed orders 页的 BM25 排序行,agent 把 "status": "unfulfilled"(属于 ord-1043)读成了紧邻其下的 "order_id": "ord-1047" 的字段,报告了 5 个未履约订单而实际只有 3 个——12 任务 bench 中答错两道。查询收窄本身有界:BM25 打分(复用 contextwindow 的确定性 packer 所用的打分器),top-k 上限 maxRetrieveSections = 20,命中后按原始顺序重排。

一个恢复面、两个 id 空间

这是文档中最反直觉的 Gotcha,源码注释(engine.go#L235-L266)给出了完整解释:CCR 存储同时保存压缩 blob handle(ccr_…,走 store.Get)和原生运行时的类型化对象(store.GetObject),而运行时在遮蔽整段工具输出时向 agent 展示的是 ccr://<objectID>Engine.Retrieve 因此先查 blob 表、未命中再落 object 表、两者都未命中才失败;MCP 侧的 normalizeRecoveryHandle 接受 ccr_…<<ccr:…>>ccr:…ccr://… 四种形式。

动机是循环:agent 只会复制它被展示的那个引用;一个无法解析的形式不会“降级”,而是循环——失败的工具结果本身又会被遮蔽成新的指针,“一个指向另一个指针的指针,无限循环”。文档引用的实测:2026-08-08,inventory-mismatch 与 webhook-delivery-gaps 两个任务拿到 ccr://<objectID> 后每次 retrieve 都回答 cave_unknown_handle,发生 27–97 次恢复调用、未写出任何答案、两任务 0/6;同构建下 rate-limit-forensics 却以约便宜 35% 的成本拿了 3/3。

CCR 存储:SQLite、单序列化连接、生命周期

ccr/ 目录提供 ~/.caveman/ccr.db(SQLite)恢复存储加原生会话存储:内容寻址 handle、字节级精确的 Get、会话作用域、依赖、current/stale 状态,以及 Hot/Warm/Cold/Archived 生命周期。文档强调的实现事实:嵌入式 SQLite 使用单条序列化连接,使并发 hook/仓库写入既不会撕裂内存中的 schema,也不会与 SQLITE_BUSY 竞态;store_sqlite.go 注释补充了为什么需要 journal_mode(WAL)——否则读者在写者持锁期间被完全锁出。JS/WASM 构建则使用 store_wasm.go 的内存存储。引擎层面的 CCR 联动规则在 engine.go#L94-L102info.RequiresCCR && e.store == nil && !opts.ExternalRecovery 时直通——这就是“CCR-or-pass-through”的实现本体。

token 计数:离线 BPE,永远是 inferred

tokens/ 定义 Counter 接口,默认实现是词表内嵌的离线 BPE 分词器(OpenAI o200k_base 词表),Default() 返回共享实例(见 tokens.go#L87-L92)。所有 ratio 都是带 inferred 标签的本地估算:绝不verified,也绝不重新投影为 provider usage——因为压缩发生在请求计费之前,provider 侧数字在此时不可得。这也是 ResultTokenCountBasis 字段存在的理由:它标明前后两个数字用的是同一个估算器。

目录布局与构建约定

文档给出的完整布局与仓库一一对应:

  • engine.go:Engine 核心,含 record/miss/parse-fail/not-smaller/no-store 五路直通;
  • result.goResult/Options/Mode
  • detect.go:内容路由器;
  • listing.go:行号边栏剥离与恢复;
  • safety/:S0–S4 注册表;
  • tokens/:Counter 接口与默认 o200k_base BPE;
  • contextwindow/:确定性 BM25 上下文打包器,带 recency/error/priority 信号与 token 预算计量;
  • compressors/:接口 + 注册表,Default() 注册 15 个;
  • ccr/:SQLite 恢复 + 原生会话存储;
  • pixel/:pxpipe 移植(MIT,见其 NOTICE):文本→PNG 请求压缩,内嵌字形图集 + 渲染器 + 盈利门 + 按 wire 格式的变换(Anthropic/OpenAI/Gemini)。它是 S4 有损、白名单门控(CAVE_PIXEL_MODELS)、由 proxy 的 pixel 模式消费;从不接入 Detect,也从不被 WASM 构建导入(约 4 MB 资源)。applicability.go 验证了文档声明:CAVE_PIXEL_MODELS 缺省时默认白名单就是 claude-fable-5,gpt-5.6
  • evals/:本地 eval 框架 + fail-closed grader + 内嵌 fixtures,Run() 是质量门,未知 grader 返回 passed:false
  • cmd/caveman-engine/:CLI shell 出来的二进制,子命令为 compress | detect | retrieve | stats | registry | toon encode|decode | evals run | pixel render|simulatetoon 是无状态(无 CCR)的 JSON⇄TOON 转换器,双向都 fail closed。retrieve <handle> [query] 带 query 时只返回最相关 section(BM25),与 RetrieveQuery 对应。

构建/测试约定为 make product-build PRODUCT=engine / make product-test PRODUCT=engine。新增压缩器是“compressors/ 下一个自包含文件 + 测试 + 在 Default() 中注册”;toolschematoon 这类 forced-only 压缩器不得加入 Detect

其余诚实性不变量速览

  • cgo:完整代码压缩(Python/JS/TS)需要 tree-sitter 构建;无 cgo 构建只压缩 Go。内嵌 eval fixtures 在 cgo 下覆盖这三种语言(对应 compressors/code_cgo.gocode_nocgo.go 的构建期选择);
  • fail-closed 三件套:未知模式 → record;未知内容类型 → text;未知 grader → passed:false
  • boundary:engine 处于 public/ 区,绝不导入 cloud/…,由 make check-boundaries 强制;
  • 确定性要求贯穿始终:invariants 的渲染路径刻意保持顺序保留、无 map 迭代序依赖——因为压缩块必须在后续每个 turn 以相同方式重新序列化,否则 provider 前缀缓存会失效。

小结

Caveman Engine 的设计可以用 engine/CLAUDE.md 的一句话概括:“Everything it reports is inferred; it never says verified”。围绕这句话,仓库实现了完整的不变量体系:五个直通条件、S0–S4 分级与 RequiresCCR 门控、keepNonRedundant 的“剔除重复但不剔除文档”、invariants 标记“只陈述验证过的事实”、恢复视图“完整单元 + 非相邻哨兵 + 双 id 空间解析”,以及 CCR-or-pass-through 的存储前置。理解这套机制的实用价值在于:当你通过 CLI(caveman-engine compress / retrieve / stats)或 SDK 调用引擎时,每一个 inferred ratio、每一个 ccr_… handle、每一个省略标记里的计数,背后都有上面这些可定位到具体文件的护栏在支撑——而所有护栏的失效方向都一致:宁可不压缩,不压缩错

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

项目优选

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