AI-Nativeシリーズ・エージェント型エンジニアリング
アプリはずっと「完了」と言い続けていた。だがそれは嘘だった。だから、アプリ自身に監査させることにした。
1分でわかる要点――この記事から得られること
AI共同開発者と作っているあるアプリが、ユーザーに「ログインリンクを送信しました!」と表示した――その裏にメール送信サービスは存在しなかった。バグを一つずつ潰していく代わりに、私たちは製品が交わすすべての約束(80個)を書き出し、そのうち20個に機械チェックを組み込んだ。正直なスコアボードは1日で緑6個から15個へと増え――そして監査役は、誰でも入り込める管理者用のドアを見つけ出した。うまくいった理由は、たった一つのルールに尽きる。すなわち、証拠なくして合格なし。
高齢者向け自叙伝アプリと、嘘をついた成功トースト、そしてモグラ叩きに終止符を打った主張台帳についての、build-in-publicな物語。約8分。
嘘をついたトースト
私が開発しているのは新遗产传记(New Legacy Biography) — 高齢者に寄り添うAIインタビュアーが、孫世代が生前ついぞ思いつかない質問を投げかけ、その答えを本の各章に仕立てていくアプリだ。バグが単なる不便では済まない類の製品である。祖母が育った川のほとりの物語をソフトウェアが失えば、それは再ダウンロードの効かない何かを失ったことになる。
このプロジェクトでの共同開発者はAIエージェントだ。開発は速い。ユニットテストは全135件がグリーン。TypeScript strict modeもクリーン、Lintもエラーゼロ。ところが私が一般ユーザーになりすまし、メールリンクでログインを試みると、画面にはこう表示された。「登录链接已发送!」(訳:ログインリンクを送信しました!受信箱をご確認ください)
メールは一通も届かなかった。届くはずがなかったのだ。本番環境にはメールサービスがどこにも設定されていなかったからである。コードには誰も読まないサーバーログにメールアドレスを出力するだけの「開発用フォールバック」があり、それでいてユーザーには成功を報告していた。信頼していたゲートはすべてグリーンだったのに、製品は肝心のユーザーに嘘をついていたのだ。
さらに悪いことに、製品の核心である「家族を招待する」機能——親族が写真や音声メモを誕生日の思い出の本に寄せる機能——が生成する招待リンクは、こんな文字列で始まっていた。http://localhost:3000。私のデスクにしか存在しないコンピューターへのリンクだ。これを別の街に住む叔母に送ったところで、何も起こらない。しかも登録ページの唯一の導線が同じ幻のメール頼みだったため、しばらくの間、地球上の誰一人としてこの製品に新規登録できなかった——それでいてテストはすべてグリーンのままだった。
グリーンなテストが救いにならなかった理由
Edsger Dijkstra は1972年のTuring Award講演で、誰もが言いたがらない本質を突いていた。“program testing can be a very effective way to show the presence of bugs, but is hopelessly inadequate for showing their absence”(訳:プログラムのテストはバグの存在を示すには非常に有効な手段となりうるが、バグが存在しないことを示すにはまったく不十分である) [1]。それから54年、AI支援開発はこの警告を和らげるどころか、むしろ鋭さを増している。生成ツールは「完成しているように見える」コードを作るのが極めて得意だからだ。完成しているように見えるユニットテストが検証するのは関数であって、誰も検証していなかったのは約束.
だった。長年MLプロダクトを手がけてきた Hamel Husain も、同じ診断を現代の言葉で言い表している。“I’ve found that unsuccessful products almost always share a common root cause: a failure to create robust evaluation systems”(訳:うまくいかない製品にはほぼ例外なく共通の根本原因がある。それは、堅牢な評価システムを構築できていないことだ) [2]。彼の論のうち借用に値するのはこの部分だ——評価システムがなければ、一つの不具合を直しても別の不具合が浮かび上がるだけだ。彼はこれを「モグラ叩きゲーム」と呼んでいる。[2]。まさに私のその週がそれだった。メールの嘘を直せばlocalhostのリンクが見つかり、リンクを直せば音声入力が未デプロイのモデルファイルを必要としていることが判明する。誰もスコアをつけていなかったせいで、どのモグラも不意打ちだった。
発想の核:主張台帳という仕組み
アイデアの全体像はこうだ。この記事がなくても明日には再現できるくらいシンプルなものである。製品が交わすすべての約束——ボタン一つ、ナビ項目一つ、マーケティングの一文一文——は主張。機械チェックのない主張は、単なる噂だ。 というわけで:
- 主張をデータとして書き出す。 頭の中でも、ドキュメントの中でもなく、コードが読めるファイルの中に書く。私たちのファイルには「亲友无需注册可上传照片」(親族は登録なしで写真をアップロードできる)や「新用户可以完成注册」(新規ユーザーが実際に登録を完了できる)といった項目が並んでいる。
- 各主張には、ユーザーと同じ動きで検証する1つのチェックを与える。 実際のブラウザが本番サイトの実際のフォームに入力する。クッキーなしのリクエストが、叔母がやるのと同じ方法で招待リンクを開く。
- 2段階ではなく3段階で正直に採点する: PASS、FAIL、そしてNOT MEASURED。この3つ目の等級こそが要だ。チェックできなかった主張は除外され、こっそり合格扱いにされることはない。
- それをゲートにする。 ゲート対象の主張が失敗すれば、実行は怒って終了する。偽の✅より、正直な❌のほうがいい。
1日の正直さがもたらしたもの
本番環境に対する最初のフルランでは、スコアは6件PASS、11件FAIL、3件NOT MEASURED——機械チェック対象20件の主張のうちだ。アプリの大半がガラクタだったからではない。何ヶ月もの間、誰もアプリに何も証明させていなかったからだ。その日の終わりには、15件PASS、そして残る各FAILには、漠然とした嫌な予感の代わりに、名前と範囲のついた解消策がついていた。
読む価値のある3つの発見:
- セルフサインアップの穴。 登録ページはメールリンク方式のみだったが、本番環境にメールサービスは存在しなかった。チェックは赤字でFAILと表示し、我々はパスワード方式を追加した。同じチェックは今も、ロボットユーザーが本番サイトで登録を完了する様子を、これからずっと見守り続ける。
- 開けっ放しの管理者用の裏口。 これがお気に入りの一件だ。というのも、監査の仕組み自体が生んだバグを、その監査の仕組み自身が捕まえたからだ——その仕組みを作った当人が、同じ日に出荷したバグを。我々は管理者コンソールを追加した。認可ガードのロジックはこうだった:ユーザーにロールがあり、それが許可されていなければ拒否する。一見問題なさそうだが、ログイントークンにロールフィールドそのものが存在しない場合、このチェックはすり抜けられ、堂々と通過してしまう。作ったばかりのテストアカウントが、管理者APIからHTTP 200を受け取った。用心棒のルールは、文字通りこうだった:「身分証を持っていないなら、問題ないに違いない」。検証パスは数分でこれを捕まえ、認可は今では閉じる方向に失敗するよう変更され、恒久的なチェックも組み込まれている。
- 10個の症状の裏にあった、根の深いバグ。 監査は同じ形の問題を何度も発見し続けた。一度も実行されないバックグラウンドの写真処理、消えてしまうエクスポートジョブ、電話をかけたきり応答しないテキスト読み上げのルート。
localhost:8888根本原因は、一言で言えばこうだった:コードは常に生き続けるサーバー向けに書かれていたが、実際に動いているのはサーバーレス関数であり、応答を終えた瞬間に凍結されてしまう。 我々のコードは、さよならを告げた瞬間にキャンセルされる未来に向かって、ずっと処理をスケジュールし続けていた。10個の謎めいたバグは、ひとつのアーキテクチャ上の問いに収斂した。新機能はすべて、今後この問いに答えなければならない。
パターンと反パターン
うまく機能したパターン:
- 主張をデータとして、チェックをコードとして。 仕様がテストからずれることはあり得ない。なぜなら仕様こそが、テストへの入力そのものだからだ。
- ユーザーの目線でテストする。 本番URL上で実際のブラウザを動かし、実データでしか答えられない問いを投げかける。フィクスチャはコードが動くことを証明し、本番環境は約束が守られていることを証明する。
- 大声で失敗し、決して偽装しない。 メールサービスは今、心地よい嘘の代わりに「メールが未設定です——パスワードログインをお使いください」という誠実な表示を返す。ユーザーは機能の欠落を許してくれる。だが偽の成功は許してくれない。
- 2段階ではなく3段階で評価する。 NOT MEASURED は圧力弁だ。これがあることで、無理にグリーンを偽装する誘惑が消える。
痛い目を見たアンチパターン:
- 実体より表示ラベルを信用してしまうこと。 「テスト135件パス」はラベルにすぎない。見知らぬ人が実際に登録を完了すること、それこそが実体である。
- 静かなフォールバック。 もっともらしい成功で誤魔化す処理が、
catch途中に紛れ込んでいれば、それは遅延タイマー付きの嘘生成器にほかならない。 - 「hi.」だけで通す受け入れテスト。 定型の返答なら「hi.」でも通ってしまう。AIインタビュアーには、本物のデータベースしか知り得ないことを尋ねるべきだ。
その仕組みには名前がある。生成系ツールは、もっともらしい完成度――つまり見た目だけ完成しているコードを作ることを最適化している。その目的関数のどこにも、メールが実在しなければならないという制約はない。だからこそ、偽の完成へ向かう圧力は道徳の問題ではなく構造の問題であり、対策もまた構造的でなければならない。
第一原理を一文にまとめるなら、証拠なきものにパスなし、である。
月曜日から使えるやり方
時間を区切って、具体的に進める。ひとつの作業セッションで完結する内容だ。
- (30分) 自社プロダクトのランディングページとナビゲーションを開き、そこで語られている約束を20個、そのままJSONファイルに書き出す。
- (2〜3時間) それぞれについて、ユーザーがするのと同じようにその約束を検証する、できるだけ単純なチェックを書く。ヘッドレスブラウザや curl を使い、対象は本番環境であって、ステージング環境が対象ではない。各claimに印をつける
gate: true/false. - (10分) 実行する。PASS、FAIL、NOT MEASUREDを、チームの誰もが見られる正直な一覧表として公開する。
- 成功はただ一つの数字で測る、ゲート対象のFAILがゼロであること。そしてこの実行をCIに組み込み、静かに腐っていくことがないようにする。自信満々だったリリースを止めてくれた瞬間に、うまくいっていることが分かるはずだ。
我々のものはリポジトリの中にあり、eval/claims.json + eval/run.mjsとして置いてある——20個のclaim、1つのコマンド、正直な出力。このパターンは、私が常設の規律として保持しているOECループ(Observe → Evaluate → Control)の一適用例であり、観測可能性は評価に先立ち、制御フックのない評価はただのスコアボードにすぎない。
ソフトウェアだけの話ではない
この一件が起きたアプリはすでに稼働中だ。北京語で語りかけ、耳を傾けるAIインタビュアーが記憶をたどってキッチンにまで踏み込む——先週のあるテストインタビューでは、なつめと竜眼干しの香りがする記憶だった——そしてそれを章として書き起こす。さらに、「私の本はどこまで進んでいる?」という問いに実データから答える相棒エージェントも備えている。今週このアプリが手に入れたのは、どんな機能よりも珍しいものだ。自分自身について正直に語る習慣である。記録しようとずっと思っている親御さんの話があるなら、ここにある——そして、このアプリがまだ何をできて何をできないかを示すスコアボードは公開されている。それこそ、私が何かを売り込まれるときに望む形だ。
参考文献
- Dijkstra, E. W. (1972). The Humble Programmer(ACM Turing賞講演), EWD340: "program testing can be a very effective way to show the presence of bugs, but is hopelessly inadequate for showing their absence."(訳:プログラムテストはバグの存在を示すには非常に有効な手段になり得るが、その不在を示すには絶望的に不十分である) cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html
- Husain, H. (2024). Your AI Product Needs Evals. "I've found that unsuccessful products almost always share a common root cause: a failure to create robust evaluation systems"(訳:うまくいかない製品には、ほぼ必ず共通の根本原因がある。それは、堅牢な評価システムを構築できていないことだ)。症状について:"Addressing one failure mode led to the emergence of others, resembling a game of whack-a-mole."(訳:ある不具合を直しても別の不具合が現れる、まるでモグラ叩きのようだった) hamel.dev/blog/posts/evals
AI-Nativeシリーズ、その他の記事
AI-Nativeシリーズの一篇。この記事のすべては、失敗も含めて公開された評価台帳から再現可能だ。証拠なくして合格なし。Publishボタンを押すのはあなただ。