LangGraphエージェントは、数週間にわたるサイレントなデータ消失を経て、ようやく状態を保持するための信頼できる方法を手に入れました。SQLite、生のオブジェクトストレージ、そしてそれらの不完全なバージョンの3つのチェックポインティング手法が失敗した後、著者は、リクエストが届くたびにエージェントが最初からやり直すのを防ぐ「アトミック更新パターン」に辿り着きました。

LangGraphにおいてチェックポインティングが重要な理由

LangGraphは、LLMの呼び出しを再利用可能な「エージェント」として繋ぎ合わせ、会話の以前の内容を記憶できるようにするものです。これらのエージェントは、ユーザーのリクエストをサブタスクに分解し、中間結果を保存し、次の呼び出し時に中断した場所から再開します。保存された状態が消失すると、エージェントはすべてを再計算することになり、計算リソースの浪費、レイテンシの増大、そしてユーザー体験の低下を招きます。Telegramのメッセージを処理する本番環境のボットでは、この消失により数週間分の会話履歴が消えてしまいました。

最初の修正:SQLite saver

内蔵の SqliteSaver は、単一のインスタンスがエージェントを実行している場合は問題なく動作します。各チェックポイントをローカルのSQLiteファイルにJSONブロブとして書き込みます。問題は、開発者が AgentState 型に新しいフィールドを追加して再デプロイしたときに発生しました。スキーマ変更前に作成された既存のチェックポイントには、新しいフィールドがありませんでした。SqliteSaver はマイグレーションを実行しないため、LangGraphは不完全なJSONをロードし、欠落しているデータを破棄し、エージェントは最初から再起動してしまいました。

Key point: SQLiteストレージはデモ用のツールであり、スキーマの進化が必要な場合には本番環境向けのソリューションではありません。

2番目の修正:オブジェクトストレージ

シリアライズ形式を制御するために、著者はJSONチェックポイントをOracle Cloud Object Storageにアップロードするカスタムセーバーを作成しました。この移行により、手動でスキーマのバージョン管理を行う柔軟性が得られましたが、新たな失敗モードが導入されました。2つのリクエストが同時に同じ会話スレッドに到達すると、両方が同じオブジェクトを上書きしようとします。オブジェクトストレージサービスは「一度書き込み、何度も読み取る(write-once, read-many)」パターンに最適化されており、アトミックな上書きセマンティクスは提供していません。このレースコンディションにより、不正な形式や切り詰められたJSONファイルが生成され、エージェントは再びコンテキストを失いました。

Key point: 複数のワーカーが同時に同じキーにアクセスする可能性がある場合、オブジェクトストレージでの単純な上書きは安全ではありません。

3番目の修正:バージョニングによるアトミック更新

最終的な安定した設計は、「明示的なバージョン番号」と「オブジェクトのETag(ストレージサービスのチェックサム識別子)に基づいた条件付き書き込み」という2つのアイデアを組み合わせたものです。

  1. 現在のチェックポイントを読み取り、そのETagを取得する。
  2. チェックポイントのエンベロープ内のバージョンフィールドをインクリメントする。
  3. 先ほど読み取ったETagと一致する場合にのみ成功する条件付きリクエストを使用して、更新されたチェックポイントを書き込む
  4. 他のプロセスがオブジェクトを変更したために条件付き書き込みが失敗した場合は、読み取り・インクリメント・書き込みのループ全体をリトライする。

書き込みは、他のプロセスがファイルを変更していない場合にのみ成功するため、一度に1つのワーカーだけが新しい状態をコミットできます。また、バージョンフィールドにより、古いチェックポイントの検出や、スキーマ変更時の移行も容易になります。

このパターンは、ETagベースの条件付き書き込みをサポートするオブジェクトストレージで機能します。

AIエンジニアへの教訓

  • SQLiteはプロトタイプのみに使用する。 本番環境のエージェントには、スキーマ変更や並行書き込みを処理できるストアが必要です。
  • スキーママイグレーションは自身で計画する。 型付き辞書(TypedDict)は静的解析のための形状を記述しますが、実行時の構造を強制するものではありません。
  • 状態を共有リソースとして扱う。 並行性のバグはサイレントなデータ消失として現れます。これらは、明らかな例外が発生する場合よりもデバッグが困難です。
  • クラウドのプリミティブを活用する。 ETagベースの条件付き書き込みを使用すれば、個別のロックサービスを用意することなく、安価に楽観的ロックを実現できます。
  • すべてのステップをログに記録する。 LangGraphが無視してしまう欠落したフィールドのような「サイレントな失敗」は、追跡が最も困難です。

LangGraphのチェックポインティングの今後

すでに同じ障害に直面しているチームにとって、このアトミック更新のレシピは、迅速かつ低コストな解決策となります。信頼できる本番用パイプラインには、重量級のステートストアは必要なく、並行性とバージョニングを慎重に扱うことさえできればよいということを示しています。

Takeaway: シンプルなバージョン付きエンベロープと条件付き書き込みを組み合わせることで、不安定なシステムを信頼できるものへと変え、AIエンジニアが果てしないデータ消失のデバッグではなく、エージェントのロジックに集中できるようにします。