Responses API で apply_patch を使うと、モデルはファイルの作成、更新、削除を構造化された差分として提案できます。アプリケーションは各操作を適用し、その結果をモデルに返します。このガイドの例では gpt-6-astra を使用します。
主な用途
apply_patch は次のようなタスクに使えます。
- 複数ファイルにまたがるリファクタリング:多数のファイルを対象に、シンボル名の変更、ヘルパーの抽出、モジュールの再編成をまとめて行えます。
- バグ修正:モデルに問題を診断させ、修正箇所を的確に指定したパッチを生成させます。
- テストとドキュメントの生成:コードの変更に合わせて、テストファイル、フィクスチャ、ドキュメントを新規作成できます。
- 移行と定型的な編集:API の移行、型アノテーションの追加、書式の修正など、一定のパターンに従う変更を繰り返し適用できます。
リポジトリの概要と希望する変更を文章で説明できれば、通常は apply_patch で対応する差分を生成できます。
Responses API でのパッチ適用ツールの使い方
apply_patch を Responses API で使う手順は次のとおりです。
apply_patchツールを指定して Responses API を呼び出す- 利用可能なファイルの情報やその要約を
inputに含めるか、ファイルシステムを探索できるツールをモデルに提供します。 tools=[{"type": "apply_patch"}]を指定してツールを有効にします。
- 利用可能なファイルの情報やその要約を
- モデルから 1 件以上のパッチ操作を受け取る
- レスポンスの出力には、1 件以上の
apply_patch_callオブジェクトが含まれます。 - 各呼び出しは、1 つのファイルに対する作成、更新、削除のいずれかの操作を表します。
- レスポンスの出力には、1 件以上の
- 実行環境でパッチを適用する
- 次の処理を行うパッチ適用用のハーネスまたはスクリプトを実行します。
operationに含まれる差分をapply_patch_callごとに解釈します。- 作業ディレクトリまたはリポジトリにパッチを適用します。
- 各パッチの適用結果と、ログやエラーメッセージがあればその内容を記録します。
- 次の処理を行うパッチ適用用のハーネスまたはスクリプトを実行します。
- パッチの適用結果をモデルに返す
previous_response_idを指定するか、これまでの会話の項目をinputに渡して、Responses API を再度呼び出します。apply_patch_call_outputイベントをcall_idごとに含め、statusと、必要に応じてoutput文字列を指定します。- モデルが必要に応じて編集を続けられるよう、引き続き
tools=[{"type": "apply_patch"}]を指定します。
- モデルに編集を続けさせるか、変更内容を説明させる
- モデルは追加の
apply_patch_call操作を出力する場合があります。 - または、何を変更したのか、その理由とともにユーザーに説明します。
- モデルは追加の
パッチ適用ツールによる関数名の変更例
ステップ 1:変更の計画とパッチの生成をモデルに依頼
import OpenAI from "openai";
// Application-owned helper; see "Implementing the patch harness" in the guide.
// This demo only applies exact updates to the two in-memory files below.
import { applyOperation } from "../../support/apply-patch.mjs";
const client = new OpenAI();
const 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
print(fib(42))
`,
};
const fileContext = `The user has the following files:
<BEGIN_FILES>
${Object.entries(files)
.map(([path, content]) => `===== ${path}\n${content}`)
.join("\n")}
<END_FILES>
Help me rename the fib() function to fibonacci().`;
let response = await client.responses.create({
model: "gpt-6-astra",
input: fileContext,
tools: [{ type: "apply_patch" }],
});
let patchCalls = response.output.filter(
(item) => item.type === "apply_patch_call"
);apply_patch_call オブジェクトの例
{
"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:パッチの適用と結果の返却
for (let turn = 0; patchCalls.length > 0; turn++) {
if (turn === 8) throw new Error("Patch workflow exceeded eight tool turns.");
const results = patchCalls.map((call) => {
const { success, output } = applyOperation(files, call.operation);
return {
type: "apply_patch_call_output",
call_id: call.call_id,
status: success ? "completed" : "failed",
output,
};
});
response = await client.responses.create({
model: "gpt-6-astra",
previous_response_id: response.id,
input: results,
tools: [{ type: "apply_patch" }],
});
patchCalls = response.output.filter(
(item) => item.type === "apply_patch_call"
);
}
console.log(response.output_text);
for (const [path, content] of Object.entries(files)) {
process.stdout.write(`===== ${path}\n${content}`);
}ファイルが見つからないなどの理由でパッチを適用できなかった場合は、status: "failed" を設定します。モデルが対処できるよう、失敗の原因がわかる output 文字列も含めます。
{
"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 オブジェクトの構築方法を把握しています。アプリケーション側では次の処理を実装します。
- レスポンスからの操作の読み取り
- レスポンスから
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イベントを必ず 1 件だけ返し、次のように結果を指定します。status: "completed"は、操作を正常に適用できた場合に指定します。status: "failed"は、エラーが発生した場合に指定します。エラーの内容を簡潔に説明するoutput文字列も含めます。
安全性と堅牢性
- パスの検証:ディレクトリトラバーサルを防ぎ、編集対象を許可されたディレクトリ内に限定します。
- バックアップ:パッチを適用する前にファイルをバックアップするか、作業用のコピーで編集することを検討してください。
- エラー処理:パッチを適用できない場合は、必ず
failedステータスと、原因がわかるoutput文字列を返します。 - 原子性:いずれかのパッチが失敗したらすべての変更をロールバックするか、ファイルごとに成否を扱うかを決めておきます。
Agents SDK でのパッチ適用ツールの使い方
Agents SDK からパッチ適用ツールを使うこともできます。実際のファイル操作を担うハーネスはこの場合も実装する必要がありますが、差分の処理には applyDiff 関数を利用できます。
import {
mkdirSync,
realpathSync,
lstatSync,
readFileSync,
writeFileSync,
unlinkSync,
} from "node:fs";
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
import { applyDiff, Agent, run, applyPatchTool } from "@openai/agents";
class WorkspaceEditor {
constructor(root = "workspace") {
mkdirSync(root, { recursive: true });
this.root = realpathSync(root);
}
path(name) {
const target = resolve(this.root, name);
const relativePath = relative(this.root, target);
if (
!relativePath ||
isAbsolute(relativePath) ||
relativePath === ".." ||
relativePath.startsWith(`..${sep}`)
) {
throw new Error("Patch paths must stay inside the workspace");
}
let current = this.root;
for (const part of relativePath.split(sep)) {
current = join(current, part);
if (lstatSync(current, { throwIfNoEntry: false })?.isSymbolicLink()) {
throw new Error("Patch paths must not traverse symbolic links");
}
}
return target;
}
async createFile(operation) {
const path = this.path(operation.path);
const content = applyDiff("", operation.diff, "create");
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, content, { encoding: "utf8", flag: "wx" });
return { status: "completed", output: `Created ${operation.path}` };
}
async updateFile(operation) {
const path = this.path(operation.path);
const content = applyDiff(readFileSync(path, "utf8"), operation.diff);
writeFileSync(path, content, "utf8");
return { status: "completed", output: `Updated ${operation.path}` };
}
async deleteFile(operation) {
unlinkSync(this.path(operation.path));
return { status: "completed", output: `Deleted ${operation.path}` };
}
}
const editor = new WorkspaceEditor();
const agent = new Agent({
name: "Patch Assistant",
model: "gpt-6-astra",
instructions:
"Use apply_patch to edit files in the workspace. Supply paths relative to that workspace.",
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}`);実行可能なサンプルコードの全体は GitHub で確認できます。
TypeScript で Agents SDK のパッチ適用ツールを使う例
Python で Agents SDK のパッチ適用ツールを使う例
よくあるエラーへの対処
モデルがエラーに対処できるよう、status: "failed" とともに、原因を明確に伝える output メッセージを返します。
{
"type": "apply_patch_call_output",
"call_id": "call_abc",
"status": "failed",
"output": "Error: File not found at path 'lib/baz.py'"
}{
"type": "apply_patch_call_output",
"call_id": "call_abc",
"status": "failed",
"output": "Error: Invalid Context:\n@@ def fib(n):"
}モデルはこれらのエラーメッセージをもとに、プロンプトに含まれるファイルを読み直したり、変更内容を単純化したりして、次に生成する差分を調整できます。
ベストプラクティス
- 対象ファイルについて明確なコンテキストを提供
- Responses API を呼び出す際は、例のようにファイルのスナップショットを入力に直接含めるか、
shellツールなど、モデルがファイルシステムを探索できるツールを提供します。
- Responses API を呼び出す際は、例のようにファイルのスナップショットを入力に直接含めるか、
shellツールとの併用を検討shellツールを併用すると、モデルはディレクトリを探索し、ファイルを読み取り、grep でキーワードを検索できます。これにより、エージェントが対象ファイルを自ら見つけて編集できます。
- 変更対象を絞った小さな差分を推奨
- システム指示で、大規模な書き換えを避け、必要な箇所だけを最小限に編集するようモデルに促します。
- 変更が正しく適用されたことを確認
- 一連のパッチを適用した後でテストやリンターを実行します。問題があれば、モデルが修正できるように次の
inputにその内容を含めます。
- 一連のパッチを適用した後でテストやリンターを実行します。問題があれば、モデルが修正できるように次の