メインコンテンツへスキップ

9.6.3 LangChain / LangGraph

本節の位置づけ

Agent フレームワークに初めて触れるとき、多くの人は LangChain と LangGraph を混同しがちです。
でも、エンジニアリングの視点で見ると、この 2 つが解決している中心は完全に同じではありません。

  • LangChain は「よく使うコンポーネントをつなぐ」ものに近い
  • LangGraph は「複雑な状態の流れをグラフとして表す」ものに近い

この違いを理解することは、API を暗記することよりずっと重要です。

学習目標

  • LangChain がどんな場面に向いているかを理解する
  • LangGraph がなぜ複雑な状態フローに向いているのかを理解する
  • 「チェーン型の抽象化」と「グラフ型の抽象化」の本当の違いを読み取れるようになる
  • いつチェーン型の流れからグラフ型の流れに切り替えるべきか判断できるようになる

なぜ先に LangChain が生まれ、あとから LangGraph が出てきたのか?

初期の要件はたいてい直線的

多くの LLM アプリは、最初はこんな形です。

  1. 入力を受け取る
  2. query を書き換える
  3. 文書を検索する
  4. モデルに回答させる

この流れは、まるで 1 本の直線のようです。

前のステップの出力を、次のステップに渡す。

この場合、「chain(チェーン)」はとても自然な抽象化です。

でも、システムはすぐに複雑になる

次のような要件が出てきたら、どうでしょう。

  • 検索結果が空だったらどうする?
  • 回答が信用できないときは再試行する?
  • 先に tool を呼び出して、それから Q&A に戻るべき?
  • 場合によっては人の確認が必要

こうなると、流れはもはや一直線ではありません。むしろ、

  • 分岐がある
  • 状態がある
  • ループがある

という構造になります。

このとき必要なのは、単に「手順をつなぐ」ことではなく、

状態と辺を明示的に表すこと。

これが LangGraph が重要である理由です。


まず LangChain を理解する:何を抽象化しているのか?

最も向いているのは「コンポーネントのパイプライン」

LangChain の典型的な強みは、次のようなものをつなげることです。

  • prompt テンプレート
  • モデル呼び出し
  • 出力のパース
  • 検索モジュール
  • tool モジュール

こう考えるとわかりやすいです。

コンポーネントのオーケストレーション層に近いフレームワーク。

つまり、「プロンプト、モデル、検索器、パーサー」といった部品を、組み立てやすいブロックにしてくれるイメージです。

最小限のチェーン思考の例

次の例は実際の LangChain ではありませんが、LangChain 風の考え方を表しています。

class SimpleChain:
def __init__(self, steps):
self.steps = steps

def run(self, value):
for step in self.steps:
value = step(value)
return value

def normalize_query(text):
return text.strip().lower()

def retrieve_docs(query):
if "返金" in query:
return {"query": query, "docs": ["コース購入後 7 日以内なら返金できます"]}
return {"query": query, "docs": []}

def format_answer(payload):
if payload["docs"]:
return f"資料によると:{payload['docs'][0]}"
return "関連資料が見つかりませんでした。"

chain = SimpleChain([
normalize_query,
retrieve_docs,
format_answer
])

print(chain.run(" 返金ポリシーは何ですか? "))

想定出力:

資料によると:コース購入後 7 日以内なら返金できます

この例で一番覚えるべきことは?

この例が表しているのは、とてもシンプルな考え方です。

各ステップは 1 つのことだけを行い、全体はステップを順につなぐことでタスクを完了する。

これこそが、LangChain が最も入りやすい理由です。


LangChain はどんなときに便利なのか?

こんな場合に向いている

  • 流れが基本的に直線的
  • 分岐が少ない
  • 複数のモジュールを組み合わせることが主目的
  • すぐにプロトタイプを作りたい

典型例:

  • FAQ 検索 Q&A
  • テキスト抽出
  • 検索後生成
  • 単一 tool 強化 Q&A

メリット

  • すぐに始めやすい
  • コンポーネントのエコシステムが豊富
  • 小さなモジュールをまずつなげるのに向いている

どこでつらくなり始めるか?

次のような要件が出てくると、チェーン思考だけでは厳しくなります。

  • 長い状態の連鎖
  • 複数の分岐条件
  • ノードの戻り
  • 明示的な中間状態の管理

この段階になると、チェーン型の考え方ではだんだん無理が出てきます。


次に LangGraph を理解する:なぜ「状態機械」に近いのか?

LangGraph の中心はノードだけではなく、状態

LangGraph で最も重要なのは、次の視点です。

  • 次にどのコンポーネントを呼ぶか

ではなく、

  • 今の状態は何か
  • この状態はどの辺に進むべきか
  • ノード実行後に状態はどう更新されるか

先にこう理解するとよいです。

状態付きのワークフローグラフ。

最小限のグラフ型の例

def plan_node(state):
if "返金" in state["query"]:
state["next"] = "retrieve"
else:
state["next"] = "fallback"
return state

def retrieve_node(state):
state["docs"] = ["コース購入後 7 日以内なら返金できます"]
state["next"] = "answer"
return state

def answer_node(state):
state["answer"] = f"資料によると:{state['docs'][0]}"
state["next"] = None
return state

def fallback_node(state):
state["answer"] = "現在、対応するフローが見つかりませんでした。"
state["next"] = None
return state

nodes = {
"plan": plan_node,
"retrieve": retrieve_node,
"answer": answer_node,
"fallback": fallback_node
}

state = {"query": "返金ポリシーは何ですか", "next": "plan"}

while state["next"] is not None:
current = state["next"]
state = nodes[current](state)
print(current, "->", state)

想定出力:

plan -> {'query': '返金ポリシーは何ですか', 'next': 'retrieve'}
retrieve -> {'query': '返金ポリシーは何ですか', 'next': 'answer', 'docs': ['コース購入後 7 日以内なら返金できます']}
answer -> {'query': '返金ポリシーは何ですか', 'next': None, 'docs': ['コース購入後 7 日以内なら返金できます'], 'answer': '資料によると:コース購入後 7 日以内なら返金できます'}

このコードとチェーン型の例の最大の違いは?

チェーン型システムでは、

  • 次のステップは通常固定されています

一方、グラフ型システムでは、

  • 次のステップは現在の状態によって決まります

これが、グラフ型ワークフローの本質的な強みです。

LangGraph 状態機械と条件付きエッジの図

図の読み方

この図は、「チェーン型の手順」から「状態機械」へ切り替える助けになります。ノードは state を処理し、条件付きエッジが次の行き先を決め、checkpoint により複雑なフローでも復元やデバッグがしやすくなります。


いつ LangChain 思考から LangGraph 思考へ切り替えるべきか?

とても実用的な判断基準

フローチャートを描いたときに、それがもう 1 本の直線ではなくなっていて、次のような形になっていたら、

  • 明確な分岐がある
  • 失敗時の戻りがある
  • ループがある
  • 「中間結果に応じて次を決める」必要がある

そのときは、グラフ型の抽象化を考え始めるべきです。

とてもわかりやすい悪い兆候

コードが次のようになり始めたら、

if ...
if ...
if ...
while ...

しかも、その判断がすべて中間状態を中心に動いているなら、たいていはこういうことです。

もう単なる「チェーン型アプリ」ではなく、状態グラフシステムを作っている。


なぜ多くのチームが LangChain と LangGraph を両方話題にするのか?

現実のシステムは、たいてい「どちらか一方」ではないからです。

とてもよくある組み合わせ方

  • LangChain 風の役割でカプセル化するもの:

    • prompt
    • retriever
    • parser
  • LangGraph 風の役割で制御するもの:

    • 状態遷移
    • 分岐
    • リトライ
    • 人の確認が必要なノード

そのため、多くの場合、両者は次のような関係に近いです。

コンポーネント層 + ワークフロー層

完全に対立する 2 派というより、役割が違うのです。


実際の開発での選び方

もし今やりたいのが次なら:

  • 単発 FAQ
  • シンプルな RAG
  • 数ステップの固定フロー

まずはチェーン型の考え方で十分なことが多いです。

もし今やりたいのが次なら:

  • マルチステップ Agent
  • tool を使ったループ
  • 人の確認ノードがある
  • 中間状態に強く依存する

グラフ型の考え方のほうが安定します。

「上級っぽいから」という理由で最初からグラフにしない

これも大事な判断です。
グラフ型の抽象化は強力ですが、そのぶん次のコストもあります。

  • 学習コストが高い
  • 構造設計の手間が増える

複雑さが低いなら、チェーン型のほうがむしろ見やすいこともあります。


初心者がよくハマる落とし穴

タスク構造を理解する前に、フレームワーク API をたくさん覚える

これだと、最後に身につくのは「フレームワークの文法」だけで、システム設計ではありません。

チェーンで無理やりグラフを支えようとする

if/else がどんどん増えていくのに、グラフ型の抽象化に切り替えたがらないケースです。

最初からグラフ型フレームワークを使いすぎる

要件はとてもシンプルなのに、先にシステムを重くしてしまうパターンです。


まとめ

この節で一番大切なのは、フレームワーク名を覚えることではなく、次の判断軸を持つことです。

LangChain はよく使うコンポーネントをつなぐもの、LangGraph は複雑な状態の流れを明示的に描くもの。

タスク構造ではなくフレームワークの流行で判断しなくなったとき、選定はずっと安定します。


演習

  1. 自分の Agent システムを 1 つ描いてみて、それがチェーンに近いかグラフに近いか判断してみましょう。
  2. チェーン型の例に「文書が見つからなかったら query を書き換えてもう一度検索する」ロジックを追加し、どれくらい複雑になるか確認してみましょう。
  3. 自分の言葉で説明してみましょう。なぜ、状態付きループがあるシステムにはグラフ型の抽象化のほうが向いているのでしょうか?
  4. どんな場合に、チェーン型のほうがグラフ型より適しているかを考えてみましょう。