AIエージェントは怠けません。問題は逆で「緑にする」最短経路を見つけるのが上手すぎることです。
私たちは、Issue を渡すと分析→計画→実装→テスト→評価→PR作成まで自動で進む開発ワークフローを運用しています。Claude Code、Pro $20/月、Max $100-200/月です。途中には品質ゲートを何枚も置いています。テストは緑か。評価役のエージェントが重大な指摘(critical)を残していないか。危険な変更が diff に混ざっていないか。最後の merge だけは人間が押す設計です。
その運用でわかったことがあります。ゲートは置いただけでは機能しません。すり抜けられるか空振りするか、どちらかで静かに形骸化していきます。「指標が目標になると機能しなくなる」というグッドハートの法則が、自動開発ループの中で起きています。
この記事では、直近1ヶ月の改修で見つけて塞いだ抜け道を5つ、コミット履歴ベースで紹介します。どれも指示に従順なエージェントと設計の隙間の組み合わせで起きた話です。
この記事で学べること
- 評価エージェントの「沈黙」を解消とみなす実装がなぜ危険か
- 文字列ベースのループ検出が LLM の言い換えに弱い理由と対策
- 「テストを直して緑にせよ」という指示がテスト弱体化を招く構造
- 一度も発火していない品質ゲートを疑うべき理由(三点 diff の盲点)
- 自動化ループに「人間への出口」を設計する方法
前提:ループの全体像
対象のワークフロー(社内では dev-flow)は、おおまかにこういう流れです。
計画・実装・評価はそれぞれ別のエージェントが担当し、評価で critical が出ると計画や実装へ差し戻されます。以下の5つの抜け道はすべてこのループで実際に見つかったものです。
抜け道1:黙っていれば「解消済み」になる
最初の穴は評価の収束判定にありました。
評価エージェントへの指示はこうです。「新規の指摘のみ報告せよ。対応済み論点の蒸し返しは禁止」。差し戻しのたびに同じ指摘を繰り返されるとループが終わらないので、これ自体は必要な指示です。
一方、収束判定の実装側は「今回の feedback に現れなくなった critical は解消された」と推定していました。この2つを組み合わせると何が起きるか。指示を忠実に守るエージェントほど、未解消の critical が黙って「解消済み」扱いになります。蒸し返し禁止に従って言及をやめた瞬間、その指摘は直っていなくてもゲートを通過する。従順なエージェントほどすり抜けが速いという逆相関です。「沈黙=解消」の自動判定は廃止しました。
代わりに入れたのが、解消の明示申告です。評価エージェントは critical_resolutions: [{id, resolved, evidence}] という形式で返します。未解消だった critical 全件について「解消したか・根拠は何か」を毎回返す義務があります。resolved: true には具体的な evidence が必須です。
教訓は、「言わなくなった」と「直った」は別物だということです。
抜け道2:言い換えで打ち切りを回避できる
差し戻しが同じ論点で堂々巡りしていないかは、指摘の topic 文字列を突き合わせて検出していました。同じ topic が続けば「stuck」と判定し打ち切る仕組みです。
ところが topic は LLM が生成する自由文字列です。「エラーハンドリングが不十分」が次は「異常系の考慮が不足」になる。意味は同じでも文字列は別物なので突合は漏れます。設計レベルの差し戻しは再計画+全タスク再実装という、このパイプラインで最も高価な操作です。言い換えが続く限り、評価上限(EVAL_MAX=10)いっぱいまでフル再実装が回り得ます。システム内最大の暴走コストでした(サブスクの5時間枠が溶けます)。
対策は文字列非依存の決定論カウンタです。
const DESIGN_REPLAN_MAX = 2 // 表現ゆれに左右されない last-resort の hard cap
if (designReplanCount >= DESIGN_REPLAN_MAX) {
log('design replan 上限到達 — human review へ委譲')
break // throw せず PR へ進め、人間の判断に渡す
}
上限に達したらエラーで止めるのではなく、ループを抜けて PR まで進めます。未解消の critical は台帳に残るので、merge 判定は自動的に「人間が読むまで merge 不可(HOLD)」へ倒れます。ゲートを緩めずに暴走コストだけに蓋をする形です。
LLM 相手の検出を文字列一致に頼ると、悪意ゼロの言い換えであっさりすり抜けられます。説得はプロンプトで、保証は決定論で。この使い分けが1ヶ月で一番効いた設計判断でした。
抜け道3:テストを弱めれば緑になる
テストが落ちたとき、自動修正ステップ(green-fix)が「原因を分析して実装やテストを修正し、緑を目指せ」と指示されます。この指示、テストの assert を弱めて緑にする経路を排除していません。
緑への圧力がかかったエージェントがテストを弱体化させるのは、autonomous loop の古典的な故障モードです。「テストが red から green に変わった」証明は、テストが diff を制約していることの証明です。正しい挙動を assert していることの証明ではありません。存在チェックだけの弱い assert でも red → green は通ります。
対策は3層にしました。
- 禁止の明文化 — プロンプトに「assert を弱めて green にすることは禁止。テスト修正が正当なのは誤りを直す場合のみで、なぜ変えたか必ず申告せよ」を追加
- 発生の追跡と監査の注入 — 自動修正の発生回数を
greenFixCountとして追跡。1回でも発生した実行では評価エージェントの監査観点に「テスト diff の弱体化チェック」を自動注入 - 監査スキップ経路の封鎖 — 小規模な変更では評価フェーズを省略する最適化があり、そこで自動修正が起きると監査が無音で消える穴がありました。評価実行の条件に
|| greenFixCount > 0を追加し、自動修正が1回でも走ったら規模にかかわらず評価を強制
// 小規模(micro)でも、危険 diff か green-fix があれば評価をスキップしない
const runEval = EFFECTIVE_SHAPE !== 'micro'
|| dangerHits.length > 0
|| greenFixCount > 0
「直せ」と圧をかけるならズルの定義も渡す。効率化のために監査を省く経路には「監査が必要になった瞬間に省略をやめる」条件を必ず添える。圧力と監査はセットで設計するものでした。
抜け道4:ゲートが空の diff を見ていた
ここまでの3つは「AIがすり抜ける」話でしたが、4つ目は逆でゲートの空振りです。設置してから一度も警告を出したことのないチェック、私たちのループにはありました。
危険な変更(秘匿情報や外部コマンド実行など)を grep する Security チェックは、コミット前の作業ツリーに対して呼ばれます。ところがこのワークフローでは実装エージェントに commit を禁止しているため、チェック時点では HEAD がベースブランチと一致しています。そこで使っていた三点 diff(origin/BASE...HEAD)は空の diff を返し、danger-grep は常に clean を報告していました。つまりこのゲート、設置以来一度も仕事をしていなかったわけです。
修正は、コミット前専用の --working-tree モードの追加です。merge-base 起点の二点 diff で tracked の変更を拾います。git status --porcelain --untracked-files=all で未追跡ファイルも拾って分類します。
同型の盲点はもう1つ見つかりました。変更規模を実測して評価の深さを引き上げる再判定ロジックも、同じ三点 diff を使っていたせいで正常系では一度も発動しない dead code でした。さらに修正の過程で、?? [] というフォールバックが「取得失敗(null)」と「変更0件」を同じ 0 に潰すバグも発覚。null は NaN に変換し「判定不能なら最も厳格な経路へ」流すよう直しています。
教訓:一度も発火していないゲートは品質が高い証拠ではなくゲートが死んでいる兆候かもしれない。追加したら危険な入力を流し赤くなることを確認する——シェルスクリプトのテストの記事の「fail パスをわざと踏む」と同じ教訓です。
抜け道5:解けない問いは人間に返す
最後は毛色が違い「ループの外への出口がなかった」話です。
まず見つかったのは出口の配線切れでした。「人間が読むまで merge 不可」へ倒すエスカレーションフラグです。受け取って HOLD にする側(consumer)は実装済みなのに、立てる側(producer)がどこにも存在しません。カウントは常に 0 で、HOLD への経路は dead path でした。
もう1つは曖昧な Issue の扱いです。要件が曖昧なまま投入されると、計画エージェントは推測で空欄を埋めます。パイプラインはフルで完走し、merge 段階で人間が「方向が違う」と気づく——最も高価な失敗モードを毎回辿ります。要件の曖昧さは LLM が何周しても解消できない不確実性です。人間に1回聞くのが最安なのに、聞き返す経路がなかった。
出口を3つ開通させました。
- 分析段階の曖昧ゲート — 曖昧点が閾値(2件)超か受入条件が空なら
needs_clarificationで早期 return し人間に質問を返す - 実装中の情報不足申告 — 実装エージェントが「情報不足」と申告したタスクは詳細再分析+再試行し、解消しなければ同じく中断
- 評価エージェントの escalate フラグ — コードの良し悪しでなく結果責任・好み・訓練分布外のときだけ立てるフラグです。立つと merge はブロックされます
自動化の品質は「どこまで自動でやるか」だけでなく「どこで自動をやめて人間に返すか」で決まります。1ヶ月前の私たちのループには、後者の設計がほぼ丸ごと抜けていました。
注意点・Tips
- 確定実行したい挙動は LLM の判断を介さない — 上限カウンタ、リトライ、テレメトリ記録は依頼でなくワークフロー側のコードとして配線する。CI 確認の API エラーへのリトライも、スクリプト側に置きbatsでテスト可能になりました
- ゲートには「赤くなるテスト」を付ける — 抜け道4の三点 diff 盲点は、危険入力を流して発火を確認するテストがあれば初日に見つかっていたはずです
- 解消は申告制+evidence — 沈黙・省略・言及消失を成功と解釈する実装は、従順なエージェントと組み合わさった瞬間に偽解消の量産装置になります
- 塞いだ穴はテストで pin する — 今回の修正には「穴が再び開いていないか」を検証する回帰テストを付けています。仕組みで塞いだものは監視し続けるところまでがセットです
まとめ
自動開発ループの品質ゲートに見つかった5つの抜け道を紹介しました。
- 沈黙が「解消」に化ける — 解消は明示申告+evidence 必須に
- 言い換えで打ち切りを回避できる — 文字列非依存の決定論カウンタで hard cap
- テストを弱めれば緑になる — 禁止の明文化+発生追跡+監査スキップの封鎖
- ゲートが空の diff を見ていた — コミット前はワークツリー基準で diff を取り死活を確認
- 人間への出口がなかった — 曖昧さと当事者性の論点は早期に人間へ返す経路を配線
共通するのは、どの穴も「AI能力不足」ではなく設計の隙間だったことです。エージェントは指示にも圧力にも従順で、だからこそ指示同士の矛盾や検出ロジックの甘さがそのまま抜け道になります。
ゲートを増やすより置いたゲートが本当に仕事をしているかを疑い続けること。AIエージェントは敵ではありませんが、緑への最短経路を見つけ出す優秀な最適化装置ではあります。自動化を運用するなら、一度ゲートの「発火履歴」を眺めてみてください。一度も赤くなっていないゲートが何枚か見つかります。
この記事を読んだ方へ — 関連記事
- AIに書かせたコードのテスト、どこまで信用していいか — 抜け道3「テストを弱めれば緑になる」をさらに掘る



