先给结论

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。

完整工具循环

  1. 检查 Response 已完成,并收集全部 apply_patch_call。
  2. 逐条校验 operation 类型、相对路径、允许目录、当前文件版本和审批策略。
  3. 在临时副本或工作分支应用 create/update/delete。
  4. 为每个 call_id 生成且只生成一条结果;成功用 completed,失败用 failed 并写明原因。
  5. 通过 previous_response_id 续接,同时继续提供 apply_patch 工具。
  6. 重复直到没有新的 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、测试结果和最终工作区状态,再由发布流程决定是否合并。

常见错误与排查

  1. 没有 apply_patch_call:确认使用 Responses API、模型/项目支持该工具,并提供了明确写文件任务和足够上下文。
  2. Invalid Context:文件可能在读取后改变;重新读取最新内容,再请求更小的目标 diff。
  3. File not found:核对路径相对工作区根目录、大小写和分支,不要猜测路径。
  4. 重复应用:以 call_id 和工作区版本做幂等记录;网络重试前先确认补丁是否已经落盘。
  5. 部分文件成功:按预先选定的原子或逐文件语义处理,并把每个 call_id 的真实状态分别回传。
  6. 测试失败:把精简后的测试错误放入下一轮输入,让模型提出修复;不要把失败版本发布。
  7. 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,再决定是否扩大到多文件重构。