Agent Mentor Learn
エージェントハーネスの基礎: ループと制御 · 第 4 回 / 全 6 回

レッスン4: 暴走とフォールバック: デッドループ、空回り、予算バーンアウト

学習目標:

  • ループ暴走の典型的な4つの形 — エラーの累積増幅、コンテキストの肥大化と腐敗、デッドループと空回り、予算バーンアウト — を見分け、それぞれループのどの性質が原因になっているかを言える
  • それぞれに対応するフォールバックのゲートを取り付ける: チェックポイントと早期停止、コンテキストガバナンス、最大ターン数上限と無進捗検知、予算上限 — そして各ゲートが実際に何を止めているのかを説明できる
  • 「その場で空回りしている」ループにどのハードゲートが要るかを、モデルを差し替えたりプロンプトを書き直したりして境界のないループを救おうとする代わりに、判断できる

前提: レッスン2と3を読み終え、stop_reason で駆動する最大ターン数上限つきのループの骨格が手元にあること | 前: レッスン3 << | 次: レッスン5 >>

回るループは、暴走もする

前の2つのレッスンで、あなたのループは立ち上がりました: stop_reason を while の条件にし、さらにレッスン3の明示的な停止条件を付けたことで、モデルが切り上げようとしないときにも押さえ込めます。ここまで来れば、そこそこ安定したループに聞こえます。しかし「押さえ込める」のは最後のヒューズにすぎず、それが捕まえる病気はちょうど1つ、「止まらないループ」だけです。ループの壊れ方はそれだけではありませんし、しかも多くの場合、劇的なことは何も起きません — プロセスがハングするわけでも、CPU が振り切れるわけでもありません。ループはただ静かに、一周また一周と、仕事を間違ったやり方でこなしていきます。

このレッスンでは、「ループの暴走」という漠然とした言い方を、見た瞬間に名前を言える4つの形に分解します: エラーの累積増幅、コンテキストの肥大化と腐敗、デッドループと空回り、予算バーンアウト。4つとも根は同じところにあります — エージェントは次の一手を自分で決めるシステムであり、その自律性こそが有用さの理由であり、同時に脱線の理由でもあるからです。Anthropic はそれを率直にこう書いています: "The autonomous nature of agents means higher costs, and the potential for compounding errors."1(エージェントの自律的な性質は、より高いコストと、エラーが累積増幅する可能性を意味する。)そして "The LLM will potentially operate for many turns, and you must have some level of trust in its decision-making."1(LLM は多くのターンにわたって動作しうるので、その意思決定をある程度は信頼しなければならない。)信頼は放任と同じではありません。このレッスンが扱うのは、信頼の先にあなたがループへ用意すべきもの — 暴走のしかたごとに1つずつ用意するフォールバックのゲートです。

先に一つ片づけておきます。そうでないと、この先を読み違えます: この4つは、確率の低い事故ではありません。ループという構造にあらかじめ組み込まれた傾向です。ループを放っておけば、デフォルトでこの方向へ滑っていきます。だからフォールバックは、事故のあとに当てるパッチではなく、最初から溶接しておく手すりです。1つずつ見ていきましょう。

暴走1: エラーの累積増幅 — 一つの誤った手が以降のすべてを汚染する

いちばん狡猾なものから始めます。これはエラーを一度も投げないからです。レッスン2のオンコールボットを思い出してください: サービスを再起動し、ログを読み、報告する。この3ステップは連鎖しています — ステップ2はステップ1の結果に依存し、ステップ3はステップ2に依存します。ここでステップ1が静かに間違ったとしましょう: モデルがサービス名を api-staging と読み、プリプロダクション環境のほうを再起動してしまいます。ツールは「再起動に成功」と返し、モデルはそれを額面どおり受け取ります。続いてログを読み、新しいエラーがないのを見て、上機嫌で「再起動完了、ログはクリーンです」と報告します。ツール呼び出しはすべて成功しました。例外は1つも上がっていません。それでもステップ2以降は、丸ごと誤った土台の上に積み上がり、一歩ごとにさらにずれていきます。

これがエラーの累積増幅(compounding errors)です: エージェントが自律的に走るということは、誤りがループを一周するたびに蓄積し、増幅するということです1。これはレッスン2で見た「コードのバグが無限ループを生む」ケースとは別の生き物です — あちらは一目で気づける機構上の故障です。エラーの累積増幅は意思決定の層で起こり、個々のステップはどれも完全に妥当に見えるのに、誤りは連鎖の中で複利式に膨らみます。ループが回った数が多いほど、連鎖が長いほど、初期の小さなずれが最後にどこまで運ばれているかは予測しにくくなります。

フォールバックは2つの部品の連携でできています。第一にチェックポイント: 「ここが間違っていたら後続は全部無駄になる」という連鎖の節目で一度止まり、ループに中間状態を差し出させて検証を受けさせます。Anthropic はこの種の停止をそのまま記述しています — "Agents can then pause for human feedback at checkpoints or when encountering blockers."1(エージェントはチェックポイントで、あるいは障害に突き当たったときに、人間のフィードバックを待って一時停止できる。)その停止点をどう設計するかはレッスン5で扱います。第二に早期停止: すでにずれ始めたループに予算を使い切らせるより、おかしいと見えた瞬間に止めるほうがましです。これこそレッスン3の明示的な停止条件の用途であり、"it's also common to include stopping conditions (such as a maximum number of iterations) to maintain control."1(制御を保つために、停止条件(最大反復回数など)を入れるのも一般的である。)という一般的な助言とも響き合います。核心はこうです: 誤りはターン数とともに蓄積するのだから、検証のないまま長い連鎖をループに走らせてはいけません。

暴走2: コンテキストの肥大化と腐敗 — 履歴はターンごとに重くなる

レッスン2で借金を1つ作りました: ループを一周するたびに、あの messages.push の行が履歴へメッセージを2つ詰め込みます。追加のみで、決して引かれません。あのときは「あとで清算します」と言いました。いまがその「あと」です。

まず「肥大化」から。"An agent running in a loop generates more and more data that could be relevant for the next turn of inference,"2(ループの中で走るエージェントは、次の推論ターンに関係しうるデータをどんどん生み出していく。)そしてそのデータは毎ターン、手つかずのまま次のリクエストへ持ち越されます。3周や5周で終わるタスクなら、どうということはありません。しかし数十ターンを要するループになった途端、履歴は雪だるま式に膨らみ、リクエストは1本ごとにどんどん長いコンテキストを引きずることになり、遅く、高くなります。これが目に見える直接のコストです。

「腐敗」のほうが厄介な半分です。傷つくのが請求額だけでなく品質だからです。"LLMs have an 'attention budget' that they draw on when parsing large volumes of context," そして "Every new token introduced depletes this budget by some amount."2(LLM は大量のコンテキストを解析するときに引き出す「注意の予算」を持っており、新しいトークンが1つ入るたびに、その予算はいくらか目減りする。)その帰結はこうです: "as the number of tokens in the context window increases, the model's ability to accurately recall information from that context decreases."2(コンテキストウィンドウ内のトークン数が増えるほど、モデルがそこから情報を正確に思い出す能力は下がる。)これが一般に context rot(コンテキスト腐敗)と呼ばれているものです — 履歴から何かが消されたわけではないのに、重要な事実がノイズの中に沈み、モデルは見えていても掴めない状態になります。ループが長く回るほど、ターン2で与えた決定的な指示や制約に対する握力はゆるみ、振る舞いは漂流しやすくなります。

ここには捉え方を間違えると守る対象を取り違えてしまう、スケールの問題があります: この劣化は、ある閾値を超えたら落ちる崖ではなく、長さに応じてなだらかに下る性能の曲線です — "These factors create a performance gradient rather than a hard cliff."2(これらの要因は、明確な崖ではなく性能の勾配を作り出す。)「コンテキストが大きくなりすぎたら、もう何もかも台無し」と読んではいけません。水位が上がって船が沈み込んでいくのに似ています: どの水位でも航行はできますが、だんだん難しくなるだけです。それを受け入れて初めて正しい姿勢にたどり着けます — "context, therefore, must be treated as a finite resource with diminishing marginal returns,"2(したがってコンテキストは、限界収穫が逓減する有限の資源として扱わなければならない。)であり、履歴に足すひとかたまりごとに「これはまだ元が取れるのか」と答えさせるべきなのです。

フォールバックはコンテキストガバナンスです: ループが生み出す履歴を、闇雲に push するのではなく能動的に管理します。具体的な技法 — 古いターンの圧縮、初期の結果の要約、もう用のない中間成果物の破棄 — は、このシリーズのコース5「エージェントメモリと状態」がまるごと扱うテーマなので、ここでは開き直しません。ループの視点から持ち帰るべきなのはこれです: コンテキストを統治することは、ループの1ターンあたりのコストにブレーキをかける方法であり、長く回るループが進むほど鈍くなっていくのを防ぎます。

暴走3: デッドループと空回り — どこにも進まず、止まりもしない

最初の2つの失敗モードでは、ループは少なくとも前へ進んではいました。曲がっていたり、鈍かったりするだけです。こちらはもっと身も蓋もありません: ループは前へまったく進んでおらず、しかも止まりもしません。これには2つの顔があります。

1つはデッドループで、レッスン2のレベル2演習ですでに出会っています — ループ本体の末尾で response の再代入を忘れたために stop_reason が古い値のまま凍りつき、while の条件は永遠に真、プロセスはハングし、同じツールが何度も呼ばれ続けます。これは純粋にコードの機構上の故障です: ホストコードが間違っているだけで、モデルの意思決定とは関係がなく、抜けていた再リクエストの行を足せば直ります。

もう1つの顔はもっと狡猾で、空回り(ライブロックとも呼ばれます)といいます: コードは完全に正しく、毎ターン正当にリクエストを送り、ツールを実行し、新しい stop_reason を読んでいる — それなのに新しいことが何ひとつ達成されません。典型的な形はこうです: モデルが同じ検索ツールを何度も呼び、毎回ほとんど同じ空の結果を受け取り、それでも方針を変えず、次のターンも同じものを検索する。stop_reason から見れば、このループは完全に健康に見えます — つねに tool_use で、つねに正常に回っています。タスクから見れば、その場で足踏みし、ターンとトークンをぐるぐる燃やしながら、何ひとつ前進させていません。

デッドループはコードを直せば直ります。空回りはそうはいきません — コードは正しく、直すべきバグなど存在しないからです。空回りには2つのハードゲートが要ります:

  • 最大ターン数上限(レッスン3のゲート): ループに反復回数の絶対的な天井を与え、ターンがどれだけ「健康」に見えても天井で止めます。これが最後のヒューズであり、ループがどれだけ激しくその場で回ろうと、N ターンを超えては回れないことを保証します。
  • 無進捗検知: 空回りのために専用に作られたゲートです。考え方は、何か新しいことが実際に起きているかをホスト自身に見張らせること — 直近数ターンのツール呼び出しの名前と結果を記録し、N ターン連続で同じツールを使い、ほとんど同じ出力が返ってきたら「進捗なし」と判定してループから抜けます。最大ターン数上限より早く噛みます: 50ターンを燃やし切る必要はなく、3ターン目から始まった繰り返しを捕まえられます。

2つのゲートの分業はこうです: 無進捗検知は空回りを早めに気づくことを担当し、最大ターン数上限は何も気づかれなかった場合でも絶対的な天井を保つことを担当します。どちらもモデルが自分で我に返るのを待ちません — そもそも空回りが存在する理由はモデルが我に返らないことなので、境界はループの外側にあるホストコードが引くしかないのです。

暴走4: 予算バーンアウト — ターン数 × 大きくなるコンテキストで、請求が暴走する

最後のものはいちばん理解しやすく、いちばん痛みます。

先の3つの失敗モードは、いずれ結局はお金という形で現れます。エージェントの自律性はより高いコストを意味し1、モデルは何ターンも連続で動作しうるうえに1、そのターンごとのコンテキストは増え続けます — ターン数 × 1ターンあたりのトークン数、その両方が増えるので、請求額は掛け算で上がっていきます。空回りに嵌まったエージェントは、あなたが見ていない一晩のうちに相当な額の API 費用を燃やして、何ひとつ仕上げないことがあります。

ここでのリスクはお金だけではありません。多くのターンを走らせるということは、モデルの意思決定をある程度は信頼しなければならないということであり1、その信頼を天井なしで差し出したいとは、おそらく思わないはずです。

フォールバック: 予算上限。 ループに明示的な予算の天井を与えます。測り方は2つのどちらかです:

  • ターン数で測る。 最も単純な形で、これはレッスン3の最大ターン数上限そのものです — あの上限もまた予算です。
  • トークン(または金額)で測る。 実際のコストに近いやり方です: 累積消費トークン(または見積もり金額)が天井に達した時点で、直ちに停止します。

ポイントは止まることだけではなく、正直に報告することです。天井で止まり、こう率直に言うこと: 予算が尽きたので止めました、タスクは未完了です、ここまで進みました。最悪の結末は、予算を全部燃やしたうえで完成した結果を出したふりをすることです — それは失敗モード1のエラーの問題へまっすぐ舞い戻ります。正直に止まることこそが、人間に引き継ぎ方を伝える手段です。

4つのゲートを、まとめて

4つの失敗モードを振り返ると、共通の出自があることに気づきます: ループに境界がなく、モデルはその境界に気づかないか、自分をそこに留めておけない。

  • エラーの累積増幅 → チェックポイントと早期停止。誤りがまだ小さいうちに断ち切る
  • コンテキストの肥大化と腐敗 → コンテキストガバナンス。ループが毎ターン背負う荷物を軽くする
  • デッドループ → コードを直す(抜けている再代入を足す)。空回り → 無進捗検知と最大ターン数上限で、モデルに代わって終了を宣言する
  • 予算バーンアウト → 予算上限と正直な報告で、コストに天井をかける

この4つのゲートは、あってもなくてもいい飾りではありません。「自律的」が「制御不能」を意味しないための条件です。フォールバックのないループは、うまく走っているあいだは美しく見えますが、1ターン横道にそれた瞬間、自分を引き戻す仕組みを持ちません — 小さな問題を大きな事故へ転がすだけです。

逆の極端にも注意してください: これらの仕組み自体が複雑さであり、"you should consider adding complexity only when it demonstrably improves outcomes."1(複雑さを足すのは、それが結果を明確に改善するときだけにすべきである。)おもちゃのようなループすべてにゲートを山積みしてはいけません。判断基準はいつも同じです: このループが暴走したとき、どれくらい高くつくのか。コストが高いほど、ゲートは一式そろっているべきです。

ゲートが入ると、次の問いはひとりでに浮かび上がります: ゲートがループを止めたとき、あるいはループが自力では判断できない分岐に行き当たったとき、人間はどうやって割って入り、進路を正し、あるいは引き取るのか。それがレッスン5です。

まとめ

  • ループは自分で終了を宣言しません: while は自分の条件しか知らず、モデルはもう1回リクエストを撃ち続けるだけです。境界はハーネスが保つしかありません。
  • エラーの累積増幅 — 今ターンのモデルの入力の大半は前ターンの自分自身の出力なので、1つの誤った手はそれ以降のすべてのターンに運ばれ、増幅されます。それが多くのターンにわたって動作しうるという事実と組み合わさり、自律性はより高いコストとエラーの累積増幅の可能性を抱き合わせで連れてきます1。ゲートはチェックポイントと早期停止です。
  • コンテキストの肥大化と腐敗 — ループの中のデータは蓄積する一方であり2、新しいトークンはどれも注意の予算を目減りさせ2、トークンが積み上がるほど想起は悪くなり、コンテキストは限界収穫が逓減する有限の資源です2。この劣化は崖ではなく勾配です。ゲートはコンテキストガバナンス(「エージェントメモリと状態」で扱う履歴管理の道具立て)です。
  • デッドループと空回りは別物です: デッドループはホストコードのバグ(たとえば response の再代入忘れ)であり、コードを直せば直ります。空回りはコードが正しく、モデルが成功しえない同じツールを繰り返し呼びながらどこにも進まない状態であり、無進捗検知(N ターン新しい結果がなければ停止)にヒューズとして最大ターン数上限を重ねて対処します。無進捗検知をデッドループに向けてはいけませんし、コードの修正で空回りが治ると期待してもいけません。
  • 予算バーンアウト — ターン数と1ターンあたりのコンテキストが掛け算になってコストを押し上げます1。ゲートはトークンまたはターン数で数える予算上限で、天井で止まり、進捗を正直に報告します。
  • 4つのゲートは出自を1つ共有しています: ループに境界がなく、モデルは自分をそこに留めておけない。ゲートは飾りではなく、自律が暴走へ滑り落ちるのを防ぐものです — ただし、結果を明確に改善するときにだけ足してください1

>> レッスン5: 介入と操舵: 中断、方向転換、ヒューマン・イン・ザ・ループ

Footnotes

  1. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2 3 4 5 6 7 8 9 10 11 12

  2. Effective context engineering for AI agents — Anthropic Engineering — https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents 2 3 4 5 6 7 8

練習

01

以下は暴走した3つのエージェントループで、それぞれ1つの失敗モードを説明しています。それぞれについて: (1) このレッスンのどの失敗モードかを判定し、(2) 最も必要なフォールバックのゲートを挙げ、(3) そのゲートがループのどのステップを止めるのかを1文で述べてください。

レベル1: 各暴走に対応するゲートを結びつける
  • ケースA: 週次レポートのエージェントが、ターン2で「今週」を「先週」と読み違え、その後の十数ターンをかけて先週のデータを要約し、最終的に完成して見えるのに日付がすべて間違っているレポートを提出しました。
  • ケースB: リサーチエージェントが API を呼び、「invalid key」というエラーを受け取りました。再試行することにして、同じエラーを受け取り、それを繰り返し — いまターン12にいます。
  • ケースC: エージェントは本当に複雑なタスクに取り組んでいて正常に動作していますが、ターン数が多く各ターンが大きなコンテキストを運ぶため、一晩の API 請求が予想を大きく上回りました。
完了基準 · ローカルでチェック
02

ある人がこう主張します: 「最大ターン数上限を50に設定済みだ。それで十分だろう — ループがどれだけ暴れても、50ターン走って止まる。そのうえに無進捗検知なんて要らない」。

レベル2: 最大ターン数上限と並んで無進捗検知に居場所がある理由

この主張に反論するか、補ってください。要件: (1) ターン5で空回りを始めたとき、上限だけのループが何を無駄にするかを説明する、(2) 無進捗検知と最大ターン数上限が代替ではなく相補的である理由を説明する、(3) このレッスンの掛け算のコストとエラーの累積増幅の議論を結びつけて、早く止めることの根拠を示す。

完了基準 · ローカルでチェック