apply_patch 工具让 GPT-5.1 能够通过结构化差异,在您的代码库中创建、更新和删除文件。模型不仅会提出修改建议,还会生成补丁操作,由您的应用程序应用补丁并反馈结果,从而实现可迭代的多步骤代码编辑工作流。
以下是使用 apply_patch 的一些常见场景:
- 多文件重构 :一次性跨多个文件重命名符号、提取辅助函数或重新组织模块。
- 缺陷修复 :让模型诊断问题并生成精确的补丁。
- 生成测试和文档 :在修改代码的同时创建新的测试文件、测试夹具和文档。
- 迁移和机械性修改 :执行重复性的结构化更新,例如 API 迁移、添加类型注解、修复格式等。
只要您能用文字描述代码仓库和所需的更改,apply_patch 通常就能生成相应的差异。
在 Responses API 中使用 apply_patch 的大致流程如下:
- 调用 Responses API 并启用
apply_patch 工具
- 在
input 中向模型提供可用文件的上下文或摘要,或者为模型提供用于探索文件系统的工具。
- 使用
tools=[{"type": "apply_patch"}] 启用该工具。
- 让模型返回一个或多个补丁操作
- Response 的输出包含一个或多个
apply_patch_call 对象。
- 每次调用描述一个文件操作:创建、更新或删除。
- 在您的环境中应用补丁
- 运行补丁执行框架或脚本,以执行以下操作:
- 解析每个
apply_patch_call 的 operation 差异。
- 将补丁应用到您的工作目录或代码仓库。
- 记录每个补丁是否应用成功,以及相关日志或错误消息。
- 将补丁应用结果反馈给模型
- 再次调用 Responses API,使用
previous_response_id,或者将对话条目重新传入 input。
- 为每个
call_id 提供一个 apply_patch_call_output 事件,其中包含 status 和可选的 output 字符串。
- 保留
tools=[{"type": "apply_patch"}],以便模型在需要时继续编辑。
- 让模型继续编辑或解释更改
- 模型可能会发出更多
apply_patch_call 操作,或者
- 向用户解释所做的更改及其原因。
步骤 1:让模型制定计划并生成补丁
1
2
3
4
5
6
7
8
9const response = await client.responses.create({
model: "gpt-6-astra",
input: fileContext,
tools: [{ type: "apply_patch" }],
});
const patchCalls = response.output.filter(
(item) => item.type === "apply_patch_call"
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42from openai import OpenAI
client = OpenAI()
# For brevity, we are including file context in the example input.
# Most agentic use cases should instead equip the model with tools
# for exploring file system state.
RESPONSE_INPUT = """
The user has the following files:
<BEGIN_FILES>
===== lib/fib.py
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
===== run.py
from lib.fib import fib
def main():
print(fib(42))
<END_FILES>
You are a helpful coding assistant that should assist the user with whatever they
ask.
User query:
Help me rename the fib() function to fibonacci()
"""
response = client.responses.create(
model="gpt-6-astra",
input=RESPONSE_INPUT,
tools=[{"type": "apply_patch"}],
)
# response.output may contain multiple apply_patch_call entries, e.g.:
# - update lib/fib.py
# - update run.py
patch_calls = [
item.model_dump() for item in response.output if item.type == "apply_patch_call"
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(responseInput)},
Tools: []responses.ToolUnionParam{{OfApplyPatch: &responses.ApplyPatchToolParam{}}},
})
if err != nil {
panic(err)
}
patchCalls := make([]responses.ResponseOutputItemUnion, 0)
for _, item := range response.Output {
if item.Type == "apply_patch_call" {
patchCalls = append(patchCalls, item)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ApplyPatchTool;
import com.openai.models.responses.ResponseCreateParams;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input(
"Rename fib() to fibonacci() in lib/fib.py and update run.py to use the new name.")
.addTool(ApplyPatchTool.builder().build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.applyPatchCall().stream())
.forEach(System.out::println);
1
2
3
4
5
6
7
8
9
10
11require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Rename fib() to fibonacci() in lib/fib.py and update run.py to use the new name.",
tools: [{ type: :apply_patch }]
)
patch_calls = response.output.select { |item| item.type == :apply_patch_call }
puts(patch_calls)
apply_patch_call 对象示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18{
"id": "apc_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe",
"type": "apply_patch_call",
"status": "completed",
"call_id": "call_Rjsqzz96C5xzPb0jUWJFRTNW",
"operation": {
"type": "update_file",
"diff": "
@@
-def fib(n):
+def fibonacci(n):
if n <= 1:
return n
- return fib(n-1) + fib(n-2) + return fibonacci(n-1) + fibonacci(n-2),
",
"path": "lib/fib.py"
}
}
步骤 2:应用补丁并反馈结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const results = patchCalls.map((call) => {
const { success, output } = applyOperation(call.operation);
return {
type: "apply_patch_call_output",
call_id: call.call_id,
status: success ? "completed" : "failed",
output,
};
});
const followup = await client.responses.create({
model: "gpt-6-astra",
previous_response_id: response.id,
input: results,
tools: [{ type: "apply_patch" }],
});
console.log(followup.output_text);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22from apply_patch_harness import apply_operation # your implementation
results = []
for call in patch_calls:
op = call["operation"]
success, maybe_log_output = apply_operation(op)
results.append(
{
"type": "apply_patch_call_output",
"call_id": call["call_id"],
"status": "completed" if success else "failed",
"output": maybe_log_output,
}
)
followup = client.responses.create(
model="gpt-6-astra",
previous_response_id=response.id,
input=results,
tools=[{"type": "apply_patch"}],
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20results := make(responses.ResponseInputParam, 0, len(patchCalls))
for _, call := range patchCalls {
success, logOutput := applyOperation(call.Operation)
status := "completed"
if !success {
status = "failed"
}
result := responses.ResponseInputItemParamOfApplyPatchCallOutput(call.CallID, status)
result.OfApplyPatchCallOutput.Output = openai.String(logOutput)
results = append(results, result)
}
_, err = client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
PreviousResponseID: openai.String(response.ID),
Input: responses.ResponseNewParamsInputUnion{OfInputItemList: results},
Tools: []responses.ToolUnionParam{{OfApplyPatch: &responses.ApplyPatchToolParam{}}},
})
if err != nil {
panic(err)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ApplyPatchTool;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputItem;
import java.util.List;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.inputOfResponse(
List.of(
ResponseInputItem.ofApplyPatchCallOutput(
ResponseInputItem.ApplyPatchCallOutput.builder()
.callId(System.getenv("OPENAI_EXAMPLE_APPLY_PATCH_CALL_ID"))
.status(ResponseInputItem.ApplyPatchCallOutput.Status.COMPLETED)
.output("Patch applied successfully.")
.build())))
.previousResponseId(System.getenv("OPENAI_EXAMPLE_PREVIOUS_RESPONSE_ID"))
.addTool(ApplyPatchTool.builder().build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20require "openai"
client = OpenAI::Client.new
response_id = ENV.fetch("OPENAI_RESPONSE_ID")
patch_call_id = ENV.fetch("OPENAI_APPLY_PATCH_CALL_ID")
response = client.responses.create(
model: "gpt-6-astra",
previous_response_id: response_id,
input: [
{
type: :apply_patch_call_output,
call_id: patch_call_id,
status: :completed,
output: "Patch applied successfully."
}
],
tools: [{ type: :apply_patch }]
)
puts(response.output_text)
如果补丁应用失败(例如,找不到文件),请设置 status: "failed",并提供有助于解决问题的 output 字符串,以便模型从错误中恢复:
1
2
3
4
5
6{
"type": "apply_patch_call_output",
"call_id": "call_cNWm41dB3RyQcLNOVTIPBWZU",
"status": "failed",
"output": "Could not apply patch to lib/foo.py — file not found on disk"
}
| 操作类型 | 用途 | 载荷 |
|---|
create_file | 在 path 创建新文件。 | diff 是表示文件完整内容的 V4A 差异。 |
update_file | 修改位于 path 的现有文件。 | diff 是包含新增、删除或替换内容的 V4A 差异。 |
delete_file | 删除位于 path 的文件。 | 不含 diff;删除整个文件。 |
您的补丁执行框架负责解析 V4A 差异格式并应用更改。如需参考实现,请参阅 Python Agents SDK 或 TypeScript Agents SDK 的代码。
使用 apply_patch 工具时,您无需提供输入模式;模型知道如何构造 operation 对象。您需要完成以下工作:
- 从 Response 中解析操作
- 在 Response 中查找包含
type: "apply_patch_call" 的条目。
- 对于每次调用,检查
operation.type、operation.path 以及可能存在的 diff。
- 执行文件操作
- 对于
create_file 和 update_file,将 V4A 差异应用到文件系统或内存中的工作空间。
- 对于
delete_file,删除 path 处的文件。
- 记录每项操作是否成功,以及所有日志或错误消息。
- 返回
apply_patch_call_output 事件
- 为每个
call_id 生成且仅生成一个 apply_patch_call_output 事件,具体如下:
- 如果操作执行成功,使用
status: "completed"。
- 如果遇到错误,使用
status: "failed"(附上一段简短易懂的 output 字符串)。
- 路径验证:防止目录遍历,并将编辑范围限制在允许的目录内。
- 备份:考虑在应用补丁前备份文件,或在临时副本中操作。
- 错误处理:无法应用补丁时,务必返回
failed 状态,并附上说明具体情况的 output 字符串。
- 原子性:确定是采用“全部成功或全部回滚”的语义(任何补丁失败就回滚),还是允许各文件的操作独立成功或失败。
您也可以通过 Agents SDK 使用应用补丁工具。您仍需实现负责实际文件操作的执行框架,但可以使用 applyDiff 函数来处理差异。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51import { applyDiff, Agent, run, applyPatchTool } from "@openai/agents";
class WorkspaceEditor {
async createFile(operation) {
// convert the diff to the file content
const content = applyDiff("", operation.diff, "create");
// write the file content to the file system
return { status: "completed", output: `Created ${operation.path}` };
}
async updateFile(operation) {
// read the file content from the file system
const current = "";
// convert the diff to the new file content
const newContent = applyDiff(current, operation.diff);
// write the updated file content to the file system
return { status: "completed", output: `Updated ${operation.path}` };
}
async deleteFile(operation) {
// delete the file from the file system
return { status: "completed", output: `Deleted ${operation.path}` };
}
}
const editor = new WorkspaceEditor();
const agent = new Agent({
name: "Patch Assistant",
model: "gpt-6-astra",
instructions:
"You can edit files inside the /tmp directory using the apply_patch tool.",
tools: [
applyPatchTool({
editor,
// could also be a function for you to determine if approval is needed
needsApproval: true,
onApproval: async (_ctx, _approvalItem) => {
// create your own approval logic
return { approve: true };
},
}),
],
});
const result = await run(
agent,
"Create tasks.md with a shopping checklist of 5 entries."
);
console.log(`\nFinal response:\n${result.finalOutput}`);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54from agents import Agent, ApplyPatchTool, Runner, apply_diff
class WorkspaceEditor:
async def create_file(self, operation):
# convert the diff to the file content
content = apply_diff("", operation.diff, mode="create")
# write the file content to the file system
return {"status": "completed", "output": f"Created {operation.path}"}
async def update_file(self, operation):
# read the file content from the file system
current = ""
# convert the diff to the new file content
new_content = apply_diff(current, operation.diff)
# write the updated file content to the file system
return {"status": "completed", "output": f"Updated {operation.path}"}
async def delete_file(self, operation):
# delete the file from the file system
return {"status": "completed", "output": f"Deleted {operation.path}"}
editor = WorkspaceEditor()
agent = Agent(
name="Patch Assistant",
model="gpt-6-astra",
instructions="You can edit files inside the /tmp directory using the apply_patch tool.",
tools=[
ApplyPatchTool(
editor=editor,
# could also be a function for you to determine if approval is needed
needs_approval=True,
# Implement your own approval logic
on_approval=lambda _ctx, _approval_item: {"approve": True},
),
],
)
async def main():
result = await Runner.run(
agent,
input="Create tasks.md with a shopping checklist of 5 entries.",
)
print(f"\nFinal response:\n{result.final_output}")
if __name__ == "__main__":
import asyncio
asyncio.run(main())
您可以在 GitHub 上找到完整的可运行示例。
在 TypeScript 中通过 Agents SDK 使用应用补丁工具的示例
在 Python 中通过 Agents SDK 使用应用补丁工具的示例
使用 status: "failed" 并附上清晰的 output 消息,帮助模型从错误中恢复。
找不到文件
1
2
3
4
5
6{
"type": "apply_patch_call_output",
"call_id": "call_abc",
"status": "failed",
"output": "Error: File not found at path 'lib/baz.py'"
}
补丁冲突
1
2
3
4
5
6{
"type": "apply_patch_call_output",
"call_id": "call_abc",
"status": "failed",
"output": "Error: Invalid Context:\n@@ def fib(n):"
}
模型随后便可根据这些错误消息调整后续差异,例如重新读取您在提示中提供的文件,或简化某项修改。
- 提供清晰的文件上下文
- 调用 Responses API 时,请内嵌文件快照(如示例所示),或为模型提供探索文件系统的工具(例如
shell 工具)。
- 考虑与
shell 工具配合使用
- 与
shell 工具配合使用时,模型可以探索文件系统目录、读取文件,并使用 grep 搜索关键词,从而以智能体方式查找和编辑文件。
- 鼓励生成小规模、有针对性的差异
- 在系统指令中,引导模型进行最小化、有针对性的编辑,而非大规模重写。
- 确保修改能够顺利应用
- 应用一系列补丁后,运行测试或代码检查工具,并在下一次
input 中反馈失败情况,以便模型修复。