三种编辑格式 · 为什么改文件要三套协议
同样是「让模型改文件」,grok 提供三种模型协议:精确的 search/replace、省 token 的 concise 版、以及基于行锚点的 hashline。这一章讲清三者的格式差异、各自防的失败模式,以及 editor_infra 里那把设计好却还没接线的文件锁。
三种编辑格式 · 为什么改文件要三套协议
「让模型改文件」听起来是一个工具的事,grok 却提供了三种。它们干的是同一件事(最终都产出 SearchReplaceOutput),区别在给模型的编辑协议——各自针对不同的失败模式和 token 预算。
一、search_replace(默认):精确查找替换
最直觉的格式:给 old_string 和 new_string,old_string 必须在文件里恰好匹配一次(除非 replace_all);空 old_string 表示新建文件。两个生产级细节:
- 改前必读(软约束):
requires_expr()声明了一条依赖——除非配置里开了skip_read_before_edit,否则工具集里必须同时有一个 Read 工具。但这只是配置期的要求:源码注释写明「先读再改」靠工具描述和 RL 训练来引导,运行时并不强制(skip_read_before_edit在运行时已是个废弃的空操作)。它和 hunk 追踪一起,构成防「盲改」的护栏。 - 匹配的鲁棒性:精确匹配失败时,可以再走一层 Unicode 易混字符归一化的兜底匹配(模型有时把
-写成别的破折号、把直引号写成弯引号)——不过这是unicode_normalized_fallback开关,默认关。默认开着的是include_user_edit_hint:报「没找到」时顺带提示「用户可能改过这个文件」,引导模型重读,而不是拿着旧内容盲目重试。
二、concise 版:省 token
grok_build_concise 的 search_replace 格式完全一样,但两点不同:没有改前必读的要求(也不要求工具集里有 Read 工具)、回给模型的输出换成更短的 tool_output_for_prompt_concise。它存在的唯一理由是省 token——在长会话里,编辑工具的往返消息累加起来很可观。
三、hashline_edit:基于行锚点
这个格式真的不一样。它不给字符串,而是给行锚点:edits: [{op, anchor, content}]。锚点形如 LINE:HASH(默认方案还会多带一段块指纹,形如 22:abc:rst),是配套的 hashline_read / hashline_grep 在每行前发给模型的(模型绝不能自己编锚点)。操作有 replace(锚点,可带 end_anchor 表区间)、insert_after(特殊锚点 "0:"=文件头、"EOF"=文件尾)、write(整文件)。
它的 apply 语义很严谨(edit/apply.rs,2000+ 行):所有编辑先对编辑前快照校验、再自底向上原子应用;任何一个锚点失效,整批拒绝;区间重叠也拒绝。妙在失败时返回目标附近的新锚点,让模型不用重读文件就能重试。
scheme.rs 里实现了三种锚点方案,强弱不同:ContentOnly(只哈希本行内容,最弱)、ChunkFingerprint(本行 + 固定块指纹,默认)、CheckpointChain(校验点链,最强但改动后锚点变得多)。不过配置(HashlineSchemeParams)目前只放出 chunk 和 content_only 两档,CheckpointChain 还只是候选。
为什么要三种? 这是把「编辑」当成一个可调的设计空间:精确 search/replace 直观但费 token 且对空白敏感;concise 省 token;hashline 用锚点解决「大文件里重复片段难定位」和「改完还要重读才能再改」的痛点。同一个 ToolKind::Edit,三种模型协议,按场景选——这是 grok 工具系统「一种行为、多种 wire 格式」组织法的最佳例证。
一把还没接上的文件锁
editor_infra 里有一个 FileOperationLockManager:按路径加锁——同一路径的操作串行、不同路径并发;Write/StrReplace 这类改动用排他锁挡住一切,还专门防写饥饿(排队的排他等待者不会被后来的读插队)。设计很周到,但要诚实地说:当前代码里它只被导出,没有任何工具调用它(对照的 7 月版本也是如此)——所以别把「并发编辑不打架」算在它头上。
和《从 LLM 到 Coding Agent》对照
对照 tool-abstraction:教学实现里,「编辑文件」就一种格式、一个函数。grok 的增量是把编辑协议本身当成可选、可调、要防各种模型出错方式的设计维度——精确性、token 成本、大文件定位、重试友好,一样都不能少。配合下一章的 hunk 追踪,就是 grok「敢让模型大改代码」的底气。