信頼できるエージェントワークフローの設計
長時間タスクを現実から守る:冪等性、チェックポイント、再開可能性、明示的な失敗状態。
プロセスは死ぬものと仮定する
長時間ワークフローは必ず中断されます。ネットワークの一瞬の揺れ、レート制限、コンテナ再起動、ノート PC を閉じるユーザー。最初から中断を前提に設計します。直線スクリプトへの再開可能性の後付けは書き直しになるからです。
各ステップの核心の問い:ここで落ちたら、再試行時に何が起きるか?
冪等性が再試行を安全にする
2 回実行しても 1 回と同じ結果になる操作が冪等です。この性質だけが、再試行を危険から安全に変えます。
| 冪等でない | 冪等にする |
|---|---|
create_report(id) | create_report(id, dedupe_key=task_id) |
append_row(...) | upsert_row(row_id, ...) |
send_email(to, body) | send_email(idempotency_key=hash(to+body+date)) |
increment_counter() | set_counter(computed_value) |
キーはタスクから決定的に導出します。乱数にすると意味がなくなります。
チェックポイント:状態は文脈の外へ
意味のあるステップごとに進捗を永続化ストレージへ書きます。モデルの記憶には置きません:
run-2026-10-16-a1/
├── manifest.json # 入力、タスク ID、開始時刻
├── steps/
│ ├── 01-extract.json # 状態、成果物ポインタ、所要時間
│ ├── 02-transform.json
│ └── 03-review.json
└── artifacts/
再開時は manifest を読み、未完了の最初のステップを見つけて続けます。完了済みステップは 成果物の存在確認で飛ばし、状態フィールドを信じません。書き込み途中で死んだ場合、状態フィールドは嘘をつきます。
失敗状態は明示的に
「失敗」は診断ではありません。処置が違うので区別します:
| 状態 | 意味 | 対応 |
|---|---|---|
retryable | 一時的(タイムアウト、429) | バックオフして再試行 |
validation_failed | 出力が不正 | エラーを添えて再試行 |
needs_input | 必要情報の欠落 | 人に聞く。推測しない |
blocked | 外部依存が利用不可 | タスクを保留し通知 |
fatal | 回復不能 | きれいに停止し成果物を残す |
何でも retryable とするワークフローは予算を焼き、やがて状態を壊します。何でも fatal とするものは早々に諦めます。
モデルに失敗を黙って吸収させてはいけません。ステップが成功しなかったなら、ワークフローが 知っている 状態にします。ぼんやりした記憶ではだめです。
予算の追跡
長時間タスクは、アイデアが尽きる前に資金が尽きます。実行ごとに記録します:
- 消費トークン/上限
- ツール呼び出し回数/上限
- 経過実時間
- ステップごとの再試行回数
予算は各ステップの 開始前 に確認します。超過に気づいた後では遅い。上限に達したら interrupted で停止し、再開可能なチェックポイントを残します。変更が半分適用された状態で止めてはいけません。
再開チェックリスト
- manifest を読む → タスクの同一性と入力を復元
- 成果物の存在で完了済みステップを発見
- 前回実行の成果物を再検証する(無傷と決めつけない)
- 未完了の最初のステップから継続
- 以降、各ステップ前に予算を再確認
3 番が飛ばされがちです。クラッシュ直前のチェックポイントは途中で切れているかもしれません。検証は 1 回の読み込みで済み、数時間の混乱を防ぎます。