github@openai/codex 里,模型生成的 diff 是什么格式,udiff 吗?
創建於:2025年9月27日
使用 GPT-5 Thinking 回答 Chat01
創建於:2025年9月27日
使用 GPT-5 Thinking 回答 Chat01
github@openai/codex 里,模型生成的 diff 是什么格式,udiff 吗?
不是标准的 unified diff(udiff)。在 GitHub 的 openai/codex 仓库里,代码编辑是通过一个为 AI 助手设计的自定义 apply_patch 补丁格式完成的:它用“*** Begin Patch…*** End Patch”作为包裹,并在里面按文件进行操作(Add File / Delete File / Update File 等)。(GitHub)
这个格式在“更新文件”时会用到与 udiff 相似的“hunk”语法:以 @@ 引导的区块头,行首 +/- 表示增删,但整体仍属于 Codex 的自定义封装(还支持如 Move to: 的重命名声明)。(GitHub)
需要注意的是,社区里也提到过该格式的细节标记曾有过调整(例如 *** Begin Patch 标记的变体),说明它在迭代中,但本质仍是上述自定义“apply_patch”而非纯 udiff。(GitHub)
作为对比,像 Aider 这类工具使用的是改良的 udiff 编辑格式——这与 Codex 的 apply_patch 不是一回事。(Aider)
简化示例(示意):
text*** Begin Patch *** Add File: hello.txt +Hello, world! *** Update File: src/app.py @@ -print("Hi") +print("Hello, world!") *** Delete File: obsolete.txt *** End Patch
(上例为 Codex 自定义“apply_patch”风格,并非标准 udiff。)
codex 是如何 apply 那个 patch 的?
简短说:Codex 并不会真去调用系统里的 apply_patch 可执行文件。当模型生成补丁时,它把补丁作为“虚拟工具调用”交给 Codex CLI,CLI 用内置的 Rust 实现去解析→校验→写盘;必要时再向你请求批准。(GitHub)
下面按实际流水线讲清楚它“如何 apply”:
触发方式
模型会发出一条“像 shell 一样”的调用(例如 shell {"command": ["apply_patch","<PATCH>"]})。CLI 会拦截这类命令并在进程内处理,或者以 --codex-run-as-apply-patch "<PATCH>" 模式自调用;所以你在 bash 里不会找到真正的 apply_patch 二进制。(GitHub)
解析补丁
补丁是 Codex 的自定义封装格式:最外层 *** Begin Patch … *** End Patch,里面由若干“文件操作段”组成:
*** Add File: <path>(创建并写入 + 行)*** Update File: <path>(使用 @@ 区块 + +/- 行做行级修改)*** Delete File: <path>Move to: <new-path>(文件移动/改名)安全与审批
在只读工作区或需要审批的模式下,CLI 会阻止直接写盘并提示你授予写入/批准;也做路径规范化来防止目录穿越等风险。(GitHub)
应用逻辑(Update File 的核心)
CLI 的 Rust 库把文件内容读入后,按 @@ hunk 逐段匹配上下文并应用增删行;当行号/上下文有轻微偏移时,它会用相似度/序列寻位算法尽量找到合适位置再贴合(源码里能看到使用 similar::TextDiff 以及 seek_sequence 帮助定位)。定位失败则报错并中止当前文件的打补丁。(GitHub)
写盘与结果
成功时写回磁盘并更新会话状态;失败时你会在日志里看到诸如“Failed to apply patch / Failed to write file …”之类的报错。(GitHub)
无需你手动跑 git apply
官方建议把改动交给 apply_patch 来做(便于回滚/审阅),并配合 Git 的分支与小步提交来管理这些补丁。(OpenAI Developers)
一个最小例子(模型实际会用“工具调用”,这里仅示意):
textshell {"command": ["apply_patch", "*** Begin Patch *** Update File: src/app.py @@ -print('Hi') +print('Hello, world!') *** End Patch "]}
这会触发 Codex CLI 的内置补丁器去定位并改写 src/app.py;若工作区是只读或审批未通过,就会被拦下并提示处理。(GitHub)
如果你要在 CI/无人值守里启用它,确保配置里打开 include_apply_patch_tool(近期有修复让 exec 模式正确读取该开关)。(GitHub)
需要我给你一份可复制的“最小补丁模板”和常见失败排查清单吗?
我自己有一个程序想复用他这个 apply_patch 流程,他提供独立的实现吗
短答:官方没有单独发布一个“apply_patch”可执行文件或独立库;它是 Codex CLI 里的“虚拟工具”,实现代码在 monorepo 的 Rust 子包中。你可以通过两种方式复用:要么调用 Codex 可执行文件的专用入口,要么直接复用/移植那段 Rust 实现;此外也有社区打包的独立实现可用。(GitHub)
怎么选:
最省事:外部调用 Codex 可执行文件
直接把补丁文本交给 Codex 二进制:codex --codex-run-as-apply-patch "<PATCH_TEXT>"(Codex 内部会解析并写盘;无需系统里有真正的 apply_patch 命令)。配置里记得启用 include_apply_patch_tool。(GitHub)
嵌入式:复用官方 Rust 实现
代码在 codex-rs/apply-patch/src/lib.rs,仓库为 Apache-2.0 许可,可以按需要 vendor 或用 Git 依赖方式复用(官方没在 crates.io 单独发布包)。(GitHub)
社区独立实现(非官方)
额外提示
*** Begin Patch/*** End Patch 封装 + 按文件操作 + @@ hunk)。(GitHub)如果你告诉我你的运行环境(语言/运行时),我可以给你对应的最小集成示例(shell/Rust/Python 皆可)。