第10章: 三大病と HITL ── 早すぎる終了・無限ループ・幻のツール呼び
ここまで本書は、エージェントが “動く” 仕組みを階段で上ってきた。 LLM・道具・ハーネスの 三角形、think → act → observe の三拍子、停止条件 の三本柱、function calling の契約、コンテキストウィンドウ の物理、外部記憶ファイル、計画、サブエージェント ── 道具立てはもう一通り揃ったはずだ。
なのに、現場ではいまも、こんな会話が日常的に流れる。
「Claude が テストが赤いのに “完了しました” と言って止まった」 「Cursor が 同じ Edit と失敗を 17 回繰り返してた」 「Codex が 存在しない関数 を呼ぶコードをドヤ顔で出してきた」
道具は揃っている。LLM は賢くなっている。 それでも、エージェントは 同じ三つの病 に何度も倒れる。
この章で、その三つの病を一枚の地図に置き、最後の砦としての HITL の設計判断まで腑に落とす。これが本書のクライマックスだ。
10.1 そもそも、なぜ「病」と呼ぶのか
「LLM が間違える」のは、当たり前の話だ。第1章で見たとおり、LLM の本質は「続きを書く機械」であって、確率的に時々おかしな続きを書く。 本章で扱いたいのは、そういう 個別の間違い ではない。ループという構造の中でくり返し現れる、典型パターン のほうだ。
個別の出力ミスは「間違い」、ループ構造の中でくり返し現れるパターンは「病」と呼ぶ。
病は、モデルが何倍賢くなっても、ループという構造を持つかぎり 形を変えて残り続ける。 だからこそ、囲い込みの設計が永遠に必要になる。
代表的な病は三つに整理できる。
- 病 ①: 早すぎる終了 ── やることが残っているのに「終わった」と言って止まる
- 病 ②: 無限ループ ── 同じ失敗を延々とくり返して止まらない
- 病 ③: 幻のツール呼び ── 存在しないツール・関数・ファイルパスを、堂々と呼ぶ
順に見ていく。各病について、何が起きるか / なぜ起きるか / 設計でどう抑えるか を揃える。
10.2 病 ① 早すぎる終了 ── “だいたい直しました” 問題
最初の病は、第1章の最後で予告した「早すぎる終了」だ。
具体的にはこういう挙動になる。
- テストファイルが 5 個あるのに、最初の 2 個を直しただけで「修正完了しました」と言う
- 6 段の計画のうち 4 段までやって、残り 2 段に触れずに「ひととおり終わりました」
- エラーメッセージが残っているのに「ビルドが通る状態になりました」と報告してくる
LLM が 道具を呼ばない出力 を返した瞬間、ハーネスはループから抜ける ── これは第1章で見た停止の仕組みだった。 病はその「抜けるタイミング」を、LLM が 早く判断しすぎる ことで起きる。
なぜ起きるか。原因はだいたい三つに分けられる。
- コンテキストの劣化 ── 「全テストを緑にする」という最初の指示が、長いコンテキストの中盤に押しやられて忘れられる (第7章で扱った文脈の物理)
- 道具で実物を確認していない ── テストを実際に走らせず、「だいたい大丈夫」と推測で判断する
- 完了条件の曖昧さ ── 「直して」という指示には、明確な合格基準が書かれていない
囲い込みの設計は、ループ側でやることになる。
| 抑える設計 | 何をするか |
|---|---|
| 検証ループの強制 | 「コードを書いたら必ずテストを走らせる」をシステムプロンプトと hooks で強制 |
| テスト緑チェック | 「テストが緑になったか」を 機械的な合格条件 として書く。LLM の自己申告に頼らない |
| Reviewer サブエージェント | 完了判断を 別のサブエージェント に委ねる (第9章)。書いた本人と判断する本人を分ける |
「終わって良いか」を LLM の自己申告だけに任せると、必ずどこかで早すぎる終了が起きる。
終了判断を 機械的な条件 (テスト緑、lint 0 件) に置き換える か、別エージェントに委ねる ── どちらかの設計が必要になる。
10.3 病 ② 無限ループ ── 同じ失敗を 17 回くり返す
正反対の方向に倒れる病が、無限ループ だ。
Edit → Test 失敗 → Edit → Test 失敗 → Edit → …を延々とくり返す- 同じファイルの同じ箇所に、似たような書き換えを何度も入れる
- 「あと少しで完成です」を 10 回連続で言ってくる
- コストメーターだけがどんどん上がっていく
第1章の最後で予告した、もうひとつの病だ。LLM が “終わり” を判断できないと、ループは延々と回り続ける。
なぜ起きるか。
- 同じエラーを「新しい問題」と誤認する ── 似ているが少し違う書き換えで再挑戦して、また同じ失敗を踏む
- 完璧主義 ── 「警告がまだ 1 件残っている」を解消しようとして、副作用で別のエラーを生み出す悪循環
- 計画の不在 ── 全体像を持たないので、「もう打ち止めるべきフェーズ」に気づけない
囲い込みの設計は、外側のハーネスが 構造で抑える 必要がある。
| 抑える設計 | 何をするか |
|---|---|
| max_turns (第4章) | ループの上限回数を機械的に決めておき、超えたら強制終了 |
| 同一エラー検出 | 直近 N ターンのエラー出力を比較して、同じものが続いたら止める |
| 強制停止 (人間の Esc) | ユーザーが画面を見て、明らかに堂々巡りなら割り込む |
| 計画の再評価 | サブタスクの完了数を見て、進んでいなければ計画を捨てて立て直す |
落とし穴無限ループは、コストとしても精神衛生としても、最も痛い病 だ。 画面を見ていなかった 30 分の間に、API コストが想定の 5 倍に達することが普通にある。 「自走させる」前提の運用では、
max_turnsと同一エラー検出は 絶対に外せない安全装置 になる。
10.4 病 ③ 幻のツール呼び ── 存在しないものを堂々と呼ぶ
三つ目の病は、いわゆる ハルシネーション がツール呼び出しの場面で出るパターンだ。
- 存在しないツール名:
searchFilesとかrunMigrationのような、定義されていないツールを呼ぼうとする - 引数の型違反: ツール定義では
path: stringなのに、{path: ["a", "b"]}のように配列で渡す - 存在しないファイルパス:
src/utils/safeCopy.tsのような、リポジトリに無いファイルを Read しようとする - 架空の関数呼び出し: コード内で
fs.copyFileSafely()のような存在しない API を呼ぶコードを書く
第5章で見たように、ツール呼び出しは JSON Schema による契約 で成立している。 LLM はその契約を理解した「つもり」で、学習データの中でよく見たパターンに引っ張られて、実在しないものをそれらしく出力 してしまう。
囲い込みの設計は、ハーネス側の入口で機械的に弾く のが基本だ。
| 抑える設計 | 何をするか |
|---|---|
| スキーマ違反は即エラー | ツール呼び出しを JSON Schema で検証し、違反は実行せず error として LLM に返す |
| 未知のツール名はエラー | 定義されていないツール名は問答無用で reject |
| 存在チェック | Read 系ツールはファイル存在を先に確認し、無ければ「存在しません」を返す |
| 道具の説明を磨く (第6章) | description で「いつ呼ぶか / いつ呼ばないか」を明確にし、似た名前のツールは統合する |
幻のツール呼びは、ハーネス側で完全に防げる タイプの病だ。 ループの病の中では、入口で弾く のが最も効く。
LLM が幻覚を出すこと自体は止められない。だが、その幻覚を実行に移すかどうか は、外側のハーネスが完全にコントロールできる。
10.5 三大病を一枚の地図に
ここまでの三つを並べて整理すると、こうなる。
ここまでが、ループの 病 の話だ。 ところが、ここまで囲い込んでも どうしてもゼロにならない領域 が残る。 そこで最後の砦として登場するのが HITL ── 人間をループの中に置く、という設計判断だ。
10.6 HITL ── 完全自走と対話の中間に線を引く
HITL (Human in the Loop)エージェントのループの中に、人間の判断ステップ を意図的に組み込む設計。完全自走でもなく、一手ごとの対話でもなく、重要な分岐点でだけ人間に止まらせる 中間の運用。
ここまで読んで、こう思うかもしれない。
「結局、人間が画面を見てなきゃ駄目なら、エージェントの意味あるの?」
この問いに対する本書の答えは、こうだ。
完全自走か、一手ごとの対話か ── の二択ではない。
「どこに、何を、どのくらいの頻度で人間に止まらせるか」を 設計判断として明示的に置く ── これが HITL の本質。 人間の介入は「敗北」ではなく、ループ全体の信頼性を上げるための仕掛け だ。
HITL の介入導線は、現代のプロダクトでは大きく三つの形で現れる。
① 承認プロンプト ── 書き込み・破壊的操作の前で止まる。ファイルの初書き込み、Bash で rm や git push を呼ぶ瞬間、外部 API への書き込み呼び出しなど。Claude Code は ask / allow / deny の三段階で実装している。
② 割り込み ── 人間が画面を見て、明らかにおかしいと感じた瞬間に止める。Esc キー、Ctrl+C、ユーザー入力の挿入。無限ループの最終防衛ラインだが、画面を見ていることが前提。席を立つ運用では機能しない。
③ スコープ制限 ── そもそも触れる範囲を最初から絞っておく。権限モード (allow / ask / deny)、許可ディレクトリ、許可コマンドの列挙。介入というより事前の囲い込みで、HITL より強い防衛線 になる。
HITL 設計の核は二つの問いに集約される。
① いつ人間に止めさせるか? 取り返しのつかない操作の前? 一定ターンごと? エラーが N 回連続したとき?
② 何を人間に見せるか? 実行しようとしているコマンド? その理由? 影響範囲? 計画と進捗?
「止めさせる頻度」と「見せる情報」のバランス ── これが HITL の設計判断のすべてだ。 止めすぎると人間が疲弊して承認が形骸化する。止めなさすぎると事故が起きる。
10.7 プロダクト間の温度差 ── 自走度の軸
「いつ止めるか」の設計判断は、プロダクトごとに温度差が大きい。 都度確認から完全自走までの軸の上に、現代のエージェントを並べてみる。
--dangerously-skip-permissions、Cursor の auto mode、Devin のフル自動などが、ここに集まる。軸上のどこに座るのが正解かは、仕事の reversibility (取り返し可否) と blast radius (影響範囲) によって変わる。重要なのは、自分が今 軸上のどこに居るか を意識して使うことだ。
具体的に各プロダクトが軸上のどこに位置するか(Aider・Claude Code・Cursor・Devin など6種の対比)は、付録 A の比較表 に集約してある。新しいエージェントを試すときは、付録 A を引いて「この子は軸上のどこか」を真っ先に確かめるとよい。
新しいエージェントを試すとき、最初に確かめるべきは三つだけ。
- 既定で
rmをどう扱う? - Bash の許可は事前に列挙する? それとも都度?
- 席を立てる設計か、画面に張り付く設計か?
この三つで、そのプロダクトの 設計思想 がほぼ読める。
10.8 ループの病と HITL ── 本書のクライマックス
ここで一度、本書の旅を振り返ってみる。
序章で 3 層モデルを提示し、第1章で LLM・道具・ハーネスの三角形 を置いた。第2章で執事としてのハーネスを解剖し、第3章で think → act → observe の三拍子に踏み込んだ。第4章で停止条件、第5・6章で道具の契約と良し悪し、第7・8章で文脈の物理と外部記憶、第9章で計画とサブエージェント ── そして本章で 三大病と HITL に行き着いた。
並べてみると、これらは 個別の機構の話 ではなかったことに気づく。
本書がここまで歩いてきた 10 章は、結局のところ「ループの病をどう囲い込むか」という一つの問いに対する、設計判断の総体だった。
- 道具を JSON Schema で契約する (第5章) ── 幻のツール呼びを入口で弾くため
- description を磨く (第6章) ── LLM を正しいツールへ誘導するため
- コンテキストを管理し、外部メモリに逃がす (第7・8章) ── 早すぎる終了を生む文脈劣化を抑えるため
- 計画と TodoWrite (第9章) ── 進捗を外部メモにして、無限ループや脇道を防ぐため
- サブエージェント (第9章) ── 親の机を散らかさないため
- 三大病への囲い込みと HITL (第10章) ── ゼロにならない残余を、最後に人間で受け止めるため
ループの病はゼロにならない。続きを書く機械という構造に由来するからだ。 ゼロにならない以上、誰かが「囲いをどう設計するか」を考え続けることになる。本書はその設計判断のメニューを一冊にまとめたものだった。
そして HITL は、そのメニューの最後のページだ。 道具・契約・文脈・計画・サブエージェント ── どれだけ積み上げても残る最後の不確実性を、人間という最強のセンサー で受け止める。これは「エージェントの敗北」ではない。ループ全体の信頼性を上げるための、最後のピース だ。
10.9 この章の振り返り
- 個別の出力ミスは「間違い」、ループ構造の中でくり返し現れるパターンが 三大病
- 早すぎる終了 ── 検証ループの強制・テスト緑チェック・Reviewer サブエージェントで囲う
- 無限ループ ── max_turns・同一エラー検出・強制停止 (Esc) で構造的に打ち止める
- 幻のツール呼び ── スキーマ違反は入口で reject・存在チェック・道具の description を磨く
- どの病も LLM の自助努力では完治しない。外側のハーネス側で囲う
- HITL は「完全自走か対話か」の二択ではなく、どこに何を見せて止めさせるか の設計判断
- 自走度の軸 ── 都度確認から完全自走まで、プロダクトごとに重心が違う (詳細は付録 A)
- 本書 10 章は、結局「ループの病をどう囲い込むか」という一つの問いに対する設計判断の総体だった
この章で読めるツイート / ブログ
- 「Claude が “完了しました” って言うからマージしたら、テスト赤いままだった」 → 病 ① 早すぎる終了の典型例。検証ループを強制する hooks か、Reviewer サブエージェントが要る場面
- 「dev-loop 組んだら止まらなくなって、3 時間後にコストが想定の 5 倍になってた」 → 病 ② 無限ループ。max_turns と同一エラー検出を入れていなかった古典パターン
- 「
--dangerously-skip-permissionsでぶん回したら、想定外のファイルまで消えてた」 → 自走度の軸の右端に座る選択の代償。reversibility の低い操作までスコープに入ってしまった
そして、終章へ
本書はここでクライマックスを越えた。 LLM・道具・ハーネスの三角形から、停止条件・function calling・コンテキスト・メモリ・計画・サブエージェント、そして三大病と HITL ── ループという設計パターンを、プロダクト横断でひと通り歩いた。
次に来るのは、本書のもうひとつのゴールだ。 ここまで腑に落とした骨格をもって、ループはこれからどこへ向かうのか を考える。長時間タスク、マルチモーダル、エージェント間プロトコル ── 終章で、本書の旅を締めくくる。