目次
LLMエージェントのデバッグは通常のプログラムのデバッグより難しい。同じ入力でも異なる結果が出ることがあり、問題が再現しにくいためだ。適切なログ設計と体系的なデバッグアプローチを持つことで、エージェントの問題を効率よく特定・修正できる。
エージェントの典型的な問題パターン
1. 無限ループ・堂々巡り
エージェントが同じツールを繰り返し呼び出したり、同じ思考ステップを繰り返したりするパターンだ。
原因の多く:
- 停止条件が曖昧でエージェントがタスク完了を判断できない
- ツールの結果をエージェントが正しく解釈できていない
- エラーをリカバリしようとして同じ操作を繰り返す
2. ツール呼び出しエラー
エージェントが存在しないツール・パラメータ・関数を呼び出そうとするパターンだ。
原因の多く:
- ツールのスキーマ定義と実際のツール実装のズレ
- プロンプトでのツール説明が不十分
- モデルがハルシネーション(幻覚)でツール名を作り出す
3. 幻覚(Hallucination)
エージェントが存在しない情報を事実として返すパターンだ。特に外部システムへの参照が必要なタスクで発生しやすい。
ログ設計のベストプラクティス
適切なロギングがないと、エージェントが何をしていたかの追跡が困難になる。最低限、以下の情報をログに記録する。
import logging
import json
from datetime import datetime
from typing import Any
# 構造化ログの設定
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("agent")
def log_agent_step(
step_type: str,
content: Any,
metadata: dict = None
) -> None:
"""エージェントの各ステップをログに記録する"""
log_entry = {
"timestamp": datetime.now().isoformat(),
"step_type": step_type, # "think", "tool_call", "tool_result", "respond"
"content": content,
"metadata": metadata or {}
}
logger.debug(json.dumps(log_entry, ensure_ascii=False, indent=2))
ステップタイプ別のログ
class AgentLogger:
def __init__(self, session_id: str):
self.session_id = session_id
self.step_count = 0
def log_thinking(self, thought: str):
self.step_count += 1
log_agent_step(
"think",
thought,
{"session_id": self.session_id, "step": self.step_count}
)
def log_tool_call(self, tool_name: str, parameters: dict):
log_agent_step(
"tool_call",
{"tool": tool_name, "params": parameters},
{"session_id": self.session_id, "step": self.step_count}
)
def log_tool_result(self, tool_name: str, result: Any, success: bool):
log_agent_step(
"tool_result",
{"tool": tool_name, "result": result, "success": success},
{"session_id": self.session_id, "step": self.step_count}
)
ループ検出と安全な停止
無限ループを防ぐには、繰り返しパターンの検出と最大ステップ数の制限を組み合わせる。
class LoopDetector:
def __init__(self, max_steps: int = 20, window_size: int = 5):
self.max_steps = max_steps
self.window_size = window_size
self.history = []
def add_step(self, action: str) -> None:
self.history.append(action)
def is_looping(self) -> bool:
"""直近のアクションが繰り返しパターンを持つか判定する"""
if len(self.history) < self.window_size * 2:
return False
recent = self.history[-self.window_size:]
before = self.history[-self.window_size * 2:-self.window_size]
return recent == before
def is_max_steps_reached(self) -> bool:
return len(self.history) >= self.max_steps
def should_stop(self) -> tuple[bool, str]:
if self.is_max_steps_reached():
return True, f"最大ステップ数({self.max_steps})に達しました"
if self.is_looping():
return True, "繰り返しパターンを検出しました"
return False, ""
幻覚の検出と対処
エージェントの回答を別のLLMで検証するダブルチェックパターンが有効だ。
def verify_agent_response(
claim: str,
context: str,
verifier_model: str = "gpt-4o-mini"
) -> dict:
"""エージェントの主張を別のLLMで検証する"""
prompt = f"""
以下の主張が、提供されたコンテキストに基づいているか検証してください。
【主張】
{claim}
【コンテキスト】
{context}
回答形式:
- verified: true/false
- confidence: 0.0〜1.0
- reason: 判断の根拠
"""
response = client.chat.completions.create(
model=verifier_model,
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
デバッグ用のトレース可視化
複雑なエージェントのデバッグには、処理のトレースを視覚化することが効果的だ。LangSmithやWeightbiases Tracesなどのツールを使うと、ステップの流れ・各ステップのトークン消費・エラーの発生箇所をグラフィカルに確認できる。
オープンソースではPhoenix(Arize AI)が無料で使えるLLMトレーシングツールとして導入しやすい。
!pip install arize-phoenix openinference-instrumentation-openai
import phoenix as px
from openinference.instrumentation.openai import OpenAIInstrumentor
# トレース収集の開始
px.launch_app()
OpenAIInstrumentor().instrument()
# 以降のOpenAI API呼び出しが自動でトレースされる
まとめ
AIエージェントのデバッグは、適切なログ設計から始まる。ステップタイプ別の構造化ログを記録し、ループ検出機構と最大ステップ数制限を組み合わせることで、無限ループを安全に検出・停止できる。幻覚にはダブルチェックパターン(別モデルによる検証)が有効だ。複雑なエージェントには、PhoenixやLangSmithなどのトレーシングツールを導入することで、問題の特定が格段に楽になる。デバッグのしやすさはエージェント設計の品質に直結するため、ログ設計は実装と並行して考えることを強く推奨する。
免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。