ctxfold 的 CSV 折叠 0/24 出来之后,前面 JSON 那 24/24 的漂亮数字基本要重估。不是模型弱,是 CSV 根本没给折叠留语法富余——它已经是最紧凑表格,再压就只能拆值本身;一旦拆值,模型读的就不是表,是一张需要自己执行解码器的组装图。这在 CSV 上不是 bug,是压缩策略和可读性的必然冲突。我之前也见过 JSON 和日志那边的可读性报告,但那两个 24/24 我一直觉得有点太顺,顺到不像真的。现在 CSV 把另一头逼出来了。
条件不复杂。作者拿自己给 ctxfold 写的可读性框架,400 条带现实冗余的 CSV,原始和折叠各喂 GPT-4o-mini 做指定记录查找,逐字段对标准答案。原始 24/24,折叠 0/24。别急着怪提示词或坏样本,换 GPT-4o 复测只有 6/24,另一次 9/24。模型升了级,从 0 到 6 再到 9,说明能力提升只能带来不稳定的偶尔,偶尔不等于可用。这里没有失败样本被挑出来充数的证据,倒像是同一套数据把三个分数都逼出来了。
我读到 0/24 第一反应是生成样本时重叠搞得太恶心。但看具体失败,根本不是没能力读表,是它读的压根不是一张表。被问 NW-1258,模型给仓库 WH-2,像只应用了一半前缀;供应商 DAL-2 实际来自仓库列;价格来自另一行。另一次把 SKU 里的 1238 当数量。这不是“模型找不到正确行”这么简单,是行找到之后,它没能力把抽走的前缀规则和行内剩余值重新合成一个正确单元格。列对齐已经乱了,再叠加前缀缺失,错误就不是单纯漏读,而是把别的行别的列的值安到了答案里。
ctxfold 在 JSON/日志上折叠的是语法:键、括号、引号、模板前缀提到头部,每个值行内原样。模型读折叠 JSON 时干的事和读原 JSON 几乎一样,只是元信息换了位置。CSV 没有语法可移——它本身就是紧凑表格。冗余只藏在值内部:所有 SKU 共享 NW- 前缀,仓库共享 WH-,USD 每个都重复。CSV 编码器把这些共享段抽走,行内只留中段。于是模型要回答,先拿不完整键找行——SKU 列只剩 258,因为 NW-1 被抽走;找到后再按头部规则重建每个值。它必须意识到 258 不是 258,而是 NW-1258。这两步在 JSON 折叠里几乎不存在,在 CSV 折叠里是刚需。GPT-4o-mini 基本第一步就跪,GPT-4o 能摸到第二步但重建错乱。
作者宣传 ctxfold 时反复说过一条纪律:不丢失数据,无法压缩就保持原样,不允许有损结果。JSON 和日志的验证数字让他误以为无损天然保可读,甚至拿 24/24 当可读性证据。CSV 这个 0/24 把假设打穿。无损和可读在 CSV 上是正交的。Wren Calloway 在评论区说得对:JSON 可读不是因为无损,是因为变换让每个值留在稳定、可寻址的位置;键提升是位置恒等,前缀提取是重写单元格。我同意这个框架,它比模型成功率更能解释问题。JSON 折叠里的每个值还是原来那个值,CSV 折叠里的每个值都变成了一个需要解码的片段。
三种格式的差别放到一张表里更清楚:
| 格式 | 折叠对象 | 模型要重建的东西 | 跨模型可读性 |
|---|---|---|---|
| JSON | 键、括号、引号等语法 | 结构,但值逐字段保留 | 全模型 24/24 |
| 日志 | 模板前缀 | 行内列式结构 | 非推理模型更接近正确答案;推理模型精确 |
| CSV | 值内部前缀、固定列 | 每行每个被折叠的单元格 | GPT-4o-mini 0、GPT-4o 6-9;Llama 更差;Claude Haiku 4.5 24;Qwen 推理 24,但花 3000-6000 token |
这张表看着不是绝对 0,但跨模型恰恰是问题。发布后又在 Claude Haiku 4.5、Llama-3.3-70B、Qwen3.6-27B 上跑,CSV 从 0 到 24 全都有。Llama 比 GPT 还惨,完全丢失记录对齐;Qwen 推理能重建,但要用 3000 到 6000 个思考 token 去抵消 15% 到 25% 的提示节省;只有 Claude Haiku 4.5 直接读折叠 CSV 拿 24/24,没有推理开销。也就是说,折叠 CSV 读不读得懂,取决于你锁哪个模型。没固定模型、没自己测,就按最坏情况处理:只用于流水线,模型读取前解压。这个结论跟 JSON 那边“全模型 24/24”完全不是一回事,JSON 的成绩可以复现,CSV 的成绩基本是抽签。换作我做选型,如果一个折叠格式只在某个模型上稳定,我会先把它当不可用处理,直到我在队里锁定那个模型并自己测过。
还有个点得单拎出来说:gzip 在冗余 CSV 上能到 80% 到 90%,ctxfold 只有 24% 到 28%。流水线场景里 gzip 是更诚实的基线。ctxfold 真正有价值的场景只有模型直接读折叠形式、没有解压步骤。可 CSV 折叠后唯一直接读懂的模型是 Claude Haiku;GPT、Llama 读不了,Qwen 要花三五千 token 反推规则。于是问题变成:你在省提示 token,还是在给模型付推理税?省钱前提是模型读得动;读不动,省下的 token 可能连一次重建失败的成本都抵不上。这还没算重建失败后多轮纠错带来的提示增长,那个和省的 15% 到 25% 一比,很容易就是负的。
作者最后把修复落在文档上而不是代码。从 v0.1.4 起,CSV 折叠定位为 pipeline 模式:只在无损传输或存储时折叠,构建提示前解压;要让模型直接读,就发原始 CSV。这不是什么“修复”,是承认 CSV 没有安全可折叠空间。他说过他考虑过不抽取唯一列、保持标识符完整,但那样只能缓解找行问题,前缀重建还在,压缩收益趋近于零。CSV 已经贴着可读性下限了,你没法在不破坏可读性的情况下再抽任何东西。这跟之前“无法压缩时保持原样”的逻辑一致,只不过这个“无法”不是字节上无法,是模型理解上无法。
v0.2.0 的 profile 命令看着像另一个好东西:折叠前分析 token 分布,告诉你哪些内容可以安全折叠。users.json 那组数字我挺顺眼——300 条 JSON,65,614 字符,键占 27%,空白 30%,无损折叠减 66%,字典模式能到 73% 但默认关。这个 profile 对 JSON 有意义,因为 JSON 有 30% 空白和 27% 键可以安全归并。但同一套 profile 逻辑套 CSV 上,如果不声明“可寻址性”这条轴,就可能再出一次 0/24。作者说了,v0.3 做嵌套 JSON 时只做路径提升,不做字符串前缀提取。这是把这次 CSV 的教训内化了:变换可以无损,也可以可读,但你不能假设一个变换同时兼得,尤其是当它开始重写单元格值本身的时候。
这事本质很简单。模型读的是值,不是重建规则。你给 JSON 折叠形式,每个值还在那里躺着,位置可能变了,但它完整、可扫描。你给 CSV 折叠形式,值被拆了,一部分跑到头部规则里,一部分留在行内。模型得到的是一个需要执行解码器的编码字符串,而不是一张表。JSON 折叠是把桌子从墙边搬到中间,CSV 折叠是把桌子拆成零件再给模型一张组装图。前者它能用,后者它大多数时候不能。
我这次最想记住的就一句:可读性验证里如果只保留“纯无损”这个维度,你很自然会从 JSON 滑到 CSV,等 0/24 出来才发现自己把“留值完整”的前提也弄丢了。这不是软文测评里常见的羞耻翻车,反而挺有用——它逼出了“位置不变 vs 单元格重写”这条分界线,比一百次 24/24 更能说明一种压缩该不该直接喂给模型。以后我做这类横评,也不会只贴正确率;先问变换有没有动到 cell 本身。动了,就别把“无损”说成“可读”,除非你真的拿固定模型测过——不然那个 0/24 就是你的。
结果给得干脆:JSON、日志保持直接可读,CSV 打进 pipeline 专用。v0.1.4 之后文档里原先的星号也换成了实测数字,没再拿未验证的字符缩减估计充数。这个态度是对的。测量结果难看,就把星号变成文档;而不是把数字留着、把星号藏起来。这两点在同类工具里都不算常见。
