AI エージェントの監視アラートと障害復旧:ログからステートマシンまでの設計実践
Gartner の 2024 年レポートでは、企業向け AI エージェントプロジェクトの 87% が、本番投入から 3 か月以内にタスク失敗率が 25% を超えると報告されています。原因は入れ子になったツール呼び出しの奥に隠れ、ログはあちこちに散らばり、追跡できないことが多い。
本質はアラートの数ではなく、エージェント向けの監視アーキテクチャそのものが誤っていることです。エージェントは普通のバックエンドサービスではありません。非決定性ゆえに従来の監視では足りない——実行パスは動的で、同じタスクでも毎回ルートが変わります。本記事では、ログ・メトリクス・トレースからステートマシンまでの設計を一通り示し、「制御不能なブラックボックス」を「すべての失敗が追跡・復旧できる透明なシステム」に変える方法を説明します。
第 1 章:なぜ従来の監視はエージェントでは機能しないのか
こんな経験はありませんか。エージェントのタスクが失敗し、ログを延々と追うと LLM 出力の断片ばかりで、実行の全体像が組み立てられない。結局やり直して、今度こそ成功を祈るしかない。
従来のバックエンド監視はこうです。リクエストが入り、マイクロサービス A・B・C を経て、各ノードが状態とタイムスタンプを記録する。障害時はチェーンを辿ればよい。エージェントは違います。
実行パスは動的に生成されます。同じタスクでも、1 回目はツール A、2 回目はツール B、3 回目はツール呼び出しをスキップするかもしれません。OpenAI の 2024 年レポートでは、エージェントの平均タスク完了率は 61.8% にとどまります。背景には、推論中にエージェント自身が意思決定し、その決定に不確実性が伴うことがあります。
さらに厄介なのが God Prompt——エージェントのロジック全体を巨大な 1 本のプロンプトに詰め込むやり方です。ArizenAI の技術ブログはこれを「本番環境の最大のリスク」と呼んでいます。理由は 3 つ。テスト不能、デバッグ不能、予測不能。
5000 字のプロンプトを単体テストすることはできません。どの推論ステップが壊れたのか精密に特定もできません。パラメータを 1 つ変えただけで連鎖的に崩れるかも予測できません。あるプロジェクトでは God Prompt の例を 1 つ変えただけで、成功率が 70% から 30% に落ちました。1 週間調査した末、新しい例が「ツール A を優先呼び出し」と学習させたが、そのシーンでは A を呼ぶべきではなかった、という結論でした。
OpenAI の報告では、エージェント失敗の 82% は修復可能なエラーです。能力不足ではなく、設計の脆弱さが原因です。監視は「問題を見つける」だけでなく、「エージェントを改善するフィードバックループ」であるべきです。各状態の成功率、各ツール呼び出しの所要時間、各エラーの出現頻度——これらがどこを直すべきかを示します。
従来の監視思考は「障害が起きてから調べる」。エージェントの監視思考は「すべてのステップに痕跡を残し、失敗そのものを学習機会にする」。この観念の転換が、システム設計の出発点です。
第 2 章:AI エージェントの可観測性 3 層アーキテクチャ
エージェントの監視は単一手段ではなく、ログ・メトリクス・トレースの 3 層の重ね合わせです。各層が異なる次元の問題を解きます。
第 1 層:混沌としたログから構造化記録へ
エージェントの生ログを見たことはありますか。LLM 生成テキストの断片とエラースタックが混ざり、タイムスタンプも散らばる。こうしたログは事後の「発掘」には使えても、リアルタイム監視には向きません。
構造化ログの鍵は、各レコードにラベルを付けることです。エージェント ID、タスク ID、現在状態、入出力の要約——これでタスク単位の集約、状態でのフィルタ、時系列ソートが可能になります。
# 構造化ログの例
import structlog
logger = structlog.get_logger()
def log_agent_step(agent_id: str, task_id: str, state: str, input: dict, output: dict):
logger.info(
"agent_step",
agent_id=agent_id,
task_id=task_id,
state=state,
input_summary=str(input)[:100], # 切り詰めてログ肥大化を防止
output_summary=str(output)[:100],
timestamp=time.time()
)
単純に見えますが、多くのチームは LLM の生出力をそのままログに流し、grep で価値ある情報が取れると期待しています。それでは足りません。
第 2 層:エージェント専用メトリクス
メトリクスは「傾向分析」を担います。ログはある 1 回の失敗を教え、メトリクスは失敗率の上昇を教えます。
エージェントに必要なコアメトリクスは 4 類です。
| 指標タイプ | 具体指標 | アラート閾値の目安 |
|---|---|---|
| トークン消費 | 総消費、単一タスク消費、ツール呼び出し消費 | 単一タスク > 10000 トークン |
| レイテンシ | P50、P99、ツール呼び出し所要時間 | P99 > 30 秒 |
| エラー率 | タスク失敗率、ツール失敗率、再試行成功率 | 失敗率 > 20% |
| コスト | タスクあたりコスト、日次総コスト | 日次コストが 50% 急増 |
LangSmith のダッシュボードは良い例です。エージェント単位でこれらを表示し、タスク詳細までドリルダウンできます。閾値は履歴データに基づき、感覚では決めないでください。1 週間稼働して正常範囲を統計し、正常上限のおよそ 1.5 倍に設定するのが現実的です。
第 3 層:OpenTelemetry トレース標準
トレースは「チェーンの再現」を担います。1 本の Trace がユーザー要求から意図認識、ツール選択、実行、検証、最終出力までを貫きます。各段階が Span で、タイムスタンプ・状態・入出力を含みます。
OpenTelemetry は業界標準になりつつあります。PredictionGuard のブログでは、フレームワークやツールをまたいでトレース形式を統一できると述べています。Pydantic AI、smolagents、Strands Agents、LangGraph など主要フレームワークがすでに対応しています。
# OpenTelemetry トレースの例
from opentelemetry import trace
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
tracer = trace.get_tracer("agent_tracer")
async def run_agent_with_trace(task: str):
with tracer.start_as_current_span("agent_task") as span:
span.set_attribute("task_input", task)
# 意図認識
with tracer.start_as_current_span("intent_detection") as intent_span:
intent = await detect_intent(task)
intent_span.set_attribute("intent_result", intent)
# ツール呼び出し
with tracer.start_as_current_span("tool_call") as tool_span:
result = await call_tool(intent)
tool_span.set_attribute("tool_result", str(result)[:200])
span.set_attribute("final_output", result)
return result
Langfuse と LangSmith はどちらも OpenTelemetry のインポートに対応します。オープンソースでトレースを収集し、商用プラットフォームで可視化・分析する構成も可能です。単一ベンダーへのロックインを避けられます。
3 層を重ねる利点は明確です。ログで詳細、メトリクスで傾向、トレースで全体像——どの次元も取りこぼしません。
第 3 章:ステートマシン設計——失敗を可観測にする中核パターン
God Prompt の問題は本質的に「全部入り」です。ロジックが混ざると、どこが壊れたか分かりません。ステートマシンは大きな鍋を小さな鍋の連鎖に分けます。
ArizenAI の技術ブログは、ステートマシンで推論コストを 80% 削減できると示しています。各状態が 1 つのことだけを行うため、LLM が毎回全体を頭から推論する必要がないからです。
ステートマシン vs God Prompt:根本的な違い
| 次元 | God Prompt | ステートマシン |
|---|---|---|
| テスト可能性 | 単体テスト不可 | 各状態を独立テスト |
| デバッグ可能性 | 失敗位置が曖昧 | 状態境界が明確 |
| コスト制御 | 毎回プロンプト全体を推論 | 現在状態に必要な部分だけ |
| エラー処理 | プロンプト内に隠れる | Typed transitions で明示 |
典型的なエージェントのステートマシン構造:
[初期化] → [意図認識] → [ツール選択] → [実行] → [検証] → [完了]
↘ ↗
[エラー処理]
ArizenAI は 5〜12 状態を推奨します。少なすぎると God Prompt に戻り、多すぎると遷移が複雑になります。各状態には明確な入出力型——Typed transitions——が必要です。
# 状態定義の例(疑似コード)
from typing import TypedDict, Literal
class IntentState(TypedDict):
task_input: str
intent_type: Literal["query", "action", "clarify"]
class ToolState(TypedDict):
intent: IntentState
selected_tool: str
tool_params: dict
class ErrorState(TypedDict):
failed_state: str
error_type: str
retry_count: int
# 状態遷移:明示的なエラーパス
def transition_from_intent(intent: IntentState) -> ToolState | ErrorState:
try:
tool = select_tool(intent)
return {"intent": intent, "selected_tool": tool, "tool_params": {}}
except IntentError as e:
return {"failed_state": "intent", "error_type": "ambiguous", "retry_count": 0}
各状態の監視ポイント
ステートマシンの利点は、各状態が自然に監視単位になることです。混沌としたログを掘らなくても、状態別にメトリクスを見られます。
- 初期化状態:タスク開始時刻、入力の完全性チェック結果
- 意図認識状態:意図タイプ分布、認識所要時間、曖昧率
- ツール選択状態:ツール呼び出し頻度、選択所要時間、マッチなし率
- 実行状態:ツール実行時間、成功率、失敗タイプ分布
- 検証状態:検証合格率、修正試行回数
- エラー処理状態:エラータイプ分布、再試行成功率、デグレード発火回数
これらの指標で、どの段階に問題があるか一目で分かります。意図認識が 2 秒から 10 秒に急増?プロンプトが長くなった可能性。ツール失敗率が 5% から 30%?特定 API の障害かもしれません。
ステートマシンは監視の粒度を「タスク全体」から「各ステップ」へ下げます。問題の特定そのものが監視の一部になる——どのアラートルールより効くことが多いです。
第 4 章:障害復旧のエンジニアリング実践
監視は問題を見つけ、復旧は問題を解く。ただし復旧は「再試行」だけではありません。盲目的な再試行は事態を悪化させます。
エラー分類:すべての失敗が同じではない
実プロジェクトでは、おおよそ次の 3 類に分かれます。
| タイプ | 割合 | 特徴 | 処理方法 |
|---|---|---|---|
| 一時的エラー | 〜60% | API タイムアウト、サービス揺らぎ、レート制限 | 指数バックオフで再試行(最大 5 回) |
| 論理的エラー | 〜30% | パラメータ形式誤り、存在しないツール、意図の曖昧さ | 自己反省 + 戦略調整 |
| カスケードエラー | 〜10% | コアサービス障害、設定ミス | 遮断 + デグレード |
アリババクラウドのデータでは、適切な再試行で API 成功率を 85% から 99.5% まで引き上げられるとされています。前提は「適切」であることです。
再試行の罠:コンテキスト汚染
2026 年 5 月の Arxiv 論文は、直感に反する現象を指摘しています。単純な再試行だけでは、成功率がむしろ下がることがある。
理由は、失敗情報が後続推論を「汚染」するからです。
シーンを想像してください。エージェントがツール A を呼び出して失敗し、エラーが会話履歴に追記されます。エージェントは「A に問題がある、B を試そう」と推論するかもしれません。B も失敗すると、履歴に 2 件の失敗が残ります。エージェントは「タスクが複雑すぎる、諦めよう」と結論づくこともあります。
これがコンテキスト汚染(Context Contamination)です。失敗情報そのものが推論パスを変え、後続の試行を諦めや誤戦略に寄せます。
対策は状態隔離です。再試行ごとに完全な失敗履歴を引き継がず、「クリーンな状態」からやり直す。または再試行前に、生のスタックトレースではなく構造化されたエラー要約だけを渡す。
# 状態隔離による再試行の例
async def retry_with_clean_state(task: str, error: AgentError, max_retries: int = 3):
for attempt in range(max_retries):
# 完全な失敗履歴は渡さず、構造化されたエラー要約のみ
error_summary = {
"type": error.type,
"failed_step": error.step,
"hint": get_recovery_hint(error)
}
result = await run_agent_state(
start_state="error_recovery",
context={"original_task": task, "error_summary": error_summary}
)
if result.success:
return result
return {"status": "failed", "reason": "max_retries_exceeded"}
デグレード:失敗を認め、優雅に退出する
自動復旧できないエラーもあります。3〜5 回連続失敗したら、デグレードを起動すべきです。
シーン別のデグレード戦略:
- タスクの簡略化:複雑タスクを単純版に分解し、部分結果を返す
- 人間の介入依頼:タスクを保留し、運用担当やユーザーに通知
- フォールバック応答:定型の汎用回答で UX の中断を防ぐ
NIST SP 800-61 Rev. 3(2025 年更新)は、インシデント対応の 6 機能を定義しています。Govern(統治)、Identify(識別)、Protect(保護)、Detect(検知)、Respond(対応)、Recover(復旧)。もともとサイバーセキュリティ向けですが、エージェント運用にそのまま当てはまります。
NIST フレームワークをエージェントにマッピングすると:
- Govern:失敗閾値、デグレード戦略、責任分界を定義
- Identify:エラータイプの分類、失敗チェーンの追跡
- Protect:デグレード戦略の事前設定、サーキットブレーカー
- Detect:リアルタイム監視、異常検知
- Respond:再試行またはデグレードの起動、イベント記録
- Recover:正常サービスへの復帰、振り返りと改善
このフレームワークの強みは、復旧を一時しのぎではなく一連のプロセスとして扱う点です。
第 5 章:実践事例とツール推奨
理論のあとは実装です。具体的な統合案をいくつか示します。
LangGraph + Langfuse の監視設定
LangGraph は OpenTelemetry をネイティブサポートし、Langfuse への接続は数行で済みます。
from langfuse import Langfuse
from langfuse.callback import CallbackHandler
langfuse_handler = CallbackHandler(
public_key="pk-xxx",
secret_key="sk-xxx",
host="https://cloud.langfuse.com"
)
# LangGraph コンパイル時にコールバックを注入
agent = graph.compile()
result = agent.invoke(
{"input": task},
config={"callbacks": [langfuse_handler]}
)
Langfuse は各ノードのトレースデータ——入出力、所要時間、トークン消費——を自動収集します。ダッシュボードでタスク ID 単位の実行チェーンを確認できます。
CrewAI のヘルスチェックエンドポイント
CrewAI には組み込み監視がないため、ヘルスチェックエンドポイントを自前で設計します。
from fastapi import FastAPI
from crewai import Crew
app = FastAPI()
@app.get("/health")
async def health_check():
# 直近 100 タスクの成功率を確認
recent_tasks = get_recent_tasks(limit=100)
success_rate = sum(1 for t in recent_tasks if t.status == "success") / len(recent_tasks)
return {
"status": "healthy" if success_rate > 0.8 else "degraded",
"success_rate": success_rate,
"last_error": recent_tasks[-1].error_summary if recent_tasks[-1].status == "failed" else None
}
このエンドポイントは Kubernetes のヘルスチェックや、アラートシステムのデータソースに接続できます。
ツール推奨マトリクス
| シーン | 推奨ツール | 特徴 | 向くチーム |
|---|---|---|---|
| トレース | Langfuse | OpenTelemetry ネイティブ、オープンソース、セルフホスト可 | カスタムデプロイが必要なチーム |
| 監視 | LangSmith | LangChain 公式、アラート連携が充実 | LangChain / LangGraph 利用チーム |
| ログ | Loki + Grafana | 低コスト、K8s 向き、既存基盤と相性良い | 大規模デプロイ、予算重視 |
| 異常検知 | Luna-2 小モデル | エージェント特有パターンの認識、ノイズ低減 | アラートノイズが深刻なチーム |
PredictionGuard のブログでは、小規模言語モデル(Luna-2 など)がエージェント特有の失敗パターンを理解し、従来の閾値アラートより賢くなると述べています。アラートが毎日数十件で 90% がノイズなら、検討の価値があります。
まとめ
エージェント監視体系の有無で、どれだけ差が出るか。
| 次元 | 監視なし | 監視あり |
|---|---|---|
| 問題特定 | ログを延々追う | 状態単位で秒級に特定 |
| 障害復旧 | 盲目的再試行、成功率低下 | 分類処理で的確に復旧 |
| アラート品質 | ノイズ爆発、根本原因が埋もれる | 集約・ノイズ低減でシグナルが明確 |
| エージェント改善 | 感覚でパラメータ調整 | データ駆動の最適化 |
God Prompt からステートマシンへ、混沌としたログから OpenTelemetry トレースへ、盲目的再試行から状態隔離による復旧へ——この転換は「あると良い」ではなく、本番投入の必須条件です。
巨大な 1 本のプロンプトでエージェントを支えているなら、今日から状態に分割しましょう。5〜12 の離散状態、各状態は単一責務、失敗パスは明示的に定義。
まだ OpenTelemetry を入れていないなら、今が最適なタイミングです。主要フレームワークは対応済みで、Langfuse と LangSmith はトレースデータを直接取り込めます。
再試行は万能薬ではありません。コンテキスト汚染は、単純な再試行を深みに引きずります。状態隔離を設計することが正道です。
エージェントの本番化は、「良いプロンプトを書けば終わり」ではありません。監視と復旧こそ、本当に制御可能にする最後の一歩です。
AI エージェントの可観測性システムを構築する
ログからステートマシンまでの監視体系を一式で構築
⏱️ 目安時間: 45 分
- 1
ステップ1: 構造化ログ形式を設計する
各ログにエージェント ID、タスク ID、現在状態、入出力の要約を付与。structlog などで形式を統一し、長文は切り詰めてログ肥大化を防ぐ。 - 2
ステップ2: エージェントのコアメトリクスを設定する
トークン消費(単一タスク閾値 10000)、レイテンシ(P99 閾値 30 秒)、エラー率(失敗率閾値 20%)、コスト(日次コストが 50% 急増)を監視。 - 3
ステップ3: OpenTelemetry トレースを接続する
ユーザー要求から最終出力まで、各段階を Span で定義。LangGraph や Pydantic AI など主要フレームワークはネイティブ対応。Langfuse や LangSmith で可視化。 - 4
ステップ4: ステートマシンアーキテクチャに分割する
God Prompt を 5〜12 の離散状態に分割。各状態は単一責務とし、Typed transitions で明示的なエラーパスを定義。 - 5
ステップ5: エラー分類と復旧を実装する
一時的エラーは指数バックオフで再試行(最大 5 回)、論理的エラーは自己反省を起動、カスケードエラーは遮断してデグレード。再試行ごとに状態隔離し、コンテキスト汚染を避ける。
FAQ
なぜ従来の監視はエージェントでは機能しないのか?
ステートマシンパターンはどう推論コストを下げるのか?
コンテキスト汚染(Context Contamination)とは?
エージェントのアラート閾値はどう設計する?
OpenTelemetry と LangSmith はどちらを選ぶ?
再試行が失敗し続けたらどうする?
6分で読めます · 公開日: 2026年5月27日 · 更新日: 2026年6月8日
AI 開発実践
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
DeepAgents アーキテクチャ解説:Planning Tools、Sub-agents、ファイルシステムとシステムプロンプト
DeepAgents の4本柱アーキテクチャを深掘り。Planning Tools、Sub-agents、File System、System Prompts を解説し、LangGraph や AutoGen などと比較。実践コード例とベストプラクティス付き。
第 34 / 40 記事
次の記事
マルチモーダル AI アプリ開発実践:3 モーダル融合の完全ガイド
GPT-4V、Gemini、Claude の3大プラットフォームを比較し、テキスト・画像・音声を融合する完全なコード例を提供。システムアーキテクチャの設計原則とコスト管理のコツを解説し、マルチモーダル開発の核心スキルを素早く習得できます。
第 36 / 40 記事
関連記事
Workers AI 完全ガイド:毎日 1 万回相当の無料 LLM 呼び出し、OpenAI より最大 90% 節約
Workers AI 完全ガイド:毎日 1 万回相当の無料 LLM 呼び出し、OpenAI より最大 90% 節約
AI で 1 万行のレガシーコードをリファクタリング:1 ヶ月分の仕事を 2 週間で終えた実録
AI で 1 万行のレガシーコードをリファクタリング:1 ヶ月分の仕事を 2 週間で終えた実録
OpenAI API がタイムアウトする?Workers で専用チャネルを構築、コストゼロで安定化
コメント
GitHubアカウントでログインしてコメントできます