ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

oh-my-pi 的 patch 编辑模式:JSON diff hunk 文件编辑工具的完整实战指南

oh-my-pi 的 patch 编辑模式:JSON diff hunk 文件编辑工具的完整实战指南 oh-my-pi 的 patch 编辑模式JSON diff hunk 文件编辑工具的完整实战指南【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi导读本文围绕 oh-my-pi 中crates/pi-edit/prompts/patch.md这一核心工具提示文档展开系统讲解其 JSONedits[]补丁协议、hunk 头anchor选择策略、上下文行规则、四种操作类型create / update / delete / rename、失败诊断与重试纪律。patch 是 oh-my-pi 编辑既有文件的主力工具阅读本文后你将掌握如何书写能被精确匹配的锚点、如何在 Found multiple matches 与 No match found 时自我修复、以及底层 Rust 引擎fuzzy 匹配、缩进归一化、序列回退策略的工作原理让你的 Agent 编辑调用一次成功、零误伤。一、patch 模式是什么patch 模式的定位非常明确Patches files given diff hunks. Primary tool for existing-file edits.它接收一个 JSON 参数对象{ path: string, edits: Entry[] }对一个顶层path应用一个或多个 diff hunk是编辑既有文件的首选工具而创建全新文件、整体覆盖文件则通常交给write工具处理。在 oh-my-pi 中patch 是edit工具的四种可选线协议之一hashline、apply_patch、patch、replace。按照 docs/tools/edit.md 的描述resolveEditMode()按以下优先级选择激活的协议模型专属的配置变体环境变量PI_EDIT_VARIANT配置项edit.mode默认值hashline。当选择patch模式时工具的描述、参数 schema、示例、渲染器与可选的 Lark 约束语法都会一并切换到 patch 协议。其提示文档通过include_str!(../prompts/patch.md)直接内嵌进引擎源码见 crates/pi-edit/src/lib.rs保证模型看到的规则与引擎实现严格同源。二、hunk 头Hunk Headers的两种写法每个 hunk 都以开头后跟可选的锚点。提示文档给出了两种形式裸当上下文行本身足够唯一时使用不需要额外锚点 $ANCHOR锚点必须逐字复制自文件可以是整行也可以是唯一子串。 context line -old new def greet(name): context line -old new在源码层面parse_diff_hunks位于 crates/pi-edit/src/diff_string.rs负责解析这些 hunk每个DiffHunk包含old_lines、new_lines、可选的old_start_line/new_start_line行号提示、change_context锚点文本以及is_end_of_file等字段为后续的精确/模糊定位提供全部输入。三、锚点选择Anchor Selection策略提示文档规定了明确的锚点选取优先级优先裸当上下文行单独出现即可唯一定位时不要画蛇添足否则选择高度特异的锚点从文件中逐字复制完整的函数签名如 def process_data(items):类声明如 class BaseClass唯一的字符串字面量或错误消息名称不常见的配置键。遇到 Found multiple matches 时按顺序尝试三种手段增加上下文行数用多个带独立锚点的 hunk 分别定位使用更长的锚点子串。提示文档同时给出了avoid清单禁止使用通用锚点如import、export、describe、function、const这类在文件中几乎必然多次出现的词也禁止在多个 hunk 中重复同样的新增内容会造成重复块以及为小改动做整文件覆盖仅在重大重构或文件很短时才可接受整文件覆盖。从源码看锚点的解析落在DiffHunk.change_context引擎会调用find_hierarchical_contextcrates/pi-edit/src/modes/patch.rs做分层定位对多行/多词锚点它会按空格拆分为外层 内层层次先定位外层如class BaseClass再在其后定位内层如方法声明从而精确命中嵌套结构中的目标。四、上下文行Context Lines规则上下文行是 hunk 中前缀为单个空格 的行用来让匹配变得唯一数量通常提供28 行前缀的上下文即可唯一匹配结构化块当编辑嵌套花括号、标签或缩进区域时必须包含块的开始行与结束行确保编辑动作停留在块内部不会把新代码写到块外。 class BaseClass class BaseClass: def method(self): - return old_value return new_value引擎对上下文行的处理非常宽容但严格匹配策略枚举ContextMatchStrategycrates/pi-edit/src/modes/patch.rs按exact → trim → unicode → prefix → substring → fuzzy逐级放宽而行序列匹配策略SequenceMatchStrategycrates/pi-edit/src/modes/patch.rs则包含exact、trim-trailing、trim、comment-prefix、unicode、prefix、substring、fuzzy、fuzzy-dominant、character十档。这意味着模型写的上下文与文件实际内容存在轻微空白差异时引擎不会直接失败而是按策略链逐级尝试但任何命中都要求匹配唯一match_count 1否则仍会报错要求补充上下文。五、JSON 输入协议四种 Entry 操作patch 模式的输入是 JSON// Input is { path: string, edits: Entry[] }. path is required and applies to every entry. type Entry // Diff 是一个或多个针对顶层 path 的 hunk。 // - 每个 hunk 以 开头锚点可选。 // - 每个 hunk 主体只能有 | | - 开头的行。 // - 每个 hunk 至少包含一处修改 或 -。 | { op: update, diff: string } // Diff 是完整文件内容无任何前缀。 | { op: create, diff: string } // delete 无需 diff。 | { op: delete } // 从顶层 path 更新并移动到新路径。 | { op: update, rename: string, diff: string }操作字段语义updatediff对一个或多个 hunk 做差分应用每个 hunk 至少包含一处或-修改creatediffdiff为完整文件内容不带任何前缀新建文件delete—删除既有文件不需要 diffupdaterenamerename,diff先按 hunk 更新内容再移动到新路径path是必填字段作用于所有 entry。在引擎侧操作被映射为Operation枚举crates/pi-edit/src/modes/patch.rs未指定 op 时默认update非法值会报Invalid patch operation: ...。五个可以直接落地的示例来自核心测试夹具下面的用例全部来自仓库真实测试数据 crates/pi-edit/tests/fixtures/patch/core.json可直接验证1. 新建文件{path:new.txt,edits:[{op:create,diff:hello\nworld}]}2. 用统一 hunk 更新文件{path:a.txt,edits:[{op:update,diff:\n two\n-three\nTHREE}]}two为上下文行-three删除THREE新增。3. 删除文件{path:gone.txt,edits:[{op:delete}]}4. 更新并重命名{path:old.txt,edits:[{op:update,rename:dir/new.txt,diff:\n-before\nafter}]}5. 纯新增 锚点定位changeContext配合空旧行{path:add-context.ts,edits:[{op:update,diff: function bar\n console.log(x);}]}此例演示了锚点定位的经典场景 function bar告诉引擎在function bar块内插入新行测试期望结果是把console.log(x);插入到function bar()的函数体第一行。六、输出与失败诊断调用成功时返回成功标记失败时返回错误消息提示文档明确列出了三类典型失败Found multiple matches—— 锚点/上下文不够唯一No match found—— 上下文行在文件中不存在内容写错或基于过期未更新的读取diff 格式语法错误。引擎在这些场景下会返回包含可操作信息的中文级诊断例如匹配到多个位置时character_match会返回Found N occurrences in path并附上每个候选位置的行号预览提示 Add more context lines to disambiguate.找不到足够接近的匹配时会报告最近候选的相似度百分比与行号见 crates/pi-edit/src/modes/patch.rs。在 Agent 侧packages/coding-agent/src/edit/index.tspatch/apply_patch模式的诊断还会把引擎返回的 warnings 合并进diagnostics以patch: warning前缀暴露给模型帮助模型理解失败原因并修正下一次调用。七、关键纪律CriticalAgent 必须遵守的编辑铁律提示文档用critical块固化了模型侧的编辑行为准则这些规则直接决定了编辑的安全性与成功率编辑前必须读取目标文件You MUST read the target file before editing不允许基于记忆或猜测直接打补丁锚点与上下文行必须逐字复制包括空白字符——任何删改都会导致 No match found永远不要把锚点当注释用禁止出现行号、位置标签、占位符如 之类的内容永远不要把新行放到目标块之外——上下文的开始/结束行要包含在 hunk 内保证修改留在块内部失败即重读重建如果编辑失败或破坏了结构必须重新读取文件、基于当前内容生成全新补丁绝不重试同一个 diff禁止用编辑修格式缩进、空白、代码重排一律不通过逐个编辑完成而是在全部实质性编辑结束后运行一次格式化命令bun fmt、cargo fmt、prettier --write等。如果编辑后看到缩进不一致保留它交给格式化器一次性修复——而不是发起 N 次缩进微调。这条格式化一次搞定的纪律与 oh-my-pi 的工程实践一致格式化属于单一命令的幂等操作逐行手工修缩进既浪费 token 又极易破坏语法结构。八、底层引擎fuzzy 匹配、缩进归一化与序列回退patch 模式不是一个朴素的字符串替换器crates/pi-edit/src/modes/patch.rs 中的PatchEngine提供了两层容错但不降智的能力8.1 模糊匹配开关与阈值pub struct PatchEngine { pub allow_fuzzy: bool, pub fuzzy_threshold: f64, }allow_fuzzy是否允许非精确 hunk 定位fuzzy_threshold字符级回退匹配的最低置信度。在 Agent 侧这两个参数由环境变量控制PI_EDIT_FUZZY默认auto与PI_EDIT_FUZZY_THRESHOLD默认auto经resolveAllowFuzzy/resolveFuzzyThreshold解析见 packages/coding-agent/src/edit/index.ts。模糊匹配内部有一套回退变体生成器fallback_variantscrates/pi-edit/src/modes/patch.rs依次生成TrimCommon去掉 hunk 与文件共同的头部/尾部上下文收敛到最小差异DedupeShared折叠连续重复的共享行CollapseRepeated折叠重复块aggressive 模式下才启用SingleLine当整段差异收敛为单行变化时退化为单行替换。8.2 缩进归一化indentation adjustment编辑结构化代码时最经典的失败是模型上下文里的缩进与文件实际缩进不一致tab vs 空格、缩进宽度不同。引擎为此实现了adjust_lines_indentationcrates/pi-edit/src/modes/patch.rs能够检测pattern 全 tab、actual 全空格的对称情形按比例把新增行从 tab 转换为空格或反向依据多行缩进样本推断出统一的 tab 宽度与偏移当整体缩进差恒定时把新增行整体平移 delta对新行中已存在于文件里的行直接复用文件中的真实缩进版本。这套逻辑保证了模型以 2 空格缩进书写上下文、文件实际是 4 空格时新增行仍能以正确缩进落盘而不是把格式破坏后推给格式化器。8.3 行号提示与 EOF 插入DiffHunk支持old_start_line/new_start_line行号提示与is_end_of_file标记。纯新增old_lines为空时引擎按以下优先级确定插入点crates/pi-edit/src/modes/patch.rs有change_context时以锚点定位结果为准有行号提示时定位到hint - 1并校验行号范围hint 1或超出文件行数都会报错文件以空行结尾时插入到末尾空行处否则追加到文件末尾。同时引擎会拒绝行号提示0Line numbers start at 1防止索引越界。8.4 关键边界校验测试夹具还验证了一系列引擎级安全护栏拒绝重命名到源路径本身rename path is the same as source path拒绝重命名到已存在的目标Cannot rename from.txt to to.txt: destination already exists.批量 entry 顺序执行update链式作用于同一文件测试中one→two→three最终落盘为three且只写盘一次hunk 顺序无关即使 hunk 在 diff 中乱序书写先 second后 first引擎也会按文件中的实际位置正确应用编码保真CRLF 行尾、UTF-8 BOM、缺失末尾换行的文件都能在补丁后保持原有字节特征见 crates/pi-edit/tests/fixtures/patch/core.json。这些用例统一由 crates/pi-edit/tests/patch.rs 中的patch_core_fixtures驱动通过common::run_fixture(patch/core.json, EditMode::Patch)逐条断言文件内容与删除清单是理解引擎行为的可执行文档。九、与 Agent 运行时的集成在 oh-my-pi 的 Agent 运行时中EditToolpackages/coding-agent/src/edit/index.ts会根据当前模式动态切换 schema 与示例get parameters(): TInput { switch (this.mode) { case replace: return replaceEditSchema; case patch: return patchEditSchema; case apply_patch: return applyPatchSchema; case hashline: return hashlineEditParamsSchema; case sloppy: return sloppyEditSchema; } }patch模式使用patchEditSchema并附带PATCH_EXAMPLESpackages/coding-agent/src/edit/index.ts模型在每次调用前都能看到与提示文档一致的示例。执行结果经toPerFileResult/aggregateDetails汇总为EditToolDetails包含 unifieddiff、firstChangedLine、opupdate/delete/create、move与sourcePath等字段批量多文件结果共享MAX_EDIT_SNAPSHOT_TEXT_CHARS 32_768字符的 old/new 文本预算packages/coding-agent/src/edit/index.ts超限文件的快照文本会被裁剪避免 session JSONL 无限膨胀。值得一提的是patch 引擎本身是一份 Rust 原生移植mod.rs注释明确 Port ofpackages/coding-agent/src/edit/modes/patch.ts这也意味着同一份协议同时服务于原生Rust与 JS 两条执行路径行为保持一致。十、实战要点速查编写 patch 调用时把下面这份检查清单作为提交前自检检查项要求编辑前读取必须基于最新read/grep结果书写 hunk锚点优先裸需要锚点时必须逐字复制、选特异内容函数签名/类声明/唯一字符串/少见配置键上下文28 行结构化块必须含开闭行避免泛化锚点内容行只有 、、-前缀每个 hunk 至少一处修改编码保持 CRLF / BOM / 无末尾换行等原文件特征交由引擎处理失败处理遇 multiple matches 加上下文或拆 hunk遇 no match 重新读取后重建补丁绝不重试同一 diff格式化不做缩进微调实质性编辑后运行一次bun fmt/cargo fmt/prettier --write结语patch 模式的设计哲学可以概括为给模型一套简单到不会被语法卡住的 hunk 协议把定位的容错做进引擎把纪律写进提示词。锚点逐字复制、失败即重读、格式化最后统一执行这三条铁律配合引擎的序列回退、缩进归一化与多策略匹配让 oh-my-pi 的 Agent 能够在真实代码库上高成功率地完成既有文件编辑同时把误改风险压到最低。理解这份协议无论是接入自定义 Agent 流程还是排查编辑失败你都能直击根因。【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表