AI Agent の状態機械設計:複雑なワークフローを Prompt だけに任せられない理由

"LangGraph Persistence のドキュメントは、checkpoint を thread-scoped な graph state snapshot として説明し、conversation continuity、human-in-the-loop、time travel、fault tolerance を支えるものとしています。"
レポート Agent が第 5 ステップのメール送信直前で失敗しました。運用担当者がタスクを再実行すると、Agent は第 1 ステップからやり直し、新しいレポートを生成して、前回承認済みだった版を上書きしました。承認状態は失われ、承認者の署名記録は新しい結果に置き換わり、最初のレポートが承認済みだったことを証明できるログも残っていません。
これはデータベースのトランザクション rollback でも、メッセージキューの retry でもありません。Prompt には「続けて処理する」という 1 文しか残っておらず、モデルはワークフロー全体をもう一度推論しました。第 1〜4 ステップで副作用がすでに発生していたことを、モデルは知りません。承認 API を呼び、レポートを生成し、一時ファイルを書き込んでいたのです。失敗点は第 5 ステップでしたが、副作用は第 2 ステップから始まっていました。
本当の問題は「モデルの能力が足りない」ことではありません。タスク進捗が prompt の自然言語に隠れていて、復旧可能な状態 snapshot がなかったことです。Prompt が持つ messages はモデルのコンテキストであり、実行事実ではありません。
この種の事故を直すには、prompt に「続行前に進捗を確認する」と 1 文足すだけでは足りません。現在ノード、発生済みの副作用、次の action、失敗時の補償を、復旧可能な状態表に落とすほうが安定します。
事故の要点
レポート Agent の実行フロー:
| ステップ | 操作 | 副作用 | 幂等性 |
|---|---|---|---|
| 第 1 ステップ | データ照会 | データベースを呼び出し、ユーザーデータを照会 | 幂等(読み取り操作) |
| 第 2 ステップ | レポート生成 | レポート生成ツールを呼び出し、PDF を生成 | 幂等ではない(ファイルを上書き) |
| 第 3 ステップ | 承認待ち | 承認リクエストを送信し、人間の承認を待つ | 幂等(API が対応) |
| 第 4 ステップ | 承認完了 | approve event を受信 | 幂等(状態照会) |
| 第 5 ステップ | メール送信 | メール API を呼び出し、レポートを送信 | 失敗(timeout) |
失敗原因:第 5 ステップのメール送信が外部 API の rate limit で timeout し、タスクが FAILED とマークされました。
再実行ロジック:prompt から「現在の進捗」を読み取ります。Prompt には「承認済み、処理を続ける」という文しかありません。実際の実行:第 1 ステップから再開 -> 第 2 ステップでレポートを再生成(承認済み版を上書き)-> 第 3 ステップで再度承認 -> 第 5 ステップで送信成功。
業務影響:承認済みレポートが差し替わり、承認記録と実際に届いたレポートが一致しなくなりました。ユーザーは「承認した内容と受け取った内容が違う」と苦情を出し、承認フローは 2 つの版を承認したのに、実際に送られたのは 1 つだけという無駄を生みました。
アンチパターン識別表
あなたの Agent が次のアンチパターンに当てはまるか確認してください:
| アンチパターン | 表れ方 | 隠れたリスク | 修正案 |
|---|---|---|---|
| 進捗を Prompt に書く | 「現在第 3 ステップ」などの自然言語要約 | 再起動後に失われ、復旧できない | State フィールドで現在ノードを記録する |
| Trace を State とみなす | 完全な Trace があるので状態もあると思い込む | Trace は次の判断を記録しない | State に次に行うべきことを記録する |
| 幂等チェックなしの Retry | 失敗したら最初から再実行 | 副作用が重複実行される | 幂等キー + 実行済みチェック |
| 承認後 Resume の検証なし | そのまま続行する | 正しい実行点に戻れない | checkpoint + thread_id |
1. 状態機械の基礎:State、Event、Transition、Guard、Action
状態機械はすべての Agent に必要な構成ではありません。単純なカスタマーサポート Q&A なら messages 配列で十分です。しかし、多段階、承認、外部システム呼び出し、失敗後の復旧を含む複雑なタスクでは、タスク進捗を明示する必要があります。
1.1 中核用語の定義表
状態機械の基本用語は Stately の公式ドキュメントに基づきます:
| 用語 | 定義 | Agent の例 | 出典 |
|---|---|---|---|
| State(状態) | 機械が置かれているモードで、単一の意味的意図に対応 | INIT、PLAN_READY、TOOL_RUNNING、APPROVAL_PENDING、FAILED、COMPLETED | Stately state machines |
| Event(イベント) | 状態変化を引き起こす外部信号 | timeout、approve、reject、retry、resume、task_received | Stately state machines |
| Transition(遷移) | 状態間で許可された経路。決定的な mapping | INIT -> PLAN_READY(event: task_received) | Stately state machines |
| Guard/Condition(ガード) | ある状態へ入るための前提チェック | 「予算が十分」のときだけ TOOL_RUNNING に入る | Stately state machines |
| Action(動作) | 遷移時に実行される操作 | TOOL_RUNNING に入るときにツールを呼び出す | Stately state machines |
| Checkpoint(snapshot) | 復旧に使う状態 snapshot | LangGraph checkpointer が graph state を保存 | LangGraph Persistence |
決定性の原則:同じ State + Event の組み合わせは、曖昧さを避けるために 1 つの next state だけを指すべきです。有限状態集合:状態機械は無限のフローチャートではなく、有限の到達可能状態と明確な遷移ルールです。
1.2 Trace vs State vs Audit の比較表
Trace、Audit Log、State Snapshot は、それぞれ別の問題を解きます:
| 概念 | 解く問題 | 業務状態か | 次の一手を決めるか | Agent の例 |
|---|---|---|---|---|
| Trace(追跡) | 観測と診断の骨格 | いいえ | 決めない | OpenAI Agents SDK trace(workflow_name、trace_id) |
| Audit Log(監査ログ) | コンプライアンス記録、監査追跡 | いいえ | 決めない | 権限モデルの監査フィールド(actor、traceId、action、result) |
| State Snapshot(状態 snapshot) | 次の一手を決める現在状態 | はい | 決める | LangGraph checkpoint(現在ノード、完了済みステップ、次に行うべきこと) |
重要な区別はここです。Trace は「何が起きたか」を観測するためのものですが、業務状態そのものではありません。Audit Log はコンプライアンス履歴を残し、監査追跡に使います。State Snapshot は「次に何をすべきか」を決めるためのもので、復旧の中核です。3 つは互いに代替できません。Trace があることは State があることではなく、Audit があることは復旧可能であることではありません。
2. LangGraph は状態永続化をどう扱うか
Checkpoint は prompt 内の自然言語要約ではありません。復旧でき、検査でき、replay できる状態 snapshot です。LangGraph persistence のドキュメントでは、checkpoint は graph state snapshot と定義され、完全な状態と次に実行するノードを含みます。
2.1 Checkpointer と Thread State
中核メカニズム(LangGraph Persistence 公式ドキュメントより):
- Checkpointer:thread-scoped な状態 snapshot(graph state snapshot)を保存する
- Store:thread をまたぐ長期データ(application-defined store)を保存する
- Thread_id:特定 thread state を復旧する唯一の入口
- 4 つの用途:conversation continuity、human-in-the-loop、time travel、fault tolerance
LangGraph persistence は、短期の thread-scoped state を checkpointers に任せ、thread をまたぐ長期データを stores に任せます。Checkpoint には state snapshot と application-defined store が含まれます。Thread_id は復旧入口であり、同じ thread_id なら停止点から続行できます。
LangGraph checkpoint には graph state、次に実行するノード一覧、checkpoint_id、timestamp、version が含まれます。機密データは checkpoint に入れない方がよい場合があります。graph state の一部フィールドに機密情報が含まれるなら、保存しないための明示的な設定が必要です。
2.2 Interrupts と復旧メカニズム
中核メカニズム(LangGraph Interrupts 公式ドキュメントより):
- interrupt():graph node 内で実行を動的に一時停止し、graph state を保存して外部入力を待つ
- 復旧方法:同じ thread_id と Command(resume=…) を使う
- よくあるパターン:承認、review/edit、tool call review、human input validation
- 幂等副作用の警告:interrupt 前の副作用は幂等でなければならない。復旧時、node は interrupt を呼んだ node の先頭から再実行されるため
承認待ちは、モデルに「承認を待っていることを覚えていて」と期待するのではなく、状態機械上の一時停止状態にするべきです。復旧には同じ thread cursor が必要です。
復旧時は同じ thread_id と Command(resume=…) を使います。幂等な副作用は復旧の前提条件です。承認前に外部 API 呼び出しなどの副作用がある場合、それを幂等にしておかないと、復旧時に node が再実行され、API が重複呼び出しされます。
3. エンジニアリング上の類比:Temporal Durable Execution
長時間タスクの信頼性は新しい問題ではありません。Temporal durable execution は成熟した比較対象になります。
3.1 Durable Execution の定義
中核概念(Temporal Durable Execution 公式ドキュメントより):
- Durable Execution の定義:workflow execution が失敗、クラッシュ、サービス中断時にも state/progress を保持する
- Event History:各ステップの状態を記録し、失敗後に最後の記録イベントから復旧できるようにする
- 3 つの特性:Resumable(再開可能)、Recoverable(復旧可能)、Reactive(反応可能)
長時間タスクの信頼性は event history と復旧可能な実行から生まれます。単一プロセスのメモリや prompt コンテキストからではありません。Agent 状態機械にも同じ発想が必要です。checkpoint/event log + 業務状態であり、モデルによる再推論だけでは足りません。
Temporal の Event History と LangGraph の checkpoint は概念的に近いものです。どちらも実行履歴を記録し、失敗点からの復旧を支えます。違いは、Temporal が完全な workflow engine で、LangGraph が Agent の状態管理フレームワークであることです。Agent 開発者が Temporal から学べるのは、durable execution には構造化された状態履歴が必要であり、プロセスメモリやモデルコンテキストに依存してはいけない、という点です。
4. 状態表設計テンプレート:コピーして使える Agent State Table
状態機械の概念は抽象的です。実装に落とすには、具体的な状態モデルが必要です。ここでは状態表、イベント表、事故駆動の状態表例の 3 つを用意します。
4.1 状態表テンプレート(実行可能なステップブロック)
テンプレート構造:
| State(状態) | Event(トリガーイベント) | Guard(ガード条件) | Action(必須動作) | Next(次状態) |
|---|---|---|---|---|
| INIT | task_received | なし | コンテキストを初期化し、開始時刻を記録 | PLAN_READY |
| PLAN_READY | plan_generated | plan_valid | 実行計画を生成し、ツール列を記録 | TOOL_RUNNING |
| TOOL_RUNNING | tool_completed | budget_sufficient | ツールを呼び出し、結果を記録し、予算を更新 | APPROVAL_PENDING または COMPLETED |
| APPROVAL_PENDING | approve | approval_required | 承認リクエストを送信し、承認者を記録 | COMPLETED |
| APPROVAL_PENDING | reject | なし | 拒否理由を記録し、ユーザーへ通知 | FAILED |
| FAILED | retry | retry_count < max | 幂等性を確認し、前の checkpoint へ戻す | TOOL_RUNNING または APPROVAL_PENDING |
| COMPLETED | なし | なし | 完了時刻を記録し、リソースをクリーンアップ | Terminal |
テンプレート説明:State 列は到達可能な状態(INIT、PLAN_READY、TOOL_RUNNING、APPROVAL_PENDING、FAILED、COMPLETED)を定義します。Event 列は遷移を引き起こす event(task_received、approve、reject、retry)を定義します。Guard 列は状態に入る前提条件(budget_sufficient、retry_count < max)を定義します。Action 列は遷移時の必須動作(ツール呼び出し、結果記録、承認送信)を定義します。Next 列は決定的な遷移先を定義します。
4.2 イベント表テンプレート(状態表の補足)
テンプレート構造:
| Event(イベント名) | トリガー条件 | 前置状態要件 | 後置状態 | 副作用を生むか |
|---|---|---|---|---|
| task_received | ユーザーがタスクを送信 | INIT | PLAN_READY | 生まない |
| plan_generated | LLM が実行計画を生成 | PLAN_READY | TOOL_RUNNING | 生まない |
| tool_completed | ツール実行が完了 | TOOL_RUNNING | APPROVAL_PENDING または COMPLETED | 生む(外部 API 呼び出し) |
| approve | 承認者が承認 | APPROVAL_PENDING | COMPLETED | 生む(メール送信、予算扣減) |
| reject | 承認者が拒否 | APPROVAL_PENDING | FAILED | 生まない |
| retry | 失敗後の再試行リクエスト | FAILED | TOOL_RUNNING または APPROVAL_PENDING | 幂等チェックが必要 |
| timeout | 実行 timeout | TOOL_RUNNING | FAILED | 生まない |
イベント表の説明:前置状態要件により、どの状態でどの event を受け取れるかを明確にします。副作用の有無を示すことで、どの event に幂等性や補償が必要かが分かります。
4.3 事故駆動の状態表例(レポート上書き事故から導く)
完全な例:レポート Agent 状態表(冒頭の事故から導出)
| State | Event | Guard | Action | Next | 幂等/補償チェック |
|---|---|---|---|---|---|
| INIT | task_received | なし | thread_id を初期化し、開始時刻を記録 | QUERY_RUNNING | 不要 |
| QUERY_RUNNING | query_completed | なし | データを照会し、結果を state に保存 | REPORT_GENERATING | 不要 |
| REPORT_GENERATING | report_generated | なし | レポートを生成し、report ID を state に保存 | APPROVAL_PENDING | 幂等チェック:レポートが既に存在するなら生成をスキップ |
| APPROVAL_PENDING | approve | なし | 承認者と承認時刻を記録 | EMAIL_SENDING | 不要 |
| APPROVAL_PENDING | reject | なし | 拒否理由を記録 | FAILED | 不要 |
| EMAIL_SENDING | email_sent | なし | メールを送信し、email ID を記録 | COMPLETED | 幂等チェック:メール送信済みならスキップ |
| EMAIL_SENDING | timeout | retry_count < 3 | 失敗を記録し、幂等性を確認 | EMAIL_SENDING(retry)または FAILED | 幂等キー:email_id + thread_id |
| FAILED | retry | retry_count < max | 幂等性を確認し、前の checkpoint から復旧 | QUERY_RUNNING または REPORT_GENERATING または EMAIL_SENDING | checkpoint に基づき復旧点を決定 |
| COMPLETED | なし | なし | 完了時刻を記録し、リソースをクリーンアップ | Terminal | 不要 |
事故復旧の修正:第 5 ステップの失敗(EMAIL_SENDING -> timeout)では、QUERY_RUNNING ではなく EMAIL_SENDING から復旧すべきです。checkpoint には現在ノード(EMAIL_SENDING)、完了済みステップ(QUERY、REPORT_GENERATED、APPROVAL_APPROVED)、次に行うべきこと(EMAIL_SENDING)を記録します。レポート生成とメール送信には幂等キーが必要で、重複実行を避けます。
5. 幂等性と補償:復旧は checkpoint だけではない
Checkpoint があるからといって、すべての副作用を安全に復旧できるわけではありません。復旧には幂等性、トランザクション、補償、外部システム状態チェックも必要です。
5.1 幂等性と補償の概念
定義:
- 幂等(Idempotent):複数回実行しても結果が同じで、重複副作用を生まないこと
- 補償(Compensation):すでに起きた副作用を取り消し、一貫性を回復すること
- トランザクション rollback:原子的な操作が失敗時に自動 rollback されること
- 外部システム状態チェック:復旧前に外部システム状態を確認し、重複操作を避けること
状態一貫性の 3 本柱:幂等性識別子(action_id + schema_hash)、状態 snapshot チェーン(snapshot + prev_hash + delta)、補償 action 登録(undo_op)。
5.2 幂等性と補償の判断チェックリスト
どの操作に幂等性が必要で、どの操作に補償が必要かを判断します:
| 操作タイプ | 幂等性が必要か | 補償が必要か | 幂等キー設計 | 補償案 |
|---|---|---|---|---|
| データ照会(副作用なし) | 不要 | 不要 | - | - |
| レポート生成(ファイル上書き) | 必要 | 必要 | report_id + thread_id | 新しいレポートを削除し、承認済み版を復元 |
| メール送信(外部 API) | 必要 | 難しい | email_id + thread_id | 一部シナリオで訂正メールや取消メールを送る |
| 在庫扣減(データベース) | 必要 | 必要 | inventory_id + order_id | 在庫を戻す(扣減の補償) |
| チケット作成(外部システム) | 必要 | 必要 | ticket_id + thread_id | チケットをクローズする(作成の補償) |
| 予算扣減(内部状態) | 必要 | 必要 | budget_id + thread_id | 予算を戻す(扣減の補償) |
| 承認リクエスト送信(永続副作用なし) | 不要 | 不要 | - | - |
判断ロジック:外部副作用を生むかどうかが、幂等性の必要性を決めます。取り消せる操作には補償が必要です。システムをまたぐ呼び出しでは、幂等キーに外部システム識別子を含めるべきです。原子的な操作は transaction rollback を使えます。
復旧は checkpoint だけではありません。幂等性、トランザクション、補償、外部システム状態チェックが必要です。checkpoint さえあればすべての副作用を安全に復旧できる、という言い方は正確ではありません。
6. Agent タスク状態チェックリスト:復旧可能 vs 復旧不能
すべての checkpoint が復旧できるわけではありません。Terminal state は workflow execution の終端状態です。完了、失敗、timeout、cancelled などが該当します。Terminal state は復旧できず、再実行または補償しかありません。
6.1 状態分類表
| 状態タイプ | 復旧可能か | 復旧条件 | 復旧方法 | 例 |
|---|---|---|---|---|
| Failed(失敗) | 可能 | retry_count < max | 前の checkpoint から復旧 | ツール呼び出し timeout |
| Retry(再試行) | 可能 | 幂等チェック通過 | 失敗ノードから再実行 | メール送信失敗 |
| Compensation(補償) | 部分的に可能 | 補償案がある | undo_op を実行 | 在庫扣減失敗 |
| Approval Pause(承認停止) | 可能 | approve/reject event | Command(resume=…) | 承認待ち |
| Terminal(終端) | 不可 | なし | 復旧経路なし | COMPLETED、FAILED(retry_count = max) |
状態チェックリストの説明:Failed 状態は retry_count < max なら retry で復旧できます。Retry 状態は幂等チェックが必要で、失敗ノードから再実行します。Compensation 状態は補償案があれば部分的に復旧できます。Approval Pause 状態は approve/reject event で復旧します。Terminal State は復旧できません。COMPLETED、または最大 retry 回数に達した FAILED が該当します。
7. 次に読むもの
状態機械設計は出発点にすぎません。状態モデリングは具体的な業務シナリオと結びつける必要があり、タスクごとに状態粒度も復旧戦略も変わります。
シリーズ上下流ナビゲーション
| 記事 | 関係 | リンク |
|---|---|---|
| Human-in-the-loop Agent 設計:どのステップに人間の承認が必要か | 承認停止の詳細 | /blog/ja/posts/ai/20260707-human-in-the-loop-agent-approval-design/ |
| Agent コスト制御:モデルルーティング、ツール予算、失敗 retry の設計 | 予算、retry 戦略 | /blog/ja/posts/ai/20260707-agent-cost-control-model-routing-tool-budget-cache-retry/ |
| LangGraph 状態管理実践:2026 年の Agent アーキテクチャベストプラクティス | LangGraph 状態管理 | /blog/ja/posts/ai/20260424-langgraph-agent-architecture/ |
| AI Agent の監視・アラート・失敗復旧:ログから状態機械まで | 監視、復旧 | /blog/ja/posts/ai/20260527-ai-agent-monitoring-recovery/ |
| LangGraph vs AutoGen の状態追跡 | フレームワーク比較 | /blog/ja/posts/ai/20260526-langgraph-autogen-state-tracking/ |
| Agent 評価データセットと回帰テスト:「1 箇所の変更で全体を壊す」を避ける方法 | 評価、回帰テスト | 予告、同シリーズ次回 |
外部参考資料
信頼度の高い情報源:
| 出典 | 信頼度 | テーマ | リンク |
|---|---|---|---|
| LangGraph Persistence 公式ドキュメント | high | Checkpointer、Store、Thread State、Checkpoint | https://docs.langchain.com/oss/python/langgraph/persistence |
| LangGraph Interrupts 公式ドキュメント | high | interrupt()、Command(resume=…)、thread_id | https://docs.langchain.com/oss/python/langgraph/interrupts |
| Temporal Durable Execution 公式ドキュメント | high | Event History、Durable Execution、Resumable/Recoverable | https://docs.temporal.io/temporal |
| OpenAI Agents SDK Tracing 公式ドキュメント | high | Trace、Span、workflow_name、trace_id | https://openai.github.io/openai-agents-python/tracing/ |
| AWS Step Functions State Machines 公式ドキュメント | high | State Machine、Flow State、Task State、StartAt、Next | https://docs.aws.amazon.com/step-functions/latest/dg/concepts-statemachines.html |
| Stately: State machines and statecharts | medium | State、Event、Transition、Guard、Action、Hierarchy | https://stately.ai/docs/state-machines-and-statecharts |
状態機械はすべての Agent に必要な構成ではありません。しかし、複雑なタスクでは進捗を明示する必要があります。次にやるべきことは、さらに多くのフレームワークを導入することではありません。業務シナリオに合う State、Event、Transition、Guard、Action を設計し、タスク進捗を prompt の自然言語から構造化状態へ移すことです。
複雑な AI Agent の状態機械を設計する
複雑な AI Agent タスクを state、event、guard、action、checkpoint、retry、compensation、terminal state に分解し、進捗を prompt だけに隠さないようにします。
⏱️ 目安時間: 45 分
- 1
ステップ 1: リスク箇所を洗い出す
タスクの外部副作用、人間による停止点、失敗点、終了条件を列挙します。 - 2
ステップ 2: 最小限の状態集合を定義する
pending、running、waiting_approval、retrying、compensating、succeeded、failed、cancelled など、最小限の state 集合を定義します。 - 3
ステップ 3: イベントと次状態を結びつける
各 state が受け取れる event と、その event 後に遷移する next state を明記します。 - 4
ステップ 4: ガード条件を追加する
危険な transition には、権限、予算、承認、幂等キー、外部リソース状態の guard を追加します。 - 5
ステップ 5: ツール action を分離する
ツール呼び出しを action 層に置き、input 要約、output 要約、traceId、副作用の結果を記録します。 - 6
ステップ 6: 失敗ポリシーを定義する
各失敗経路について retry policy、terminal state、compensation policy を定義します。 - 7
ステップ 7: 復旧根拠を永続化する
復旧のための checkpoint または event log を定義し、prompt は一時コンテキストとして扱い、唯一の真実の情報源にはしません。
FAQ
Agent が第 5 ステップで失敗したら、第 1 ステップから再実行すべきですか、それとも checkpoint から続行すべきですか?
タスク状態は prompt、データベース、LangGraph checkpoint、キュー job のどこに置くべきですか?
状態機械と workflow/フローチャートは何が違いますか?
承認後、Agent が同じ実行点に戻ることをどう保証しますか?
retry と compensation は prompt に書くべきですか、それとも状態遷移ルールに書くべきですか?
単純なカスタマーサポート Agent にも状態機械は必要ですか?
10分で読めます · 公開日: 2026年9月17日
AI Agent エンジニアリング: アーキテクチャ、tool calling、評価、復旧
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。



コメント
GitHubアカウントでログインしてコメントできます