この記事について
Xcode で iOS アプリを開発しています。画像を LLM に渡して構造化された結果を得る機能の精度改善に手こずった結果、設計を考える AI と実装する AI を別モデル・別セッションで立て、そのあいだを人間がゲートで区切る、というワークフローに行き着きました。
何段階もの改善作業や、大幅なリファクタ作業などで威力を発揮します。
動かすまでにたくさんハマったので(笑)、記事のどれかが誰かのヒントになればと思って書いています。
1. なぜこれを作ることになったか
作っていたのは、撮影した画像を LLM に渡して構造化された結果を返す機能です(NDA に抵触するかもなので詳しい名称は伏せます)。動くところまではすぐでしたが、その後の精度改善で詰まりました。良くなったかどうかを機械的に判定できないのです。
- UnitTest / UITest は全部 green。でも実機で撮ると、期待した項目が出てこない
- プロンプトを直したら別の画像で悪化した。どちらが良いのかは並べて数えないと分からない
- モデルがもっともらしいが事実でない結果を返す。出力を眺めても気づけない
- 「精度が上がった」という報告が、期待値を緩めただけのことがある
XCTAssert で決着しません。実機で撮って、出てきた項目を1件ずつ数えて前回と比べる。それをプロンプトや前処理を変えるたびに繰り返す。
AIの自己判定が当てにならない
最初は AI 1体に任せて自己判定を報告させていました。これが当てになりません。自分が書いたコードの出力を自分で厳しく検証するのは構造的に無理があるのだと思います(人間でも自分の仕事を自分で否定するのは嫌ですよね)。
だから「検証役と実装役を分ける」「検証できる生データを報告に必ず載せさせる」「画像を見る・実機で体感する判定は人間がやる」の3つが要る。そして3者が順番にループするなら「いま誰の番か」を持つ仕組みが要る。それが「バトン」でした。
1周ごとに、測れるものが1つ増える
このループの本質は「速く回すこと」ではありません。1周ごとに、次から機械的に検証できるものが1つ増えることです。
人間がゲートで実機を見て「この画像分析ではこれが検出されるべき」と言語化する。それが次の周から受け入れ条件になり、以降は AI が自動で検算できる。仕様がループの中で育っていく。人間を評価関数にした hill climbingです。評価関数が最初から書けないので、1周ごとに人間から少しずつ受け取って、機械的な評価関数になっていく。
当初はこれを「要件を伝える → 設計 → 保存 → 人間が実装側に伝える → 実装 → 実機でログ取得 → 読ませる → 保存 → 人間が設計側に伝える」というステップで手回ししていました。この伝える作業は機械的で回数が多く、逆に実機確認と起点は人間にしかできない。この線引きが今回の仕組みの出発点です。
2. 全体像 — 最終的な回し方
| 誰 | モデル | 何をするか | 質問できるか |
|---|---|---|---|
| 設計役 AI | Opus 5 | 報告を検算し、次の指示書を書く。コードは1行も書かない | できない(無人) |
| 実装役 AI | Opus 4.8 | 指示書どおりに実装し、報告を書く。方針は疑わない | できない(無人) |
| 人間 | — | 方針判断・実機確認・停止。ゲートのときだけ登場する | — |
この3者のあいだを回るのがバトン、実体は state.json というファイル1個です。中身は「いま誰の番か」。3者とも、まずこれを読んで自分の番でなければ待機します。
1周目は、人間が起票して Cowork に要件を伝える(要件は人の頭の中にあるので人間が起点)→ 設計役が指示書を書いてバトンを渡す → 実装役が実装・ビルド・報告してバトンを返す → 設計役が報告を検証(検算)して次の指示を書く。この繰り返しです。判断が要る/実機が要る/規定周回に達したところでゲートが立って全部止まり、バトンが人間に回ります。規定周回での停止は休み時間も兼ねています(Claude のクォータとの兼ね合い)。
人間の道具
バトンが人間に回ってきたとき、人間は対話相手によって道具を持ち替えて相談や指示を行います。たとえば「ここを改善するためにはどんな方法が考えられる?なるほど、ではそれを設計書に落として実装部門にまわしてくれるかな」といった会話です。
| 道具 | 誰との対話か | 何をするか | 対応するゲート |
|---|---|---|---|
| Cowork | 人間 ⇄ 設計役 | 方針を決める、画像を見て判定する、結論を言語化して残す | policy |
| Xcode IDE | 人間 ⇄ 実装役 | 実機に流す、撮る、目視する、コンソールログを取る | device |
3. なぜ AI を2体に分けるのか
§1 の「自分の出力を自分では甘く判断する」に加えてもう1つ。1セッションで設計も実装もやると、実装の細部(型、ビルドエラー、API の癖)が積み上がった状態で方針を考えることになり、「いま書いたコードを活かす方向」に判断が引っ張られます。
モデルは意図的にズラす
視点の多様性を維持するために設計と実装とではモデルを変えます。実際の経験上、設計役は「報告の言い分を疑って生データに戻る」動きが強く、実装役は「目の前の1点を最後まで詰める」動きが強い。Opus 5 はおしゃべりなので「結論から言ってくれ」と CLAUDE.md で釘を刺しました。
: "${MODEL_DESIGN:="claude-opus-5"}" # 設計役:全体を見る
: "${MODEL_IMPL:="claude-opus-4-8"}" # 実装役:1点に集中する
また人間の役割は5つあり、どれも AI に委譲できません。
| 役割 | 何をするか | ゲートとの対応 |
|---|---|---|
| 発案者 | 何を作るかを決める | チケット起票、要件の伝達 |
| メンター | 方針の当否を判断し、迷いを断つ |
policy ゲート |
| QA | 実機で動かし、AI が見られないものを見る |
device ゲート |
| ユーザー | 「動く」と「使える」の差を体感で判定する |
device / review ゲート |
| 確認ゲート | 暴走を止める、公開可否を決める | 停止コマンド、push と main マージ |
特に「ユーザーとしての体感」は、全機能が仕様どおり動いても運用に耐えないことがあると知っている人間にしか判定できません。
4.マルチエージェントやエージェント teams と何が違う?
私の理解では、こう分かれます。
| 何をする構成か | 向いている仕事 | 人間の位置 | |
|---|---|---|---|
| マルチエージェント | コンテキストを分けて、得意分野に専念させる流れ作業 | 仕様が決まっていて、分解して流せる作業 | ループの外 |
| エージェント teams | 複数のエージェントがメッセージを送り合いながら処理する | 仕様が決まっていて、担当間の調整が要る作業 | ループの外 |
| この構成 | **人間との協業が前提。**動かして分かったことを仕様に戻しながら進む | プロトタイプから製品へ向かう過程。動作させてみないと仕様が決まらない作業 | ループの中 |
「品質を含めた仕様が先に決まっているか」が大きな分かれ目です。 最初に仕様が完成しているならマルチエージェントや teams のほうが速いしコストも安い。
プロトタイプから製品に向かう過程で、動かしてみて初めて「これは仕様が間違っていた(特に品質に関して)」と分かることがあります。LLM が想定していない返事を返したり、全機能が仕様どおり動くのに使うと明らかに使いにくかったり。実行してみるまで存在しない情報があり、それを判定できるのは人間だけです。
人間ゲートは仕様の生成装置として機能します。ゲートで人間が見て指示したものが、次の周回の指示書に入る。AI と人間が協力してブラッシュアップしていく種類のプロジェクトでは、人間ゲートを前提にしないと良い製品にならないというのが回してみての実感です。
5. バトン
制御プレーン(state.json=誰の番か)と、内容プレーン(知識ストア=指示書・報告)を分けています。「いま動いてよいか」の判定は AI なしの python3 で完結させたい。判定に必要な情報を JSON に切り出し、中身は ID で参照します。技術的な都合から出発した分離ですが、結果として §3 のコンテキスト分離と噛み合いました。
| 主体 | 動いてよい条件 |
|---|---|
| ランナー(Mac) |
paused==false かつ PAUSE ファイル不在 かつ owner=="impl" かつ gate==null かつ 上限内 |
| 設計役(定期タスク) |
paused==false かつ owner=="design" かつ gate==null
|
| 人間 |
owner=="human"(= ゲートが立っている) |
色が owner です。オレンジの箱(人間ゲート)に入った瞬間、AI 側は全部止まります。
6. 人間ゲートは3種類
| gate | いつ立つ | 誰が立てる | 人間がやること |
|---|---|---|---|
policy |
方針の分岐、スコープ変更、周回上限到達、設計役が確信を持てない | 設計役 | 判断を伝えて解除 |
device |
実機での撮影・目視・コンソールログが必要 | 設計役 or 実装役 | 実機で操作し、ログを所定の場所に置く |
review |
差分が大きい/要注意ファイルに触った/保護ブランチへの影響 | ランナー(自動判定) | 差分を目視 |
7. 最大の失敗モード:2人のAIが互いを承認し合う
§1 の相互補完は「勝手に起きる」ものではありません。実装側が「改善しませんでした」と書き、設計側が「そうですか、では次の手を」と受ける。これでループは回りますが価値はゼロ。これを防ぐために、設計役のプロンプトに固定文言を埋め込んでいます。
報告の自己判定を鵜呑みにしてはいけません。生の出力から、あなた自身が数え直します。
- 報告の結論(「不発」「改善した」等)を、根拠を見ずに引き写さない
- 数え直せる生データが報告に含まれていない場合は、判定を保留して方針ゲートを立てる
- 期待値を緩めた「見かけの改善」は認めない
- 実装役が「自信がない」と書いている箇所は、特に自分で確かめる
実装役のプロンプトには「解析結果の項目名を1件も省略せず列挙すること。集計値のみの報告は不可」を入れています。この2つは1組で、生データが上がってこなければ検証できず、検証しないのなら生データを載せる意味がない。
「設計役が報告の判定をそのまま引き写している。コードの断片を指示書に書き始めている。ゲートの通知を読まずに解除している」など、2人のAIがお互いの傷を舐め合うような動作をしはじめたら、構造が崩れます。
8. 画面構成
最終的にこの4分割に落ち着きました。ループが自動で回っているあいだは下段が動き続け、人間ゲートに入って止まると画面の上段を使ってAIと人間が相談します。
まとめ
画像認識などの場合、自動化しても実機で撮影する作業や目視する作業、そして「ロジックは本当に改善しているのか」の最終判断は人間側に残ります。自動ループは判断を代行しません。 だから「自動化率 90%」のような数字にはあまり意味がなくて「人間が介入して結論と品質が変わっていくこと」が大切だと思うのです。




