2026年9月7日時点の公開ドキュメントを根拠に、LLMエージェントで現場がつまずきやすい失敗パターンと、公式ガイドが示す対策の骨格を整理します。前日までの「ReAct」「Plan-and-Execute」「HITL」「マルチエージェント」の続きとして、動くデモの次に壊れる場所を押さえます。
この記事のゴールは、「プロンプトを直す」前に、ループ上限・ツール境界・ガードレール・再開時の冪等性のどれが欠けているかを切り分けられる状態になることです。
結論:失敗は「賢さ不足」より「終了条件と境界」の欠落が多い
| 失敗の型 | 起きやすい症状 | 先に入れる対策 |
|---|---|---|
| 無限ループ/ターン過多 | 同じツールを何度も呼ぶ、課金だけ増える |
max_turns 等の上限、終了条件、同一呼び出しの検知 |
| 誤ツール/存在しないツール | 権限外操作、名前違いで落ちる | ツール一覧の最小化、見つからない時の回復方針 |
| ガードレールが入口だけ | 途中の書き込み・送信が無検査 | 入力/出力/ツール単位の検査を使い分ける |
| 再開で副作用が二重実行 | 承認後にメールが2通、DBが二重登録 | interrupt前の副作用を冪等にする/後ろへ移す |
| マルチ化の早すぎ | トレースが読めない、所有権が曖昧 | まず1体。分割は能力・ポリシー・プロンプトが分かれるときだけ |
迷ったら次の一文で十分です。
エージェントは「考える」より先に「止まらない・誤って書く・再開で二重実行する」で壊れる。上限・ツール境界・ガードレール・冪等性をコード経路に置く。
パターン1:エージェントループが止まらない
確認できる事実
- OpenAI Agents SDK の Runner は、最終出力になるまで LLM呼び出し →(handoff/tool)→ 再実行 のループを回します。
- ターン数が
max_turnsを超えるとMaxTurnsExceededを送出します。max_turns=Noneにすると上限自体を無効化できます(本番では原則避ける)。 - 最終出力と判定される条件は、「望ましい型のテキスト出力があり、かつツール呼び出しが無い」ことです。
原文: "If we exceed the max_turns passed, we raise a MaxTurnsExceeded exception. Pass max_turns=None to disable this turn limit."
日本語訳: 「渡した max_turns を超えたら MaxTurnsExceeded を送出する。max_turns=None にするとこのターン上限を無効化できる」(出典: OpenAI Agents SDK — Running agents)
実務解釈
| 症状 | よくある原因 | 対策 |
|---|---|---|
| 同じ検索を繰り返す | ツール結果が「完了/失敗」を明示していない | 結果に SUCCESS/ERROR と次に取るべき行動を書く |
| ツールを呼んだまま終わらない | モデルが常に tool call を出し続ける |
max_turns を必須化し、超過時のユーザー向けメッセージを用意する |
| 上限を上げるだけで直したつもり | 終了条件が無い | ルーター(条件分岐)が END/最終出力へ戻れるかを先に直す |
上限は最後の安全網です。根本は「いつ最終回答にするか」を状態とツール結果で機械的に決められるかにあります。
パターン2:誤ったツールを呼ぶ/存在しないツールを呼ぶ
確認できる事実
- Agents SDK では、モデルが発行した function tool 名が現在のエージェントに無い場合、既定では
ModelBehaviorErrorになります。 -
RunConfig(tool_not_found_behavior="return_error_to_model")にすると、モデル可視のエラー出力を返して再試行させ、実行を回復可能にできます。 - ツール名の衝突(未名前空間)は既定
"warn"、厳格にするなら"error"で起動前に止められます。
実務解釈
| NG | OK |
|---|---|
| 読み取り・書き込み・送信を全部同じエージェントに載せる | 危険ツールは別エージェント/承認付きに分離する |
似た名前のツールを並べる(send_mail / send_email) |
名前・説明・引数スキーマを一意にし、不要な候補を消す |
| 存在しないツールで即クラッシュ | 開発初期は return_error_to_model、本番は監視しつつ候補を絞る |
ツールを増やせば賢くなるのではなく、候補が増えるほど誤選択も増えます。昨日のマルチエージェント記事と同じく、分割のトリガーは「能力・ポリシー・プロンプトの分離」です。
パターン3:ガードレールが「最初の入力」だけで終わる
確認できる事実
- OpenAI Agents SDK の Guardrails は、大きく 入力/出力/ツール の3層があります。
- 入力ガードレールはチェーン先頭のエージェントにだけ、出力ガードレールは最終出力を出すエージェントにだけ走ります。
- マネージャ・handoff・専門エージェントをまたぐワークフローで、毎回の function tool 呼び出し前後を検査したい場合は、エージェント級ではなく tool guardrails を使います。
- 入力ガードレールは並列実行(既定)だと、トリップしてもエージェントが既にトークンやツールを消費していることがあります。コスト/副作用を避けたいときは blocking(
run_in_parallel=False)を選びます。
原文: "If you need checks before and/or after each custom function-tool call in a workflow that includes managers, handoffs, or delegated specialists, use tool guardrails instead of relying only on agent-level input/output guardrails."
日本語訳: 「マネージャや handoff、委譲された専門家を含むワークフローで、各カスタム function-tool 呼び出しの前後に検査が必要なら、エージェント級の入出力ガードレールだけに頼らず tool guardrails を使え」(出典: OpenAI Agents SDK — Guardrails)
実務解釈
| 置き場所 | 向く検査 |
|---|---|
| 入力ガードレール | スコープ外依頼、プロンプトインジェクションの粗い検知 |
| 出力ガードレール | 最終回答の方針違反、機密の漏洩っぽい文言 |
| ツール入力ガードレール | 送信先・金額・削除対象など実行直前の検証 |
| ツール出力ガードレール | ツール結果に秘匿情報が混ざった場合の差し替え |
「システムプロンプトに書いただけ」はガードレールではありません。実行経路で止められるかが境界です。
パターン4:Human-in-the-Loop 再開で副作用が二重に走る
確認できる事実
- LangGraph の
interrupt()は、再開時にそのノードを先頭からやり直します。interruptより前のコードも再実行されます。 - 公式ドキュメントは、
interruptより前の副作用は冪等であることを求めています(upsert、idempotency key、読み取り後書き込みなど)。可能なら副作用は interrupt の後か、別ノードへ分離します。
原文: "When execution resumes ... the runtime restarts the entire node from the beginning—it does not resume from the exact line where interrupt was called."
日本語訳: 「再開時、ランタイムはノード全体を先頭からやり直す。interrupt が呼ばれた行の直後から再開するわけではない」(出典: LangGraph — Interrupts)
実務解釈
| NG | OK |
|---|---|
send_email() の直後に interrupt("承認?")
|
承認後にだけ送る/送信APIに冪等キーを付ける |
| メモリだけの承認待ち | checkpointer 等で永続化し、プロセス再起動後も再開できる |
静的エッジと Command(goto=...) の二重経路 |
どちらか一方のルーティング権限に統一する |
HITL は「人が見る」だけでなく、再開モデルを前提に副作用を設計する問題です。
パターン5:複雑化しすぎて原因が追えない
確認できる事実
- OpenAI のオーケストレーション解説は、まず1体から始め、専門家を足すのは能力分離・ポリシー分離・プロンプト明確化・トレース可読性に実質的な改善があるときだけ、と述べています。
- Tracing(トレース)を早期に開き、モデル呼び出し・ツール・handoff・ガードレールを可視化することが Quickstart でも推奨されています。
実務解釈
失敗調査の順番を固定すると早くなります。
-
止まったか/回り続けたか(
MaxTurnsExceeded、recursion limit) - どのツールが何引数で呼ばれたか(トレース)
- ガードレールはどの層で発火したか(入力/出力/ツール)
- 再開パスでノードが二重に走っていないか(HITL)
- 最後にプロンプト文言
プロンプト調整を最初にやると、境界バグを隠したままコストだけ増やすことが多いです。
実装チェックリスト
ループと終了
-
Runner/グラフにターン上限(例:
max_turns)を入れ、超過時のユーザー向け文言を決める - ツール結果に完了/失敗が読める形で返す
- 同一ツール+同一引数の連続呼び出しを検知して打ち切る方針を持つ
ツール境界
- エージェントごとに見えるツールを最小化する
- 書き込み・送信・削除は承認または専用エージェントへ分離する
-
開発時は
tool_not_found_behaviorの回復方針を決め、本番監視に載せる
ガードレール
- 入口・最終出力・危険ツールの3層で「どこで止めるか」を表にする
- handoff/マネージャ構成なら tool guardrails の要否を確認する
- コスト重視の入力検査は blocking 実行を検討する
HITL/再開
-
interrupt前の副作用を冪等にするか、後ろへ移す - 承認状態を永続化する
-
静的エッジと
Command(goto=...)を混在させていないか確認する
観測
- トレースで tool/handoff/guardrail を1本の失敗シナリオとして追える
- 失敗時に「上限/誤ツール/ガード/二重実行」のどれかを必ずラベル付けする
失敗パターン(再掲・短文)
パターン1:上限なしのエージェントループ → 対策: max_turns 必須化+終了条件を状態で表現する。
パターン2:ツールを全部載せた万能エージェント → 対策: 危険操作を分離し、候補を減らす。
パターン3:入力ガードレールだけで安心する → 対策: 実行直前は tool guardrails。
パターン4:承認前にメール送信や課金APIを呼ぶ → 対策: 副作用は承認後、または冪等キー必須。
パターン5:プロンプト修正だけで直そうとする → 対策: まずトレースで境界バグを切り分ける。
パターン6:マルチエージェント化で所有権が曖昧 → 対策: 最終回答の持ち主を先に決める。増やしすぎない。
パターン7:見つからないツールで即クラッシュ → 対策: 回復可能なエラー返却と監視をセットにする。
参考リンク
- OpenAI Agents SDK — Running agents(agent loop / max_turns)
- OpenAI Agents SDK — Guardrails
- OpenAI Agents SDK — Agents
- OpenAI Developers — Agents Quickstart
- LangGraph — Interrupts(Human-in-the-loop)
- LangGraph — Graph API(Command / idempotency)
この記事を書いた人✏️@YushiYamamoto
ITPRODX.com代表 / AIアーキテクト
Next.js / TypeScript / n8nを活用した自律型アーキテクチャ設計を専門としています。
日々の自動化の検証結果や、ビジネス側の視点(ROI等)に関するより深い考察は、以下の公式サイトおよびnoteで発信しています。
