「状態管理とフロー制御がすっきりするはず」という期待で LangGraph を導入したのに、かえってコードが複雑になりました。
フレームワークを導入しても構造が悪化しうることを、自分で体験して整理した記録と共有です。
診断してみると、状態はこうなっていました。
- グラフにノードが 1 つしかない。
current_step → ENDの構造で条件付きエッジも分岐もなく、graph.invoke()は関数を直接呼ぶのとまったく同じ。Step の切り替えはグラフ外のサービスコードが判断する形。 - 同じ状態を 2 か所に保存。LangGraph
MemorySaver(インメモリ)と独自の PostgreSQL Checkpointer が同一セッションを二重に保存・復元し、不整合が発生。 - 約 8,700 行に及ぶ 3 つの LangGraph 実行コード(Executor)に重複。実質的な中核ロジックは「LLM 呼び出し + プロンプト組み立て」程度で、残りの大半は状態管理・条件分岐・エッジケースのパッチ。
表面的には、フレームワークの実際の機能(条件付きエッジ、組み込みチェックポインタ、Human-in-the-Loop)を使わず、外殻だけ導入したのが問題でしたが、さらに掘り下げると原因は別にありました。
- 大部分はバイブコーディングで作られていたが、制作過程で内部コードや設計原則を深く見ず、毎回要件と意図だけを整理して実装を続けていった方式。
- 問題はバイブコーディング自体ではなく、検証なしで進めたことだった。目の前の機能を最適化するだけで、全体構造の責務境界は守ってくれない。二重チェックポインティングも、散在した状態も、重複したロジックも、すべて部分最適化の蓄積。
- 設計者がいなかったのではなく、フレームワークが何を責務として設計されているかを十分理解しないまま、その上で指示だけを出していた状態こそが本当の原因。
そこで反省し、こうやって戻しました。
- 独立トポロジー +
thread_id分離によるセッション分離 - State にはメタデータだけを置き、本体は Store に移動
- regex パースの代わりにネイティブ
tool_useを使用 - テスト可能にするためのノード分離
- フレームワークに対する正確な理解と活用構造が必要だった自分自身
「どう使うか」ではなく、「どう間違って使ったか」についての記録と共有です。
2件のコメント
LangChain や LangGraph を使うあらゆるケースは、AI SDK で代替できそうです
そうですね。共感します。SDK への依存は生じるでしょうが、学習コストが高いので、そのほうがむしろ良さそうです。