ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

openinterpreter apply_patch 补丁格式完全指南:模板指令、完整语法与 Rust 解析器实现

openinterpreter apply_patch 补丁格式完全指南:模板指令、完整语法与 Rust 解析器实现 openinterpreter apply_patch 补丁格式完全指南模板指令、完整语法与 Rust 解析器实现【免费下载链接】openinterpreterA coding agent for open models like Kimi K3 and GLM 5.3项目地址: https://gitcode.com/GitHub_Trending/op/openinterpreter本篇指南围绕 openinterpreter 仓库中的apply_patch工具指令模板展开讲解这种文件导向的裁剪版 diff 格式的完整语法、书写规则与调用方式并结合codex-rs/apply-patch中的解析器与应用器源码说明每一条格式约定背后的实现逻辑。读完本文你可以独立写出合法、可唯一匹配、可安全应用的补丁并理解 agent 在哪些情况下会收到何种错误反馈。1. apply_patch 是什么一份注入模型提示词的工具说明书apply_patch是 openinterpreter基于 Rust 实现的编码代理codex-rs工作区用于编辑文件的标准工具。它不是一个泛化的文本替换工具而是一个高层信封envelope式的文件操作协议模型通过 shell 命令面shell command surface调用apply_patch传入一段结构化的补丁文本代理解析后对文件执行新增、删除、更新含重命名三类操作。仓库中这份工具的使用说明就存放在指令模板里模板文件codex-rs/prompts/templates/apply_patch_tool_instructions.md模板编译期嵌入codex-rs/prompts/src/apply_patch.rs 通过include_str!将其编译为常量APPLY_PATCH_TOOL_INSTRUCTIONS并从 codex-rs/prompts/src/lib.rs 对外导出实际拼接进系统提示词时它与基础指令用换行连接例如 codex-rs/core/tests/suite/prompt_caching.rs 中[base_instructions, APPLY_PATCH_TOOL_INSTRUCTIONS.to_string()].join(\n)的用法此外 core 侧还维护了合并版提示词文件 codex-rs/core/prompt_with_apply_patch_instructions.md。也就是说本文接下来的所有格式约定都是模型在运行时逐字读到的指令。模板注释写明其面向 gpt-4.1 这类模型见 apply_patch.rs 第 1 行这也是解析器默认采用宽松lenient模式的原因后文第 7 节会展开。2. 补丁的整体结构信封与文件操作apply_patch的补丁语言是一种裁剪版、文件导向的 diff 格式设计目标是易于解析、安全应用。你可以把它理解为一个高层信封*** Begin Patch [ one or more file sections ] *** End Patch信封内部是一串文件操作file operations。每个操作必须以三行 header 之一开头明确指出你要执行的动作Header语义后续内容*** Add File: path创建新文件其后每一行都是行即文件的初始内容*** Delete File: path删除已存在的文件后面不能跟随任何内容*** Update File: path就地修补一个已存在的文件可选重命名一个或多个 hunk在*** Update Fileheader 之后可以紧跟一行*** Move to: new path用于在更新的同时把文件重命名/移动到新的相对路径。2.1 hunk 与三种行前缀每个 Update File 操作由一个或多个 hunk 组成每个 hunk 以开头后可以跟一个 hunk header用来指明代码片段所属的类或函数。hunk 内部每一行都必须以下列三种前缀之一开头空格上下文行表示保持不变-删除行old_code新增行new_code。解析器把这些内容组织为UpdateFileChunk结构见 codex-rs/apply-patch/src/parser.rspub struct UpdateFileChunk { /// 用于缩小 chunk 定位范围的一行上下文通常是类/方法/函数定义 pub change_context: OptionString, /// 应当被 new_lines 替换的连续旧行块 pub old_lines: VecString, pub new_lines: VecString, /// 若为 trueold_lines 必须出现在源文件末尾 pub is_end_of_file: bool, }其中change_context正是后 hunk header 的解析结果——它是把补丁定位到哪个函数的关键。3. hunk 的书写规则上下文行数与 定位3.1 默认 3 行上下文且相邻变更不重复模板对[context_before]和[context_after]的要求原文规则默认展示每个变更上方紧邻的 3 行和下方紧邻的 3 行代码如果一次变更距离上一次变更不足 3 行不要把前一次变更的[context_after]行复制到后一次变更的[context_before]中即相邻 hunk 的上下文不要重叠重复。3.2 用header 指明所属类/函数当 3 行上下文不足以在文件中唯一定位代码片段时使用后跟 header 指明片段所属的类或函数例如 class BaseClass [3 lines of pre-context] - [old_code] [new_code] [3 lines of post-context]3.3 重复代码块多行逐级跳转如果某段代码在一个类或函数里重复出现次数太多以至于单条加 3 行上下文仍然无法唯一确定位置可以使用多个语句逐级跳到正确上下文 class BaseClass def method(): [3 lines of pre-context] - [old_code] [new_code] [3 lines of post-context]3.4 文件尾标记*** End of File完整语法还允许在 hunk 末尾追加一行*** End of File表示该 chunk 的旧行必须落在文件结尾解析为is_end_of_file: true。仓库中专门有对应测试场景 022_update_file_end_of_file_marker 和 021_update_file_deletion_only删除整段内容、不新增行可以查阅。4. 完整形式语法BNF 与 Lark 对照模板给出的完整文法定义如下Patch : Begin { FileOp } End Begin : *** Begin Patch NEWLINE End : *** End Patch NEWLINE FileOp : AddFile | DeleteFile | UpdateFile AddFile : *** Add File: path NEWLINE { line NEWLINE } DeleteFile : *** Delete File: path NEWLINE UpdateFile : *** Update File: path NEWLINE [ MoveTo ] { Hunk } MoveTo : *** Move to: newPath NEWLINE Hunk : [ header ] NEWLINE { HunkLine } [ *** End of File NEWLINE ] HunkLine : ( | - | ) text NEWLINE解析器实现中使用的官方 Lark 文法见 codex-rs/apply-patch/src/parser.rs 的模块注释与上述 BNF 语义一致并额外揭示了两个模板未强调的细节start: begin_patch environment_id? hunk end_patch begin_patch: *** Begin Patch LF environment_id: *** Environment ID: filename LF end_patch: *** End Patch LF? add_line: /(.)/ LF - line change_context: ( | /(.)/) LF eof_line: *** End of File LF两点补充事实补丁信封支持可选的*** Environment ID: id行解析结果保存在ApplyPatchArgs.environment_id见 codex-rs/apply-patch/src/lib.rs用于多执行环境场景下标明补丁应作用于哪个环境Lark 文法中end_patch的结尾LF?说明最后一个换行符是可选的——实现比模板更宽容解析器注释明确写道 The parser below is a little more lenient than the explicit spec and allows for leading/trailing whitespace around patch markers见 parser.rs 第 24-25 行。测试场景 017_whitespace_padded_hunk_header 和 018_whitespace_padded_patch_markers 验证了空白容忍行为。5. 一个组合多操作的完整示例一个补丁可以把多种操作混在一起。以下示例完整继承了模板给出的用法新增文件、更新并重命名文件、删除文件*** Begin Patch *** Add File: hello.txt Hello world *** Update File: src/app.py *** Move to: src/main.py def greet(): -print(Hi) print(Hello, world!) *** Delete File: obsolete.txt *** End Patch逐行解读*** Add File: hello.txt创建hello.txt其下所有行都必须带前缀这里文件内容为Hello world*** Update File: src/app.py*** Move to: src/main.py先对src/app.py应用 hunk把greet()里的print(Hi)改为print(Hello, world!)再把结果写入src/main.py并删除原文件 def greet():是 hunk header把变更定位到greet()函数内*** Delete File: obsolete.txt删除文件其后不跟任何行。在实现侧Move to的执行顺序是先写新路径、后删旧路径见 codex-rs/apply-patch/src/lib.rs 中Hunk::UpdateFile分支先对dest_uri执行write_file_with_missing_parent_retry成功后再ensure_not_directoryfs.remove删除原文件。目标目录若不存在会自动递归创建父目录lib.rs 第 629-665 行 的write_file_with_missing_parent_retry。测试场景 004_move_to_new_directory 与 010_move_overwrites_existing_destination 覆盖了移动到不存在的目录和移动目标已存在会被覆盖两种情况。6. 三条必须记住的规则模板原文以 It is important to remember 列出的三条铁律必须带 header每个文件操作都要写明意图Add / Delete / Update不能只贴 diff 行新建文件也要前缀即使是*** Add File创建的初始内容每一行都要以开头路径只能是相对路径绝不使用绝对路径File references can only be relative, NEVER ABSOLUTE。关于第 3 条需要说明一个实现事实解析器与执行器在技术上也接受绝对路径Hunk::resolve_path通过cwd.join(...)统一处理相对与绝对路径测试test_apply_patch_hunks_accept_relative_and_absolute_paths见 codex-rs/apply-patch/src/lib.rs但指令明确要求模型只用相对路径——这是为了把补丁锚定在会话的工作目录上避免模型越权写入工作区之外的位置。作为作者请遵守相对路径约定。7. 如何调用 apply_patch7.1 标准调用形式模板给出的调用方式是把它作为 shell 工具的一个程序名调用第二个参数是整段补丁文本\n为转义换行shell {command:[apply_patch,*** Begin Patch\n*** Add File: hello.txt\nHello, world!\n*** End Patch\n]}即command数组的形如[程序名, 参数...]apply_patch的第一个参数就是完整补丁字符串。7.2 解析器实际接受的调用形态从源码看调用识别逻辑在 codex-rs/apply-patch/src/invocation.rs 的maybe_parse_apply_patch命令名apply_patch与别名applypatch都认常量APPLY_PATCH_COMMANDS [apply_patch, applypatch]见 invocation.rs 第 27 行直接调用[apply_patch, patch 文本]shell heredoc 形式bash -lc apply_patch EOF ... EOF也支持zsh/sh的-lc/-c、PowerShell-command、cmd /c解析器会提取 heredoc 正文作为补丁若 heredoc 前有cd path 该目录会被记录为workdir作为解析相对路径的基准invocation.rs 第 119 行 注释Shell heredoc form: (optional cd path ) apply_patch EOF ...。7.3 两种重要的调用错误隐式调用会被明确拒绝。如果你把裸补丁文本直接当命令而不是[apply_patch, ...]验证函数maybe_parse_apply_patch_verified会识别出这是一个补丁但没有显式调用 apply_patch并返回错误invocation.rs 第 147-158 行错误文案见 codex-rs/apply-patch/src/lib.rspatch detected without explicit call to apply_patch. Rerun as [apply_patch, patch]heredoc 包裹的补丁会被自动剥离。解析器默认运行在宽松模式PARSE_IN_STRICT_MODE false见 parser.rs 第 47-53 行。原因是个别模型注释点名 gpt-4.1习惯把补丁写成EOF\n*** Begin Patch\n...\nEOF\n这样的 heredoc 字符串作为单个参数传入而工具实际以类execvpe(3)方式执行、并不经过 shellheredoc 会被当成字面量。宽松模式下解析器会检测首行EOF/EOF/EOF且末行以EOF结尾、总行数不少于 4 的情况自动剥掉 heredoc 标记再按严格边界校验parser.rs 第 217-239 行。7.4 独立可执行程序与自调用协议apply-patch除了作为库被调用还有一个独立可执行入口codex-rs/apply-patch/src/main.rs、standalone_executable.rs支持从 stdin 读取补丁。此外 core 可执行文件自调用内部apply_patch路径时使用特殊argv[1]标志--codex-run-as-apply-patch常量CODEX_CORE_APPLY_PATCH_ARG1见 lib.rs 第 34-41 行。这两点属于进程调用协议层面普通使用者只需记住 7.1 的调用形式即可。8. 解析与应用流程源码级原理整条调用链可以概括为边界校验 → 流式解析出 hunk → 逐 hunk 定位替换 → 写文件系统 → 打印摘要。8.1 解析parse_patch入口parse_patchparser.rs 第 130-137 行按当前模式校验首末行标记后把文本交给StreamingPatchParser增量解析出Hunk列表。Hunk是三种变体的枚举AddFile { path, contents }、DeleteFile { path }、UpdateFile { path, move_path, chunks }parser.rs 第 64-82 行其中UpdateFile.chunks要求按文件中出现的先后顺序排列源码注释一个 chunk 的change_context必须出现在前一个 chunk 之后。解析阶段的典型错误首行不是*** Begin Patch→The first line of the patch must be *** Begin Patch末行不是*** End Patch→The last line of the patch must be *** End Patch*** Update File后没有任何 hunk →Update file hunk for path ... is empty非法 hunk header如后内容不符合约定→ 带行号的InvalidHunkError。对应回归测试005_rejects_empty_patch、008_rejects_empty_update_hunk、013_rejects_invalid_hunk_header。8.2 定位header 如何变成行号应用阶段的核心是compute_replacementslib.rs 第 712-801 行。对每个 chunk若 chunk 带change_context即 header先用序列搜索函数codex-rs/apply-patch/src/seek_sequence.rs从上一个 chunk 结束位置开始向前找到该上下文行找不到即报Failed to find context ... in path然后在上下文之后的范围内对old_lines做逐行精确匹配若直接匹配失败且old_lines以空行结尾代表被替换区域的终止换行符会去掉末尾空行重试一次以稳妥处理触及文件末尾的修改每个成功定位的 chunk 记为(start_index, old_len, new_lines)三元组最终按位置排序、从后往前应用替换apply_replacementslib.rs 第 803-829 行避免前面的替换移动后面 chunk 的坐标。这解释了第 3 节两条书写规则的必要性上下文必须唯一可寻址且 chunk 顺序不能乱——定位是从前往后单指针扫描的乱序的 chunk 会导致Failed to find expected lines错误对应场景 006_rejects_missing_context。8.3 执行逐 hunk 落盘与部分成功语义apply_hunks_to_fileslib.rs 第 361-566 行按补丁顺序执行各 hunkAddFile写入内容若目标已存在会先记录被覆盖的旧内容overwritten_content再覆盖——场景 011_add_overwrites_existing_file 验证了Add 覆盖已存在文件是允许的DeleteFile先ensure_not_directory目录不能删场景 012_delete_directory_fails不存在则报错场景 007_rejects_missing_file_deleteUpdateFile读原文件 → 计算新内容 → 有Move to时先写新路径再删旧路径否则原地写回。Update File要求目标文件必须已存在场景 009_requires_existing_file_for_update。部分成功不自动回滚。失败时返回的ApplyPatchFailure携带AppliedPatchDelta——在失败观测点之前已确定落盘的那些文本变更lib.rs 第 247-273 行exact标志标明 delta 是否精确写失败可能已截断文件此时 delta 标记为不精确。场景 015_failure_after_partial_success_leaves_changes 的期望结果里保留了失败前已创建的文件。实践含义是多文件补丁应尽量把高风险操作放在后面或在出错后核对已落盘部分再修正代理无法保证事务性回滚。8.4 成功输出全部 hunk 成功后print_summarylib.rs 第 868-885 行以 git 风格输出Success. Updated the following files: A 新增文件路径 M 修改/移动文件路径 D 删除文件路径A/M/D顺序固定为新增、修改、删除。9. 测试场景索引用仓库自带的回归用例自检apply-patch的端到端回归测试位于 codex-rs/apply-patch/tests/fixtures/scenarios/每个场景目录含input/变更前文件树、expected/期望结果与patch.txt待应用补丁。值得对照阅读的有场景验证点001_add_file新增文件行构成全部内容002_multiple_operations单补丁混合增/删/改003_multiple_chunks同一文件多个 hunk014_update_file_appends_trailing_newline无结尾换行文件的处理实现会自动补上结尾换行见 lib.rs 第 700-705 行016_pure_addition_update_chunk纯新增 chunk无-行插入到文件末尾实现见 lib.rs 第 741-751 行019_unicode_simpleUnicode 内容测试入口见 codex-rs/apply-patch/tests/suite/scenarios.rs 与 cli.rs。10. 快速核对清单写出补丁前用这份清单对照模板与实现首行*** Begin Patch、末行*** End Patch二者之间至少一个文件操作每个操作带正确 header*** Add File:/*** Delete File:/*** Update File:重命名在 Update 下紧跟*** Move to:新增文件内容每行开头hunk 行一律 /-/前缀默认 3 行前后上下文3 行不够唯一时加 类/函数header仍不够则多行逐级定位相邻变更 3 行内不复制重复上下文触及文件末尾可加*** End of File全部使用相对路径调用形式为[apply_patch, patch]别名applypatchheredoc 包裹可被宽松模式剥离裸补丁直发会被显式拒绝预期输出为Success. Updated the following files:A/M/D列表失败时注意已落盘的部分变更不会自动回滚。这套信封 header hunk的设计把模型生成 diff从自由文本约束为可流式解析StreamingPatchParser、可逐行定位seek_sequence、可审计AppliedPatchDelta记录前后内容的结构化操作是 openinterpreter 这类编码代理能安全执行多文件编辑的底层基础。【免费下载链接】openinterpreterA coding agent for open models like Kimi K3 and GLM 5.3项目地址: https://gitcode.com/GitHub_Trending/op/openinterpreter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表