For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
メインナビゲーション
Theme

パッチの適用

モデルが提案する構造化された差分をアプリケーションで適用できます。

Responses API で apply_patch を使うと、モデルはファイルの作成、更新、削除を構造化された差分として提案できます。アプリケーションは各操作を適用し、その結果をモデルに返します。このガイドの例では gpt-6-astra を使用します。

主な用途

apply_patch は次のようなタスクに使えます。

  • 複数ファイルにまたがるリファクタリング:多数のファイルを対象に、シンボル名の変更、ヘルパーの抽出、モジュールの再編成をまとめて行えます。
  • バグ修正:モデルに問題を診断させ、修正箇所を的確に指定したパッチを生成させます。
  • テストとドキュメントの生成:コードの変更に合わせて、テストファイル、フィクスチャ、ドキュメントを新規作成できます。
  • 移行と定型的な編集:API の移行、型アノテーションの追加、書式の修正など、一定のパターンに従う変更を繰り返し適用できます。

リポジトリの概要と希望する変更を文章で説明できれば、通常は apply_patch で対応する差分を生成できます。

Responses API でのパッチ適用ツールの使い方

apply_patch を Responses API で使う手順は次のとおりです。

  1. apply_patch ツールを指定して Responses API を呼び出す
    • 利用可能なファイルの情報やその要約を input に含めるか、ファイルシステムを探索できるツールをモデルに提供します。
    • tools=[{"type": "apply_patch"}] を指定してツールを有効にします。
  2. モデルから 1 件以上のパッチ操作を受け取る
    • レスポンスの出力には、1 件以上の apply_patch_call オブジェクトが含まれます。
    • 各呼び出しは、1 つのファイルに対する作成、更新、削除のいずれかの操作を表します。
  3. 実行環境でパッチを適用する
    • 次の処理を行うパッチ適用用のハーネスまたはスクリプトを実行します。
      • operation に含まれる差分を apply_patch_call ごとに解釈します。
      • 作業ディレクトリまたはリポジトリにパッチを適用します。
      • 各パッチの適用結果と、ログやエラーメッセージがあればその内容を記録します。
  4. パッチの適用結果をモデルに返す
    • previous_response_id を指定するか、これまでの会話の項目を input に渡して、Responses API を再度呼び出します。
    • apply_patch_call_output イベントを call_id ごとに含め、status と、必要に応じて output 文字列を指定します。
    • モデルが必要に応じて編集を続けられるよう、引き続き tools=[{"type": "apply_patch"}] を指定します。
  5. モデルに編集を続けさせるか、変更内容を説明させる
    • モデルは追加の 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 オブジェクトの例

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 文字列も含めます。

apply_patch の呼び出しに失敗した場合の報告
{
  "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 オブジェクトの構築方法を把握しています。アプリケーション側では次の処理を実装します。

  1. レスポンスからの操作の読み取り
    • レスポンスから type: "apply_patch_call" の項目を探します。
    • 呼び出しごとに operation.type、operation.path を確認し、diff があればその内容も確認します。
  2. ファイル操作の適用
    • create_file と update_file では、V4A 形式の差分をファイルシステムまたはメモリ上のワークスペースに適用します。
    • delete_file では、path にあるファイルを削除します。
    • 各操作の成否と、ログやエラーメッセージがあればその内容を記録します。
  3. apply_patch_call_output イベントの返却
    • call_id ごとに apply_patch_call_output イベントを必ず 1 件だけ返し、次のように結果を指定します。
      • status: "completed" は、操作を正常に適用できた場合に指定します。
      • status: "failed" は、エラーが発生した場合に指定します。エラーの内容を簡潔に説明する output 文字列も含めます。

安全性と堅牢性

  • パスの検証:ディレクトリトラバーサルを防ぎ、編集対象を許可されたディレクトリ内に限定します。
  • バックアップ:パッチを適用する前にファイルをバックアップするか、作業用のコピーで編集することを検討してください。
  • エラー処理:パッチを適用できない場合は、必ず failed ステータスと、原因がわかる output 文字列を返します。
  • 原子性:いずれかのパッチが失敗したらすべての変更をロールバックするか、ファイルごとに成否を扱うかを決めておきます。

Agents SDK でのパッチ適用ツールの使い方

Agents SDK からパッチ適用ツールを使うこともできます。実際のファイル操作を担うハーネスはこの場合も実装する必要がありますが、差分の処理には applyDiff 関数を利用できます。

Agents SDK でのパッチ適用ツールの使い方
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)

TypeScript で Agents SDK のパッチ適用ツールを使う例

パッチ適用ツールの使用例(Python)

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'"
}

モデルはこれらのエラーメッセージをもとに、プロンプトに含まれるファイルを読み直したり、変更内容を単純化したりして、次に生成する差分を調整できます。

ベストプラクティス

  • 対象ファイルについて明確なコンテキストを提供
    • Responses API を呼び出す際は、例のようにファイルのスナップショットを入力に直接含めるか、shell ツールなど、モデルがファイルシステムを探索できるツールを提供します。
  • shell ツールとの併用を検討
    • shell ツールを併用すると、モデルはディレクトリを探索し、ファイルを読み取り、grep でキーワードを検索できます。これにより、エージェントが対象ファイルを自ら見つけて編集できます。
  • 変更対象を絞った小さな差分を推奨
    • システム指示で、大規模な書き換えを避け、必要な箇所だけを最小限に編集するようモデルに促します。
  • 変更が正しく適用されたことを確認
    • 一連のパッチを適用した後でテストやリンターを実行します。問題があれば、モデルが修正できるように次の input にその内容を含めます。

利用にあたって