自宅のWindows PC(RTX 3060 12GB)上で、OllamaやBGE-M3、ChromaDB、FastAPIを組み合わせた完全ローカルの自律AIエージェント基盤を作ろうと思い立ち、Gemini Sparkとの壁打ちを交えながら開発を進めていた。
AIとの協働開発はとにかく速い。こちらが「こういう機能が欲しい」と大まかな構想を投げれば、数往復のやり取りだけでアーキテクチャの提案からサーバー実装、フロントエンドのコードまで一気に仕立て上げてくれる。最初のうちは「これはとんでもない時代になった」と興奮していた。
だが、機能が増え、システムが形を帯びてくるにつれて、背筋に冷たいものが走るような強烈な違和感に襲われることになったのだ。
「いま、自分の画面の前で動いているこのシステムは、本当に正しく動いているのか?」――それが、作った本人であるはずの自分にすら、まったく分からなくなっていたのだ。
1. ログをAIに投げて初めて「正常動作」を知る情けなさ
何が起きているのかが分からない。画面上でチャットを送信し、バックエンドのコンソールにずらりと流れるログを眺めても、どのモジュールがどういう順序でデータを渡し、どの判定を通って出力に至ったのか、頭の中に「見取り図」が存在しないのだ。
動いているのか止まっているのかの判断すらつかず、困り果ててターミナルのログをそのままGemini Sparkに貼り付け、「これって動いているの?止まっているの?」と問いかける始末だった。
AIから「出力されているログは正常です。現在の仕様では、この判定を通ってバックグラウンドでインデックス処理が走っています」と冷静に解説されて初めて、「あ、これで合っているんだ……」と知る。作ったはずの開発者が、自分のプログラムの仕様と現在地をAIに教えてもらうという、本末転倒な事態に陥っていたのだ。
2. 「丸投げ」がもたらすブラックボックス化の恐怖
なぜこんなことになったのか。原因は極めて単純だった。AIとの会話の往復回数を減らし、初速を優先するあまり、設計の意思決定も、内部のデータフローの把握も、すべてAIに「丸投げ」してしまっていたからだ。
単機能の使い捨てスクリプトなら、それでも何とかなる。だが、FastAPIのイベントループ、GPUメモリのキャッシュ解放、ベクトルの差分同期、プロンプトの自律ループといった複数の要素が絡み合う中規模以上のシステムになると話は一変する。
人間が内部構造を理解していないコードは、動いている間は魔法のように見えても、ひとたび不具合や改善点が見つかった瞬間にただの「ブラックボックス」へと変貌する。
どこに手を入れていいか分からないから、不具合の修正もAIに丸投げするしかない。AIはその場しのぎのパッチを当ててコードを書き換えるが、人間が全体整合性をチェックできないため、別の場所が静かに壊れる。規模が大きくなればなるほど、この悪循環は加速し、最終的には「誰も全体像を把握していない、触るのが怖いコード」ができあがる。このまま突き進めば確実に取り返しがつかなくなると、強い危機感を覚えた。
3. 開発開始時の基本指針を決める:モジュール単位のミニ・ウォーターフォール
「人間が気づくべき観点は山ほどある。設計の理解を放棄してAIに丸投げすることは、絶対に避けなければならない」
そう痛感した私は、今後の開発プロセスを根本から見直すことにした。最初に頭をよぎったのは「古典的なウォーターフォール開発に戻すべきではないか」という案だった。要件定義から基本設計、詳細設計、実装と順番に進めれば、人間が理解を置いてきぼりにされることはない。
だが、ローカルLLMや新しいライブラリを扱う開発は不確実性の塊だ。動かしてみて初めてVRAMの消費量や推論速度の限界が分かることも多い。システム全体を一括でガチガチに設計するウォーターフォールでは、途中で前提が崩れた際の手戻りが重すぎて個人開発が破綻してしまう。
そこで行き着いたのが、システム全体を一括にするのではなく、**「機能・モジュール単位でミニ・ウォーターフォールを回す」**という協働指針だった。
-
全体像とモジュール分割の合意
システムの目的と技術スタックを整理し、疎結合なモジュール(例: ベクトル管理部、APIサーバー部、UI部)に責務を切り分ける。 -
未知技術の解説・知識共有(AI → 人間)
そのモジュールで扱う未知のライブラリや技術概念を、まずAIに平易に解説させる。自分が納得して腹落ちするまで質問を重ねる。 -
設計・データフローの壁打ち合意(人間 ⇄ AI)
入出力パラメータ、例外処理、データの流れを壁打ちし、人間の頭の中に明確な「見取り図」を構築・合意する。 -
実装・単体テスト作成(AI担当)
合意した仕様をインプットとして渡し、コーディングとテスト作成はAIに一任する。ここは完全に任せて工数を削る。 -
早期検証・動作確認(人間担当)
手元ですぐに動かし、挙動やリソース消費を確認する。モジュール単位だからこそ、何が起きていてどこに問題があるかが手に取るように分かる。
あわせて、トラブルシューティング時にも鉄則を設けた。エラーが出た際、いきなりAIに修正コードを書かせるのを禁止したのだ。まずは「考えられる原因」と「対応の選択肢(トレードオフ)」を提示させ、人間が方針を納得・選択してから初めてコードを修正させる。このワンクッションを挟むだけで、設計の主導権を人間側に引き戻すことができる。
4. 丸投げするかしないか、その境界線はどこにあるのか?
では、すべての開発においてこの丁寧なステップを踏むべきなのだろうか。過去に作った単機能ツール(例えば3D音響シーケンサーのBinaural Editorなど)を振り返ると、最初からAIに勢いよく丸投げして形にしたものもある。
「小規模なものなら、最初から丸投げでも良いのではないか?」という疑問について壁打ちを重ねた結果、一つの極めて明快な境界線にたどり着いた。
「もし後で機能拡張したくなったとき、人間が後追いでコードを読んで短時間で全体像を把握できる規模かどうか」
数ファイル・数百行程度で収まり、状態遷移がシンプルで、後から自分でコードを追える自信があるなら、最初からAIに丸投げして超高速でプロトタイプを作るのは大いにアリだ。
ただし、その場合であっても**「保存するデータ構造(JSONスキーマ等)」**だけは人間が確認して握っておく必要がある。データモデルさえ綺麗であれば、内部の実装がスパゲッティ化しても、後からAIにロジックをごっそり作り直させることができるからだ。
| 比較項目 | AI丸投げ(高速プロトタイプ型) | モジュール分割(ミニ・ウォーターフォール型) |
|---|---|---|
| 適用規模 | 1〜数ファイル、単機能スクリプト | 複数画面、非同期連携、中〜大規模システム |
| 後追い把握 | あとで拡張したくなっても人間が短時間で読める | コード量が多く、後から全体を把握するのが困難 |
| 技術スタック | 単一ドメイン・単純な処理 | 異種技術の複合(Electron × Three.js × 音声など) |
| 事前の必須合意 | 保存データの形式(JSON等)のみ | 各モジュールの責務、データフロー、入出力型 |
| 典型例 | 単機能データ変換、使い捨てツール | 自律エージェント基盤、常駐型連携システム |
5. おわりに:ハンドルを手放してはいけない
試行錯誤の末に定めたこの方針は、Gemini Sparkのカスタムスキル(dev-workflow-guide)として登録し、Google Driveの第二の脳にも開発手順書として格納した。
今後は「〇〇を開発します」とAIに告げた瞬間、AIの側から「これは後追いで把握できる小規模ツールとして丸投げで進めますか?それともモジュール単位で合意しながら進めますか?」と問いかけてくれる仕組みになっている。
AIを使った開発のスピード感は圧倒的だ。だが、速度に目が眩んで「自分が何を作っているのか」を見失ってしまっては、道具に使われているのと変わらない。
コードをタイピングする手はAIにいくらでも貸してもらえばいい。けれど、アーキテクチャの納得感と、データがどう流れているかという「見取り図」のハンドルだけは、決して人間が手放してはならないのだと、痛烈に学んだ経験だった。
コメント
コメントを投稿