一天的真实编辑:混合工具链基准测试
一句话结论:把这一天的 14 处修改各自交给合适的 GEML 动词,全天读取量降 3.65 倍,说清楚"改哪里"降 7 倍。
需要看懂再改的 4 处走
find+get,它们吃掉了全天一半的读取;已知旧串的 10 处走replace,它一个字节都不读。没有一处需要离开 GEML。
这份测试和 寻址成本基准 互补:那份用受控语料量单次修改 的上限,这份看一整天真实工作在按场景选工具之后是什么样。
复现:
GEML_SRC=../geml node benchmarks/real-session-replay.mjs前提:不是让 agent 只用 GEML
一个 agent 手上有很多工具,按场景挑最合适的那个本来就是对的。已知确切旧串的 机械替换,sed 和一次性脚本又快又省;需要看到内容、需要稳定寻址的修改,才轮到 GEML。
所以这份测试问的不是"GEML 能不能取代 sed",而是:把 GEML 只用在它擅长的那类 修改上,一整天下来省多少。
基线不是模型
real-session-edits.json 是一个 agent(Claude)花一整天真实编辑本仓库 README_CN.md 的记录还原:33 处修改,每一处都带着它当时的真实开销——为定位 读进来多少字节、花了几次调用、为了指认位置写出去多少字节。会话日志本身不公开, 这个文件是从中导出的全部内容,可以直接审。
那 33 处是用两种工具做的:
| 工具 | 次数 | 做法 |
|---|---|---|
Edit(一次一处) | 12 | 先读,再替换一段唯一文本 |
| node 脚本(批量) | 19 | 一条脚本里多处 s.replace('旧','新'),盲替换,不读内容 |
Write | 2 | 整文件写入 |
14 处可回放。 其余 19 处的落地文本已经不在文档里了——它们在同一天里被后续修改 覆盖掉了,两种工具都定位不到,所以只能丢弃。这是回放历史的固有上限,不是偏袒 哪一侧。
分工规则(运行前就写死)
没有任何一步是"哪个便宜挑哪个"。 分工沿用 agent 当时自己的选择:
- 它当时读了内容再逐处改的 4 处 →
geml find+geml get:按内容定位,只读那一块。 - 它当时批量替换做掉的 10 处 →
geml replace:旧串已知,不读任何内容。
第二条是新的,也是这次数字和早先几次不同的原因。旧规则把批量那一档留在原命令 上,因为 GEML 当时没有"旧串已经知道"的动词;geml replace 就是那个动词,于是这 条例外的前提消失了。
顺带它戳破了旧规则藏着的一个假设。"盲替换不读内容"并不是这一天实际发生的事: 19 处批量修改里,只有 1 处真的没读。其余都是先读一个窗口、再写一条脚本做好几处 替换——同一次读取被摊给多处修改。而那笔被摊薄的读取,正是 replace 完全消掉的。
GEML 那一侧是现跑的:脚本把 README_CN.md 转成 GEML,然后对每一处修改, 拿它当时落地的文本去 geml find 搜、再 geml get 读出所在的块——两条命令都真跑, 全额计费。
结果
| 全 Markdown(真实发生的) | 混合 | 比值 | |
|---|---|---|---|
| 读进来的字节 | 21,732 | 5,953 | 3.65× |
| 说清楚"改哪里"(写出去的字节) | 2,971 | 424 | 7.01× |
钱花在哪:
| n | 读进来 | 说清楚改哪里 | |
|---|---|---|---|
批量替换(走 replace) | 10 | 10,755 → 1,230 | 旧串同样要写,不计入差异 |
需要寻址(find + get) | 4 | 10,977 → 4,723 | 2,582 → 35 |
这才是重点:那 4 处只占 14 处里的四分之一,却占了全天读取量的 51%。 "必须看懂再改"的修改数量少、单价高;GEML 只对这一部分下手,就把全天总量砍掉近三分之一。
一个对照:replace 之前是什么样
同一批修改,在 GEML 还没有 replace 的时候只有 1.40×——因为批量那 10 处必须 离开 GEML,它们在原命令上读掉的 10,755 字节原样计入。
1.40× 到 3.65× 之间的差距,就是一个动词的价值。 它没有让 GEML 更会替换字符串 (sed 一直会),它只是让那 10 处修改不必再离开——而离开的代价不只是字节:走出去 的那些写入不重解析、不报告、不进历史、写坏了没人拦。
一次性成本
把 Markdown 文档搬成 GEML 不是免费的,脚本把这笔账摆在明面上:10 个 <a id="…"></a> 裸 HTML 锚点被折叠成了标题 id。中文 README 现在靠塞 HTML 给小节 命名,因为 Markdown 没有别的办法;GEML 里标题自己带 id。这一步是自动的,但它是 真实发生的转换工作。
这个测试没有证明什么
- 不是"GEML 全面更省"。 省下来的是读取:
replace说清"改哪里"用的仍是 旧串,和脚本一模一样,所以那一栏两侧同价——7.01× 全部来自另外 4 处。 - 只有一份文档、14 处可回放的修改。 这是回放真实历史能拿到的样本上限; 要更大的受控样本,看 寻址成本基准(4 份文档、47 处修改)。
- 不衡量写作本身。 把一段话写好需要读懂上下文,这部分两种格式一样贵。
- 数字会随语料漂。 这份测试读的是仓库里真实的
README_CN.md,改了它数字就变; 要引用精确值请自己跑一遍。
复现
git clone https://github.com/geml-spec/geml && cd geml
cd geml-parser && npm install && npm run build && cd ..
GEML_SRC=../geml node benchmarks/real-session-replay.mjs
GEML_SRC=../geml node benchmarks/real-session-replay.mjs --json > result.json # 逐条数据benchmarks/real-session-edits.json 是冻结的基线数据集,可以逐条审;GEML 那一侧 每次运行都是现跑的,所以 CLI 一变,数字就会跟着变。