1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

なぜAIは1回の出力上限を超えて巨大コードを書けるのか?コーディングエージェント徹底解剖

1
Posted at

カバー画像

Claude Code や Cursor、GitHub Copilot のエージェントモードなど、近年のコーディングエージェントに大きなタスクを投げると、何十個ものファイルを読み書きし、テストを走らせ、エラーを自律修復しながら、数千〜数万行に及ぶ大規模なソースコードをいつの間にか書き上げてしまいます。

しかし、冷静に考えると不思議ではないでしょうか。

LLM(大規模言語モデル)のAPI仕様書を見れば、1回の生成で返せる出力上限(max_tokens)はたかだか数千〜数万トークン程度。一般的なPythonコードに換算すれば、せいぜい数百行から千数百行が限界です。それなのに、なぜ彼らはその壁を平然と超えて、破綻することなく巨大なシステムを構築できるのでしょうか?

「AIの中に巨大な記憶装置があるのか?」「全体を一瞬で脳内展開しているのか?」——いいえ、まったく違います。

その裏側には、出力制約を回避する巧妙なソフトウェア設計(ハーネス)と、強化学習によって叩き込まれたモデルの「振る舞いの癖」、そして「ファイルシステムを外部記憶として扱う」現実的なアプローチが組み合わさった、極めて泥臭く洗練されたメカニズムが存在します。

この記事では、コーディングエージェントが大規模なコードベースを構築していく一連のプロセスと内部構造を、具体的な実行トレースやハーネスのコードを交えて徹底的に解剖します。


1. 基本情報クイックリファレンス

エージェントの挙動を理解する上で、まず押さえておくべき主要コンポーネントと制約の全体像を整理します。

概念・コンポーネント 役割と実態 大規模開発における意味
出力上限(max_tokens) 1回のAPIリクエストでモデルが生成できるトークン数の限界(数千〜数万)。 1発で巨大ファイルは作れないため、「小分けにする」絶対的な動機となる。
コンテキストウィンドウ 1回の推論で入力・参照できる総量(数十万〜数百万トークン)。 過去の会話やコードを読める「作業机の広さ」。作業が進むほど圧迫される。
ハーネス(Harness) モデルの外側でループを回す実行基盤(Claude CodeのCLI、Cursorのエンジン等)。 手足(ツールの実行)、記憶の管理、コスト・安全制御を担当する実質的な司令塔。
ツール呼び出し(Tool Use) モデルが出力する「このコマンドを実行して」というJSON構造化データ。 エージェントが外部環境(ファイル、シェル、リンタ)と対話する唯一の手段。
コンパクション(Compaction) 肥大化した会話履歴を要約し、トークン消費を圧縮する処理。 長時間作業を継続可能にするが、詳細なコードの文脈が消えるトレードオフを持つ。
ファイルシステム & Git ローカルのファイル、進捗管理ファイル、コミットログ。 会話履歴に依存しない、開発における「唯一の真実の源泉(Single Source of Truth)」。

2. なぜ出力上限があっても大規模開発ができるのか?

「出力上限がある=大規模なものは作れない」という直感は、「1回の応答の量」と「作業全体の累積量」を混同していることから生じます。

┌────────────────────────────────────────────────────────┐
│ コンテキストウィンドウ(1回で読める量:数十万〜100万 tokens) │
├────────────────────────────────────────────────────────┤
│ 出力上限(1回で書ける量:数千〜数万 tokens)                │
├────────────────────────────────────────────────────────┤
│ 作業全体の成果物(ループを回すことで理論上の上限なし)     │
└────────────────────────────────────────────────────────┘

エージェントは「人間が一気に1万行のコードを書かない」のと同じように動きます。「少し読む → 最小限の差分を書く → コマンドを実行して確かめる」というループを何十回、何百回と繰り返すことで、結果として巨大な成果物を生み出しているのです。

このサイクルを成立させるため、最新のエージェントシステムには以下の4つの工夫が組み込まれています。

① 出力量を削る「差分編集」

数百行のファイルを少し直すたびにファイル全体を再生成していたら、あっという間に出力トークンを使い果たし、時間もかかります。
そのため、実用的なエージェントは create_file ではなく、文字列置換ツール(str_replace)や unified diff パッチを好んで使います。
「変更前の数行」と「変更後の数行」だけを出力させれば、1ターンの出力はわずか数行〜数十行(数十トークン)で済みます。

② 入力(コンテキスト)を節約する情報取得

いくらコンテキストが100万トークンあっても、リポジトリ全体を丸ごと読み込めばすぐにパンクします。
エージェントは grep、find、ls、ファイルの部分読み込み(行数指定 read_file)を駆使し、「いま編集したい周辺のコードだけ」をピンポイントでコンテキストにロードします。

③ 外部記憶としてのファイル活用

モデル自身はAPIを叩くたびにリセットされる「ステートレス(無記憶)」な存在です。
後述する履歴圧縮(コンパクション)が起きると過去の詳細なやり取りは忘れてしまいます。そのため、エージェントは TODO.md や PROGRESS.md、アーキテクチャ設計書といった実ファイルをディスク上に作り、それを自分の外部記憶(ハードディスク)として読み書きします。

④ 検証ループ(自己修復機能)

これが規模を支える最大の要因です。
人間でも「1行のミスもなく最初から完璧な1万行を書く」のは不可能です。エージェントも同様にコードを間違えます。しかし、エージェントはコードを書いた直後にコンパイラ、リンタ、テストコマンド(pytest や npm test)を実行し、そのエラー出力を自ら受け取って修正します。「一発で正しく書く必要がない」からこそ、大規模な作業が破綻せずに前進します。


3. モデルとハーネスの境界線——誰が何をどう決めているのか?

モデルとハーネスの協調

コーディングエージェントを語る際、「AI(モデル)が全部自分で考えて動いている」と思われがちですが、実態は モデル(頭脳) と ハーネス(手足・作業環境) の高度な分業です。

                    ┌──────────────┐
                    │  ユーザー指示 │
                    └──────┬───────┘
                           │
 ┌─────────────────────────▼─────────────────────────┐
 │               ハーネス (Harness)                   │
 │                                                   │
 │  ・ループ制御(Turn管理)   ・ファイル/シェル実行  │
 │  ・コンテキスト組み立て     ・安全/権限ガード      │
 │  ・履歴のコンパクション     ・予算/トークン管理    │
 └─────────────┬─────────────────────────▲───────────┘
               │                         │
 (プロンプト+全履歴+結果)         (ツール呼び出しJSON / テキスト)
               │                         │
 ┌─────────────▼─────────────────────────┴───────────┐
 │               モデル (LLM / 頭脳)                  │
 │                                                   │
 │  ・次に何をすべきかの判断(次トークン予測)        │
 │  ・「ツールを呼ぶか、完了するか」の決定            │
 └───────────────────────────────────────────────────┘

役割分担の真実

領域 モデル(LLM)が担うこと ハーネス(実行基盤)が担うこと
判断 次に何をするか、どのツールをどの引数で呼ぶか。 判断はしない(モデルの要求通りに淡々と動く)。
実行 一切実行しない(文字列を出力するだけ)。 実際にファイルを書き換え、OSコマンドを叩く。
記憶 生まれつき記憶を持たない。 過去の会話ログを保持し、毎ターン頭から渡し直す。
制約 学習によって培われた「行儀の良さ」。 危険なコマンドのブロック、ターン数上限、強制ルール。

なぜモデルは「切りのいいところ」で止まれるのか?

モデルの出力は、本質的には「確率に基づいて次の1トークンを予測する」ことの連続です。「ここまでにしよう」という特別なスイッチがあるわけではありません。

モデルが止まる瞬間は、基本的に以下の2パターンです。

  1. 次の判断を下すために外部情報が必要になった時:例えば「ファイルを読まないと次が書けない」場合、モデルは read_file のツール呼び出しJSONを出力した時点でトークン生成をストップします(ハーネス側は stop_reason: tool_use を検知)。
  2. すべてのタスクが終わったと判断した時:ツールを呼び出さず、人間への報告メッセージを出力して終了トークン(EOS)を生成します(ハーネス側は stop_reason: end_turn を検知)。

モデルは「頭脳」、ハーネスは「手足と作業台」です。いくらモデルの知能が高くても、ハーネスのツール設計やコンテキスト管理が拙ければ、エージェントは自律的に作業を継続できません。


4. なぜモデルは自律的に小さく分割できるのか?(強化学習の舞台裏)

「差分で書く」「先にテストを確認する」「小分けにする」——こうした賢い振る舞いは、プロンプトで細かく指示しているからだけではありません。モデル自身の 訓練(特に環境フィードバックを用いた強化学習:RL)によって骨の髄まで染み付いているからです。

近年のフロンティアモデルは、以下のような多段階の訓練を経てコーディングエージェント化しています。

┌────────────────────────────────────────────────────────┐
│ ① 事前学習 (Pre-training)                              │
│ 世界中のコード、Gitコミットログ、PRレビューを網羅的に学習  │
│ → 「コードの書き方」「少しずつ修正する文化」を獲得     │
├────────────────────────────────────────────────────────┤
│ ② SFT (Supervised Fine-Tuning)                         │
│ 「課題 → ツール呼び出し → 結果確認 → 回答」の軌跡を学習  │
│ → ツール呼び出しの文法やエージェントの「基本の型」を身につける │
├────────────────────────────────────────────────────────┤
│ ③ 環境での強化学習 (RL in Sandboxed Environment) ★核心 │
│ サンドボックス内で実際にエージェントループを何万回も自律実行 │
│ テスト通過やタスク完了を「報酬(Reward)」として最適化   │
└────────────────────────────────────────────────────────┘

強化学習(RL)で何が起きているのか?

強化学習の環境では、モデルに仮想のバグ修正や機能実装タスクを与え、自由にツールを使わせます。

パターン エージェントの行動ログ 環境からのフィードバック 報酬(Reward)
試行 A いきなり1,500行のファイルを1発で生成しようとする。 出力上限(max_tokens)でJSONが途中で壊れ、ファイル作成失敗。テスト全滅。 0 点(失敗)
試行 B コードを書き、テストを実行せずに「完了しました」と宣言する。 バックグラウンドの自動テストに引っかかり、実は構文エラーだったことが判明。 0 点(失敗)
試行 C ①ファイルを読む → ②骨組みを作る → ③1関数ずつ差分編集 → ④テスト実行でエラー発見 → ⑤修正 → ⑥全テスト通過 仕様を満たし、テストがすべてグリーンになる。 +1.0 点(成功!)

何十万回という試行錯誤の中で、「一度に全部書こうとすると自滅する」「テストで検証しながら小さく刻むと勝率が跳ね上がる」という行動パターンに対して強烈なプラスの報酬が与えられます。

その結果、「分割して進める」「編集前に元ファイルを読む」「こまめにテストを回す」という行動が、人間がルールを事細かに指示しなくても、モデルの自然な確率的選択(最も報酬が高くなる選択肢)として現れるようになるのです。


5. 実例で見る:エージェントの思考トレース

ここからは、実際の開発現場でエージェントがどう動いているのかを具体例で追ってみましょう。

実例①:小さなタスク(既存ファイルに関数を1つ追加)

既存の BasicFunc.py に新しいAPI関数 get_vms_v4() を追加するシナリオです。

【ターン 1】
モデル思考:まずは既存のコードスタイルと関数の配置を確認しよう。
モデル出力:
{
  "type": "tool_use",
  "name": "read_file",
  "input": { "path": "BasicFunc.py" }
}
─── ハーネスがファイルを読み込み、中身を次ターンの入力に含めてモデルに渡す ───

【ターン 2】
モデル思考:v3用の関数 get_vms_v3 の直前に、ページング対応の get_vms_v4 を差分挿入する。
モデル出力:
{
  "type": "tool_use",
  "name": "str_replace",
  "input": {
    "path": "BasicFunc.py",
    "old_str": "def get_vms_v3(session, pc_ip):",
    "new_str": "def get_vms_v4(session, pc_ip, limit=100):\n    # ページング処理の実装\n    ...\n\ndef get_vms_v3(session, pc_ip):"
  }
}
─── ハーネスが文字列置換を実行し、成功メッセージを返す ───

【ターン 3】
モデル思考:編集が成功した。壊れていないか単体テストを実行して確認しよう。
モデル出力:
{
  "type": "tool_use",
  "name": "run_command",
  "input": { "command": "pytest tests/test_basicfunc.py -k vms_v4" }
}
─── ハーネスがシェルでpytestを実行。結果(1 passed)を返す ───

【ターン 4】
モデル思考:テストが通った。これで作業完了だ。
モデル出力:
「BasicFunc.py に get_vms_v4() を追加し、単体テストがパスすることを確認しました。」

注目すべきは、毎ターンの入力量(コンテキスト)の膨らみ方 です。

ターン ハーネスから渡される入力量(Tokens) モデルが出力した量(Tokens) 状態
Turn 1 約 2,000(初期指示+システム設定) 約 50(read_file) ファイル探索
Turn 2 約 6,000(+ファイル全文) 約 350(差分コード) 差分編集
Turn 3 約 6,400(+差分結果) 約 80(pytest) テスト実行
Turn 4 約 7,000(+テスト実行ログ) 約 100(完了メッセージ) 完了報告

出力トークンは常に数百トークン以下に抑えられていますが、入力トークンは履歴が積み重なるため雪だるま式に増えていきます。
これが50ターンを超えると、入力が10万〜15万トークンに達し、ハーネスによる コンパクション(要約圧縮) が発動します。

[ハーネスによる自動コンパクション結果の例]
・BasicFunc.py に get_vms_v4() を追加完了(ページング実装済み)
・単体テスト(tests/test_basicfunc.py)通過確認済み
・現在:次のAPI関数 get_clusters_v4() の追加待ち

コンパクションが走ると、過去の詳細なコード本文は文脈から消去されます。そのため、エージェントは次の作業を始める前に、再び read_file で最新の状態をディスクから読み直す必要があります。


実例②:大きなタスク(1,500行・30関数のモジュール新規作成)

次に、1回では到底出力しきれない巨大ファイル nutanix_v4_client.py(約1,500行)を新規作成するケースです。

❌ 失敗パターン:1ターンで全て書かせようとする「一括生成」

{
  "type": "tool_use",
  "name": "create_file",
  "input": {
    "path": "nutanix_v4_client.py",
    "content": "class NutanixV4Client:\n    # 1,500行を一気に書き込もうとする\n    ... (途中でトークン上限に到達) ... \"vmExtId\": vm_"
  }
}

出力上限(max_tokens)に引っかかり、JSONの閉じカッコが出力されずに構文エラーとなります。ハーネス側はツールの実行を拒否し、トークンと時間だけを激しく浪費してファイルは1行も作られません。

✅ 洗練されたエージェントのアプローチ:骨組み → 穴埋め方式

骨組み方式の概念図

ここで言う「成熟したエージェント」とは、Claude Code、Cursor(Agent mode)、Devinなど、サンドボックス環境での強化学習(RL)や実務向けのプロンプト設計が施された自律型エージェントシステム を指します。初期のチャットボットのように無謀に一括生成するのではなく、熟練エンジニアのように段階を踏んで作業を進めます。

【ステップ 1:計画書の作成(PROGRESS.md)】
まずディスク上に作業計画ファイルを作成する。
- 全体のモジュール構造を定義
- グループ分け(G1: 共通基盤, G2: VM操作, G3: クラスタ, G4: 保護...)
- チェックボックスで残タスクを可視化

【ステップ 2:中身が空の「骨組み(Skeleton)」を作成】
全関数のシグネチャと型定義、raise NotImplementedError だけを持った
200行程度のファイルを新規作成する。
(この時点で構文エラーがなく、他ファイルから import できる状態を作る)

【ステップ 3:グループごとに差分で実装&テスト】
G1(認証・HTTPセッション)を str_replace で実装 ──> テスト実行 ──> コミット
G2(VM操作の8関数)を str_replace で実装 ──> テスト実行 ──> コミット
...

この「骨組み方式」の圧倒的なメリットは、どのターンで作業が中断・コンパクションされても、ファイルが常に壊れていない(構文的に正しい)状態を維持できる 点です。

コンパクションによって古い会話ログが消去された場合でも、エージェントは以下のコマンドを実行するだけで一瞬で自分の現在地を取り戻せます。

grep -n "NotImplementedError" nutanix_v4_client.py

「残っている NotImplementedError の数 = 残タスクの数」が一目瞭然になるため、記憶喪失に左右されず、確実に1,500行のゴールまで辿り着けるのです。


6. ファイル分割とフォルダ構成はどうやって決まるのか?

大規模開発では、1つのファイルを大きくするだけでなく、適切にモジュールを分割することが求められます。エージェントはフォルダ構成をどうやって決めているのでしょうか?

エージェントがゼロからあてずっぽうに決めているわけではありません。実は 4つの優先順位(手がかり) に従って決定しています。

優先度 高
  ▲   ① 雛形生成ツール(create-next-app, cookiecutter 等の公式構成)
  │   ② 既存リポジトリの文化(tree や ls で見たディレクトリ規約)
  │   ③ 人間が書いたルールファイル(ARCHITECTURE.md, CLAUDE.md)
  ▼   ④ モデルの事前学習による慣習(Pythonなら src/, tests/ 等)
優先度 低

「雛形(ひな形)生成ツール」とは?
create-next-app や django-admin startproject、Pythonの cookiecutter、poetry new、Ansibleの ansible-galaxy init など、コマンド一発で公式推奨のディレクトリ構成や設定ファイルを自動展開してくれるCLIツール(スキャフォールディングツール) のことです。
エージェントはゼロから手動でフォルダや設定ファイルを1つずつ作っていくのではなく、まずこれら標準コマンドを実行して「業界標準の土台」を一括生成してから開発に取り掛かります。

モデルがモジュールを分割する5つの基準

  1. 単一責任の原則:1ファイル1関心事(認証、データ取得、例外定義など)。
  2. 外部構造へのマッピング:APIのエンドポイントやDBのスキーマ構造に合わせる(例:REST APIの名前空間ごとにファイルを切る)。
  3. ファイルサイズ制約:1ファイルあたり 300〜500 行以内。ファイルが長くなると読み込みトークンが増え、差分置換の失敗率が跳ね上がるため。
  4. 依存関係の一方向性:ビジネスロジック → 共通クライアント → 外部HTTPライブラリ(循環参照の防止)。
  5. テストとの1対1対応:vmm.py に対して test_vmm.py を用意しやすい構成。

特に「外部仕様(API名前空間など)の構造にファイル構成を寄せる」アプローチは、AIにとっても人間にとっても最も迷いが少なく、破綻しにくい設計パターンです。


7. 実務への示唆:人間がエージェントに大規模開発をさせるための作法

エージェントの内部挙動を理解すると、私たちが実務でAIに大規模開発を依頼する際の「勝ちパターン」が見えてきます。

成功のための3大管理ファイル

エージェントに自律的な長期タスクを任せるなら、以下の3つのファイルをリポジトリ直下に配置するのが最も効果的です。

.
├── CLAUDE.md (または AGENTS.md)  ← エージェントの「行動規範」
├── ARCHITECTURE.md              ← システムの「設計方針・依存関係」
└── PROGRESS.md                  ← タスクの「進捗・決定事項の外部記憶」
  1. CLAUDE.md / AGENTS.md(行動規範)
    • 「1ファイルの新規作成は300行まで。超える場合はスケルトンから作ること」
    • 「ファイルを編集する前には必ず対象ファイルを読み直すこと」
    • 「新しい関数を作る前に、既存のユーティリティに同様のものがないか grep すること」
  2. ARCHITECTURE.md(設計方針)
    • フォルダ階層の役割、許可される依存の向き、禁止事項を明文化。
    • コンパクションで方針が吹き飛んでも、セッション再開時にこれを読ませればブレを防げます。
  3. PROGRESS.md(進捗管理)
    • チェックボックス形式のタスクリストと、途中で決まった設計判断(決定事項ログ)を随時記録させる。

人間とAIの境界線

どんなにモデルが進歩しても、「最上位のアーキテクチャ設計」「モジュール分割の境界線」「最終的な受入テストの設計」は人間の役割 です。

  • 人間が決めること:最上位のフォルダ構成、レイヤー間の依存関係、テスト方針、ドメインモデルの境界。
  • AI(エージェント)に任せること:各モジュール内部の関数の肉付け、単体テストの作成、テスト失敗時のデバッグと修正。

枠組み(外枠)を人間がガチガチに固めて文書化し、その枠の中でエージェントを高速に走らせる。これこそが、現代のAIコーディングで最大の成果を出すための実践的な黄金律です。


8. まとめ

コーディングエージェントが大規模なソースコードを紡ぎ出すメカニズムをまとめます。

核心のまとめ

  1. 出力上限は1回の量に過ぎない:エージェントは「読む → 差分編集 → テスト実行」のループを何十ターンも回すことで巨大な成果物を作る。
  2. モデルは頭脳、ハーネスは手足:モデルは文字列を出力しているだけであり、状態の保持・ツールの実行・履歴の圧縮は外側のハーネスが支えている。
  3. 小さく刻む知性はRLの産物:一括生成で失敗し、分割と検証で成功した膨大な強化学習の経験が、モデルの自律的なステップ実行を支えている。
  4. 真実の源泉は会話ログではなくファイル:コンパクションで記憶が消えても、Gitとファイルシステム(進捗メモ・骨組み)があれば開発は止まらない。

皆さんのチームでは、コーディングエージェントにどこまで任せていますか?ルールファイルや進捗管理で工夫している点があれば、ぜひコメント欄で教えてください!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?