LangGraphとは?StateGraphの書き方・人間承認・永続化【2026年】

2026年8月最新 — v1.0対応・実装コード付き
LangGraphとは?
StateGraphの書き方・人間承認・
永続化まで完全解説【2026年】

LangChainだけでは組めない処理がある。「条件によって処理を分岐させる」「失敗したら前のステップに戻る」「実行を途中で止めて人間の承認を待つ」——この3つが必要になった瞬間が、LangGraphの出番だ。2025年10月のv1.0で永続状態管理が実装され、長時間動作するエージェントを本番で運用できる基盤が整った。本記事では最小のStateGraphから、Human-in-the-loop・状態永続化までをコード付きで解説する。

StateGraph最小実装
Human-in-the-loop
LIF Tech
目次

1. 結論——LangChainとどう使い分けるか

両者は競合ではなく、役割が違う。v1.0以降は「LangChain=構築、LangGraph=実行、LangSmith=運用」という3層構造に整理された。

やりたいこと 使うべきもの
処理を一直線につなげる(入力→加工→出力) LangChainのみで十分
ツールを呼びながら回答を作る単純なエージェント LangChainのcreate_agent
条件によって次の処理を変えたい LangGraph
失敗したら前のステップに戻ってやり直したい LangGraph
実行を止めて人間の承認を待ちたい LangGraph
複数のエージェントを協調させたい LangGraph
長時間動作し、中断・再開が必要な処理 LangGraph(永続状態管理)
💡 判断基準は単純だ。「処理の流れが一本道か、分岐するか」。一本道ならLangChain、分岐・ループ・中断があるならLangGraph。実務では、LangChainで部品を作り、それをLangGraphのノードとして配置する形になることが多い。LangChain側の基礎はLangChainとは?v1.0で何が変わったか・使い方・RAG実装まで完全解説で解説している。

2. LangGraphとは何か——なぜグラフなのか

LangGraph(ランググラフ)は、AIエージェントのワークフローをグラフ構造で表現・実行・管理するOSSフレームワークだ。LangChainと同じく2025年10月22日にv1.0が正式リリースされ、PythonとTypeScriptに対応している。

なぜ「グラフ」なのか

従来のChain(チェーン)は、ベルトコンベアのように処理を一本道でつなぐ考え方だった。しかし現実のエージェント処理はこうならない。

# 現実に必要な処理の流れ
質問を受け取る

社内文書を検索する

検索結果は質問に答えられる内容か? ←── 判定する
├─ Yes → 回答を生成する
└─ No → Web検索に切り替える → 再度判定へ戻る
↑ ループ

「判定して分岐する」「条件次第で前に戻る」——これを一本道のChainで書くと、途端に複雑になる。グラフ構造(ノードとエッジ)で表現すれば、この流れをそのままコードに落とせる。これがLangGraphの存在理由だ。

3. 4つの基本概念

📦
State

グラフ全体で共有される状態。会話履歴・検索結果・判定フラグなどを保持する

⚙️
Node

処理の1単位。Stateを受け取り、更新した内容を返す関数として書く

➡️
Edge

ノード間のつながり。条件に応じて行き先を変える「条件付きエッジ」もある

💾
Checkpointer

状態の保存先。中断・再開や、人間の承認待ちを実現する土台になる

StateGraphが基本形

LangGraphには複数のグラフ型があるが、2026年時点の新規開発ではStateGraphを前提にするのが安全だ。

グラフ型 用途 2026年の位置づけ
StateGraph 状態を共有しながらノード間を遷移する これが基本。条件分岐が少しでも想定されるならこれ
Graph 共有Stateが不要な軽量フロー 明確な入出力変換のみの軽量ワークフロー向け
MessageGraph メッセージ列を扱う旧式 旧式。新規開発では使わない

4. 最小のStateGraphを書く

pip install langgraph langchain-anthropic

まずは最小構成。Stateを定義し、ノードを追加し、エッジでつないでコンパイルする——これだけだ。

from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END

# 1. Stateを定義する(グラフ全体で共有されるデータ)
class State(TypedDict):
question: str
answer: str

# 2. ノードを関数として書く(Stateを受け取り、更新分を返す)
def think(state: State) -> dict:
return {“answer”: f”「{state[‘question’]}」について考えました”}

def polish(state: State) -> dict:
return {“answer”: state[“answer”] + “(整形済み)”}

# 3. グラフを組み立てる
builder = StateGraph(State)
builder.add_node(“think”, think)
builder.add_node(“polish”, polish)

builder.add_edge(START, “think”)
builder.add_edge(“think”, “polish”)
builder.add_edge(“polish”, END)

graph = builder.compile()

# 4. 実行
result = graph.invoke({“question”: “LangGraphとは?”})
print(result[“answer”])

💡

ノードは「更新したい部分だけ」を辞書で返す。State全体を返す必要はない。返された辞書がStateにマージされる仕組みなので、複数のノードが別々のキーを更新しても衝突しない。

Reducerで累積させる

会話履歴のように「上書きではなく追加していきたい」項目には、Reducer(リデューサー)を使う。

from typing import Annotated
import operator

class State(TypedDict):
question: str
# operator.add で、返り値がリストに追加されていく
logs: Annotated[list[str], operator.add]

def step_a(state: State) -> dict:
return {“logs”: [“step_a を実行”]}

def step_b(state: State) -> dict:
return {“logs”: [“step_b を実行”]}
# 実行後、state[“logs”] は [“step_a を実行”, “step_b を実行”] になる

5. 条件分岐を実装する

LangGraphの本領はここからだ。add_conditional_edgesで、状態に応じて次のノードを動的に決められる。

def grade(state: State) -> dict:
# 検索結果が十分かをLLMに判定させる想定
is_enough = len(state[“documents”]) >= 2
return {“is_relevant”: is_enough}

# 判定結果に応じて行き先を返す関数
def route(state: State) -> str:
if state[“is_relevant”]:
return “generate” # 十分 → 回答生成へ
return “web_search” # 不十分 → Web検索へ

builder.add_node(“grade”, grade)
builder.add_node(“generate”, generate)
builder.add_node(“web_search”, web_search)

# 条件付きエッジ:routeの返り値でノードを切り替える
builder.add_conditional_edges(
“grade”,
route,
{
“generate”: “generate”,
“web_search”: “web_search”,
},
)

# Web検索したら、もう一度判定に戻す(ループ)
builder.add_edge(“web_search”, “grade”)

⚠️

ループを作るときは必ず終了条件を設ける。上の例で「Web検索しても常に不十分」と判定され続けると、無限ループになりLLMのAPI料金が際限なく発生する。実装時はrecursion_limitの設定と、リトライ回数をStateで数える仕組みを必ず入れてほしい。API課金の考え方はAIコスト最適化完全ガイドも参照。
# 再帰回数の上限を設定する(デフォルトは25)
graph.invoke(input_data, config={“recursion_limit”: 10})

6. Human-in-the-loop——AIを途中で止める

法人でAIエージェントを本番運用するなら、この機能が実質的な必須要件になる。メール送信・DB更新・外部発注のような「取り返しのつかない操作」を、AIに単独で実行させるわけにはいかない。

LangGraphではinterrupt()を使い、実行を任意の地点で停止して人間の判断を待てる。

from langgraph.types import interrupt, Command
from langgraph.checkpoint.memory import MemorySaver

def send_email(state: State) -> dict:
# 送信前に人間の承認を要求して停止する
approval = interrupt({
“action”: “メール送信”,
“to”: state[“recipient”],
“body”: state[“draft”],
})

if approval == “approve”:
# 実際の送信処理
return {“status”: “送信しました”}
return {“status”: “キャンセルしました”}

# Checkpointerがないと中断・再開できない点に注意
graph = builder.compile(checkpointer=MemorySaver())

config = {“configurable”: {“thread_id”: “user-123”}}

# 1回目の実行 → interrupt で停止する
graph.invoke({“recipient”: “client@example.com”}, config=config)

# 人間が内容を確認した後、承認して再開する
graph.invoke(Command(resume=”approve”), config=config)

💡 thread_idが会話・タスクの識別子になる。同じthread_idで再度invokeすると、中断した地点から処理が再開される。これがLangGraphの「止めて、待って、続きから動かす」を成立させている仕組みだ。

どこで止めるべきか

操作 人間承認 理由
社内文書の検索・要約 不要 読み取り専用で副作用がない
下書きの作成 不要 送信しなければ影響がない
メール・チャットの送信 必須 取り消せない。誤送信の影響が大きい
データベースの更新・削除 必須 データ破壊のリスク
決済・発注・契約に関わる操作 必須 金銭的損害に直結する
外部APIへの書き込み 推奨 相手先システムへの影響が読めない

7. 【v1.0の目玉】状態の永続化

v1.0で実装された永続状態管理(Durable state)により、エージェントの状態が自動保存され、再開できるようになった。長時間動作するエージェントや、中断・再開が必要なワークフローを安定して運用できる。

Checkpointer 保存先 用途
MemorySaver メモリ上 開発・検証用。プロセスを落とすと消える
SqliteSaver SQLiteファイル 小規模・単一サーバーでの運用
PostgresSaver PostgreSQL 本番運用の標準。複数サーバーからアクセス可能
from langgraph.checkpoint.postgres import PostgresSaver

DB_URI = “postgresql://user:password@localhost:5432/langgraph”

with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
checkpointer.setup() # 初回のみ:必要なテーブルを作成
graph = builder.compile(checkpointer=checkpointer)

config = {“configurable”: {“thread_id”: “session-001”}}
graph.invoke({“question”: “…”}, config=config)

# 過去の実行履歴を取得できる
for snapshot in graph.get_state_history(config):
print(snapshot.values)

💡

開発はMemorySaver、本番はPostgresSaverという切り替えが定石だ。SqliteSaverは手軽だが、複数プロセス・複数サーバーからの同時アクセスに弱いため、本番のスケールを見込むなら最初からPostgresを想定した設計にしておくほうが後の移行コストが小さい。

8. 実践:Agentic RAGを構築する

ここまでの要素を組み合わせると、「検索結果を自己評価し、不十分なら検索し直す」RAGが作れる。通常のRAGとの違いは、AIが自分の検索結果を判定して行動を変える点だ。

1

retrieve——社内文書のベクトルストアを検索する

2

grade——取得した文書が質問に答えられる内容かをLLMに判定させる

3a

十分 → generateで回答を生成して終了

3b

不十分 → rewriteで質問を言い換え、1に戻る(最大3回まで)

from typing_extensions import TypedDict
from typing import Annotated
import operator
from langgraph.graph import StateGraph, START, END

class RagState(TypedDict):
question: str
documents: list
answer: str
retry_count: Annotated[int, operator.add]

def retrieve(state: RagState) -> dict:
docs = retriever.invoke(state[“question”])
return {“documents”: docs}

def grade(state: RagState) -> dict:
# LLMに「この文書で質問に答えられるか」をYes/Noで判定させる
context = “\n”.join(d.page_content for d in state[“documents”])
verdict = model.invoke(
f”以下の資料で質問に答えられますか。yes か no のみで答えてください。\n”
f”【質問】{state[‘question’]}\n【資料】{context[:2000]}”
).content.strip().lower()
return {“is_relevant”: verdict.startswith(“yes”)}

def rewrite(state: RagState) -> dict:
# 検索にヒットしやすい表現へ質問を言い換える
new_q = model.invoke(
f”次の質問を、社内文書の検索にヒットしやすい表現へ言い換えてください。”
f”言い換えた質問のみを出力してください。\n{state[‘question’]}”
).content
return {“question”: new_q, “retry_count”: 1}

def generate(state: RagState) -> dict:
context = “\n”.join(d.page_content for d in state[“documents”])
ans = model.invoke(
f”以下の資料のみを根拠に答えてください。記載がなければ「資料に記載がありません」と答えてください。\n”
f”【資料】{context}\n【質問】{state[‘question’]}”
).content
return {“answer”: ans}

def route(state: RagState) -> str:
if state.get(“is_relevant”):
return “generate”
if state.get(“retry_count”, 0) >= 3: # 無限ループ防止
return “generate”
return “rewrite”

builder = StateGraph(RagState)
builder.add_node(“retrieve”, retrieve)
builder.add_node(“grade”, grade)
builder.add_node(“rewrite”, rewrite)
builder.add_node(“generate”, generate)

builder.add_edge(START, “retrieve”)
builder.add_edge(“retrieve”, “grade”)
builder.add_conditional_edges(“grade”, route,
{“generate”: “generate”, “rewrite”: “rewrite”})
builder.add_edge(“rewrite”, “retrieve”) # 言い換えて再検索
builder.add_edge(“generate”, END)

graph = builder.compile()

💡 retry_count >= 3で強制的にgenerateへ抜ける設計がポイントだ。「答えが見つからないときに、いつ諦めるか」を明示するのが、本番で動くエージェントと暴走するエージェントの分かれ目になる。RAGの精度をチャンク設計から詰めたい場合はAIヘルプデスクおすすめ比較|RAGとハルシネーションの正確な理解も参照してほしい。

9. LangGraph Studioでデバッグする

グラフ構造は、コードだけ見ても流れが追いづらい。LangGraph Studioを使えば、ノードの遷移とStateの変化を視覚的に確認できる。

pip install “langgraph-cli[inmem]”

# プロジェクトルートで実行するとStudioが起動する
langgraph dev

Studio上では次のことができる。

  • グラフ構造の可視化(どのノードからどこへ遷移するか)
  • ステップごとのState変化の確認
  • 任意のノードから実行を再開
  • interruptで停止した箇所での承認・却下の操作
💡

条件分岐が3つ以上になったら、Studioでの確認をルーチンにしたほうがいい。「なぜこのノードに来たのか」をコードから逆算するより、遷移を目で追うほうが圧倒的に速い。

10. 本番運用で押さえるべき設計

① 無限ループを構造的に防ぐ

recursion_limitの設定に加え、State内にリトライ回数を持たせて上限で強制脱出させる。ループの終了条件は、LLMの判定だけに委ねてはいけない。判定が常にNoを返す入力が来た瞬間に、課金が止まらなくなる。

② Checkpointerは最初からPostgresを想定する

MemorySaverで作り込んでから本番移行時に差し替えると、thread_id設計や同時実行の考慮漏れが表面化しやすい。スケールを見込むなら早めにPostgresSaverで検証しておく。

③ 副作用のある操作は必ずinterruptで挟む

第6章の表の通り、送信・更新・削除・決済はHuman-in-the-loopの対象だ。「AIが賢くなったから承認は不要」という判断はしない。賢さと、取り返しのつかなさは別の問題である。

④ 過剰にグラフ化しない

これが最も多い失敗だ。分岐が1つもない処理をStateGraphで書くと、素直に関数を並べるより読みにくくなるだけだ。LangGraphが効くのは「分岐・ループ・中断」のいずれかが本当に必要な場合に限られる。要件がシンプルならLangChainのcreate_agentで十分である。

メリット・デメリット

✓ LangGraphのメリット
  • 条件分岐・ループを自然にコードで表現できる
  • interrupt()で人間の承認を組み込める
  • v1.0の永続状態管理で中断・再開が安定
  • Studioで遷移とStateを視覚的にデバッグできる
  • マルチエージェント構成を組みやすい
  • OSSで無料。処理を完全に自社制御できる
✗ LangGraphのデメリット
  • 学習コストが高い(State設計の理解が前提)
  • 単純な処理には明確にオーバースペック
  • 無限ループ設計を誤るとAPI料金が膨らむ
  • 本番運用にはDB(Postgres)の準備が必要
  • v0.x時代の情報が多く、鮮度の見極めが要る
  • ノーコードで扱いたい非エンジニアには不向き

11. よくある質問

LangChainとLangGraph、どちらから学ぶべきですか?
LangChainからです。モデル・ツール・RAGといった基本部品の扱いを理解してから、条件分岐や人間承認が必要になった段階でLangGraphへ進むのが自然な順序です。v1.0以降は「LangChain=構築、LangGraph=実行」と役割が整理されており、実務では両方を併用します。
StateGraphとMessageGraphの違いは?
MessageGraphは旧式で、新規開発では使わないほうが安全です。2026年時点ではStateGraphが基本形として位置づけられており、条件分岐や複数ステップが少しでも想定されるならStateGraphから設計を始めてください。
interrupt()を使うのにCheckpointerは必須ですか?
必須です。中断した状態を保存する仕組みがないと再開できないためです。開発中はMemorySaver、本番運用ではPostgresSaverを使うのが定石になります。
無限ループでAPI料金が跳ね上がるのを防ぐには?
2段構えで防ぎます。①invoke時にrecursion_limitを設定する(デフォルトは25)、②Stateにリトライ回数を持たせ、上限に達したら強制的に終了ノードへ抜ける分岐を書く。LLMの判定結果だけをループの終了条件にするのは危険です。
LangGraphは無料で使えますか?
ライブラリ自体はオープンソースで無料です。コストが発生するのはLLMのAPI利用料と、運用監視プラットフォームのLangSmith(Developerは無料、Plusは月$39〜)です。
どのくらいの規模から使う価値がありますか?
規模より「処理の性質」で判断してください。小規模でも、人間承認が必要な操作や条件分岐があるならLangGraphの価値があります。逆に大規模でも、処理が一本道ならLangChainだけで十分です。
Difyのようなノーコードツールとの違いは?
Difyはノーコードでフローを組める代わりに、実装の自由度に制約があります。LangGraphはコードで書く分、独自のミドルウェア・複雑な分岐・既存システムとの深い統合が可能です。社内の非エンジニアがフローを編集する運用ならDify、エンジニアが本番システムに組み込むならLangGraphという使い分けになります。
AIエージェントを「本番で回る」状態にするなら

LangGraphの実装で難しいのは、コードよりも「どこで止めるか」「どう諦めるか」の設計です。人間承認の設計、無限ループの防止、状態永続化を含めた本番運用の設計は、LIFRELLのAIマーケティング相談をご活用ください。各種AI・SaaSツールを実際に有料契約して検証している立場から、現場で回る形に落とし込みます。

社内での生成AI定着から着手したい場合は生成AIeラーニング研修おすすめ完全ガイドもご覧ください。

🕸️
LIF Tech 編集部(株式会社LIFRELL)

// lifrell-tech.com — AI × マーケティング最前線

AIマーケティング・テクノロジー専門メディア。国内外のAIカンファレンスへの現地取材と、各種AI・SaaSツールの実契約による検証をもとに解説しています。v1.0の変更点はLangChain公式のリリースノートおよび移行ガイドの内容に基づき整理しました。GITEX AI EUROPE 2026(ベルリン)公式メディアパートナー。

本記事は2026年8月時点の情報をもとに作成しています。LangGraphは更新が速いフレームワークのため、実装にあたっては必ず公式ドキュメントで最新のAPI仕様をご確認ください。掲載コードは動作の考え方を示すサンプルであり、本番利用の際はエラー処理・認証情報の管理・再帰上限の設定を適切に実装してください。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次