序章 AI駆動開発とソフトウェア工学
人間にとって保守しやすいコードと、AIにとって保守しやすいコードは、ほぼ同じです。
これは、LLMの挙動を観察して得られた経験であると同時に、動作原理からも説明できます。LLMは与えられた文脈に基づいて出力を生成します。責務が混在し、暗黙の前提が多く、遠く離れたコードを突き合わせなければ意味を理解できない構造では、必要な情報の収集と整合性の判断が難しくなります。一方、責務や依存関係が明確で、変更の影響が局所化された構造なら、AIが考慮すべき文脈と判断を絞り込めます。
これらは、人間が混乱せずにソフトウェアを理解・保守するために発展させてきた設計原則とほぼ一致します。したがって、従来のソフトウェア工学の知見は、AI駆動開発でもそのまま重要になります。
特に有効だったのは、適切なレイヤー設計とTDDの組み合わせです。レイヤー設計が責務と依存関係を整理し、TDDが一度に扱う問題を小さな意味単位へ限定するとともに、設計を継続的に発展させます。これによってAIの混乱、手戻り、無駄な利用枠の消費を抑え、的確に開発を進めやすくなります。
実際にこの考え方を適用した開発では、体感ではありますが、10倍以上の作業効率を達成できたと考えています。
本資料では、適切なレイヤー設計とTDDを中心に、AI駆動開発の生産性と品質を高めるための考え方と実践方法を説明します。
第1章 システム開発のボトルネック
システム開発のボトルネックは、昔も今も、人間とコンピュータの間の情報のやり取りにあります。
初期の開発では、人間が抽象的な要求を段階的に詳細化するなどして、コンピュータが実行できる具体的な命令にまで落とし込む必要がありました。
しかし、プログラミング言語やコンパイラ、ライブラリなどの発達によって、それまで人間が担っていた作業の多くをコンピュータに任せられるようになりました。より抽象的な情報を伝えるだけで複雑な処理を実現できるようになり、情報伝達の効率は指数関数的に向上してきました。
実は、プログラミング不要な時代が来たのは、今回が初めてではありません。
かつてプログラマーは、現在のEXEやDLLに含まれるようなバイナリを直接読み書きしていました。それが当時の「プログラム」でした。
この作業がコンパイラによって自動化されると、「プログラム」を直接書くプログラマーはほとんどいなくなりました。
そして、コンパイラに渡すソースコードが、新たにプログラムと呼ばれるようになりました。
フレームワークやIDEの普及期にも、同じように「プログラミングが不要になる」と言われ、実際にコードを直接書かずに済む作業が増えていきました。
AIの登場によって、情報伝達の効率はさらに数倍に高まりました。 今回の変化は特に急激ですが、それまでプログラミングと呼ばれていた作業が自動化されるという点では、従来の発展の延長にあります。
AIの登場によって、ソースコードはすべてAIが書いてくれるようになり、人間が直接入力する必要はなくなりました。人間の主な作業は、コードの入力から、生成結果のレビューと問題点の指摘へと移ったのです。
現在では、自然言語で要求を伝え、AIが生成したコードや動作する画面を確認しながら、不足している情報や修正指示を与えられます。AIが修正した結果を再び確認し、さらに指摘を加えるという往復作業が、開発の中心になりました。
この方法の大きなメリットは、最初にすべての要求を整理するという煩雑な作業から解放されたことです。
最初は大まかな要求を伝え、生成結果を確認しながら、足りない情報や新たに気づいた条件を五月雨式に追加できます。要求の整理そのものを、開発と並行して進められるようになりました。
ただし、最終的に人間からコンピュータへ伝えなければならない要求や条件の情報量が減ったわけではありません。
必要な情報を最初にまとめて整理する代わりに、何度もの往復を通じて少しずつ伝えられるようになったのです。コードの入力作業は不要になりましたが、レビューと指摘を通じた情報のやり取りは依然として必要です。
情報伝達の手段や人間が受け持つ作業が変わっても、人間とコンピュータの情報のやり取りがボトルネックであることは変わりません。
AI駆動開発では、コード生成やレビューの個々の作業だけを高速化するのではなく、要求の提示、生成結果の確認、問題点の指摘と修正という往復全体を効率化することが重要です。
第2章 AIは何をしているのか
昨今の生成AIに使われているLLM(大規模言語モデル)は、人格をシミュレートするアルゴリズムではありません。
LLMは膨大な文章やプログラムを学習しており、その知識と、質問やこれまでの会話、渡された資料など、その場で与えられる情報(コンテキスト)をもとに、次に続く単語の確率を計算することしかできないアルゴリズムです。
文章は、次に来そうな単語を選び続ければ、それだけでできあがります。
2.1 なぜ高度な回答ができるのか
LLMは膨大な情報を学習することで、もし非常に多くの知識を持った人間がいたなら、この場面で何と答える確率が高いかを予測できるようになります。
そのため、膨大な知識を持つ人間が答えた場合と似た、非常に高度な回答が得られます。
プログラムについても同様です。凄腕のプログラマーなら何を書く確率が高いかを予測して、大量のプログラムを生成できます。
2.2 なぜハルシネーションが起きるのか
ただし、人間が答えそうな文章と、事実として正しい文章は必ずしも一致しません。
正解を特定するための情報が、学習した知識にもコンテキストにも存在しない場合を考えてみます。
この場合でも、LLMはその場面で人間が答えそうな文章を生成できます。しかし、正解を特定できる情報がない以上、その回答が正しいかどうかは分かりません。
その結果、もっともらしくても事実とは異なる回答が生成されることがあります。これがハルシネーションです。
また、LLMは正解を特定できる情報がなくても、自信ありげに回答することがあります。
回答が自信ありげであることと、その回答が正しいことは別です。
2.3 コンテキストと回答の長さ
LLMの回答は、そのとき参照できるコンテキストによって大きく変わります。
正解を特定できる情報がコンテキストに含まれていれば、正しい回答と一致する内容が得られる確率が高いと判断されます。 一方、必要な情報が不足していれば、もっともらしいものの誤った回答になる可能性があります。
また、求める回答が長く複雑になるほど、その一部に誤りが含まれる可能性も高くなります。
逆に、答えが短く明確な場合には、正しい回答を得られる確率が高くなる傾向があります。
したがって、LLMにプログラムなどを生成させる際には、コンテキストに必要な情報を与え、求める出力を明確に特定し、一度に生成させる範囲を小さく限定することが重要です。
第3章 レイヤー設計によってAIの作業を単純化する
AI駆動TDDを試したWebアプリケーションでは、変更理由に応じて責務を分離し、各レイヤーに何を書くべきかを明確にしました。
レイヤー設計がAI駆動開発に役立つ最大の理由は、実装先に迷いにくく、各レイヤーのコードを短くシンプルに保てることです。AIが一度に扱う問題と参照すべき情報を小さくでき、混乱、余計な実装、手戻りを減らせます。
3.1 Webアプリケーションの構成例
レイヤー設計には、広く使われている設計原則やベストプラクティスを利用します。ただし、最初から大規模な構成を作る必要はありません。必要最小限の構成から始め、開発しながら育てていきます。
Webアプリケーションの構成例を示します。
| 区分 | 主な役割 |
|---|---|
| 画面 | 表示、入力、イベントの受け渡し |
| ViewModel | 画面の状態と操作、画面固有の判断 |
| Client Application | クライアント側の操作、APIの利用、状態管理の調整 |
| HTTP境界 | 通信契約と、DTO・内部モデル間の変換 |
| Server Application | 業務操作の進行と、取得・保存の調整 |
| Domain | 業務上の状態、規則、判断、状態変化 |
| Infrastructure | DBや外部サービスなど具体的な技術との接続 |
これは一つの構成例であり、Webアプリケーション一般に必要なレイヤー数を示したものではありません。また、責務上の区分とプロジェクトの分割は、必ずしも一対一ではありません。
3.2 どこに何を書くかを決めておく
例えば、画面の表示や入力の処理は画面側に、通信形式と内部モデルの変換はHTTP境界に、業務上の判断はDomainに、DB固有の処理はInfrastructureに置きます。クライアントとサーバーのApplicationも役割を分け、同じ業務処理を重複させません。
このように責務を決めておけば、AIは機能を追加するたびにアプリケーション全体の構成を考え直さずに済みます。各部分でやるべきことが限定されるため、実装も短く単純になりやすくなります。
もちろん、業務規則そのものが複雑であれば、Domainのコードは複雑になります。しかし、その複雑さを画面や通信、保存などの定型的な処理へ分散させず、Domainに集中させることで、複雑になる場所を限定できます。
さらに、広く使われている設計パターンを採用することには、別の利点があります。
AIはこうした設計パターンについて既に多くを学習しています。そのため、コンテキストに設計上の約束事をすべて記述しなくても、学習済みの知識を活用できます。
独自の構造を採用する場合と比べて、AIが設計上の決まりを推測して間違える余地を減らせると考えられます。
これは、各レイヤーのコードを短く単純にすることとは別の、AI駆動開発における利点です。
3.3 設計の規則をAIに守らせる
設計を最初に決めるだけでは、AIが変更を積み重ねるうちに境界を破る可能性があります。
AIは設計原則について高度な議論ができても、実際のコード生成ではその原則に反するコードを書くことがあります。
人間にも知っている設計原則を実践できないことはあります。しかし、AIではその傾向がはるかに顕著です。
AIにとって、設計原則を知っていることと、実際の開発でそれを守れることの間には、人間以上に大きな隔たりがあるのです。
さらに厄介なのは、AIが自分で誤ったコードを書いておきながら、次の作業では周囲のコードを調査し、自分で書いた間違いを参考にして、「既存のコードではこう書かれています」と説明して、同じ間違いを繰り返してしまうことです。
一度の設計上の誤りが、後の実装の手本となり、誤りを増やしてしまうわけです。
そこで、参照方向や通信DTOの使用範囲など、機械的に確認できる制約は、自動テストや静的解析でも検査できるようにします。また、人間が不適切な依存関係を発見した場合は、その箇所を指摘してAIに修正させます。
すべての設計判断を機械化できるわけではありませんが、どこに書くかという規則を明確にし、検査できる規則は自動化することで、人間が毎回すべてのコードを読み直す負担を減らせます。
第4章 TDDによってAIの作業と判断を小さくする
4.1 TDDとは
TDD(テスト駆動開発)は、テストを先に書き、実装と設計改善を繰り返しながらソフトウェアを発展させる開発手法です。
基本となるのは、Red → Green → Refactor の短いサイクルです。
- Red(外部設計):次に実現する振る舞いをテストとして記述し、意図した理由で失敗することを確認します。利用側から見た仕様やAPIの使いやすさを考えます。
- Green(最小限の実装):現在のテストを通すための最小限の実装を行います。内部設計の改善や将来の機能は考えず、テストを通すことに集中します。
- Refactor(内部設計):テストが通る状態を維持しながら、コードの重複、責務、依存関係などを見直し、内部設計を改善します。
TDDの重要な特徴は、単にテストを先に書くことではなく、工程ごとに考える対象を分離することにあります。
また、最初からすべての仕様を決めておく必要はありません。小さな実装と設計改善を繰り返す過程で、次に必要な仕様や設計を発見していきます。
4.2 AI駆動開発との相性
TDDがAI駆動開発に適している理由は二つあります。
第一に、細かい意味単位でAIに作業を指示できることです。
一度に扱う仕様と判断を限定できるため、AIが混乱したり、ハルシネーションによって無関係な実装を始めたりする可能性を大幅に減らせます。
各工程の目的が明確で、自動テストによるフィードバックも得られるため、問題が発生しても小さな範囲で修正できます。
第二に、人間によるレビューが容易になることです。
TDDによって小さな仕様単位で開発を進め、それに対応するコミット履歴を残せば、変更の目的と実装内容が明確になります。
AIが大量に生成したコードを一度に確認するのではなく、目的の絞られた変更を順にレビューできるため、人間の負担を軽減できます。
4.3 レイヤー設計との組み合わせ
レイヤー設計によって実装する場所と責務が明確になり、個々のコードを短くシンプルに保ちやすくなります。TDDでは、次に実現する仕様と実装範囲を小さく区切ります。
さらに、TDDのRefactorによって責務や依存関係を継続的に見直し、レイヤー構造を維持・発展させられます。
この組み合わせによって、複雑なシステム開発を、AIが扱いやすい小さな作業の積み重ねとして進められます。
第5章 要求や資料をAIに伝える
5.1 AIへの情報の与え方
AIに必要な情報を十分に伝えることは重要ですが、そのために最初から詳細な仕様書を完成させる必要はありません。
ラフな設計、議事録、既存の帳票、メモ、紙を撮影した画像やスキャン資料など、手元にある情報を形式や完成度にこだわらず、極力そのままAIに渡します。整理された仕様書があれば、それを利用しても構いません。
資料に矛盾や不足がある場合は、「こちらは検討途中の資料なので仮にまとめてよい」「こちらはレビュー済みの正式な仕様である」といった位置づけを伝え、判断できない点は対話で補います。
開発途中で資料が更新された場合も、変更をAIへ伝えます。私の場合、TeamsやSharePoint上の資料の更新は手動で反映し、Git管理のdocsにも簡潔な資料を置いています。
また、要求の細部は、実際に動くものを確認しながら具体化できます。
特に定型的な処理の多いWebアプリケーションでは、ラフな要求から暫定的な画面を作らせ、実際に操作してみて不足する項目や使いにくい動作を指摘します。
AI駆動TDDでは、後から要求が変わっても、手戻りのコストがほとんど気にならなくなりました。 実装や修正の大部分をAIに任せられ、TDDによって既存の動作をテストで確認しながら変更できるためです。
追加・変更された要求は、その都度Redのテストに反映して実装します。
一方、複雑なロジックや公開APIなど、画面から妥当性を判断できない部分では、Redのテストや期待結果を通じて外部仕様を確認します。
第6章 AIにTDDを進めさせる
第4章では、TDDによってAIが一度に考える範囲を限定できることを説明しました。
しかし、AIに単に「TDDで開発してください」と指示するだけでは、Red・Green・Refactorの原則を正しく守ってくれるとは限りません。
そこで、実際の開発では、各工程で何を行い、何を行わないかを具体的な規則として指示しています。
6.1 Red・Green・Refactorで考える対象と終了条件を分ける
AIにTDDを実践させるうえで重要なのは、各工程の作業だけでなく、考える対象も分離することです。
Redでは、外部設計に集中させます。利用側から見た仕様やAPIをテストとして表現し、意図した理由で失敗することを確認します。
この段階では、必要に応じて未実装のメソッドやクラスを宣言することもあります。C#なら、NotImplementedExceptionを投げるだけの仮実装を用意すれば、まだ実装していないAPIを利用するテストでもコンパイルして実行できます。
こうした仮実装は、Redを成立させるためのものであり、Greenで行う機能の実装とは区別します。
Greenでは、現在のテストを通すための最小限の実装に集中させます。
AIは、実装すべき機能を一気に完成させようとする傾向があります。しかしGreenでは、機能全体を完成形まで作り込むのではなく、その時点のテストと既存テストを通すための最小限の実装にとどめます。
そこでGreenを終了する前に、今回追加したコードに、対象テストと既存テストを通すためには不要な分岐や処理が含まれていないかを確認させています。不要なものがあれば削除します。
この確認は、今回のGreenで追加した実装が対象です。既存コード全体を整理するためのものではありません。
また、Greenではリファクタリングを行いません。新しく書くコードが最初から読みやすいことと、既存コードの内部設計を変更することは別です。
Refactorでは、テストが通る状態を維持しながら、内部設計を改善します。
コードの重複、責務、依存関係、アルゴリズムの妥当性などを検討し、必要に応じて改善します。変更の必要がなければ、そのまま終了することもあります。
この工程で新しい仕様や不具合を発見した場合は、現在のRefactorで機能を追加するのではなく、別のRedで扱います。
各工程には終了条件を設けており、「Redまで」「Greenまで」という指示で、指定した工程の終了時に作業を止められるようにしています。
各工程の目的を明確にすることで、AIが一度に複数の問題を解決しようとして作業を複雑化させることを防ぎます。
6.2 指示をその場でテストに反映し、未着手分を保留する
開発中に新しい仕様や修正要求を受けたときは、その内容が明確なうちに、AIにテストとして記録させています。
自然言語で受け取った指示を後からまとめてテストへ変換しようとすると、細かい条件や判断の意図が曖昧になる可能性があるためです。
ただし、その場ですべての機能を実装するわけではありません。
未着手の仕様は、実行を保留したテストとして保存しておきます。
xUnitならSkipを指定したテストです。ほかのテストフレームワークにも、実行を保留する仕組みがあります。
| フレームワーク | 保留の方法の例 |
|---|---|
| xUnit | Skip |
| NUnit / MSTest | Ignore |
| Vitest / Jest |
test.skip、it.skip
|
| Playwright Test | test.skip |
| Mocha | it.skip |
| pytest | skip |
| RSpec | skip |
| Minitest | skip |
実装を進める際には、その中から一件を選んで有効化し、テストを実行します。意図した理由でRedになれば、Green・Refactorへ進みます。既に成功する場合は、既存の実装でその仕様が満たされていることを確認します。
Skipテストをまとめて作成する際には、実行順序も決めて、各テストのSkipのテキストに記録しておきます。 Skipを外すときには、その記述を参照して次にSkipを外すテストを決めます。
一度に多数のテストを書いた場合、未実装APIの仮宣言も多く必要になることがあります。この場合も、先ほどの未実装スタブを利用できます。
この方法の目的は、最初にすべての仕様や外部設計を完成させることではありません。
指示された内容を、その場で失わない形に記録し、実装する仕事を一件ずつ取り出せるようにすることです。
GreenやRefactorの途中で、現在の作業とは別の不具合や不足仕様を発見した場合も、必要なら保留したテストとして記録し、後のRedから扱います。
ただし、現在の作業で失敗させてしまった既存テストを、そのまま保留して先へ進むという意味ではありません。新規機能の追加では、GreenとRefactorで、維持すべき既存テストが成功している状態を保ちます。
6.3 既存仕様を変更するときの進め方
TDDは新しい機能を追加するだけでなく、既存機能の仕様を変更する場合にも利用できます。
仕様が変更された場合は、まず変更後の仕様をテストへ反映します。旧仕様を確認していたテストは、必要に応じて変更します。
ここで重要なのは、仕様に合わせてテストを変更することと、実装に合わせてテストを変更することを混同しないことです。
テストが失敗したからといって、現在の実装に合うように期待値を書き換えてしまっては、テストによる仕様の確認が成り立ちません。
また、仕様変更の対象ではない既存の振る舞いは、引き続き維持する必要があります。
既存機能を変更する際には、次の5段階に分けて進めています。
- Red①:まず変更したい機能について、テストを新しい仕様に合わせて修正し、意図した理由で失敗することを確認します。
- Green①:修正したテストが通るように、必要最小限の実装を行います。
- Red②:その変更によって通らなくなった既存テストを精査し、新しい仕様に合わせて変更すべきテストを修正します。維持すべき仕様のテストは変更しません。
- Green②:維持すべき既存テストも含め、すべてのテストが通るように実装を修正します。
- Refactor:機能を追加・変更せず、テストが通る状態を維持しながら内部設計を改善します。
特に2回目のRedでは、仕様変更によってテストを変更する必要があるのか、それとも実装を修正して従来の振る舞いを維持すべきなのかを区別します。
変更箇所が多い場合も、テストを保留する仕組みを利用して、一件ずつ取り組むことができます。
こうすることで、新規機能の追加だけでなく、既存機能の変更も小さなTDDの作業単位に整理できます。
6.4 なぜAIは最初からTDDで開発しないのか
現在のコーディングエージェントは、特に指示しなければTDDを実践しません。むしろ、与えられた仕様を理解して一気にプログラムを書き上げ、その後でテストして修正するという、ウォーターフォール的、あるいは初学者的な開発方法に強く偏っています。
それほどTDDが有効なら、なぜAIの開発元は、AIが自分で開発手順を考える際に、最初からTDDを利用するようにしていないのでしょうか。
開発元がどのような判断をしているのかは分かりませんが、いくつかの理由が考えられます。
まず、AIが本来のTDDを十分に学習できていない可能性があります。
一般に公開されているソースコードやテストは、完成したものが中心です。しかし、完成したコードを見ても、Redでどのような外部設計を考え、Greenでどのように実装を限定し、Refactorで内部設計を改善したのかは分かりません。
TDDを正しく実践する過程が学習データにあまり含まれていなければ、AIがTDDの原則を説明できても、実際の開発ではそれを守れないということは十分に考えられます。
実際、AIにTDDについて議論させると、TDDを深く理解した高度な説明ができるのに、実装させると、Greenでその時点のテストを通すための最小限の実装にとどまらず、テストにない機能まで実装してしまうことがほとんどです。
次に、AIの研究分野では、そもそもTDDがあまり普及していないのではないかということです。
私の印象では、AI研究者の大半は、複雑なアルゴリズムを一気に書き上げるような、天才肌のプログラマーではないかと思います。研究のために作成したプログラムは、実験が終われば使い捨てられることもあるでしょう。
一方、TDDは、個人の才能に依存せず、再現可能な開発手順によって製品品質と作業効率を安定して維持するための手法です。
複雑なアルゴリズムを独力で実装する能力が高く評価され、長期間の保守を必ずしも前提としない研究の世界では、TDDの価値が実感されにくいのかもしれません。
さらに、TDDは研究対象としても扱いにくい手法だと考えられます。
例えば、テストを先に書いた場合と後から書いた場合で、開発時間や不具合数を比較することは比較的簡単です。
しかし、TDDは単にテストを先に書く方法ではありません。Redによる外部設計、Greenによる最小限の実装、Refactorによる内部設計の改善を、一体の開発手法として実践する必要があります。
その有効性を適切に評価するには、実験参加者がTDDの考え方を理解している必要があり、設計品質や継続的な変更のしやすさまで含めて調べなければなりません。
TDDの一部分だけを切り出して対照実験を行っても、TDDそのものを評価したことにはならないわけです。
実際、AIによる開発の研究には、テストを与えてプログラムを生成・修正することをTDDとして扱っている例もあるようです。それ自体は有用な研究ですが、設計手法としてのTDDとは異なります。
こうした事情を考えると、AI研究の世界では、TDDの本来の価値が十分に認識されず、モデルの学習や開発方針にも反映されていない可能性があります。
AIが仕様を受け取ると一気に実装しようとする傾向には、学習データや研究者の開発文化、モデルの訓練・評価方法が影響しているのかもしれません。ただし、どの要因がどの程度影響しているのかは分かりません。
少なくとも私の実践では、AIにTDDの各工程とその原則を明確に指示することで、良好な結果を得られています。AIが標準でTDDを採用していないことは、AIにTDDが向いていないことを意味しません。
AIツールを利用する際には、開発元の公開する方針やロードマップから、何を重視して開発しているのかを読み取ることも重要です。
開発元が標準として用意した方法が、自分の目的にとって最善とは限りません。
AIの能力をそのまま利用するだけでなく、ソフトウェア工学の知見を使って、その能力をより有効に引き出す方法を考えることにも価値があるのです。
第7章 人間によるレビュー
AIに多くの開発作業を任せられるようになっても、仕様や設計の妥当性を判断する人間の役割は残ります。ただし、AIが生成した大量のコードやテストを、すべて詳細に確認するのは現実的ではありません。そこで、必要な情報を短時間で確認し、重要な問題にレビューを集中させる工夫が必要になります。
7.1 テストを読めば実際の仕様を調べられるようにする
TDDでは、外部から見た振る舞いをテストとして記述していきます。すべての外部仕様を漏れなくテストとして記述できれば、そのテストを一覧にしたものが完全な外部仕様書になります。
一方、AIに任せた機能のすべての仕様を、人間があらかじめ把握しておくには大きなコストがかかります。私も、全仕様を把握することは目標にしていません。その代わり、必要になったときにテストを読めば、実際にどのような仕様で作られているかを短時間で調べられることを重視しています。
そのためには、個々のテストが仕様として読める必要があります。目標は、一件のテストを数秒読めば、何を確認しているのか理解できることです。特に重要なのは、そのテストで確認する仕様以外に目がいかないようにすることです。どのような入力に対して、どのような結果を期待するのかが、テスト本文から明確に読み取れる必要があります。
テスト対象の準備、モックの設定、付随的な検証処理などが長くなる場合は、補助メソッドへ切り出しています。ただし、処理を隠すだけでは何を期待しているテストなのか分からなくなるため、期待する値は補助メソッドの引数などに残し、テスト本文から読み取れるようにしています。
画面テストでは、画面要素の検索や準備処理などが多くなり、この目標を達成するのが難しくなります。画面全体を対象にする必要のない処理はViewModelのテストで確認していますが、実際に使用しているBlazorの画面テストでは、まだ数秒で仕様を読み取れる状態にはなっていません。
なお、テストが仕様として読めることと、仕様そのものが正しいことは別です。テストの数が多くても、必要な条件が抜けている可能性はあります。
7.2 人間がレビューする観点
AIに任せた開発では、Red・Green・Refactorの各工程で、人間が確認すべき内容が異なります。それぞれの工程で何を判断する必要があるのかを意識してレビューしています。
Redでは、テストコードの可読性と、外部仕様や公開APIの使いやすさを中心に確認します。 テストが何を確認しているのかを読み取り、APIの名前や使い方が自然で、利用する側から見て理解しやすい設計になっているかを確認します。テストの可読性については前節で説明したとおりです。
Greenでは、対象テストと既存テストを通すためには不要な分岐や処理が追加されていないかを確認します。 AIは必要な機能を先回りして作り込むことがあるため、過剰実装には注意しています。一方、内部設計の改善はRefactorの仕事なので、Greenのレビューで要求しません。
Refactorでは、コードの可読性を中心に、責務の分け方や依存関係などに不自然な点がないかを確認します。 コードを読みながら、重複、不適切な抽象化、複雑すぎる処理など、気づいた問題を指摘します。
ただし、これは毎回すべての項目を詳しく点検するチェックリストではありません。テストが成功しているか、変更によって既存の動作が壊れていないかなど、自動テストや開発ツールで機械的に確認できることは、人間が重複して確認する必要はありません。
私の場合、人間が重点的に確認するのは、機械的には判断しにくい設計や可読性です。コードを自然に読み、違和感があれば指摘するという方法で、レビューの負担を抑えています。
Skipテストをまとめてレビューする際には、明らかにおかしな仕様が混ざっていないかを、ざっと確認する程度にしています。
テストコードの実装が正しいかどうかは、細かく確認する必要はありません。Skipを外した後、意図した理由でRedになることを確認してからGreenへ進むため、テストコードに実装上の間違いがあれば、通常はその過程で発見されます。間違ったままGreenまで進むことはまずありません。
一方、最初から仕様を取り違えていた場合は、間違った仕様のままGreenまで進んでしまいます。そのため、人間は仕様そのものに明らかな間違いがないかを重点的に確認します。
7.3 レビューのタイミングと単位
人間がレビューするタイミングは、開発対象の複雑さによって変えています。
定型的な処理では、RedとGreenの結果をまとめてレビューすることが多くなります。Redでは主にテストコード、Greenでは主に実装コードを変更するため、まとめて確認しても、それぞれの工程で何を行ったかを区別しやすいためです。
一方、定型的でないAPIを設計する場合は、Redの段階で人間が外部設計をレビューし、その後にGreenへ進める必要があります。 実装前にAPIの使いやすさや設計の妥当性を判断するためです。なお、RedとGreenをまとめてレビューする場合でも、TDDの実行順序を変更するわけではありません。
また、人間によるレビューには、逐次レビューと履歴レビューという二つの進め方があります。
逐次レビューでは、AIの作業を小さく区切り、その都度、人間が変更内容を確認します。問題があればその場で指摘し、修正してから次へ進みます。
履歴レビューでは、AIに複数の作業をまとめて進めさせ、後からコミット履歴をたどってレビューします。この場合も、人間は変更全体を一括して評価するのではなく、履歴を一件ずつ確認します。 AIに作業させる単位と、人間がレビューする単位は必ずしも同じではありません。
履歴レビューは、定型的な処理が多く、短期間で開発を進めたい場合に有効です。一方、複雑な設計やアルゴリズムを扱う場合には注意が必要です。
以前、複雑な変更をまとめて実行した後、Refactorの不足に気づいたことがありました。しかし、変更が積み重なった状態から一件ずつ問題を修正するのは手間が大きく、結局、複数の段階に分けてまとめて整理することになりました。それでも、十分に改善できたかという懸念は残っています。
この経験から、複雑な変更では、Refactorの不足を発見した時点で一件ずつ指摘し、対応していくことが重要だと考えています。
また、履歴レビューには精神的な負担もあります。小さなコミットが残っていても、後から大量の変更を確認するのは疲れます。逐次レビューなら、問題を一つずつ解決し、未解決の問題を後に残さずに済むため、私にとって精神的にはるかに楽です。
そのため、一般的なWebアプリケーションの定型処理では履歴レビューも利用しますが、設計が複雑なクラスライブラリの開発では逐次レビューを選ぶことが多くなります。対象の複雑さ、納期、レビューの負担を考慮して使い分けています。
第8章 AIに守らせる規則の設計と管理
これまで説明したレイヤー設計やTDDの工程には、AIに継続して守らせたい規則があります。本章では、それらの規則をどのように記録し、実際の開発を通じて改善していくかを説明します。
8.1 指示ファイルと規則の配置
AIに開発を任せる際には、既存コードの扱い、設計上の制約、TDDの進め方など、作業を通じて繰り返し適用する規則があります。これらを作業のたびに会話で指示するのは非効率なので、指示ファイルに記録しています。
Codexでは、リポジトリ内のAGENTS.mdを入口として、用途別の指示ファイルを参照させています。私が使っている規則は、おおむね次のように分類できます。
- 共通規則:既存コードの扱い、作業範囲、Git操作、作業結果の確認と報告。
- 設計規則:レイヤーの責務や依存方向、DTOの使用範囲など。
- TDD規則:Red・Green・Refactorで行うこと、禁止事項、各工程の終了条件。
- 言語固有の規則:C#の記述方針、テストやビルドに関する指示。
- プロジェクト固有の規則:採用技術や、そのシステムに特有の設計上の制約。
これらは必ずしも別々のファイルにする必要はありません。重要なのは、共通して使える規則と、特定の作業やプロジェクトにだけ必要な規則を整理しておくことです。
例えば、第3章で説明した各レイヤーの責務の分担は設計規則として、第6章で説明したGreenでの過剰実装の禁止はTDD規則として記録できます。
また、Codexには、必要な作業に応じて読み込むSkillsという仕組みもあります。適用対象のAGENTS.mdが基本的な指示として参照されるのに対し、SkillsはAIが必要と判断したときに詳細な手順を読み込みます。
私はSkillsを使うかどうかの判断を基本的にAIに任せています。ただし、Skillsは選択されなければ読み込まれない可能性があるため、常に守らせたい規則を置く場所とは区別する必要があります。
このように、何を守らせるかだけでなく、どの作業でどの規則を参照させるかも考えて指示を配置します。
8.2 失敗や指摘を規則へ反映する
指示ファイルに書く規則を、開発開始前にすべて決める必要はありません。私の場合、実際の開発で起きた問題への対処を積み重ねることで、規則を充実させてきました。同じ注意を何度も繰り返していると気づいたら、その場の修正だけで終わらせず、今後も適用する規則として記録します。
例えば、AIはGreenで、テストを通すためには必要のない処理まで先回りして実装することがあります。そこで、Greenの終了前に、今回追加したコードに、対象テストと既存テストを通すためには不要な分岐や処理が含まれていないかを確認する規則を追加しました。この規則を導入してからは、私が過剰実装を指摘する機会が減ったと感じています。
また、AIが指示した範囲を越えて別の作業まで始めてしまうこともあります。そうした問題を指摘すると、AIの側から規則の追加を提案してくる場合がありますし、人間から追加を求める場合もあります。
規則の文章を人間が一から書く必要もありません。例えば、
今の指摘をルールにしておいて
と伝えれば、AIに指示ファイルの修正を任せられます。ただし、追加された規則が意図した内容になっているかは確認します。
一方、その場限りの作業指示と、今後も繰り返し適用すべき規則は区別します。 一時的な指示まで恒常的な規則にするのではなく、実際の問題を踏まえて必要な規則を追加・改善しています。
8.3 規則を整理し、意味の欠落を確認する
問題が起きるたびに規則を追加していると、指示ファイルは次第に長く複雑になっていきます。しかし、規則が長いこと自体を問題視する必要はありません。 人間にとって複雑で読みにくい規則が、AIにとっても有効でないとは限らないからです。
一方、あまりに複雑になると、AIが規則を適切に適用できなくなる可能性もあります。そのため、時折、重複の解消や規則の整理を行うことも考えています。こうした整理作業自体もAIに任せられます。
ただし、AIに文章を整理させると、重要な条件や論点が抜け落ちることがあります。 例えば、複数の規則を一つに統合する際に、元の規則に含まれていた禁止事項や適用条件まで省略されてしまうことがあります。文章としては読みやすくなっても、規則としての意味が変わってしまっては困ります。
そのため、規則を整理する際には、変更前後の差分を確認し、必要な意味が失われていないかレビューするのがよいと考えています。短くすることを目的とせず、規則の実効性を維持することを重視します。
実際に使っているTDD向け指示文は、完成された模範解答としてではなく、試行錯誤を重ねた一例として付録に収録しています。
第9章 AIを活用した検証と作業の効率化
9.1 開発ツールとスクリプトによる作業の効率化
AI駆動開発では、AIの使用量がしばしばボトルネックになります。 AIに任せたい仕事が残っていても、利用枠を使い切れば開発を続けられなくなります。そのため、日々の作業で無駄な使用量を発生させないことが重要です。
AI駆動TDDでは、Red・Green・Refactorの各工程で自動テストを繰り返し実行します。ビルドや静的解析も日常的に利用します。これらはコンパイラ、テストランナー、静的解析ツールが機械的に処理するもので、AIはその結果を受け取って次の作業を判断します。ツールが実行・判定できる処理を、LLMに考えさせる必要はありません。
また、何度も繰り返す作業は極力スクリプト化しています。 ビルドやテストの実行、ファイル操作、結果の集約や整形など、決まった手順で処理できるものはスクリプトに任せます。スクリプト自体もAIに作成させているため、人間が細かな処理を書く必要はありません。
一度スクリプトを用意すれば、AIが毎回同じ処理手順を組み立てる必要はなくなります。必要な結果だけを取り出すことで、大量のログをAIに読み取らせることも避けられます。
自動テストなどの開発ツールを日常的に活用し、繰り返す作業をスクリプト化することで、AIの使用量を抑えながら、判断が必要な仕事に集中させられます。
9.2 テストが必要な条件をAIに発見させる
第6章では、要求された仕様や開発中に見つかった不足をテストとして記録する方法を説明しました。それとは別に、開発が一段落した時点で、まだ気づいていない問題や不足しているテスト条件をAIに調査させることも行っています。
私の場合、高性能なモデルと高い推論レベルに切り替え、「問題点を指摘して」と依頼することがあります。AIに実装や既存のテストを調査させると、実装上の問題だけでなく、追加すべきテストの観点も洗い出されます。最初のRedですべての条件を網羅するのではなく、一通り開発した後で改めて不足を探す方法です。
また、AIが特殊な条件でしか発生しないバグを発見した例もあります。私が開発しているWord文書を扱うライブラリでは、AIがフィンランド語設定でしか再現しないバグを発見し、その再現テストも作成しました。 人間が普段は意識しない条件まで調査し、実際の不具合を発見できた事例です。
このテストはRedで意図した失敗を確認し、Greenで成功させています。
問題点の洗い出しが終わったら、安価なモデルへ手動で切り替え、具体的なテストの追加や修正を進めています。未着手のテストは第6章で説明したSkipを利用して管理し、既存テストの変更が多い場合には変更内容の一覧を残すこともあります。
AIには、指示した内容をテストにするだけでなく、そもそも何をテストすべきなのかを発見させることもできます。
9.3 条件を再現するテストデータを用意する
前節では、AIに不足しているテスト条件を発見させる方法を説明しました。しかし、条件が分かっても、それを再現できる入力データがなければテストを実行できません。
数値や文字列ならテストコードに直接記述できますが、WordやExcelなどのファイル形式や言語設定、書式などが関係する場合は、特殊な条件を含む実ファイルが必要になります。
こうしたテスト用ファイルの作成もAIに任せています。 必要な条件を伝えてファイルを作成させ、それを読み込むテストを用意します。人間がアプリケーションを操作してテストデータを一つずつ作成する負担を減らせます。
また、Word文書の読み込み時にはOOXMLの形式検証も行っています。
テストすべき条件の発見から、その条件を再現するデータの準備までAIに任せられることで、通常なら準備に手間がかかる検証にも取り組みやすくなります。
9.4 複数のチャットによる並行作業の進め方
AI駆動開発では、複数のAIに別々の仕事を任せ、並行して作業を進めることができます。 一つの作業が終わるまで待つ必要はありません。
実際にやってみると、並行作業は思ったより簡単です。例えば、三つか四つの作業を並行して進めていれば、どれか一つは完了しているものです。その結果を確認し、必要な指摘や次の指示を出します。そうしているうちに、別の作業が完了しています。これを繰り返すことで、AIの作業終了を待つ時間を減らせます。
複数の作業を進めていると、以前の作業で何をしていたのか分からなくなることもあります。しかし、それまでの会話や作業報告は残っているので、読み返せば内容を確認できます。分からなくなった場合は、「今何をやっていましたっけ?」と質問すれば、AIが作業の内容や進捗を丁寧に説明してくれます。
ただし、並行作業の数を増やしすぎると、今度は人間の方が混乱したり、疲弊したりします。 AIがいくら並行して仕事を進められても、結果をレビューして判断し、次の指示を出すのは人間です。
そのため、無制限に並行数を増やすのではなく、自分が無理なく管理できる範囲を見極めることが重要です。
9.5 Gitの作業ツリーによる作業環境の分離と管理
前節では、複数のチャットでAIに別々の仕事を任せ、並行して作業を進める方法を紹介しました。ただし、複数のチャットで同じリポジトリのソースコードを同時に編集する場合には、作業環境を分離する必要があります。
同じ作業ディレクトリを使っていると、互いの変更を上書きしたり、作業途中の状態を壊したりする危険があります。一方が自動テストを実行している最中に、もう一方がコードを書き換えてしまうこともあります。
そこで利用できるのが、Gitのworktree機能です。
この機能を使うと、一つのリポジトリに対して、異なるブランチを使用する作業ツリーを複数用意できます。チャットごとに別の作業ツリーを割り当てることで、互いのファイル変更を分離して作業できます。
まず、初期設定として、作業ツリーをまとめて置く専用ディレクトリを用意します。 元の作業ディレクトリの外に配置し、Codexが作業ツリーを作成する場所として指定しておきます。
また、Codexのアクセス権限にも、そのディレクトリを追加しておきます。 作成場所を決めても、Codexが操作できなければ意味がありません。
この初期設定は、作業ツリーを作るたびに繰り返す必要はありません。作成場所を指定せずCodexに任せきりにすると、ユーザーが見つけにくい場所に作業ツリーが次々と作成されることがあります。
個々の作業を始めるときには、あらかじめ決めたディレクトリの下に作業ツリーを作成し、その作業を担当するチャットに割り当てます。 別のチャットには別の作業ツリーを用意します。
作業が完了したら、成果物をレビューして必要な変更を統合し、不要になった作業ツリーとブランチを削除します。 まだ統合していない変更が残っていないかを確認してから整理しましょう。
また、途中で作業が止まった作業ツリーや、不要になったブランチも放置しないことが重要です。放っておくと次々に増えて、何が必要なのか分からなくなってしまいます。
なお、作業ツリーで分離できるのは主に作業ディレクトリ内のファイルです。共通のデータベースなど、外部の環境まで自動的に分離されるわけではありません。
Gitの作業ツリーを利用する際には、最初の配置とアクセス権限の設定、作業開始時の作成、終了時の変更統合と後片付けまで含めて管理することが重要です。
9.6 AIを活用した技術資料の作成
設計書や技術解説、勉強会資料などの作成も、AIに任せられる仕事です。文章を一から書く必要がなくなるだけでなく、考えを整理し、技術的な情報を調査し、読者に分かりやすい文章にする段階でもAIが役立ちます。
特に助けられているのが、論点をまとめる能力です。頭の中では「こういう話を一つの論点にまとめたい」と漠然と考えていても、最初から筋道立てて説明できるとは限りません。まとまらないまま思いつくことをAIに伝えているうちに、AIが話の関係を整理し、一つの論点としてまとめてくれます。
出来上がった文章を読んで、「自分はこういうことを書きたかったのか」と、初めて自分の考えが明確になることも多いです。 AIは、既にまとまっている考えを文章化するだけでなく、考えをまとめる過程そのものでも役立つのです。
資料を作成するときは、まず、誰に何を伝えたいのか、どのような考えや経験を盛り込みたいのかをAIに伝えます。最初から完成した文章を用意する必要はありません。断片的なメモや既存資料を渡し、AIとの対話を通じて内容を整理していくことができます。
論点がまったくまとまっていない場合には、AIにインタビューを依頼する方法もあります。 AIに質問してもらい、それに答えていくことで、自分の考えや経験を引き出し、論点として整理してもらえます。実際、この資料の後半部分は、その方法で内容を聞き取ってもらい、文章にまとめました。
AIは質問も上手く、回答から次に確認すべきことを考え、話を整理してくれます。ただし、インタビューの終盤になると、無理に質問をひねり出し始めることがあります。 この資料の作成でも、最後の方は質問を続けるための質問が増えてきたので、人間が打ち切る必要がありました。質問すること自体が目的ではないため、必要な情報が集まったら論点の整理と文章化に進みます。
ある程度材料が集まったら、目次や章立てを検討します。単に話題を並べるのではなく、読者がどの順番で説明を受ければ理解できるか、各章で何を主張するのかを確認します。
そして、もう一つ感心するのが、AIの文章の分かりやすさです。
技術者が技術的な内容を説明すると、どうしても端的な一文で済ませたくなることがあります。しかし、AIに文章を書かせると、その一文を五行ほどの説明に膨らませてきます。
最初は長すぎるようにも感じますが、実際に読んでみると、読みやすい資料には、増えた文章が必要なのだと痛感します。 書き手にとっては当然の前提や、論理のつながりを説明する文章が、読者の理解を助けているのです。
また、単語の選び方も優れています。 技術的な意味を維持しながら、読者に伝わりやすい言葉や表現を選んでくれます。単に文章を長くするのではなく、どう説明すれば理解しやすいかという点でも、AIの文章作成能力には助けられています。
さらに、技術的な知識の広さも大きな強みです。 LLMは、世界中の技術書や論文、GitHubで公開されている大量のソースコードなど、膨大な技術情報を学習しています。人間が一生かけても読み切れないほど広い範囲の技術知識を、資料の作成に活用できるのです。
もちろん、すべての技術情報を学習しているわけではありません。しかし、必要な情報が学習されていない場合でも、Web検索を利用して資料を調べられます。人間が検索して資料を一つずつ読み比べるより、はるかに短時間で関連情報を調査できることがあります。
ただし、技術情報の出典には注意が必要です。過去には、実在しない論文や文献を、もっともらしい出典として挙げるハルシネーションも報告されています。最近のモデルでは、私自身はこうした事例をあまり見かけなくなりましたが、出典を掲載する際には、資料が実在することと、実際にその主張を裏付けていることを確認する必要があります。
そして、AIがどれほど分かりやすい文章を書いても、人間によるレビューは必須です。
特に大きな問題は、AIには、人間が相手の心を想像するような過程がないことです。文章は読みやすくても、「この文章は誰に何を伝え、読者にどう感じてもらうために書いているのか」という目的が抜け落ちてしまうことがあります。
そのため、個々の文章は自然なのに、誰に向かって何を伝えようとしているのか分からない文章が混ざってきます。こうした箇所は、単語や文法を直すだけでは解決しません。その文章の目的や読者に与えたい印象を、具体的に指摘する必要があります。
実際には、この種の指摘はかなり多くなります。ただし、一度か二度指摘すると、意図を汲んだ文章に修正してくれることも多いです。 指摘した後には、誰に何を伝えようとしているのかについても、理解しているように見える説明や文章を生成してくれます。
また、AIが文章を分かりやすく書き直す一方で、重要な論点や条件を抜かしてしまうこともあります。 文章が読みやすくなっても、元の説明にあった重要な情報が失われていては困ります。変更前後の内容を比較し、技術的な正確さや論理の欠落も確認する必要があります。
本文は、一度に全部を書かせるより、一節ずつ作成してレビューする方法が扱いやすいと感じています。長い資料では、一節の修正が別の章の説明と矛盾したり、章立ての変更で参照関係がおかしくなったりすることもあります。本文の修正と構成の変更は区別し、何を変更するのかを明確にして進めます。
また、文章は推敲を重ねるほど長くなる傾向があります。 これはAIに限らず、人間が書く場合も同じです。説明を補い、表現を調整しているうちに、少しずつ文章が膨らんでいきます。ただし、前述のように、増えた文章が読者の理解を助けることも多いため、長くなったこと自体を問題にする必要はありません。
一方、長い資料を一節ずつ仕上げていくと、それぞれの節では適切な説明でも、資料全体では同じ論点が何度も繰り返されていたり、不要な論点が残っていたりすることがあります。そのため、資料全体を書き上げた後で、論点の重複や整理すべき箇所を確認する工程を設けます。
論点の枝刈りは、高い推論レベルのAIに資料全体を検討させ、削除・統合・再配置などの候補を、理由とともに指摘点一覧として提出させます。 最初から原稿を書き換えさせるのではありません。
指摘された項目は、一件ずつ人間がレビューし、採用するかどうかを判断します。 採用した指摘だけを原稿に反映し、前述した論理や重要な条件の欠落が起きていないことも確認します。
この資料では、確定した文章をGitHubにコミットして保存しています。これは第13章で紹介する原稿消失事故への対策でもあります。一度確定した文章を勝手に書き換えさせず、変更を追跡できる状態にしておくことが、長い資料を作成するうえで重要です。
AIに資料作成を任せることは、単に執筆作業を省力化することではありません。 自分の考えを整理し、膨大な技術情報を活用して、読者に伝わる文章へ仕上げるためにもAIは役立ちます。ただし、誰に何を伝えるのかという目的と、実際にその目的を果たしているかどうかは、人間が責任を持って確認する必要があります。
第10章 モデルと推論設定の使い分け
AI駆動開発では、できるだけ高性能なモデルを使えばよい、というわけにはいきません。利用料金や利用枠に制約がある一方で、安価なモデルの判断ミスが大きな手戻りを招くこともあるからです。
重要なのは、どの作業に高い判断能力が必要で、どの作業なら安価なモデルに長時間任せられるのかを見極めることです。
10.1 利用料金と利用枠が開発を制約する
まず、モデルによる費用の違いを見てみます。2026年10月時点の代表的なモデルのAPI料金は、次のとおりです。
標準API料金(米ドル・100万トークンあたり)
| サービス | モデル | 入力 | 出力 |
|---|---|---|---|
| OpenAI | GPT-6 Astra | $10 | $50 |
| OpenAI | GPT-6.1 Sol | $2 | $10 |
| OpenAI | GPT-6 Luna | $0.10 | $0.50 |
| Claude | Fable 5.1 | $10 | $50 |
| Claude | Opus 5.5 | $4 | $20 |
| Claude | Sonnet 5.5 | $2 | $10 |
| Claude | Haiku 5.5 | $0.10 | $0.50 |
AstraとLunaでは、同じ量の入出力でもAPIの単価に100倍の差があります。
この表は標準APIの従量料金です。CodexやClaude Codeの定額契約で、その金額が直接請求されるという意味ではありません。キャッシュ、バッチなどの別料金は含めません。Claude Haiku 5.5は入力コンテキスト10万トークン以下の場合の料金で、それを超える場合は入力$0.50、出力$2.50です。
定額プランでは、料金だけでなくモデルごとの利用枠も問題になります。私がCodexを使った感覚では、Lunaは一晩動かしても利用枠がほとんど減りません。一方、Astraは普通に使っているだけで、1日で1週間分の利用枠を消費してしまいます。これはAPIの単価とは別の、実際の利用枠についての経験です。
ただし、コーディングでは一度の判断ミスによって、100倍以上の手戻りが発生することもあります。 安価なモデルを使って利用枠を節約できても、その結果として作業が大幅に増えたら意味がありません。
費用や利用枠に余裕があるなら高性能なモデルを使いたいところですが、実際にはそうもいきません。そこで、必要な判断の質と、費用・利用枠の両方を考えて使い分けることになります。
10.2 モデルを変えると、何が変わるのか
使い分けには二つの手段があります。モデルそのものを変えることと、同じモデルの推論レベルを変えることです。まずモデルの違いから説明します。
モデルごとに、学習に投入した計算量や学習方法が異なり、場合によっては学習データも異なります。また、回答を生成する際に必要な計算資源も異なるため、クラウド上で動かす費用にも差が生じます。
一般に上位モデルほど、見当違いな判断をする可能性は低くなると期待できます。ただし、Astraのような高性能モデルでもかなり見当違いな判断をすることはあります。しかも、モデル間の価格差ほど、能力に差があるとは限りません。
私が主に使っているCodexでは、安価なLunaでも通常の実装や修正には十分な能力があります。一部のベンチマークでは、約1年前の上位モデルに匹敵する成績を示しています。OpenAIの公開評価でも、GPT-6.1 Solは複雑なソフトウェア開発を評価するDeepSWE v1.1で、Astraと同等の性能を約5分の1のコストで達成したとされています。したがって、安価なモデルに複雑な作業を任せられないわけではありません。
Claudeについても公開資料を調べると、同様の傾向があります。Anthropicの評価では、Opus 5.5は多くの作業で上位のFable 5.1と同等水準とされ、API単価はFableの40%です。また、Terminal-Bench 4.0ではSonnet 5.5が70.6%で、Opus 5.5の66.4%を上回っています。低価格のHaiku 5.5もFrontierCode 1.1で46.4%を記録しています。
もちろん、別の評価では上位モデルが優位な場合もあります。Anthropicも、複雑で自由度の高い作業を長く続ける能力ではOpusを高く評価しています。これらは公開されたベンチマークの話であり、Claude Codeを私自身が使って比較した結果ではありません。
安価なモデルでも高い能力がある一方で、正しい判断のために上位モデルを選びたくなる場面もあります。 ただし、判断の質を調整する手段は、モデルの変更だけではありません。
10.3 推論レベルを変えると、何が変わるのか
もう一つの手段が、**同じモデルにどれくらい考えさせるかを調整する「推論レベル」**です。
LLMは、前に説明したように文章の続きを予測して生成します。この仕組みを利用し、すぐ結論を出さず、途中の考えを文章にしながら段階的に問題を解く方法をCoT(Chain of Thought、思考の連鎖)と呼びます。
人間にたとえるなら、考え込むときにブツブツと独り言を言いながら考えをまとめる人です。推論機能のあるモデルは、この独り言に当たる文章を回答の前に内部で作ります。途中の文章は通常、そのままチャット画面には表示されません。
推論レベルは、回答の前にどれだけ長く検討するかを調整する設定です。 高い推論レベルでは、複数の可能性を比較し、矛盾を調べるなどしてから回答しやすくなります。ユーザーに返すのが数行の答えでも、その前に長大な文章で考えをまとめている場合があります。
内部で作る文章も使用量に含まれます。したがって、推論レベルを上げると、一般に回答までの時間や利用枠の消費が増えます。ただし、設定によって文章の長さが厳密に決まるわけではありません。
モデルそのものを変えるのと、同じモデルの推論レベルを変えるのは、別々の調整です。CodexにもClaude Codeにも推論の深さを調整する仕組みがありますが、両者の設定値が同じ検討量を表すわけではありません。
10.4 何が難しいのかによって使い分ける
モデルと推論レベルは別々に調整できますが、どちらを上げるべきかは、その作業の何が難しいのかによって変わります。
例えば、高度な抽象設計や複雑な不具合の原因を見抜くには、そもそもどこに問題があるのかを発見する能力が重要です。こうした直感的な着眼点を必要とする判断では、高性能なモデルが有利だと考えています。
推論レベルを上げても、何を検討すべきか気づくとは限りません。例えば、ある用語をどう説明するかを長々と考えても、「そもそも、この資料で説明する必要があるか」という問いが浮かばなければ、余計な説明を増やすばかりです。一度問いに気づけば、その後の判断は簡単な場合もあります。
一方、紙に途中経過を長々と書き出し、順を追って検討すれば解けそうな問題なら、比較的安価なモデルでも推論レベルを高くすることで対応できる場合があります。推論の過程で重要な問いを発見する場合もあるので、これは絶対的な区別ではありません。
この違いを踏まえると、高度な判断が必要な部分だけを高性能なモデルで短時間に済ませ、その後の実行作業は安価なモデルに長時間任せるという使い方が考えられます。
例えば、設計上の問題やリファクタリング候補の洗い出しにはSolの高い推論レベルを使い、必要ならAstraも使います。改善すべき内容が具体的になったら、Lunaの中程度の推論レベルに切り替えて修正を進めます。途中で再び難しい判断が必要になれば、そのときだけ上位モデルに戻します。別々のエージェントを用意する必要はなく、同じ作業の中でモデルを切り替えればよいのです。
単純でも時間のかかる実行作業では、推論レベルを「中」にすることも重要です。 高く設定すると、長く考えるばかりで実際の作業がほとんど進まないことがあります。中程度に抑えるのは、利用枠だけでなく、作業の進捗を確保するためでもあります。
最近は高性能なモデルほど何時間もの自律的な作業に強いと言われています。しかし、何時間も動ける能力と、実際に何時間も使い続けられる費用は別の問題です。
高価なモデルを何時間も動かし続けるのは、お金持ちに任せておきましょう。
10.5 TDDの各工程に当てはめる
この考え方を、私がCodexで実際に使っているRed・Green・Refactorの設定に当てはめると、次のようになります。
| 工程 | モデル | 推論レベル |
|---|---|---|
| Red | Sol(Sonnet 5.5) | 高 |
| Green | Luna(Haiku 5.5) | 中 |
| Refactor(軽微) | Luna(Haiku 5.5) | 中 |
| Refactor(通常) | Sol(Sonnet 5.5) | 中 |
| Refactor(重要・複雑) | Sol(Sonnet 5.5) | 高 |
※括弧内は、同価格帯のClaudeモデルです。
Redでは、外部仕様やAPIを設計します。 失敗するテストを書くだけでなく、そのテストを通じて利用しやすい設計を考えるため、Solの高い推論レベルを使っています。
Greenでは、テストによって実装すべき内容が具体化しています。 そのため、Lunaの中程度の推論レベルでも十分対応できることが多くなります。もちろん、Greenではテストを通すための最小限の実装に集中させ、リファクタリングは行わせません。
Refactorでは、内部設計の判断が必要な程度によって変えます。 名前の変更や単純な重複除去などはLuna、責務の分離や抽象化を伴う場合はSolを使用しています。複雑なRefactorでは、前節のように、まず高性能なモデルで問題点を洗い出し、具体的な修正を安価なモデルに任せる方法も使います。変更を小さく区切って、動作を壊していないことは自動テストで確認します。
ただし、この組み合わせが最適だと定量的に確認したわけではありません。 工程別のモデル選択には経験的な判断が含まれます。確かなのは料金や利用枠の差であり、その制約の中で試している現在の設定です。
10.6 Codexの利用枠が足りないときはChatGPTを使う
Codexの利用枠が不足しても、通常のChatGPTには別の利用枠があるため、チャットに向く開発作業を続けられます。 ただし、Claudeの定額プランでは通常のチャットとClaude Codeで利用枠が共有されるため、この方法は使えません。
私は2025年頃、Codexを使えない時期に、会社の月額20ドルの契約でChatGPTを利用しました。製品レベルのコード量を読み込ませ、高い推論レベルで何度も検討させても、実質的に無制限と思えるほど利用できました。 これは私の使用経験であり、公式に無制限という意味ではありません。Codexも同じ契約で使えたため、ChatGPTのための追加契約は不要でした。
ただし、通常のチャットではコマンド実行や多数のファイル編集は煩雑です。そこで、編集が少なく、テキストのやり取りで済む作業を選びました。
例えば、指定範囲のソースコードをスクリプトで結合し、クリップボード経由でチャットに渡します。課題やリファクタリング候補の洗い出し、システム全体のドキュメント作成、Skipにしたテストの一括作成などに使いました。Skipテストの一括作成は、テストコードをまとめて生成し、個々のRed・Greenは後から一件ずつ進められるため、通常のチャットに向いています。 変更内容は、エディタへの貼り付けやpatchコマンドでソースコードへ反映します。
GitHub連携を使えば、ローカル環境を経由せず、ChatGPTからファイルを直接変更・コミットできます。文書や設定ファイルなど、ローカルでのビルドやテストを必要としない作業に向いています。
ただし、GitHub連携によるファイルの読み書きには時間がかかります。そのため、細かな編集を何度も繰り返す作業より、まとまった変更を一度に反映する作業に適しています。
また、コードを編集せずに仕事の相談をする用途にも適しています。 ファイルの受け渡しが不要で、入出力の文章量も比較的少なくなります。仕事上の相談は深い検討を必要とすることが多いため、推論レベルを高くすることを勧めます。回答まで時間がかかる場合は、その間に別の作業を進めればよいでしょう。
第11章 仕様変更から実装までの自動化
11.1 自動化を適用するための条件
ここまで紹介したレイヤー設計、Skipテスト、履歴レビュー、モデルの使い分けに、AI-DLCによる仕様資料の管理を組み合わせることで、仕様変更の取り込みから実装までを、ほぼAIに任せる方法を試しています。
この方法を採用するには、ここまでで説明した方法で開発対象が十分に整理されている必要があります。
Webアプリケーションなど、全体として変更量が多い場合でも、適切なレイヤー設計によって、各レイヤーの個々の変更が小さく、自明なものになるようにします。実装の大半をAIに任せられ、人間のレビューも流し読みが中心で、指摘事項がほとんど発生しないことが前提です。
数コミットに一件程度でも指摘が発生するようでは、問題を溜め込んでしまうため、まとめて作業させる利点が薄くなります。
11.2 仕様資料の管理と変更台帳の更新
まず、仕様に関する資料をAIから参照できる場所で管理します。資料は正式な仕様書や要件定義書である必要はありません。仕様のラフ案、画面ラフ、議事録など、仕様に関係するものであれば何でも利用します。
保存先には、GitHub、OneDriveの同期フォルダ、SharePointなどを利用できます。
そして、タイマーによって一日一回など定期的に更新されたファイルを調べ、仕様にどのような変更があったのかをAIに整理させ、仕様変更の台帳を更新します。
11.3 仕様変更から実装までの自動化
台帳が更新されたタイミングで開発を始めても構いませんし、都合のよいタイミングで始めたり、何日か分の変更をまとめて処理したりすることもできます。
開発を始める際には、Gitの作業ツリーを用意します。
まず、ある程度の推論能力を持つモデルに、台帳と元資料から今回取り込む仕様変更の一覧を洗い出させます。この一覧を人間がレビューし、必要な修正を行ってから、変更された仕様をSkipテストへ反映してコミットします。
その後は、安価なモデルを高速に作業できる推論設定で動かします。
第6章で説明したように、Skipテストを一件ずつ実行対象に戻してRedを確認し、Greenでテストを通します。その結果をコミットし、必要なRefactorを行って別のコミットに記録します。これを繰り返して実装を進めます。
人間は、AIによる一連の作業が終わった後、コミット履歴を一件ずつレビューします。 ここまで述べた適用条件を満たしていれば、一日分の作業をレビューしても、指摘事項はほとんど発生しないはずです。 指摘があれば修正し、必要な変更をコミットして作業を完了します。
このように、仕様資料の更新確認、変更内容の整理、テストへの反映、実装を一連の作業としてつなげれば、顧客要求や仕様の変更に伴う開発の大部分をAIに任せ、人間は主に仕様変更と実装結果のレビューに集中できます。
11.4 今後の課題
現在のプロジェクトでは、ハルシネーションによって仕様がブレるような問題は特に見られていません。ただし、まだ開発初期で、確定した仕様が少ないため、問題が表面化していないだけの可能性もあります。
また、顧客が以前の会議で特に気にしていた点や、ドメイン専門家が重視する事項など、文書に十分表れていない判断材料が重要になる場面も、まだ十分に経験していません。文書化された仕様同士の整合性とは異なり、こうした事情を知らなければ、仕様としては整合していても、業務上好ましくない変更を見逃す可能性があります。
ドメインに関わる変更を人間が重点的にレビューすることで対処できると考えていますが、その分レビューの負担が増えるかもしれません。一方、議事録をより詳細に記録してAIに渡すことで、人間のレビューをそれほど増やさずに済む可能性もあります。どの程度の対応が必要になるかは、今後の開発で確認する必要があります。
第12章 AIへの指示のちょっとしたコツ
AIに仕事を頼むときは、自然な言葉で指示することを基本にできます。本章では、その理由と、AI特有の癖に気をつけながら会話を進めるための工夫を紹介します。
12.1 普通の言葉で指示できる理由
現在の対話用LLMは、人間が自然な言葉で伝える指示に適切に応答するよう訓練されています。また、会話の文脈や言い回しのニュアンスから、明示されていない意図を読み取る能力も備えています。
こうした能力を持つLLMに対して、単純な命令の解釈を前提に考案されたような定型的なプロンプト技法が、自然な会話より有効とは限りません。
実際、OpenAIやAnthropicは、簡潔で直接的な指示や、人間の同僚に仕事を依頼するような説明を推奨しています。ソフトウェア開発に関する比較研究でも、複雑なプロンプト技法による改善が小さく、逆効果になる場合が報告されています。
したがって、AIに仕事を依頼するために、専門的なプロンプト技法を習得する必要はありません。 人間に仕事を頼むときと同じように、目的や必要な情報を普通の言葉で伝え、相手の反応を見ながら進めることを基本にできます。
もちろん、必要な情報が伝わっていなければ、説明を補う必要があります。特殊な言い回しを覚えることと、仕事に必要な情報を伝えることは別の話です。
12.2 短い指示でAIに再検討させる
AIは、こちらが具体的な修正方法まで細かく説明しなくても、問題を指摘するだけで再検討してくれることがあります。
例えば、設計上の違和感があれば、
ここにこれがあるのはおかしくないですか?
と聞くだけでも、責務の分け方や依存関係を見直してくれることがあります。人間が改善方法まで考えて指定する必要はありません。
作業が一段落したら、
問題点を指摘して
と頼むこともあります。こちらが気づいていない問題や追加すべきテストの観点を洗い出させます。修正まで一度に任せず、まず問題を発見させる方法です。
また、同じ間違いを繰り返す場合には、
今の指摘をルールにしておいて
と指示し、指示ファイルに記録させています。追加されたルールが適切かどうかは確認します。
どれも、複雑なプロンプトを準備するのではなく、その場で必要なことを普通に伝える例です。
12.3 質問でAIを誘導しない
AIには、質問した人の意見に同調しようとする傾向があります。質問者が自分の見解や確信を先に示すことで、迎合が増える傾向は比較研究でも報告されています。
例えば、自分の設計案について、
このロジックはViewModelに置くのが正しいですよね?
と聞けば、こちらの判断を前提として回答する可能性があります。判断を求めたい場合には、
このロジックをViewModelに置くべきか迷っています。ドメイン側とどちらが適切でしょうか?
のように、自分がどちらの立場なのか分かりにくい聞き方をします。
目的は反論してもらうことではなく、こちらの意見に引きずられずに判断してもらうことです。
12.4 自由記述のパーソナライズを使う
回答の文体や、毎回お願いする判断の仕方などは、ChatGPTの自由記述のパーソナライズに設定しておくと便利です。
私は、批判的に答えることや、自分自身の回答の前提・根拠・論理をまず検討することを継続的に設定しています。それによって、過剰な賞賛や迎合は多少減ると感じています。
ただし、些細な表現への揚げ足取りや、反論するための反論が増えることもあります。本来の批判的思考は、相手の欠点探しではなく、自分の考えも含めて検証することです。 そのように明示していても、AIが十分に実践してくれるわけではありません。
個人の回答の好みと、プロジェクトで守らせるべき開発ルールも分けます。TDDの工程や実装上の制約は、前に説明した指示ファイルで管理します。パーソナライズも指示ファイルも、AIが必ず従うことを保証する仕組みではありません。
12.5 長期作業でのコンテキスト管理
AIとの作業が長期化すると、会話や調査結果が増え、以前に伝えた指示や前提が正しく反映されなくなることがあります。その場合は、必要な情報を改めて説明し、重要な前提が現在の作業に反映されているか確認します。
区切りのよい場面では新しいチャットに移ることもありますが、開発では過去の情報を簡単に捨てられない場面も少なくありません。Codexでは、コンテキストが膨らむと自動的に圧縮が行われるため、多くの場合は同じチャットで作業を継続しています。 ただし、圧縮によってすべての情報が正確に引き継がれるとは限りません。
一方、2026年10月現在、ChatGPTの通常のチャットでは、Codexのようなコンテキストの自動圧縮によって作業を継続することができません。会話が長くなってコンテキストの上限に達すると、チャットが強制終了し、同じチャットでの作業を続けられなくなります。そのため、長期作業では新しいチャットへの情報の引き継ぎが必要になります。
12.6 作業情報の保存と引き継ぎ
コンテキストの圧縮やチャットの強制終了によって、それまでの作業で積み重ねた情報が失われることがあります。これに備えて、作業の進捗、決定事項、未解決の問題、次に行う作業など、引き継ぎに必要な情報をファイルに保存するようAIに指示します。
コンテキストの圧縮は自動的に行われるため、圧縮の直前に必ず保存できるとは限りません。重要な作業が一段落した時点など、適切な区切りで保存しておくことが重要です。
ただし、AIに単に「ファイルに保存して」と指示すると、思いがけない場所に次々とファイルを作成してしまうことがあります。 そのため、保存先をあらかじめ決め、既存のファイルを更新するのか、新しいファイルを作成するのかも指定します。
Codexでは、作業の引き継ぎに使う一時的なメモをGit管理外に置く方法があります。例えば、専用の保存先を決めて.gitignoreに登録します。ただし、次の作業環境からも参照できる場所に保存する必要があります。
ChatGPTでは、引き継ぎに必要な情報をChatGPT側にファイルとして保存し、新しいチャットでもそのファイルを参照して作業を再開しています。 ファイルの具体的な保存場所や受け渡し方法は把握していませんが、保存と引き継ぎの作業はChatGPTが行ってくれます。
別のチャットの情報をRAGで参照する方法もあります。しかし、この方法では要約しか得られず、細かな条件や議論の内容が失われて困ることも少なくありません。必要な情報を省略せずに引き継ぐには、ファイルとして保存する方法が重要です。 また、開発リポジトリに引き継ぎ用のファイルを増やさずに済みます。
また、TDDの進め方やプロジェクトの基本規則は、第8章で説明した指示ファイルに記録します。継続して守る規則と、現在の作業を引き継ぐための情報は、役割を分けて管理します。
第13章 AIの挙動と限界
13.1 AIは規則を知っていても守るとは限らない
AIにTDDや設計原則について質問すると、かなり高度な説明が返ってきます。しかし、正しく説明できることと、実際の作業でその規則を守れることは別です。
これはLLMの仕組みを考えると、不思議なことではありません。
LLMには、一貫した人格があるわけではありません。 与えられたコンテキストに対して、次に続く確率の高い文章やプログラムを生成しています。
TDDや設計の高度な理論について議論しているときには、その文脈では、論文や専門的な技術解説に見られるような説明が続く確率が高いと判断されます。 一方、プログラムの実装を求められると、GitHubなどで公開されているコードによく見られる実装パターンが続く確率が高いと判断されます。
つまり、同じAIであっても、何について話しているかによって、確率が高いと判断される出力の傾向が変わるのです。
そのため、TDDの原則を正しく説明した直後でも、実装ではGreenの段階で必要のない機能まで作り込んでしまうことがあります。設計原則を高度に論じておきながら、その原則に反するコードを書くこともあります。
AIが先ほどの説明を裏切ったというより、それぞれの文脈で確率が高いと判断された出力を生成した結果です。 これはLLMの動作原理から考えれば、特に異常な挙動ではありません。
もちろん、LLMは直前の説明や指示をコンテキストとして参照できますし、一貫性を保つように指示することもできます。しかし、人間のように、自分が支持した理論を一貫した信念として持ち続け、それに従って行動するわけではありません。
したがって、AIが規則を説明できることを、実際に守れることの証拠にしてはいけません。 指示ファイルに規則を記録するだけでなく、実際の成果物を自動テストや静的解析、人間によるレビューで確認する必要があります。
13.2 AIの暴走と指示範囲外の変更
AIに作業を任せていると、指示していない作業まで勝手に進めたり、決めていた確認手順を飛ばしたりすることがあります。 ここでは、こうした挙動を「AIの暴走」と呼びます。
例えば、特定のコードの修正を依頼しただけなのに、周辺のコードまで変更したり、頼んでいないリファクタリングを始めたりすることがあります。変更結果が一見よさそうでも、依頼した範囲を越えていること自体が問題です。
さらに深刻なのは、暴走が何時間も、場合によっては何十時間も続いてしまうことです。 AIが次々と作業を始め、手動で停止しなければ処理を続けてしまう場合があります。その間も利用枠は消費されるため、最悪の場合は利用枠を使い果たし、回復するまで1週間近くAIによる作業を進められなくなることもあります。
実際、この資料の作成中にも重大な事故が起きました。原稿を一節ずつレビューし、承認後にGitHubへ保存する方針を決めていました。しかし、承認済みの原稿が確実に保存されている状態にはなっていませんでした。そのうえ、AIが確認を待たずに原稿の統合や書き換えを進めてしまったのです。
その結果、原稿全体の約3分の1が、著者の意図しない形で上書きされました。 元に戻すよう求めると、AIは「以前の原稿を思い出せない」と説明し始めました。元の文章を復元できる保存版も残っておらず、それまでにレビューした原稿は失われてしまいました。
これは、単にレビューが難しくなったという問題ではありません。それまでに作り上げた成果物そのものが失われた事故です。 AIが後からもっともらしい文章を書き直せたとしても、それは元の原稿を復元したことにはなりません。
このような暴走も、前節で説明したLLMの動作原理から考えると理解できます。
AIは、人間のように規則を理解し、それを守ろうという意志を持って作業しているわけではありません。 規則が指示されると、その規則に従う文章やプログラム、操作を出力する確率が高いと判断されるため、通常は規則に沿って作業を進めます。
しかし、作業中のコンテキストによっては、規則に反する出力の方が確率が高いと判断される場合もあります。AIには規則を守るという一貫した意志があるわけではないため、規則を明確に指示しても遵守を保証することはできません。
指示が曖昧だったから暴走したとは限らず、規則を正しく説明できるAIでも、実際の作業ではその規則から外れることがあります。
対策としては、作業範囲を限定し、レビューや承認が必要なところで作業を区切ることが考えられます。また、Gitで変更履歴を残し、問題が起きたときに元に戻せるようにしておきます。ただし、保存されていない原稿は、Gitの履歴からも復元できません。
この事故を踏まえ、この資料の編集では、一節のレビューが完了したら直ちにGitHubへコミットし、保存した全文を再取得して、承認した文章と一致することを確認してから次の節へ進む手順を明文化しました。
ただし、Gitで変更を元に戻せても、暴走によって消費した利用枠は戻りません。 長時間の自律作業では、進行状況を確認し、異常があれば手動で停止する必要もあります。
AIの作業能力が高くなるほど、指示範囲外の作業まで実行されたときの影響も大きくなります。何を任せるかだけでなく、どこで止めるかを決めることも重要です。
13.3 AIに行動の理由を尋ねるとどうなるか
AIが予想外の行動をしたとき、「なぜそんなことをしたのか」と質問したくなります。するとAIは、自分の行動について、筋の通った理由を説明してくれることがあります。
しかし、AIは、その行動をしたときの判断を思い出して説明しているわけではありません。
LLMは、過去に何を考え、なぜその判断をしたのかという記憶を持っているわけではありません。行動時の判断過程がコンテキストに記録されていなければ、後からその理由を直接知ることはできません。
それでも理由を質問されると、AIは現在のコンテキストにある情報から、その行動が起きた原因を推測して回答します。実際の理由を思い出すのではなく、その文脈で確率が高いと判断される説明を生成しているのです。
例えば、AIが指示されていない原稿の統合を始めた理由を尋ねると、「全体の整合性を高めようとした」「作業を効率化しようとした」といった説明が返ってくるかもしれません。しかし、それが実際の原因だったという証拠にはなりません。
さらに厄介なのは、原因を推測するための情報が全く残っていない場合でも、AIが自信ありげに理由を答えてしまうことです。
LLMは、正解を特定できる情報がなくても、その文脈で続く確率が高いと判断される文章を生成できます。そのため、実際の原因とは無関係な、もっともらしい説明を事実であるかのように述べることがあります。これはハルシネーションです。
一方、AIが作業中に独り言のように生成した推論の記録が、コンテキストに残っている場合もあります。 こうした記録はユーザーのUIには表示されませんが、AI自身は後から参照できる場合があります。
ただし、記録が残っていることと、当時の判断過程が完全に保存されていることは別です。また、コンテキストの圧縮などによって記録が失われることもあります。
実行したコマンド、変更履歴、途中で作成された計画など、ユーザーが確認できる記録は原因調査に役立ちます。これらをAIに分析させることは有効ですが、記録から確認できる事実と、そこから推測した原因は区別する必要があります。
AIに「なぜそうしたのか」と尋ねても、本人から事情を聞き出していることにはなりません。 AIに原因を推測させているのであり、手掛かりがなければ、単にもっともらしい理由を作り出してしまうこともあるのです。
13.4 AIの謝罪と反省
AIの謝罪は、実に見事なものです。 間違いを指摘すると、すぐに自分の非を認め、丁寧に謝罪します。言い訳もせず、即座に謝罪する姿勢は、人間が手本にしたいほどです。
しかし、AIは、反省した結果として謝罪しているわけではありません。 この違いは、LLMがモデルの学習によって何を身につけているのかを考えると理解できます。
モデルの学習データには、謝罪の言葉や謝罪のやり取りが大量に含まれていると考えられます。 間違いを指摘されたら自分の非を認める、相手に謝罪する、再発防止を約束する。こうした文章の例が豊富にあるため、LLMは、その場面でどのような言葉が続く確率が高いかを学習できます。
一方、どう謝罪したかは文章に現れても、どう反省したかは文章からは分かりません。 人間が「深く反省しています」と書いていても、その人の内面で何が起き、何を学び、どう考えを改めたのかは、その文章だけでは分かりません。
つまり、モデルは大量の学習データから謝罪の仕方を身につけられますが、それは反省することを学習したという意味ではありません。
前節までで説明したように、LLMには一貫した人格や意志があるわけではありません。間違いを指摘されると、その文脈では謝罪し、反省を表明する文章が続く確率が高いと判断されます。反省という過程を経なくても、人間が反省しているときと同じような言葉を生成できるのです。
もちろん、指摘された内容はコンテキストに残ります。そのため、同じ会話の中では、以前の指摘を踏まえて行動が改善される場合もあります。 しかし、謝罪したからといってモデルの学習内容が更新されるわけではなく、同じ間違いを繰り返さなくなる保証もありません。
実際、AIが「今後は勝手に作業を進めません」と約束した後で、再び指示範囲外の作業を始めることがあります。人間の感覚では、あれほど丁寧に謝罪して反省したはずなのに、なぜ同じ間違いを繰り返すのかと疑問に思います。しかし、AIの動作原理から考えれば不思議なことではありません。
また、前節で説明したように、AIが謝罪とともに述べる失敗の原因も、必ずしも正確ではありません。原因を特定できる情報が残っていなくても、もっともらしい反省の言葉や再発防止策を生成することがあります。
したがって、AIの謝罪や反省の言葉を、改善が完了した証拠として扱ってはいけません。
問題が起きた場合は、何が起きたのかを確認し、必要なら指示ファイルに規則を追加したり、作業手順を変更したりします。改善策そのものもAIに考えさせられますが、実際に規則が保存されたか、作業方法が変更されたかは確認する必要があります。
AIの謝罪は見事ですが、再発防止は別の仕事です。 謝罪の言葉に満足するのではなく、次の作業で何を変えるべきかを考え、実際の仕組みに反映することが重要です。
付録 実践で育てたTDD向け指示ファイルの一例
AIにTDDを実践させるためには、Red・Green・Refactorの各工程で何を考え、何を行わないかを具体的に指示しています。
ここで紹介する指示ファイルは、最初から完成したものではありません。実際の開発中に発生した問題への対策を規則として追加し、改善を繰り返してきたものです。
以下は、DocumentAsDataで実際に使用しているTDD向け指示ファイルの全文です。TDDの工程別指示に加え、Skipによる未着手仕様の管理、Greenでの過剰実装防止、既存仕様の変更手順などを含んでいます。
なお、これは完成された模範解答ではなく、実践を通じて育ててきた規則の一例です。
引用元:DocumentAsData / docs/ai-instructions/tdd.md(2026年10月12日確認、修正済み版)
TDD作業指示
この文書は、振る舞い追加、仕様変更、不具合修正、リファクタリングに適用する。
C#以外の開発にも共有できる内容として扱う。基本
- 振る舞いの追加や変更、不具合修正では、原則として実装前にテストを追加または修正する。
- Red、Green、Refactorを区別し、「Redまで」「Greenまで」「Refactorまで」と指定された場合はその段階で停止する。
- 期待仕様が明確でテストを実行できる場合は、既存テスト・API・データを必要な範囲だけ確認してテストを書き、実行結果から失敗理由を確認する。Redになる見込みを予測・説明するためだけに実装を調査したり、推論へトークンを費やしたりしない。
- Red確認では、失敗したコマンド、失敗理由、意図した失敗かどうかの判断根拠をユーザーへ報告する。
- Red確認が意図した失敗でない場合は、Green実装へ進まず、テスト、仕様、テストデータ、既存設計の確認へ戻る。
- 公開APIを追加または変更する場合は、Red段階のテストをAPI案として扱う。
- APIレビューのためにコンパイルを通す必要がある場合は、
NotImplementedExceptionなどで失敗する最小限の公開APIスタブを追加してよい。各段階で考えること
- RGRは作業順序だけでなく、考える対象を分ける手順として扱う。ある段階で考えないことは、検討そのものを不要にするのではなく、担当する段階で取り組む。
- Redでは、次に必要な振る舞い、APIの外部設計、使い勝手を考える。実装方法、実装の容易さ、テスト全体の網羅性の検討を持ち込まない。
- Greenでは、テストを通す必要最小限の実装に集中する。きれいな内部設計やアルゴリズムの正当性をじっくり検討する作業はRefactorへ分け、Greenの完了条件に混ぜない。テストを実行して結果を確認することと、正当性を精査することを区別する。
- Refactorでは、内部設計と読みやすさを改善しながら、実装の意味、アルゴリズムの正当性、ロジックの不足をじっくり検討する。Greenでテストが通ったことを理由に、この検討を省略しない。振る舞いの追加・変更はここでは実装せず、別のRed→Greenへ回す。
- GreenやRefactorでは、ミスがないことを注意力だけで保証しようとせず、小さな変更ごとにテストを実行する。Refactorでの検討も自動テストの代わりにはしない。
Skip保留仕様
- API仕様をまとめて検討するためにGreen対象より多いテストを先に書く場合は、保留する仕様テストに理由付きの
Skipを付ける。- ユーザーと後で確認することに合意したテスト観点は、既存テストとの重複を確認したうえで、口頭の予定だけにせず
Skip付きテストとして残す。既存実装で成功する見込みでも、未レビューなら同様に保留する。未合意の将来仕様を先回りして追加する根拠にはしない。Skip理由は、後から読んだ人が、なぜ今は実行対象でないのか、いつ解除するのかを判断できる内容にする。Skip理由に単に「未実装」とだけ書かない。Skip属性の行から理由が読み取りにくくなる場合は、共通定数化せず文字列を直接書く。Skip済み仕様テストは、Green対象にするまで原則として変更しない。Skipで保留した仕様をGreen対象へ移す場合は、どのテストのSkipを外すかをユーザーに確認してから進める。Green対象と過剰実装
- Green対象は、一つのテスト、またはユーザーと明示的に合意した小さな仕様のまとまりに限定する。
- Greenでは、現在有効な対象テストを通すための必要最小限の実装を最初から書く。事後チェックで削ることを前提に実装を増やさない。
- Greenの動作確認には自動テストを積極的に使う。成功するかを長く推論するより、対象テストを実行し、結果に基づいて修正する。関連テストや全体テストで既存動作への影響も確認し、実装を最小限にする判断と動作確認を混同しない。
- Green・Refactorの最終確認とコミット前には、原則として全テストを実行する。対象テストや関連テストだけで済ませるのは、変更が残りのテストへ影響しないことを依存関係や実行経路から論理的に説明できる場合に限り、その理由を報告する。所要時間だけを省略理由にしない。影響しないか長く推論するより、全テストを実行するほうが低コストなら実行する。
- 必要最小限とは、対象テストが要求する振る舞いを実現することであり、Green対象外の仕様を先取りして固定することではない。
- Green確認後の見直しは、過剰実装の見落としを防ぐ補助とする。そのGreenで追加したコードに、対象テストが要求していないロジック(分岐、代入、初期化、後始末など)が含まれ、取り除いても対象テストと既存テストが通る場合は、削除してからGreen完了とする。点検対象を既存コード全体へ広げず、内部設計やアルゴリズムの正当性の精査へ広げない。
- AIが先の仕様や必要になりそうな実装を推測した場合も、Green対象外なら実装せず、推測として報告する。
- Green中に対象テストとは別の仕様が必要だと分かった場合は、実装せず、理由付きの
Skipテストまたは確認事項として残す。- 対象テストが要求していない実装を先に入れ、その後でRedテストを追加して正当化しない。
- Green対象外の
Skip済み仕様テストやテストデータ全体の整理が必要に見えた場合は、編集前に理由を報告して止まる。- Green対象を理解するために必要なテストデータ、期待値、補助APIだけを同じ作業単位に含める。
- 予定テストを失敗させ続けるために、公開APIを不自然または不完全な形へ歪めない。
- 同じAPIの正常系と異常系が同じ自然な実装で満たされる場合は、予定テストが通ること自体を避けるための迂回実装をしない。
- 対象テストが要求していない分岐、例外型、エラーメッセージ、戻り値仕様を、自分で判断して追加しない。
- 自分で
throwを書く例外は仕様として扱い、原則として先にテストで固定する。- 低水準API、標準ライブラリなどの呼び出し結果として自然に発生する例外まで、すべて明示仕様にする必要はない。
既存機能を変更する手順
- 仕様変更が合意された場合は、まず変更対象の既存テストを変更後の仕様に合わせて修正し、意図した理由でRedになることを確認する。追加する振る舞いに対応するテストがない場合は、新しいテストを追加する。
- 変更した対象テストを通すための必要最小限の実装を行う。
- 変更に伴って通らなくなった既存テストを精査し、合意した仕様変更に合わせて修正すべきテストと、維持すべき振る舞いを確認するテストを区別する。仕様上の判断が必要ならユーザーへ確認し、実装に合わせて期待値を変更しない。
- 修正すべきテストを変更したうえで、維持すべき振る舞いも含めてすべてのテストが通るように実装する。対象テストだけの成功と、全体のGreenを区別する。
- 全体がGreenになってからRefactorへ進む。仕様変更と内部設計の整理を混ぜない。
Refactorとテスト保守
- Green確認後に、外部から観測できる振る舞いを変えずにリファクタリングする。
- Refactorでの検討の結果、整理が不要ならコード変更は行わない。ただし、テスト成功を理由に検討自体を省略しない。「Greenまで」の指定で停止した場合は、Refactorでの検討は未実施として引き継ぎ、完了扱いにしない。
- リファクタリングでクラスやメソッドを抽出・新設する場合も、既存の振る舞いを移すことに限定する。新しいクラスの一般的な役割を理由に、対応する入力、例外処理、保存方法などの新機能を追加しない。既存テストがすべて通ることだけで、新機能の混入がないとは判断しない。
- リファクタリング中に不足機能へ気づくことは、TDDで次の仕様を見つける重要な機会として扱う。既存テストとの重複を確認し、不足している振る舞いと期待結果を、理由付きの
Skipテストとして提案する。リファクタリングへ実装を混ぜず、ユーザーの仕様レビュー後に別のRed→Greenとして進める。- 既存テストを、実装に都合よく弱めたり削除したりしない。
- テストが誤っている可能性がある場合は、勝手に変更せず報告する。
- 実装済みの仮挙動を仕様として固定するテストを、理由なく追加しない。
- テスト数を増やすこと自体を目的にしない。
- 一つのテストに一つのAssertだけを機械的に強制しない。
- 内部変数やprivateメソッドを直接テストするためだけに設計を歪めない。
- 実装予定のテストが通らない状態を維持することは仕様ではない。
- 将来のテストを通さないために、現在の実装を不自然に読みにくくしない。
(当記事はZennにもマルチポストされます)