OpenAI Apply Patch 让模型通过 Responses API 提出结构化文件补丁,但不会替你的应用直接写仓库。正确流程是:提供真实文件上下文,启用 apply_patch,逐个校验并执行返回的 apply_patch_call.operation,再用原始 call_id 返回 apply_patch_call_output;直到模型不再请求补丁,最后运行测试并审查实际差异。
OpenAI Apply Patch 怎么用?
官方资料(2026-11-02 核对):OpenAI Apply Patch 指南 · Responses API Reference · Guardrails 与人工审批
什么时候适合使用 Apply Patch
| 任务 | 是否适合 | 原因 |
|---|---|---|
| 多文件重构、符号重命名 | 适合 | 可返回多条精确文件操作 |
| Bug 修复、测试与文档补齐 | 适合 | 变更可以审计、测试和继续修复 |
| 迁移、类型标注、机械修改 | 适合 | 结构化 diff 比整文件重写更容易检查 |
| 只需要解释代码 | 通常不需要 | 没有写文件需求,普通文本回答更简单 |
| 直接修改生产主分支 | 不建议 | 应先在临时副本或工作分支应用并验收 |
Responses API 最小请求
import OpenAI from "openai";
const client = new OpenAI();
let response = await client.responses.create({
model: process.env.OPENAI_MODEL,
tools: [{ type: "apply_patch" }],
input: `
<BEGIN_FILES>
===== lib/math.js
export const add = (a, b) => a + b;
<END_FILES>
把 add 重命名为 sum,并更新引用。
`
});
const patchCalls = response.output.filter(
item => item.type === "apply_patch_call"
);模型不会自动看到整个仓库。小任务可以把必要文件快照放进输入;仓库较大时,给模型配置受控、只读的文件探索工具,或结合 Shell 先列目录、读取文件和搜索引用。模型与项目是否支持该工具会变化,应在上线前重新核对当前模型页和账号权限。
apply_patch_call 返回什么
{
"type": "apply_patch_call",
"call_id": "call_...",
"status": "completed",
"operation": {
"type": "update_file",
"path": "lib/math.js",
"diff": "@@ ..."
}
}每个调用代表一个文件操作。status: "completed" 表示模型已经完成这条工具调用的生成,不代表补丁已写入磁盘。应用仍要验证路径、解析 diff、执行操作并记录结果。
三种 operation
| operation.type | 用途 | 载荷 |
|---|---|---|
create_file | 创建新文件 | path + 表示完整内容的 V4A diff |
update_file | 修改现有文件 | path + 增删改 V4A diff |
delete_file | 删除文件 | 仅 path |
不要把 operation.diff 当作可信 Shell 命令,也不要默认普通 git apply 一定兼容。官方指南把格式称为 V4A diff,并提供 Python/TypeScript Agents SDK 参考实现;生产环境应使用经过单元测试的 harness。
完整工具循环
- 检查 Response 已完成,并收集全部
apply_patch_call。 - 逐条校验 operation 类型、相对路径、允许目录、当前文件版本和审批策略。
- 在临时副本或工作分支应用 create/update/delete。
- 为每个
call_id生成且只生成一条结果;成功用completed,失败用failed并写明原因。 - 通过
previous_response_id续接,同时继续提供apply_patch工具。 - 重复直到没有新的 patch call;随后运行测试、lint、类型检查和差异审查。
while (patchCalls.length > 0) {
const results = patchCalls.map(call => {
const result = applyOperation(worktree, call.operation);
return {
type: "apply_patch_call_output",
call_id: call.call_id,
status: result.ok ? "completed" : "failed",
output: result.message
};
});
response = await client.responses.create({
model: process.env.OPENAI_MODEL,
previous_response_id: response.id,
tools: [{ type: "apply_patch" }],
input: results
});
patchCalls = response.output.filter(
item => item.type === "apply_patch_call"
);
}
补丁失败时不要伪装成功
{
"type": "apply_patch_call_output",
"call_id": "call_abc",
"status": "failed",
"output": "Invalid Context in src/app.js; file changed after snapshot"
}文件不存在、上下文冲突、编码不支持、路径越界或审批拒绝都应返回 failed。错误信息要短而具体,最好包含相对路径和失败类型;模型可以据此重新读取文件、缩小修改范围或生成新 diff。不要吞掉异常后返回 completed。
Patch harness 应负责什么
- 解析
operation.type、path和可选diff。 - 在固定工作区根目录解析规范化路径,拒绝
../、绝对路径和符号链接逃逸。 - 仅允许受支持的创建、更新和删除操作,限制文件大小、数量和总步骤。
- 检测文件是否在读取后被其他进程修改,避免把旧 diff 应用到新内容。
- 为每个调用记录结果、日志、前后哈希和审批证据。
- 明确采用整批原子回滚,还是逐文件成功/失败;不要隐式混用。
与 Shell、Function Calling、PTC 怎么配合
| 工具 | 主要职责 | 注意 |
|---|---|---|
| Shell | 发现目录、读取文件、搜索引用、运行测试 | 命令执行要用隔离沙箱和最小权限 |
| Function Calling | 暴露自有的只读文件、审批或测试工具 | 输入 Schema 与权限由应用定义 |
| PTC | 在程序中编排允许的工具 | 若开放给 programmatic caller,要单独设置 allowed_callers |
| Apply Patch | 提出 create/update/delete 的结构化文件修改 | 应用拥有执行、回滚、测试和发布决定 |
安全与人工审批
- 在临时 worktree、容器或内存工作区应用补丁,不直接写生产主分支。
- 允许目录使用白名单;保护密钥、环境变量、CI/CD、权限、账单和部署配置。
- 删除文件、修改依赖/锁文件、迁移数据库、改变认证/权限、发布代码等操作先审批。
- 补丁内容、仓库文件和测试输出都属于不可信数据,不能借文字扩大工具权限。
- 设置最大文件数、diff 大小、工具轮次、总时长、Token/费用和并发限制。
- 应用完成后检查
git diff --check、测试结果和最终工作区状态,再由发布流程决定是否合并。
常见错误与排查
- 没有 apply_patch_call:确认使用 Responses API、模型/项目支持该工具,并提供了明确写文件任务和足够上下文。
- Invalid Context:文件可能在读取后改变;重新读取最新内容,再请求更小的目标 diff。
- File not found:核对路径相对工作区根目录、大小写和分支,不要猜测路径。
- 重复应用:以 call_id 和工作区版本做幂等记录;网络重试前先确认补丁是否已经落盘。
- 部分文件成功:按预先选定的原子或逐文件语义处理,并把每个 call_id 的真实状态分别回传。
- 测试失败:把精简后的测试错误放入下一轮输入,让模型提出修复;不要把失败版本发布。
- diff 太大:让模型拆成小而聚焦的修改,先完成低风险文件,再处理依赖关系复杂的部分。
成本和质量怎么评测
Apply Patch 没有“自动省成本”的保证。成本来自模型输入输出、文件上下文、工具循环,以及你运行的工作区、测试和人工审核。用同一批真实任务比较:
- 补丁首次应用成功率、测试通过率和最终任务成功率。
- 每次成功任务的输入/输出 Token、工具轮次、延迟和环境成本。
- 无关改动数量、diff 大小、回滚率和人工修改时间。
- 路径越界、危险删除、旧内容冲突和重复调用的安全处理结果。
上线前检查清单
- 模型能读取准确、最新且最小必要的文件上下文。
- harness 覆盖 create/update/delete、V4A diff、编码和冲突错误。
- 每个 call_id 恰好回传一次真实 completed/failed 状态。
- 路径白名单、目录穿越、符号链接、文件大小和轮次限制已测试。
- 删除、依赖、权限、数据库和部署变更会停在审批前。
- 测试、lint、类型检查、diff 审查和回滚均能执行。
- 发布过程仍由受保护分支和现有 CI/CD 控制。
常见问题
OpenAI Apply Patch 会自动修改我的仓库吗?
不会。模型只返回 apply_patch_call 和结构化 operation;你的应用必须校验路径与权限、执行补丁,并用原始 call_id 返回 apply_patch_call_output。
Apply Patch 和 git apply、Codex 内置编辑命令有什么区别?
Apply Patch 是 Responses API 的模型工具协议,返回 create_file、update_file 或 delete_file 操作;git apply 是本地补丁命令,Codex 客户端编辑则属于产品运行时。接入 API 时仍需自行实现或复用 patch harness。
Apply Patch 支持哪些文件操作?
当前操作类型是 create_file、update_file 和 delete_file。create/update 携带 path 与 V4A diff,delete 只携带 path。一次响应可以包含多个 apply_patch_call,每个调用代表一个文件操作。
V4A diff 可以直接交给 git apply 吗?
不要默认可以。官方指南把它称为 V4A diff,并建议使用 Python 或 TypeScript Agents SDK 的参考实现。应用应通过经过测试的解析器应用补丁,不要把模型输出直接拼进 Shell 命令。
补丁冲突或文件不存在时怎么处理?
为对应 call_id 返回一个 apply_patch_call_output,status 设为 failed,并在 output 中写清文件路径或 Invalid Context 等错误。模型随后可以重新读取文件或生成更小的补丁。
使用 Apply Patch 一定需要 Shell 工具吗?
不需要。可以直接把相关文件快照放进输入;仓库较大时,配合受控 Shell 或只读文件工具能让模型先搜索和读取真实文件,再生成更聚焦的补丁。
生产环境怎样降低误删和越权风险?
限制工作区根目录和允许编辑的目录,拒绝目录穿越与符号链接逃逸,在临时副本或工作分支执行,备份文件,并对删除、依赖、部署和敏感配置修改保留人工审批。还要明确选择整批回滚或逐文件结果语义。
本文核对日期为 2026-11-02。Apply Patch 的支持模型、SDK、V4A 解析器、allowed_callers 和响应结构可能变化;生产上线前请重新读取 Apply Patch、Responses API、Agents SDK、审批与模型页。
先在临时工作分支验证一个小补丁
选择一个只改测试或文档的低风险任务,记录 operation、call_id、应用结果、测试与最终 diff,再决定是否扩大到多文件重构。