本番AIで高額な損失を生む典型的な障害を想像してください。火曜日の午前3時、カスタマーサポートのエージェントが無限ループに陥ります。同じツールを呼び出し、同じエラーを受け取り、パラメーターをわずかに変えて再試行し、また同じエラーを受け取ることを繰り返します。1分間に何百回もです。朝までに、チームには予想外の4桁または5桁の請求が届きます。金額は、モデルと呼び出し頻度によって変わります。
これは珍しいことではありません。ループするエージェントは、本番環境で最も一般的かつ重大な障害の1つです。厄介なのは、一見すると「動いているように見える」ことです。エージェントはアクションを実行しており、呼び出しも成功しています(または予想どおりに失敗しています)。トレースを確認して初めて、同じパターンが繰り返されていると分かります。
無限ループしないエージェントを構築するには、意識的なアーキテクチャ上の選択が必要です。エージェントの障害の多くは予測可能で、防止するパターンもよく知られています。信頼できるエージェントをリリースするチームは、これらのパターンを厳格に実装しています。
この記事では、エージェントがループする原因、それを防ぐアーキテクチャパターン、そしてすり抜けたループを検出する運用上のガードレールを解説します。
エージェントがループする理由
エージェントがループする背景には、いくつかの仕組みがあります。
1. 進捗に関する混乱
エージェントには「進展したか?」という明確な感覚がありません。何かを試し、結果を観察し、別のことを試そうと判断します。進捗を明示的に追跡しなければ、「別のことを試す」が「同じことを少し変えて試す」になり得ます。
2. 終了条件の欠如
エージェントのプロンプトには「ユーザーを支援する」と書かれていても、「Xが真になったら停止する」とは書かれていません。明確な終了条件がないと、エージェントは「支援」を続けます。もう1つ情報を探し、もう1つツールを試し、さらに改善しようとします。
3. 再試行の病理
何かが失敗すると、エージェントは自然に再試行します。再試行の上限がなければ、同じ失敗を無期限に繰り返す可能性があります。エージェントは「すでに10回試した」ではなく、「まだ成功していない」と認識します。
4. 状態の健忘
エージェントの作業記憶には、直近のターンしか含まれません。ループが20回繰り返されても、コンテキストに含まれるのは直近5回だけかもしれません。外部の観察者には明白になったパターンを見失います。
5. ツールの不整合
ツールが分かりにくい結果や矛盾した結果を返します。エージェントは再試行します。ツールはまた分かりにくい結果を返します。エージェントは「前回の解釈が間違っていたのかもしれない」と考え、別の方法を試します。それでもツールは分かりにくい結果を返します。こうしてループします。
6. 過度な楽観性
エージェントは粘り強く取り組むよう訓練されているため、停止して助けを求める方がよい状況でも試行を続けます。小さな混乱が積み重なる長期タスクでは、特に問題になります。
7. 目標のドリフト
エージェントは、何を達成しようとしていたのかを徐々に見失います。サブタスクへ分岐し、さらにそのサブタスクへ進み、主目標へ戻らないまま、周辺的に関連する領域を探索します。
エージェントによって失敗の仕方は異なりますが、防御策には重なる部分があります。
パターン1:厳格なステップ予算
最も単純で重要な防御策は、最大ステップ数を設けることです。たとえば、エージェントが利用できるツール呼び出しを20回にします。20回に達したら、最終回答を生成するか、エスカレーションしなければなりません。
実装:
def agent_loop(query, max_steps=20):
messages = [{"role": "user", "content": query}]
for step in range(max_steps):
response = call_llm(messages, tools=available_tools)
if response.is_final_answer:
return response.content
result = execute_tool(response.tool_call)
messages.append(response)
messages.append({"role": "tool", "content": result})
# 予算に到達 — 最終回答を強制
return force_final_answer(messages)
予算は、タスクに合わせて調整する必要があります。単純なタスクは5~10ステップ、複数ソースを扱う複雑なタスクは20~30ステップ、自由度の高い調査は50ステップ以上です。ただし、必ず上限を設けます。
予算に達したら、エージェントは把握している情報から最善の回答を生成するか、人にエスカレーションします。
このパターンだけで、壊滅的なループの大半を防げます。必ず実装してください。
バリエーション
トークン予算。 ステップ数の代わりに、またはステップ数に加えて、総トークン数を制限します。ステップは少なくても、各ステップで50Kトークンの推論トレースを使うエージェントを防ぎます。
コスト予算。 ステップ/トークン予算をユーロに換算します。予算超過を抽象的なものにせず、防止できます。
時間予算。 実時間の上限です。ユーザー向けのフロー(「30秒以内に応答する」)に役立ちます。
本番エージェントの多くは、何らかの形で4つすべての予算を持ちます。どれか1つに達すると、実行を終了します。
パターン2:進捗の追跡
予算だけでは、エージェント自身が行き詰まっているとは分かりません。単に最終的に停止させるだけです。進捗を追跡すると、エージェントがループに気づき、そこから抜け出せます。
単純な実装では、エージェントが明示的な「進捗」ログを管理します。各ステップで、得られた新しい情報や変化したことを記述します。
ステップ1:顧客「Smith」を検索。12件が一致。
ステップ2:有効なアカウントに絞り込み。4件が残る。
ステップ3:最近のアクティビティを確認。顧客234に料金に関する最近のチケットがあった。
ステップ4:チケットの詳細を取得。苦情は最近の料金改定に関するものだった。
ステップ5:回答の下書きを作成。送信準備完了。
各ステップで新しい情報が加わっています。エージェントがステップ6へ進んでも、進捗ログに何も加わらない、つまり同じ検索、同じ結果、同じ結論なら、空回りしています。
プロンプトには、次のように記載できます。
次のアクションを決める前に、直近数ステップで学んだことを要約してください。
直近3ステップで新しい情報を得ていない場合は停止し、次のいずれかを行ってください。
- 現在の情報で最善の回答を生成する。
- 問題をエスカレーションし、試したことと不足しているものを説明する。
これにより、「進捗がない」ことがエージェントから見えるようになり、対応できます。
パターン3:反復の検出
エージェントがまったく同じツール呼び出しを繰り返すことがあります。これはプログラムで簡単に検出できます。
def detect_repeat(history):
recent_calls = [c for c in history[-5:] if c.is_tool_call]
if len(recent_calls) < 3:
return False
call_signatures = [(c.tool, json.dumps(c.args, sort_keys=True)) for c in recent_calls]
return len(set(call_signatures)) < len(call_signatures) / 2
反復を検出したら、介入します。
- 「このツールをこのパラメーターで最近呼び出しました。結果は変わっていません。別の手法を試すか、終了してください」というメッセージを挿入する。
- または、強制終了する。
これにより、最も明白なループを自動的に検出できます。
パターン4:行き詰まり状態の検出
完全に同じ反復だけでなく、より微妙な行き詰まり状態も検出できます。
パターン認識。 別のLLM呼び出しを使い、「直近5ステップを見ると、このエージェントは進展していますか?」と評価します。進展していなければ、処理を中断します。
def is_stuck(history):
recent = format_history(history[-5:])
response = call_llm(
system="エージェントが進展しているかどうかを評価してください。",
user=f"エージェントの直近のステップ:\n{recent}\n\nエージェントは意味のある進展をしていますか、それともループで行き詰まっていますか?回答:progressing | stuck"
)
return response.content.strip() == "stuck"
この確認を数ステップごとに実行します。stuckが返されたら、介入します。
ツールの多様性。 エージェントが5ステップ以上にわたり1つのツールしか呼び出していない場合は、疑わしい状態です。別の方法を試すか、停止するよう強制します。
エラーパターン。 同じツールが同じエラーを3回以上返したら、そのツールの使用を停止します。再試行を増やしても、エージェントが不足している入力を見つけられることはありません。
パターン5:振り返りポイント
エージェントの実行中、特定の時点で明示的な振り返りを強制します。
5ステップごとに、エージェントは次の振り返りを行う必要があります。
1. 当初の目標は何だったか?
2. これまでに何を学んだか?
3. まだ何を知る必要があるか?
4. 進展しているか、それとも反復しているか?
5. 続行すべきか、停止すべきか?
振り返りにより、エージェントは直近の次のアクションを考える状態から一歩離れ、全体像を評価せざるを得なくなります。
これは、長期タスクで特に効果的です。強制的な振り返りがなければ、エージェントはドリフトします。振り返りがあれば、自らドリフトに気づけます。
パターン6:目標の固定
長時間にわたるエージェントの実行では、当初の目標が失われます。エージェントのコンテキストウィンドウが中間ステップで埋まり、元の質問は大きなコンテキストのごく一部になります。
これに対処するには、目標を繰り返し固定します。
- すべてのシステムメッセージの先頭に、当初の目標を含める。
- Nステップごとに、エージェントに目標を言い直させる。
- 別の「目標トラッカー」を使い、各ステップが目標に沿っていることを確認する。
プロンプトへの追加例:
当初の目標:[ユーザーの依頼をそのまま記載]
各アクションの前に、次を確認してください。
- このアクションは当初の目標達成に役立つか?
- はいの場合は、続行する。
- いいえの場合は、直接目標に戻る。
パターン7:サブタスクの範囲設定
長時間動くエージェントは、自然にタスクをサブタスクへ分割します。構造がなければ、エージェントが迷子になるまで、サブタスクからサブサブタスクが再帰的に生まれ続ける可能性があります。
構造を設けます。
- エージェントがサブタスクを明示的に特定する。
- 各サブタスクに独自の予算を設ける。
- サブタスクを完了(または失敗)したら、メインタスクに戻る。
- サブタスクは、無制限にサブサブタスクを生成できない。
LangGraphのようなフレームワークは、これを形式化しようとしています。各ノードが明確なステップであり、遷移も明示されたステートマシンです。
複雑なエージェントには、この構造が不可欠です。単純なエージェントには過剰です。
パターン8:脱出手段
エージェントが行き詰まったときには、明示的に停止する方法が必要です。
エスカレーション。 「このタスクは完了できません。試したことと不足しているものは次のとおりです。」エージェントは停止し、問題を表面化させます。
部分的な完了。 「AとBは完了しました。CはXによってブロックされています。」エージェントは完全に成功する必要はなく、有用な部分的出力を生成できます。
確認。 「ユーザーからさらに情報が必要です:……」エージェントは一時停止して質問します。
これらは最後の手段ではなく、エージェントにとって第一級の選択肢であるべきです。エージェントのプロンプトでこれらに触れ、行き詰まった場合の利用を促す必要があります。
プロンプトへの有用な追加例:
次のいずれかの状況に遭遇した場合、試行を停止し、適切に応答してください。
- ツールが一貫して同じエラーを返す。
- 進展がないまま、3つの異なる手法を試した。
- ユーザーだけが提供できる情報を必要としている。
- タスクがツールの対応範囲を超えて複雑である。
これらの場合:
- ツールエラー:問題を説明し、ユーザーにサポートへの連絡を提案する。
- 進展がない:試したことを報告し、指示を求める。
- 情報が不足している:ユーザーに具体的な質問をする。
- 複雑すぎる:要約を添えて人の支援へエスカレーションする。
パターン9:確信度に応じたアクション
エージェントは、自信がある場合とない場合を認識する必要があります。確信度が低いまま行動することが、ループの始まりになります。
1つのパターンとして、重大な結果を伴うすべてのアクションに、明示的な確信度を求めます。
delete_recordを呼び出す前に、このアクションが正しいという確信度を1~5で示してください。
4未満の場合は呼び出さず、代わりに人の確認を求めてください。
これは、破壊的または高価なアクションで特に効果的です。アクションを実行する前に、エージェントは高い確信度を明言しなければなりません。
振り返りと組み合わせることで、エージェントが「計画を実行」するのではなく「いろいろ試している」ケースを検出できます。
パターン10:ツールレベルのガード
エージェントレベルのパターンに加えて、ツール自体にもガードを設けられます。
セッションごとのレート制限。 1つのセッションで、ツールをN回だけ呼び出せます。N回を超えると「rate limit」を返し、エージェントに別の行動を強制します。
冪等性。 同一の呼び出しが繰り返されたら、再実行せずキャッシュした結果を返します。ツールを連打するループを防ぎます。
コスト上限。 高価なツール(負荷の高いDBクエリ、従量課金のサードパーティAPI)に、セッションごとの制限を設けます。
障害時のサーキットブレーカー。 このセッションで3回失敗したツールを無効にします。エージェントは、そのツールを呼び出せなくなります。
これらは、エージェントレベルのパターンを補完します。エージェントがループしようとしても、ツールが防ぎます。
パターン11:外部監視
エージェント内部のあらゆるパターンをすり抜けた問題は、外部モニターで検出します。
監視プロセスが、実行中のすべてのエージェントを監視します。確認する項目は、次のとおりです。
- エージェントごとのステップ数。
- エージェントごとのトークン使用量。
- エージェントごとのコスト。
- エージェントごとの時間。
- ツール呼び出しのパターン。
しきい値を超えたエージェントは停止し、アラートを送信します。
これは最後の防衛線です。エージェント自体が壊れていても、費用に損害が出る前にモニターが検出します。
実装では、次のようにします。
- 時系列データベースでエージェントの指標を追跡する。
- ルールによって停止命令を発動する(「エージェントが5分を超えて実行されている場合は停止」)。
- 小規模なサービスで監視と適用を行う。
多数のエージェントを同時に実行するシステムには、不可欠です。
パターン12:人を介在させるチェックポイント
影響の大きいエージェントには、人によるチェックポイントを組み込みます。エージェントはチェックポイントまで実行した後、人の承認を待ちます。
一般的なチェックポイントは次のとおりです。
- 破壊的なアクションの前。
- エージェントが取り消せない決定の後。
- 長期タスクの主要な節目。
- 確信度が下がったとき。
これは不信感の問題ではありません。修正コストが低いうちにエラーを捉えるためです。
実用的なワークフローでは、エージェントが準備作業を自律的に行い、要約とアクション案を提示し、人が承認した後、エージェントが実行します。人が関与するのは意思決定であり、すべてのステップではありません。
実例:長時間実行する調査エージェント
実際のエージェントにパターンを適用した例を紹介します。
タスク: 競合他社を調査し、概要資料を作成する。
想定作業: ウェブ検索10~30回、ページ閲覧20~50回、結果を1,000語の概要資料にまとめる。
適用するパターン:
-
ステップ予算: 合計60ステップ。
-
トークン予算: 300Kトークン(コンテキスト + アクション)。超過した場合は現在の調査結果を要約して続行。
-
コスト予算: 1回の実行につき€2。超過した場合は停止し、部分的な概要資料を返す。
-
時間予算: 実時間で5分。
-
進捗の追跡: エージェントが各ステップで、新しい情報を「調査結果ログ」に追加する。新しい調査結果がないまま3ステップ経過したら脱出。
-
反復の検出: 同じ検索クエリを2回実行して似た結果が返った場合、別の手法を強制。
-
振り返りポイント: 10ステップごとに、進捗と残りの作業を振り返る。
-
目標の固定: すべてのシステムメッセージの先頭に、当初の概要資料の目標を記載。
-
脱出手段: 「十分な情報を得た」と「十分な情報を見つけられない」のどちらでも、エージェントを正常に終了。
-
外部モニター: 独立した監視プロセスが、予算を超えたエージェントを停止。
結果: 実行時間の中央値は3分、コストの中央値は€0.40。失敗率(ループまたはタイムアウト)は1%未満です。作成される概要資料は700~1200語で、事実に基づき、有用な出発点になります。
これらのパターンがなければ、時折30分に及ぶ実行、€20以上のコスト、機能しなくなるセッションが発生します。パターンによって、テールリスクは劇的に低下します。
本番環境での検出
パターンを導入しても、時折問題がすり抜けます。次の方法で検出してください。
長時間実行されているエージェントへのアラート。 実行時間が中央値の2倍を超えたエージェントについてアラートを発報します。
コスト急増へのアラート。 エージェント単位または全体のコストがしきい値を超えた場合です。
反復パターンへのアラート。 ループを示唆するツール呼び出しパターンがある場合です。
長時間トレースの日次レビュー。 人が毎日、最も長い10件のトレースに目を通します。評価が見逃す問題を発見できます。
集計指標: 時間経過に伴うループ率です。何らかの変化(モデル更新、プロンプト変更)によってループ頻度が上昇した場合に検出できます。
有用なダッシュボードは、エージェントの実行時間の分布です。分布の裾を見ると、ループの発生状況が分かります。
よくある誤り
繰り返し目にするパターンをいくつか紹介します。
誤り1:ステップ予算がない。 「必要になったら追加しよう」と考えます。その後、午前3時にエージェントがループし、追加しておけばよかったと後悔します。初日から必ず追加してください。
誤り2:予算が高すぎる。 「100ステップあれば十分だろう」と考えますが、ループがすべて消費します。最悪ケースではなく、中央値の2~3倍に予算を設定してください。
誤り3:外部モニターがない。 エージェントが自ら停止すると信頼します。停止しない場合もあります。本番環境には外部モニターが不可欠です。
誤り4:ループを検出しても分析しない。 ループが発生し、モニターが停止させ、チームは次へ進みます。翌週、同じループが発生します。検出したループについては必ず事後分析を行ってください。何が引き金になったか、何が変わったか、その種類の障害を防げるかを確認します。
誤り5:単純なタスクで過剰に振り返る。 5ステップのタスクで5ステップごとに振り返りを強制しても、利益のないオーバーヘッドになります。タスクの複雑さに合わせて調整してください。
誤り6:長いコンテキストで目標を見失う。 ステップ1で1回触れただけの目標は、ステップ50まで残りません。定期的に固定し直してください。
誤り7:エージェントによる進捗の自己申告を信頼する。 実際には進展していなくても、エージェントは進展していると言います。可能な場合は、外部から検証してください。
誤り8:エージェントによる再帰的な自己呼び出しを許す。 「このタスクをサブエージェントに分解する」という指示により、エージェントが指数関数的に生成される可能性があります。許可する場合は、厳格な予算を設けてください。
ループを許容できる場合
すべてのループが悪いわけではありません。正当に多数の反復を必要とするタスクもあります。
- コードの反復改善(記述、テスト、修正、反復)。
- 分岐を伴う多段階の調査。
- 最適化タスク(バリエーションを試し、評価し、改善)。
この場合、ループは失敗ではなく、作業そのものです。適用するパターンは変わります。
- 余裕のあるステップ予算(50~200ステップ)。
- 「ループ」ではなく、明示的な「反復」として扱う。
- 品質向上の追跡。反復ごとに指標が改善する必要がある。
- 改善が頭打ちになったら、強制的に停止する。
原則は、「意図した反復作業」と「意図しないループ」を区別することです。それぞれに適切なパターンを適用してください。
本番環境向けチェックリスト
エージェントの無限ループは予測可能で、一般的で、防止できます。そのパターンはよく知られています。ステップ予算、進捗の追跡、反復の検出、振り返り、目標の固定、脱出手段、ツールのガード、外部モニターです。
これらは、あれば望ましい仕上げではありません。リリースできるエージェントと、予想外の4桁の請求を生むエージェントを分けるものです。
あらゆる本番エージェントに対するチェックリストは、次のとおりです。
- 最大ステップ予算。
- 最大トークン予算。
- 最大コスト予算。
- 最大時間予算。
- 呼び出しの反復検出。
- 進捗の追跡。
- 定期的な振り返り。
- 目標の固定。
- 複数の脱出手段。
- 停止機能を備えた外部モニター。
それぞれの実装は単純です。組み合わせることで、「実行したまま放置するには危険なエージェント」と「本番環境で信頼できるエージェント」の違いを生みます。
これらのパターンを組み込み、テストしてください。排除できるテールリスクは、作業にかかるコストを何倍も上回ります。



