このレッスンのロックを解除するには、まず「レッスン4:ツール呼び出しと外部連携」を完了してください。前のレッスンに戻る

はじめに

本番環境では、エージェントシステムは驚くような形で壊れます。LLMがツール名を幻覚する。APIが500を返す。ユーザーがエージェントの範囲外の質問をする。モデルが同じツールを同じ入力で何度も呼び出すため、ループが永遠に続く。これらのいずれも理論上の話ではありません — どんなエージェントであれ、しばらく動かせば必ず直面します。

このレッスンでは、エージェントを信頼性高くするパターンを扱います: バックオフ付きリトライ、ツール呼び出し前の入力検証、モデル応答後の出力検証、暴走ループを止めるサーキットブレーカー、モデルがパースできない出力を生成したときのグレースフルフォールバック。目標は、エージェントを完璧にすることではありません — 失敗を予測可能で、ログに記録され、復旧可能なものにすることです。

ツール実行前の入力検証

外部ツールに何かを渡す前に検証してください。最も一般的な失敗パターンは、エージェントが構文的に正しく見えるが意味的にはナンセンスなツール引数を生成することです — 9999年の日付、存在しないユーザーID、句が欠落したSQLクエリなど。

python
def get_user_orders(user_id: str, date_from: str, date_to: str) -> dict:
# Validate user_id is numeric and exists
if not user_id.isdigit() or not user_exists(user_id):
return {"error": "invalid_user_id", "message": f"User {user_id} not found"}
# Validate date format
try:
from datetime import datetime
datetime.strptime(date_from, "%Y-%m-%d")
datetime.strptime(date_to, "%Y-%m-%d")
except ValueError:
return {"error": "invalid_date", "message": "Dates must be YYYY-MM-DD"}
# Validate date range
if date_from > date_to:
return {"error": "invalid_range", "message": "date_from must be before date_to"}
# Now safe to call the actual tool
return query_orders(user_id, date_from, date_to)

検証が失敗した場合は、例外ではなく構造化エラーオブジェクトを返してください。エージェントはこれを読み、次に何をすべきかを判断します: ユーザーに明確化を求める、別のアプローチを試す、または潔くあきらめる。

リトライ、バックオフ、サーキットブレーカー

一時的な障害(ネットワークタイムアウト、503エラー、レート制限)にはリトライが必要です。ただし、単純なリトライは事態を悪化させます — ダウンストリームサービスが過負荷の場合、さらに激しく叩き続ければそれは復旧しません。

python
import time
import random
def call_with_retry(fn, max_attempts=3, base_delay=1.0):
for attempt in range(max_attempts):
try:
result = fn()
if attempt > 0:
log_recovery(attempt) # Log that retry succeeded
return result
except RetryableError as e:
if attempt == max_attempts - 1:
raise
delay = base_delay * (2 ** attempt) + random.uniform(0, 0.5)
log_retry_attempt(attempt, e, delay)
time.sleep(delay)

ジッター付き指数バックオフはリトライトラフィックを分散させ、すべてのエージェントが同時にリトライする同期を防ぎます。一時的と分類されたエラーでのみリトライしてください — 400 Bad Requestでは決してリトライしないでください。同じ入力は毎回同じ失敗を生成するからです。

サーキットブレーカーはさらに一歩進みます: あるツールが連続してN回失敗した場合、クールダウン期間中はそれを呼び出すのをやめて即座に失敗します。これにより、エージェントが死んだサービスにループするのを防ぎます。

python
class CircuitBreaker:
def __init__(self, failure_threshold=5, cooldown_seconds=60):
self.failures = 0
self.threshold = failure_threshold
self.cooldown = cooldown_seconds
self.open_until = 0
def call(self, fn):
if time.time() < self.open_until:
raise CircuitOpenError(f"Circuit open until {self.open_until}")
try:
result = fn()
self.failures = 0
return result
except Exception:
self.failures += 1
if self.failures >= self.threshold:
self.open_until = time.time() + self.cooldown
raise
closed -> open -> half-openの遷移を示すサーキットブレーカーの状態遷移図

モデル出力の検証とループ処理

モデルは時として、期待するスキーマと一致しない出力を生成します — 必須フィールドが欠落したJSONブロブ、引数の型が誤ったツール呼び出し、実際には幻覚された自然言語の文章である「関数呼び出し」など。これを捕捉するレイヤーが必要です。

python
def parse_agent_response(raw: str) -> AgentAction:
try:
parsed = json.loads(raw)
except json.JSONDecodeError:
return AgentAction.recover("Model returned non-JSON output")
if "tool" not in parsed or "arguments" not in parsed:
return AgentAction.recover("Missing tool or arguments field")
if parsed["tool"] not in ALLOWED_TOOLS:
return AgentAction.reject(f"Model tried to call unknown tool: {parsed['tool']}")
return AgentAction.execute(parsed["tool"], parsed["arguments"])

ループ検出も同様に重要です。直近のN個の(ツール, 引数)のペアを追跡します。同じペアがM回を超えて繰り返された場合、エージェントを中断し、ユーザーにエラーを表示します — 「同じ手順を繰り返しているようです」のようなものです。このガードがなければ、混乱したモデルが同じ行き止まりのツールを永遠に呼び続けて、何千ものトークンを消費する可能性があります。

グレースフルデグラデーションとユーザー向けエラー

エージェントがタスクを完了できない場合、ユーザーへの応答は具体的で実行可能なものであるべきです。「エラーが発生しました」は無意味です。「注文サービスが一時的に利用できないため、注文を取得できませんでした。1分後にもう一度お試しいただくか、代わりにアカウントの概要を表示することもできます」は、ユーザーに何が起きたかを伝え、代案を提供します。

フォールバックチェーンを構築してください: 主要ツールが失敗した場合、より簡素な代替ツール(たとえば、ライブデータの代わりにキャッシュデータ、またはスコープを絞った別のLLM呼び出し)を試してください。すべてが失敗した場合、制限を正直に説明する最終応答を返してください。欠落したツール呼び出しを隠すために決してデータを捏造しないでください — ユーザーはこれを見抜くので、信頼は失われます。

まとめ

信頼性の高いエージェントは、検証、リトライ、グレースフル障害処理の層から構築されます。ツールの前に入力を検証し、一時的なエラーを指数バックオフとジッター付きでリトライし、暴走ループを止めるためにサーキットブレーカーを使用し、厳密なスキーマに対してモデル出力を検証し、無限ループを防ぐために繰り返されるアクションを検出し、問題が発生したときには常に具体的で正直な答えをユーザーに提供します。このレッスンのパターンは、LangChain、カスタムオーケストレーション、または他のあらゆるエージェントフレームワークで構築する場合でも適用されます — フレームワークがこれらの一部を処理してくれるかもしれませんが、その下層で何が起きているかを理解しておくべきです。

レッスンの練習

レッスンのチェックポイント

学んだ内容を復習し、回答へのフィードバックを受けましょう。

0/1
回答済み · 0/1