caveman 中的 TOON 与 Pixel:两种可选上下文编码的选型、配置与源码剖析
TOON(Token-Oriented Object Notation)和 Pixel 是 caveman 提供的两种可选上下文编码:前者把结构化 JSON 重编码为紧凑文本,后者把文本渲染成 PNG 交给视觉模型阅读。本文基于 docs/technical/toon-and-pixel.md 的原始约定,结合 engine/compressors/ 与 engine/pixel/ 下的实现源码,完整讲清两者的启用条件、配置参数、CLI 用法、适配的输入形态与失效(fail-closed)行为,帮助你在代理会话中判断何时该开、何时绝不该开。
一、两者的共同前提:改变的是模型可见输入
在展开细节之前,先明确原文档给出的总纲:TOON 和 pixel 都会改变模型可见的输入字节,因此两者都不是 byte-safe(字节安全)的。它们只在输入形态和目标模型都匹配时才应使用。这一点在源码层面被明确落实:
- TOON 被实现为无损重编码器(重编码后语义不变,但文本字节变了),其安全等级为
S4,见 toon.go 中的ContentType()与SafetyClass(); - Pixel 包的文件头注释直接声明它是「S4 lossy transform」——把文本搬进图片块,改变了模型可见字节,因此转发前必须保证原始字节可通过 CCR(上下文恢复存储)找回,任何解析、渲染或存储错误都必须 fail closed 回退为直通(pass-through),见 doc.go。
这一「改字节前先留后路」的原则是理解后文所有启用规则的关键。
二、TOON:面向均匀表格 JSON 的紧凑重编码
2.1 工作原理与示例
TOON 把结构化数据重编码为紧凑的文本形式,它对字段名一致的均匀对象数组收益最大。原文档给出的示例:
[
{"name":"Ada","role":"engineer"},
{"name":"Lin","role":"designer"}
]
这类数组会被 TOON 把重复的键提升到共享表头、行内只保留单元格值。编码器 toon_encode.go 中的 writeTOONArray 正是这样做的:对表格数组输出形如 [2]{name,role}: 的表头行(行数 + 字段名列表),随后每行输出以分隔符连接的标量值。原文档也明确提醒:确切语法由实现及其测试夹具定义,调用方应当使用编码器/解码器,而不是手工拼写 TOON——因为解码器是严格校验的(见 2.4 节)。
2.2 选择规则(Selection rules)
原文档规定了三条硬性选择规则,源码逐一印证:
- 只能显式请求或经特性开关启用,
Detect通用探测函数不会选中它。 这是最强的一条约束。toon.go 中NewTOON的注释写明:该编码器「只能通过强制Options.Type = "toon"到达,Detect 永远不会路由到它」。 - 结果必须比原表示更小。 若重编码后不省,调用方应原样透传。编码器
encodeTOON的返回契约是ok=false时调用方保持原始字节不变(toon_encode.go 的注释)。 - 绝不用于 tool-call 参数。 即使数据看起来结构相似,改写工具参数也可能改变程序行为,因此 TOON 只用于「作为上下文消费的数据」,而非可执行参数。
在 caveman 的配置体系中,TOON 的持久开关是 think.toon(默认 true,「当更小且受支持时允许 TOON」),对应环境变量 CAVEMAN_TOON;项目级 overlay 可以设置 think.toon。这些参数见 configuration.md。
2.3 CLI 用法
TOON 的命令行入口:
caveman tools toon encode < data.json
caveman tools toon decode < data.toon
CLI 层的实现细节值得注意:packages/cli/src/index.ts 中 toonConvert 通过 shell 调用 caveman-engine toon encode|decode 子命令,它是无状态的。这意味着:
encode依赖caveman-engine二进制存在(通过caveman setup安装、设置CAVEMAN_ENGINE_BIN或自行构建);decode在引擎二进制缺失时会直接拒绝工作(refusing to emit unconverted TOON as JSON),即宁可报错也不产出「看起来像 JSON」的错误输出——这延续了 TOON 全链路「拒绝而非猜测」的基调。
2.4 解码器的严格性
原文档一句话「解码器拒绝畸形输入,而不是发明缺失的结构」,在 toon_decode.go 中可以找到完整的实现证据:
- 缩进必须为偶数:TOON 以 2 个空格为一层缩进,奇数缩进直接判非法(scanTOONLines);
- 行数声明必须兑现:表头
[N]{...}:声明了 N 行,解码器要求恰好有 N 个物理行跟随,且每行的单元格数必须等于字段数,否则拒绝; - 容量防滥用:解码器在分配数组容量之前先校验
n > 剩余行数即拒绝——注释说明这是防止「一个极小的不可信输入要求分配 GB 级内存」; - 字段名必须是安全键:字段名需匹配
^[A-Za-z_][A-Za-z0-9_.-]*$(safeTOONKey),解码时对重复字段、未知结构一律返回失败。
2.5 适配与不适配的输入
原文档的适用性清单,可对照源码中的判定逻辑:
适合 TOON 的输入(对应 tabularRows 的判定条件,toon_encode.go):
- 对象组成的均匀数组;
- 重复的字段名(每行字段名与顺序必须完全一致);
- 标量单元格值(null / bool / number / string;单元格内含对象或数组即不合格);
- 数据是作为上下文消费,而非可执行参数。
应避免 TOON 的场景:
- 不规则的嵌套对象(任一行结构偏离即整体回退为原样透传);
- 本来就紧凑的数据(「必须更小」的硬规则会挡住它);
- 键顺序或字节表示重要的输入(注意
asTOONValue对对象键会做排序,toon_encode.go); - tool-call 参数(行为安全的硬禁区)。
工程上还有一个可观测点:toon_eligible.go 中的 tabularEligibility 会递归统计「数组元素落在均匀扁平对象数组中的比例」,用于量化一段数据有多「表格化」,供上层决策与评测使用。
三、Pixel:把文本渲染成 PNG 交给视觉模型
3.1 机制与风险
Pixel 把文本转换为视觉模型可阅读的 PNG 图片,能降低稠密源码类素材的文本 token 输入,但引入光学识别与视觉排版风险——这正是它被定为 S4 有损变换的原因。渲染产物样例见文首配图 pixel-sample.png:一整页密集文本被排布进固定几何的像素网格。
Pixel 包是从 pxpipe 移植而来(doc.go 注明移植来源与 MIT 许可),并有意做了三处本地化:PNG 字节来自 Go 标准库编码器(测试比较解码后的像素而非 PNG 字节流)、token 估算改用 caveman 离线的 engine/tokens 计数器、且不做实时的 count_tokens 探测。
3.2 启用方式:单会话与持久配置
原文档给出的单会话启用命令:
caveman wrap --pixel <agent>
CLI 的 --pixel 标志在 index.ts 的合法标志集中注册;cli-reference.md 中进一步说明:--pixel 为「列入模型清单的模型」启用有损的文本转图像上下文传输。
Pixel 还有一个关键约束:要求显式模型允许清单。原文档给出的配置示例:
{
"think": {
"pixel": {
"models": ["model-name"],
"density": "balanced"
}
}
}
对照 configuration.md 的完整参数表:
| 配置项 | 默认值 | 取值 | 说明 |
|---|---|---|---|
think.pixel.models |
[] |
模型名数组 | 允许接收 pixel 上下文的模型 |
think.pixel.density |
balanced |
conservative, balanced, max |
pixel 打包密度 |
注意 think.pixel.models 默认是空数组,即默认任何模型都不接收 pixel 上下文——与「不允许从模型名推断支持」的原则一致。同时原文档提示:项目 overlay 不能修改 pixel 设置(configuration.md),防止被检入的项目文件悄悄启用这种更具侵入性的变换。环境覆盖方面,CAVE_PIXEL_MODELS 与 CAVE_PIXEL_DENSITY 分别对应上述两项,适合临时会话使用。
3.3 密度档位:conservative / balanced / max
原文档说明:density 支持 conservative、balanced、max 三档,密度越高,塞进单张图的文本越多,小字号对模型越难读。density.go 中给出了每档的具体几何参数,可以直接读出「密度」的工程含义:
| 档位 | 单元格水平推进 | 行距 | 墨色 | 层数 |
|---|---|---|---|---|
conservative |
5px | 8px | 单色 | 1 |
balanced(默认) |
4px | 6px | 三色斑马纹 | 1 |
max |
4px | 6px | 三色斑马纹 | 2(叠加层) |
两个 fail-closed 细节值得注意:
- 未知或非法的密度值一律落到 conservative(normalizeLevel);环境变量
CAVE_PIXEL_DENSITY未设置时默认balanced,假值(0/false/off等)映射为conservative; - 解析出的渲染参数还要经过「地板值」钳制(applyDensityFloors):斑马纹行距不低于 5px、单色不低于 6px,确保任何配置都不会把字号压到模型无法识别的程度。
3.4 模型兼容性:为什么「能看图片」不够
原文档的核心论断:仅有视觉能力是不够的,因为图片尺寸、细节设置、供应商 token 计费和文本识别质量各不相同,所以 caveman 不会从模型名推断支持。源码层面这体现为显式的前缀匹配白名单,而非能力推断:
- applicability.go 中
AllowedModelBases读取CAVE_PIXEL_MODELS(或配置项),未设置时使用内置默认基底列表;Allowed对模型名先剥掉[...]变体标签,再做精确匹配或base-前缀匹配——没有命中清单的模型一律不进入 pixel 路径; - 密度解析
ResolveDensity也执行 fail-closed:未被识别为「密度可用」的模型,无论请求哪一档,都回落到 conservative 几何参数(density.go),并且 hi-res 画布只授予被显式识别的高分辨率模型族。
3.5 恢复机制:CCR 兜底 + 收益闸门
原文档 Recovery 一节的三句话对应源码中的三道保障:
- 「原始文本在发出 pixel 输出之前先存入 CCR」——这是
engine/pixel/doc.go的硬性要求:调用方必须在转发变换后的字节前保证原始字节可经 CCR 找回,任何存储失败都要 fail closed 回退到原始文本留在请求中; - 「图片上下文应带清晰的恢复引用,以便工具在需要字符级细节时取回精确源」——这保证模型在图片里看不清某个字时,仍可通过工具链取回逐字符原文;
- 此外还有原文档未展开、但源码明确存在的收益闸门:gate.go 的
EvalCompressionProfitability会把「渲染成图后的预估 token 数(含 10% 安全边际)」与「原文本 token 数」对比,并计入 prompt 缓存的创建/读取费率差(1.25 / 0.10),只有图片侧总成本低于文本侧时才判定为「盈利」——也就是说,即使模型在白名单里,不划算的短文本也不会走 pixel 路径。
四、证据边界:更小的本地表示 ≠ 更低的供应商成本
原文档最后的 Evidence boundary 一节是对任何「省 token」宣传的清醒约束,值得原样保留:
- 更小的本地表示不能推出更低的供应商成本,因为供应商对图片输入和结构化文本输入的计费方式不同;
- 一个成立的「更便宜」声明需要针对确切模型的供应商 usage 数据或有文档记录的 benchmark;
- 「质量等价」同样需要超出「可恢复性」之外的任务级评测。
Pixel 源码中的 token 数字也自我标注为本地估算:doc.go 写明「The token reductions from this package are local estimates only」;gate.go 的 ImageCostSafetyMargin = 1.10 就是给本地估算留出的保守边际。评估这两项编码是否划算时,请以你所在供应商对目标模型的实计 usage 为准,而不是仓库内的估算值。
五、小结:一张启用决策表
| 维度 | TOON | Pixel |
|---|---|---|
| 变换性质 | 无损重编码(字节变化) | S4 有损变换(文本→图片) |
| 默认是否启用 | 不经过 Detect,必须显式/开关启用(think.toon) |
模型白名单默认空,默认完全关闭 |
| 启用方式 | caveman toon encode|decode、think.toon / CAVEMAN_TOON |
caveman wrap --pixel、think.pixel.models / CAVE_PIXEL_MODELS |
| 硬前提 | 结果必须更小;禁用于 tool-call 参数 | 模型显式白名单 + 原始文本先入 CCR + 收益闸门盈利 |
| 参数调节 | 无(分隔符固定 ,,编码失败即透传) |
density: conservative / balanced / max,非法值落 conservative |
| 失败行为 | 拒绝畸形输入,不猜测结构 | 解析/渲染/存储任一失败都回退直通原文 |
两者共同的工程哲学是:能力可以更强,但启用必须显式;任何一步不确定,就回退到原始文本。理解这一点后,你在自己的代理会话中配置这两个开关时,就已经掌握了 caveman 上下文编码的核心决策框架。更多参数上下文可继续查阅 cli-reference.md、configuration.md 与 agent-wrapping.md。
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