設計も実装もレビューもAIがやる個人開発で、精度をどう出しているか
私は個人で AI coding agent の runtime を作っています。
この製品は、設計も、実装も、実装のレビューも、AI が行っています。
私がやっているのは、AI に渡す最初の指示の精度を上げることと、最終的な成果物が目的に達しているかを確認することです。その間の設計・実装・レビューは、それぞれ別の AI が担います。この記事では、作業指示書を書く AI を設計 AI、コードを書く AI を実装 AI、実装を審査する AI をレビュー AI と呼びます。
こう書くと「それで品質は出るのか」と思われると思います。
出ます。ただし、AI の報告を信じない仕組みがあってはじめて出ます。
この記事は、その仕組みの話です。
AI の「できました」は、普通に間違っている
まず前提から書きます。
実装 AI に実装させると、完了報告が返ってきます。「テストを 26 件追加し、全件通りました」といったものです。
この報告は、普通に間違っています。
実際に私のリポジトリで起きたものを挙げます。
- 「テスト 26 件追加」と報告されたが、実際に数えると 18 件だった
- 設計判断の記録に必須のキーが欠けていたが、報告では「記録済み」になっていた
- 指示書が「必ず明記せよ」と要求した記載が、黙って落ちていた
- 実装 AI が「原因はこれ」と確信して製品コードに書いたコメントが、まるごと間違いだった
嘘をついているわけではありません。AI は本気でそう思って報告しています。
だから、報告を読んで納得するかどうかで判断すると、必ず通ってしまいます。
報告は、検証の対象であって、検証の結果ではない。
これを運用の起点にしました。
仕組みは 4 層ある
1. 発注: 指示書に「やらないこと」を書かせる
実装は作業指示書で発注します。指示書を書くのは設計 AI です。私が「この機能を足したい」「この不具合を調べたい」といった最初の指示を出し、設計 AI がそれをリポジトリの現状と突き合わせて指示書に起こします。
指示書のテンプレートには、やることだけでなく、次を必ず書く欄があります。
- 既に実装済みで、作り直してはならないもの(パス名つき)
- スコープ外(触れないこと)
- 受入条件(番号つき。1 件ずつテストに対応づけられる粒度で)
- 修正前のコードで、何が落ちるべきか
最後の項目については後で詳しく書きます。
実装 AI は、指示書に書いていないことを勝手にやり、書いてあることを黙って落とします。両方を後から機械的に照合できるように、指示書の側に境界を書かせておきます。
2. 隔離: 実装 AI と、レビュー AI を分ける
実装した AI に、自分の実装をレビューさせません。
実装は一つのモデルが隔離された作業ツリーで行い、レビューは別のモデルが、実装の作業ツリーの中で行います。
同じモデルが自分の仕事を見直しても、自分が見落としたものは同じように見落とします。
3. 機械確認: スクリプトが「入力」を出す
レビュー AI は、まずスクリプトを走らせるところから始めます。これは手順書で決まっています。
python scripts/review_evidence.py <実装commit> --work-order docs/work-orders/<name>.md
このスクリプトは、機械で確認できる範囲だけを検査します。
- 設計判断の記録に必須キーが揃っているか、採番が正しいか
- 契約文書に「実装ファイル名」「commit SHA」などの来歴が混ざっていないか
- 指示書が「触るな」と宣言したパスに変更が交差していないか
- 変更されたファイルの分類
そして残りの手順を、実行すべきコマンドを埋めた形で出力します。
ここで一つ、手順書に太字で書いてあることがあります。
スクリプトの出力はレビューの入力であって結論ではない。この出力だけを引用した報告は、何も検証していないのと同じである。
自動化を入れると、その出力を貼って「確認しました」と言いたくなります。人間も AI もそうです。だから、それは検証ではないと明文化してあります。
4. 突合: 手順書どおりに、全部やり直す
レビュー AI は、7 段階の手順を順番に実行します。
- diff を指示書と 1 項目ずつ照合する
- テストと lint を、報告の数字を信じずに自分で実行する
- 修正前のコードで、新しいテストが落ちることを確認する
- 設計判断の記録を検証する(過去の判断を覆すなら、覆すと書いてあるか)
- 契約文書の「変更なし」という主張を、ファイルを開いて確認する
- 受入条件を 1 件ずつ、どのテストが担っているか対応づける
- 可能なら、元のバグ報告と同じ操作を実際のインターフェースで再現する
そして手順書は、レビュー報告に実行しなかった手順を「未検証」と書くことを義務づけています。
「検証済み」と「未検証」を混ぜない。実行していない手順を「問題なし」と書かない。
これは製品側の設計原則(失敗したら成功を主張しない)を、そのままレビュー報告にも適用したものです。
いちばん効いた手順: 修正前で落ちるか
手順 3 について、少し詳しく書きます。
修正と一緒に追加されたテストは、修正前のコードで実行すると落ちなければなりません。 落ちないなら、そのテストは修正の効果を何も証明していません。
これは分かりきった話に見えます。しかし、ここに一つ罠があります。
新しいテストが、修正で追加された新しい関数やクラスを import しているとします。修正前のコードでそのテストを実行すると、当然落ちます。ImportError で。
実装 AI は「修正前で落ちることを確認しました」と報告してきます。実際に落ちてもいます。
でも、それはテストが集められた時点で失敗しているだけで、テストの中身は 1 行も実行されていません。
このパターンを、レビュー AI は初期に見逃していました。今は手順書にこう書いてあります。
それは挙動の証明にならない。代わりに挙動レベルの再現を行う。
つまり、修正前のコードで問題の操作を小さなスクリプトで直接実行し、バグが実在したことを確認してから、修正後に同じ操作が正しく振る舞うことを確認する。
これを手順書に書いてからは、実装 AI も最初から遵守するようになりました。個人の注意力ではなく、手順の問題でした。
「既存の問題です」を実測で覆した話
ここまでの仕組みで、たいていの不備は捕まります。
一つだけ、仕組みをすり抜けかけた事例を書きます。
GUI の改修を担当した実装 AI から、こういう報告が来ました。
全件のテストが断続的に segfault するが、これは既存の脆さで、ベースラインでも再現する。本実装の問題ではない。
説明はもっともらしく、原因の考察までついていました。
この報告を受けた設計 AI が、main と実装側の作業ツリーで全件テストを交互に実行しました。
main : 4 回中 4 回、正常終了
実装側 : 3 回中 3 回、segfault
既存の問題ではありませんでした。本実装による退行でした。
原因は最終的に、レビュー AI がデバッガで突き止めました。ウィンドウとコントローラがシグナル接続で互いを参照し合っていて、テストで放棄されたウィンドウが参照カウントでは解放されず、循環 GC でしか解放されない。その GC が後続テストのイベント処理中に走ると、まだタイマーを抱えているツリービューが破棄され、解放済みメモリにタイマーイベントが届く。
そして、実装 AI が「原因はこれ」と信じて製品コードに書き込んでいたコメントは、まったく別のことを説明していました。是正で除去しました。
ここから得た教訓は一つです。
AI の「既存の問題です」「環境要因です」は、AI が本気でそう思っている。だから議論では覆らない。実測でしか覆らない。
「本当ですか」と聞き返しても、もっともらしい説明が返ってくるだけです。main で 4 回、実装側で 3 回走らせる。それだけが答えを出します。
「実装 AI の『既存問題』『環境要因』という主張は、必ず両方を交互に実測してから受け取る」。この件は、そういう教訓として設計 AI の申し送りに残っています。
間違えるのは実装 AI だけではない
ここまで読むと、実装 AI だけが間違えるように見えるかもしれません。違います。
設計 AI も間違えます。
ある改善で、指示書には「この挙動の差は既知で、意図的な修正」と書いてありました。実装 AI はそのとおりに実装しました。ところがその差は、別の経路を伝わって、変更を適用してよいかどうかの判定を変えていました。設計 AI が、その値を誰が読むかを追わなかったのが原因です。
このときの是正は、実装を差し戻すことではありませんでした。実装は指示書の明示的な許可に従っていて、間違っていたのは「記録が要らない」と判断した指示書側だったからです。欠けていた記録を作ることで是正しました。
もう一つ。外部の評価で「未実装」とされた項目を、設計 AI が鵜呑みにして指示書を書き始めたことがあります。コードを見たら、既に実装済みでした。同じことを 2 回やりました。今は「外部評価の『未実装』主張は必ずコードで裏取りする」と手順書にあります。
レビュー AI も間違えます。
先に書いた ImportError の罠は、レビュー AI が初期に見逃していたものです。
誰かが正しいという前提を置かない。全員が間違える前提で、間違いが通り抜けられない配線にする。
気づいたことは、その場で手順書に書く
ここまでに出てきた教訓 — ImportError の罠、「既存問題」の主張、外部評価の鵜呑み — は、どれも見つけた時点で、手順書か指示書テンプレートに書き足しています。
書き足す先は、チャットの履歴ではなく、リポジトリの開発文書です。
理由は 2 つあります。
一つは、人間にも AI にも、同じものが見えること。次のサイクルで指示書を書く設計 AI も、レビューする AI も、同じ文書を読んでから始めます。私が毎回「前にこういうことがあったから気を付けて」と伝える必要がありません。
もう一つは、担当が交代しても手順が残ること。手順書とスクリプトと指示書テンプレートが互いを参照していることを検査するテストもあります。配線が腐らないようにするためです。
そして、これがいちばん大事なのですが、ルールや手順が積み重なっても、私がそれを全部覚えている必要がなくなります。
覚えるべきものは文書にあり、読むべきタイミングは手順に組み込まれている。私は「前にもこんなことがあった気がする」と思い出す仕事から解放されて、最初の指示と最終確認に集中できます。
人間は何をしているのか
改めて、私が何をしているかを書きます。
最初の指示の精度を上げること。
作業指示書を設計 AI がゼロから書くわけではありません。コードの作成、機能の変更、調査。何をしたいのかを最初に伝えるのは私です。その最初の指示が曖昧だと、指示書も、実装も、レビューも、曖昧さをそのまま引き継ぎます。ここが私の主戦場です。
ただし、このとき一つ気を付けていることがあります。
指示によって、AI の能力を縛らないこと。
「こう実装しろ」「この方法でレビューしろ」と細かく指定するほど、AI は私の想定の範囲でしか仕事をしなくなります。私の想定より良い設計、私が思いつかない検証手段があるなら、それを使ってほしい。だから指示は「何を達成するか」と「何を守るか」に留めて、「どうやるか」は各 AI に委ねます。設計・実装・レビューのそれぞれで、その AI が最高の能力を発揮することが、精度の源だからです。
最終的な成果物が目的に達しているか確認すること。
不足や過ちがあれば、そこで終わりにせず、続けて修正・改善に入ります。
コードは書いていません。設計文書もレビュー報告も、AI が書いたものを読んでいます。
私がやっているのは、AI に任せられない判断を見極めることと、AI が間違えている箇所を見つけることです。
そして、後者の多くは、個人の注意力ではなく手順にできます。一度捕まえたら手順書に書く。次からは、誰がレビューしても捕まります。私が覚えていなくても。
AI に何を任せるかという問いは、結局のところ、任せた結果をどう検証するかという問いでした。
関連記事
このシリーズ(Zenn):
- 作業指示書は、最初に書かせない ― 設計AIとのやり取りを全部見せる — 各論 1 本目、設計 AI 編
作っている製品(R2 Fugu Agent Runtime)の設計については、Qiita に別のシリーズを書いています:
Discussion