コンテンツにスキップ

9.3.8 コード生成と実行 Agent

  • コード Agent と普通のコード生成の根本的な違いを理解する
  • コード Agent の最小ワークループを理解する
  • 実行可能な例を通して、「読む-直す-動かす-確認する」がなぜ閉ループである必要があるのかを理解する
  • サンドボックス、テスト、ロールバックがコード Agent でなぜ重要かを理解する

コード Agent と「モデルにコードを書かせる」のは何が違うのか?

Section titled “コード Agent と「モデルにコードを書かせる」のは何が違うのか?”

普通のコード生成は一回きりの出力に近い

Section titled “普通のコード生成は一回きりの出力に近い”

たとえば:

  • 「クイックソートを書いて」

モデルがコードを出力したら、
そのタスクはたいていそこで終わります。

コード Agent は、実際のリポジトリの中で作業する感じに近い

Section titled “コード Agent は、実際のリポジトリの中で作業する感じに近い”

扱うタスクは、もっと次のようなものです。

  • bug を修正する
  • 関数にテストを追加する
  • 設定を変更する
  • エラーを見て二回目の修正をする

つまり、次のようなものを処理しなければなりません。

  • 文脈
  • バージョン状態
  • 実行フィードバック
  • エラー復旧

比喩でいうと:例題の答えを書く vs 本当にプロジェクトに入って問題を直す

Section titled “比喩でいうと:例題の答えを書く vs 本当にプロジェクトに入って問題を直す”

「コード生成」は、面接でホワイトボードに問題を解くようなものです。
「コード Agent」は、実際にリポジトリに入って作業する感じです。

  • まずプロジェクトを読む
  • ファイルを探す
  • 一か所直す
  • テストを回す
  • エラーを見る
  • もう一度修正する

この二つは、難易度がまったく違います。


コード Agent の最小閉ループとは何か?

Section titled “コード Agent の最小閉ループとは何か?”

通常、次のような情報が必要です。

  • 関連ファイルがどこにあるか
  • 関数が今どう書かれているか
  • テストがどう整理されているか

たとえば:

  • 実装を直す
  • テストを補う
  • 設定を調整する

ここが、みんなが最も思い浮かべやすい「コードを書く」部分です。

たとえば:

  • 単体テストを回す
  • スクリプトを実行する
  • 出力を見る

Repair:フィードバックを見てさらに修正する

Section titled “Repair:フィードバックを見てさらに修正する”

これも、コード Agent と普通の生成器の大きな違いの一つです。

  • 実行結果を読み、それを次のループに反映する

まずは最小の「コード Agent 閉ループ」例を動かしてみる

Section titled “まずは最小の「コード Agent 閉ループ」例を動かしてみる”

次の例は、実際にファイルを変更するわけではありません。
でも、非常に重要な一連の流れを完全に再現しています。

  1. 関数実装に bug があると見つける
  2. パッチ関数を生成する
  3. テストを実行する
  4. テストに通れば変更を受け入れる
def buggy_normalize_status(status):
# エラー: 生のステータスをそのまま返すため、空白と大文字小文字がそろわない
return status
def generate_patch():
def fixed_normalize_status(status):
return status.strip().lower()
return fixed_normalize_status
def run_tests(fn):
cases = [
((" OPEN ",), "open"),
(("Pending ",), "pending"),
]
failures = []
for args, expected in cases:
actual = fn(*args)
if actual != expected:
failures.append(
{
"args": args,
"expected": expected,
"actual": actual,
}
)
return failures
current_impl = buggy_normalize_status
failures = run_tests(current_impl)
print("修正前の失敗:", failures)
if failures:
candidate_impl = generate_patch()
candidate_failures = run_tests(candidate_impl)
print("修正後の失敗:", candidate_failures)
if not candidate_failures:
current_impl = candidate_impl
print("パッチを受け入れました")

期待される出力:

Terminal window
修正前の失敗: [{'args': (' OPEN ',), 'expected': 'open', 'actual': ' OPEN '}, {'args': ('Pending ',), 'expected': 'pending', 'actual': 'Pending '}]
修正後の失敗: []
パッチを受け入れました

このコードは実際の世界では何に対応するのか?

Section titled “このコードは実際の世界では何に対応するのか?”

これは、コード Agent の最も核となる閉ループに対応しています。

  • ただコードを出力するだけではない
  • コードが検証に通る必要がある

これが欠けると、
システムはすぐに次のようになりがちです。

  • 見た目はもっともらしいコードを書く
  • でも実際には動かない

なぜ generate_patch より run_tests のほうが重要なのか?

Section titled “なぜ generate_patch より run_tests のほうが重要なのか?”

なぜなら、システムを現実に引き戻すのは、
多くの場合、生成能力ではなく検証能力だからです。

検証がなければ、コード Agent はすぐに次の状態で止まりやすくなります。

  • なんとなく正しそう

なぜこれが Agent であって、単なる「関数の差し替え」ではないのか?

Section titled “なぜこれが Agent であって、単なる「関数の差し替え」ではないのか?”

次の要素があるからです。

  • 現在の状態
  • 候補となる行動
  • 外部からのフィードバック
  • 意思決定の更新

これはもう、最小構成の agentic loop です。

コード Agent サンドボックス、テスト、レビューの閉ループ図


実際のコード Agent では、他にどんな重要な工程があるのか?

Section titled “実際のコード Agent では、他にどんな重要な工程があるのか?”

実際のリポジトリでは、まず次のことを解決する必要があります。

  • どのファイルを直すか
  • どの実装部分を見るか
  • どのテストが関係するか

全ファイルの書き直しではなく patch 形式にする

Section titled “全ファイルの書き直しではなく patch 形式にする”

より安定したやり方は、通常次のどちらかです。

  • patch を生成する
  • あるいは局所的な diff にする

なぜなら、そうすると次の利点があるからです。

  • 変更が小さい
  • review しやすい
  • ロールバックしやすい

コード Agent には、次のようなことが必要になる場面が多いです。

  • コードを実行する
  • テストを実行する
  • ファイルを読み書きする

そのため、次のような要素が関わります。

  • サンドボックス
  • 権限の境界
  • タイムアウト

候補のパッチが失敗したら、
システムはできれば次のように動くべきです。

  • 元のバージョンを残す
  • 失敗した変更を捨てる
  • 別の修正方法を試す

なぜコード Agent は特に検証に依存するのか?

Section titled “なぜコード Agent は特に検証に依存するのか?”

コードタスクには客観的なフィードバックがあることが多いから

Section titled “コードタスクには客観的なフィードバックがあることが多いから”

純粋なテキストタスクと比べて、コードタスクの大きな利点の一つは、
多くの場合、はっきりした結果を得られることです。

たとえば:

  • テストが通るか
  • プログラムがエラーになるか
  • 出力が期待どおりか

これにより、コード Agent は「試して直す」反復にとても向いている

Section titled “これにより、コード Agent は「試して直す」反復にとても向いている”

次のように進められます。

  1. まず一版修正する
  2. フィードバックを実行する
  3. 失敗に応じてさらに修正する

だからこそ、コード Agent は Agent システムの中でも、特に強い閉ループを作りやすい種類なのです。

ただし、楽観しすぎてもいけない

Section titled “ただし、楽観しすぎてもいけない”

「テストが通った」からといって、必ずしも次のことが保証されるわけではありません。

  • 回帰がない
  • ロジックが本当に完全である

なので、検証は強力ですが、
万能ではありません。


コード Agent でよく起きる失敗ポイント

Section titled “コード Agent でよく起きる失敗ポイント”

文脈を理解しないまま修正する

Section titled “文脈を理解しないまま修正する”

これにより、次のような問題が起きます。

  • 間違ったファイルを直す
  • 既存の API 仕様を壊す
  • 今あるスタイルと合わない

表面的なエラーだけ直して、根本原因を理解しない

Section titled “表面的なエラーだけ直して、根本原因を理解しない”

典型例は次のようなものです。

  • if を一つ足す
  • 例外を押し込める
  • テストが「たまたま通る」状態にする

でも、本当の問題は残ったままです。

たとえば、単一の成功パスだけを実行して、 次のような観点を見ていない場合です。

  • 境界入力
  • 回帰リスク
  • 関連モジュール

コード Agent をエンジニアリング上で守るべきことは何か?

Section titled “コード Agent をエンジニアリング上で守るべきことは何か?”

自動変更はどれも、次のようにできるべきです。

  • 元に戻せる

patch が小さいほど、次のことがしやすくなります。

  • review
  • 問題の特定
  • 次の修正

たとえば:

  • 指定ディレクトリだけを変更できる
  • 特定のコマンドだけ実行できる
  • 高リスクなコマンドは人の確認が必要

このページを終えたら、この証拠カードを残します。

ツール契約
名前、説明、入力スキーマ、出力スキーマ
権限
ツールが読み取りまたは変更を許可されている範囲
呼び出しトレース
引数、結果、エラー、再試行、またはフォールバック
失敗確認
間違ったツール、不適切な引数、危険な操作、または観測不足
安全対策
検証、確認、サンドボックス化、レート制限、またはロールバック

この節で最も大事なのは、コード Agent を「コードが書けるモデル」として理解することではなく、
その本当の閉ループを理解することです。

コード Agent の核心は、実際のリポジトリの文脈を中心に、読む・直す・動かす・確認する・さらに直す、という流れを安定して回すことです。

この閉ループがはっきり分かれば、
この先もっと複雑なものを見ても、

  • 自動 bug 修正
  • 自動テスト追加
  • 自動リファクタリング

が、どこで本当に難しくなるのか分かるようになります。


  1. 例にある buggy_normalize_status を自分の bug 関数に置き換えて、patch を一つ設計してみましょう。
  2. なぜコード Agent は普通のコード生成より「フィードバック閉ループ」に強く依存すると言えるのでしょうか?
  3. 考えてみましょう: テストがなければ、コード Agent はほかにどんな検証方法に頼れるでしょうか?
  4. なぜ patch は小さいほど、通常はコード Agent に向いているのでしょうか?
参考実装と解説
  1. 置き換える bug は、小さくテストしやすいものが適しています。たとえば off-by-one、空入力処理の漏れ、sort key の誤りです。patch は失敗しているロジックだけを変えます。
  2. Code Agent は feedback loop に強く依存します。コード品質は説明の流暢さではなく、実行、テスト、diff、lint 出力、review で判断されるからです。
  3. テストがなくても、linter、type check、static analysis、sandbox 実行、サンプル入力、code review checklist、手動再現手順を使えます。
  4. 小さな patch は影響範囲を減らし、review を楽にし、ユーザー変更を守り、どの変更が失敗を直したのかを見えやすくします。