10.3 Patch 系统

模型: claude-opus-4-6 (anthropic/claude-opus-4-6) 生成日期: 2026-02-17


除了逐行编辑(Edit 工具)和全量覆写(Write 工具)之外,OpenCode 还提供了一种基于补丁(Patch)的文件修改方式——apply_patch 工具。它允许 LLM 以标准化的补丁格式一次性描述对多个文件的创建、删除和修改操作。本节将解析 patch/index.ts 的实现。

10.3.1 patch/index.ts:Diff 补丁的生成与应用

Patch 模块(681 行)实现了一个完整的补丁解析和应用系统。它的补丁格式不是标准的 unified diff,而是一种专为 LLM 设计的自定义格式。

补丁格式

OpenCode 的补丁格式使用 *** 标记来区分不同的操作:

*** Begin Patch
*** Add File: path/to/new-file.ts
+line 1 of new file
+line 2 of new file

*** Delete File: path/to/old-file.ts

*** Update File: path/to/existing.ts
@@ context line
-old line to remove
+new line to add
 unchanged line

*** End Patch

Hunk 类型

补丁被解析为一组 Hunk(操作单元),每个 Hunk 对应一个文件的操作:

update 类型的 Hunk 最为复杂——它包含多个 UpdateFileChunk,每个 chunk 描述了文件中一处局部的替换操作。

补丁解析

parsePatch() 函数解析补丁文本,提取结构化的 Hunk 列表:

模糊匹配应用

apply_patch 的一个突出特点是多级模糊匹配。当在文件中查找要替换的旧行时,它不要求严格的字符级精确匹配,而是提供了四级递进的匹配策略:

为什么需要多级匹配?因为 LLM 生成的补丁中,上下文行可能存在细微的差异:

  • 空白差异:LLM 可能多加或少加了空格/缩进。

  • Unicode 差异:LLM 可能将普通引号 ' 变成了智能引号 ',或将连字符 - 变成了破折号

normalizeUnicode() 函数处理了这些常见的 Unicode 替换:

替换应用

找到匹配位置后,applyReplacements() 以倒序应用所有替换,避免索引偏移问题:

10.3.2 apply_patch 工具的实现细节

apply_patch 工具实际上是 Bash 工具的一个"影子功能"——当 Bash 命令被检测为 apply_patch 调用时,会拦截并使用 Patch 模块来处理,而非真正执行 Shell 命令:

maybeParseApplyPatchVerified() 不仅解析补丁,还会预验证每个操作的正确性——在实际写入文件之前就检查所有替换是否能找到匹配。如果有任何 chunk 无法在目标文件中找到匹配的旧行,会返回 CorrectnessError 而不是部分应用补丁,确保操作的原子性。


本节小结

OpenCode 的 Patch 系统实现了一种为 LLM 优化的补丁格式,支持文件的新增、删除和修改操作。核心亮点是四级递进的模糊匹配策略(精确 → 去尾空白 → 去首尾空白 → Unicode 标准化),容忍 LLM 生成补丁时常见的空白和字符差异。替换以倒序方式应用以避免索引偏移,整个操作通过预验证确保原子性。

Last updated