0Pricing
AI Engineering Academy · レッスン

エージェントの失敗モードの分類

ツールエラー、形式不正な出力、推論ループ、コンテキスト枯渇、外部サービスの利用不能など、エージェントの失敗を分類する体系を作り、それぞれの復旧戦略を設計します

「エージェントの失敗モードの分類」はCoddyKit上の無料AI Engineering Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Engineering Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Engineering Academyコースには全4レッスンが含まれています。

エージェントが独特な形で失敗する理由

エージェントは、単純なLLM呼び出しとは異なる形で失敗します。単一ターンの呼び出しは、回答を返すかエラーをスローするかのどちらかです。一方、複数ステップのタスクを実行するエージェントは、どの時点でも失敗する可能性があり、その失敗が最終出力からは明らかでないこともあります。エージェントの失敗モードの分類を理解することは、自身の失敗を検出、診断、復旧できるエージェントを構築するための第一歩です。

失敗モード1:ツールエラー

ツールエラーは、エージェントが無効な引数でツールを呼び出した場合、ツールが例外を発生させた場合、またはツールが空の結果や不正な形式の結果を返した場合に発生します。例として、不正なクエリで検索APIを呼び出す、無効なSQLでデータベースにクエリを実行する、タイムアウトするコード実行環境を呼び出す、といったケースがあります。ツールエラーは、捕捉して処理できる明示的な例外シグナルを生成するため、最も検出しやすい失敗です。

class ToolError(Exception):
    def __init__(self, tool_name: str, args: dict, error: Exception):
        self.tool_name = tool_name
        self.args = args
        self.original_error = error
        super().__init__(f'Tool {tool_name} failed: {error}')

def safe_tool_call(tool_func, args: dict) -> str:
    try:
        result = tool_func(**args)
        if not result:
            return 'Tool returned empty result. Try a different approach.'
        return str(result)
    except Exception as e:
        raise ToolError(tool_func.__name__, args, e)

失敗モード2:不正な形式の出力

不正な形式の出力は、エージェントが期待される形式に一致しないテキストを生成した場合に発生します。たとえば、次のステップがJSONを期待しているのに自然言語を返す場合や、誤った構造の引数でツールを呼び出す場合です。これは、エージェントが現在のステップと以前のステップを混同したときによく起こります。使用する前にすべてのエージェント出力の形式を検証し、形式が誤っている場合は再度プロンプトを送ってください。

import json

def validate_agent_output(raw_output: str, expected_format: str) -> dict:
    if expected_format == 'json':
        try:
            return json.loads(raw_output)
        except json.JSONDecodeError as e:
            return {
                'valid': False,
                'error': f'Expected JSON but got invalid JSON: {e}',
                'raw': raw_output[:200]
            }
    return {'valid': True, 'data': raw_output}

失敗モード3:推論ループ

推論ループは、エージェントが進展することなく同じ行動や思考を延々と繰り返す場合に発生します。たとえば、同じ空の結果を受け取りながら、次に何を試せばよいかわからず、同じ検索クエリを10回連続で呼び出すことがあります。最近の行動を追跡し、繰り返しがないか確認してループを検出してください。ループを検出したら、別のアプローチを試すようエージェントに指示するメタプロンプトを挿入します。

from collections import Counter

class LoopDetector:
    def __init__(self, window: int = 5, threshold: int = 3):
        self.recent_actions = []
        self.window = window
        self.threshold = threshold

    def record(self, action: str) -> bool:
        self.recent_actions.append(action)
        if len(self.recent_actions) > self.window:
            self.recent_actions.pop(0)
        counts = Counter(self.recent_actions)
        most_common_count = counts.most_common(1)[0][1] if counts else 0
        return most_common_count >= self.threshold  # True = loop detected

失敗モード4:コンテキストの枯渇

コンテキストの枯渇は、エージェントが蓄積した履歴(ツール呼び出し、観測結果、思考)がモデルのコンテキストウィンドウを超えたときに発生します。モデルは履歴を暗黙に切り詰めて重要な情報を失うか、トークン上限エラーを発生させます。これを防ぐには、ステップ間のトークン使用量を追跡し、上限に達する前に履歴を圧縮(古いステップを要約)してください。

import tiktoken

CONTEXT_LIMIT = 100_000  # tokens
COMPRESS_AT = 80_000     # trigger compression with headroom

enc = tiktoken.encoding_for_model('gpt-4o')

def total_tokens(messages: list) -> int:
    return sum(len(enc.encode(str(m))) for m in messages)

def check_context(messages: list) -> str:
    tokens = total_tokens(messages)
    if tokens > COMPRESS_AT:
        return 'compress'
    if tokens > CONTEXT_LIMIT:
        return 'critical'
    return 'ok'

失敗モード5:外部サービスの利用不能

外部サービスの障害は、ツールの基盤サービスが停止している、レート制限を受けている、または予期しないエラーを返す場合に発生します。必要なデータベースに接続できずクエリを実行できないエージェントは、行き詰まってしまいます。エージェント側の問題である推論ループとは異なり、外部サービスの障害は環境側の障害です。リトライと指数バックオフで処理し、異なるデータソースを使って結果を近似できるフォールバックツールを用意してください。

import asyncio

async def resilient_tool_call(tool_func, args: dict, max_retries: int = 3) -> str:
    for attempt in range(max_retries):
        try:
            return await tool_func(**args)
        except (ConnectionError, TimeoutError) as e:
            if attempt == max_retries - 1:
                return f'Service unavailable after {max_retries} attempts. Error: {e}'
            wait = 2 ** attempt  # 1s, 2s, 4s
            await asyncio.sleep(wait)
    return 'Unexpected error in resilient_tool_call'

失敗モード6:目的の誤認

目的の誤認とは、エージェントがタスクを誤解し、微妙に異なる目的を追求することです。エージェントは処理を正常に完了しているように見えても、ユーザーが意図したタスクとは異なる可能性があるため、最も検出が難しい失敗です。開始時にエージェント自身の言葉で目的を言い換えさせ、結果が元の質問に実際に答えているかを確認する最終検証ステップを実装して、影響を抑えてください。

async def confirm_goal_understanding(original_task: str) -> str:
    resp = await client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {'role': 'system', 'content': 'Restate the task in your own words. Be specific about what the final deliverable should be.'},
            {'role': 'user', 'content': f'Task: {original_task}'}
        ]
    )
    return resp.choices[0].message.content

# Use the restatement as the first step of the agent
# to catch misunderstandings before any tools are called

失敗分類システムの構築

すべてのエージェント例外に種類のラベルを付ける、構造化された失敗分類器を作成してください。これにより、適切な復旧戦略へ自動的に振り分けられます。どの失敗モードが最も一般的かを分析し、優先的に対処すべきものを決められるよう、種類のラベルを付けた失敗ログを保存します。通常、ツールエラーとループは最も頻度が高く、修正もしやすい失敗です。

from enum import Enum
from dataclasses import dataclass

class FailureType(Enum):
    TOOL_ERROR = 'tool_error'
    MALFORMED_OUTPUT = 'malformed_output'
    REASONING_LOOP = 'reasoning_loop'
    CONTEXT_EXHAUSTION = 'context_exhaustion'
    EXTERNAL_SERVICE = 'external_service'
    GOAL_MISUNDERSTANDING = 'goal_misunderstanding'
    MAX_ITERATIONS = 'max_iterations'
    UNKNOWN = 'unknown'

@dataclass
class AgentFailure:
    failure_type: FailureType
    step: int
    tool_name: str | None
    error_message: str
    recoverable: bool

失敗と復旧アクションの対応付け

各失敗タイプには、適切な復旧アクションがあります。ツールエラーには引数を調整したリトライ、ループには新しいことを試すようエージェントに指示する多様性プロンプト、コンテキストの枯渇には圧縮、外部サービスの障害にはフォールバックツールが適しています。失敗が発生したときにエージェントランタイムが呼び出す復旧ルーターで、これらを明示的に対応付けてください。

RECOVERY_ACTIONS = {
    FailureType.TOOL_ERROR:           'retry_with_corrected_args',
    FailureType.MALFORMED_OUTPUT:     'reformat_output',
    FailureType.REASONING_LOOP:       'inject_diversity_prompt',
    FailureType.CONTEXT_EXHAUSTION:   'compress_history',
    FailureType.EXTERNAL_SERVICE:     'use_fallback_tool',
    FailureType.GOAL_MISUNDERSTANDING:'request_clarification',
    FailureType.MAX_ITERATIONS:       'escalate_to_human',
    FailureType.UNKNOWN:              'escalate_to_human'
}

最大反復回数の設定

すべてのエージェントには、強制的な安全境界として最大反復回数を設定する必要があります。これがないと、ループするエージェントが無限に実行され、トークンと費用を消費し続けます。上限は、想定されるタスクの複雑さに基づいて設定してください。単純な質問応答エージェントなら5ステップ、複雑な調査エージェントなら20ステップまで許容する、といった具合です。上限に達したら、失敗をログに記録し、部分的な結果を保存して、人間にエスカレーションするか、部分的な回答を返します。

MAX_ITERATIONS = 15

async def run_agent(task: str) -> str:
    messages = [{'role': 'user', 'content': task}]
    loop_detector = LoopDetector()
    for iteration in range(MAX_ITERATIONS):
        response = await get_agent_action(messages)
        if response.is_final:
            return response.answer
        action_key = f'{response.tool}:{response.args}'
        if loop_detector.record(action_key):
            messages.append({'role': 'system', 'content': 'You are repeating yourself. Try a completely different approach.'})
            continue
        result = await execute_tool(response.tool, response.args)
        messages.append({'role': 'tool', 'content': result})
    return 'Task exceeded maximum iterations. Partial results: ' + get_partial_result(messages)

事後分析のための失敗ログ記録

後から原因を診断できるよう、すべてのエージェントの失敗を十分なコンテキストとともに記録してください。記録には、タスクの完全な説明、失敗地点までの完全な行動履歴、失敗タイプとエラーメッセージ、反復回数、トークン使用量を含めます。これをfailure_typeとtask_idにインデックスを付けたfailuresテーブルに保存します。失敗ログを定期的に確認し、どのタスクタイプが特定の失敗モードを起こしやすいかを特定して、優先順位を付けて修正してください。

import json
from dataclasses import asdict

async def log_agent_failure(task_id: str, failure: AgentFailure, history: list, pool):
    async with pool.acquire() as conn:
        await conn.execute('''
            INSERT INTO agent_failures
            (task_id, failure_type, step, tool_name, error_message,
             recoverable, action_history, failed_at)
            VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
        ''',
            task_id,
            failure.failure_type.value,
            failure.step,
            failure.tool_name,
            failure.error_message,
            failure.recoverable,
            json.dumps(history)
        )

クイックチェック

エージェントの失敗モード分類について、理解度を確認します。

レッスンのまとめ

このレッスンでは、エージェントの6つの主要な失敗モードとして、ツールエラー、不正な形式の出力、推論ループ、コンテキストの枯渇、外部サービスの障害、目的の誤認があること、行動履歴によるループ検出によって反復パターンを反復予算の枯渇前に捕捉できること、そして失敗タイプと復旧アクションの対応付けによって自動的な自己修復が可能になることを学びました。次は、自己修正と内省的プロンプトを実装します。

よくある質問

「エージェントの失敗モードの分類」レッスンは無料ですか?

はい。「エージェントの失敗モードの分類」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Engineering Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Engineering Academyコースには全4レッスンが含まれています。

「エージェントの失敗モードの分類」で何を学びますか?

ツールエラー、形式不正な出力、推論ループ、コンテキスト枯渇、外部サービスの利用不能など、エージェントの失敗を分類する体系を作り、それぞれの復旧戦略を設計します ブラウザで直接実行するハンズオンコードでAI Engineering Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Engineering Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Engineering Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「エージェントの失敗モードの分類」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Engineering Academyレッスンでコードを書いて実行できますか?

はい。すべてのAI Engineering Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. エージェントの失敗モードの分類
  2. 自己修正とリフレクティブプロンプティング
  3. チェックポイントとタスクの再開
  4. Human-in-the-Loopエスカレーション
← AI Engineering Academyに戻る