Claude Media
Claude CodeでLangGraphエージェントを実装する手順 — 状態遷移グラフの設計から永続化まで

Claude CodeでLangGraphエージェントを実装する手順 — 状態遷移グラフの設計から永続化まで

LangGraphのStateGraphでノードとエッジを組み、Claude Sonnetを接続してツール呼び出しループを作る手順を、Claude Codeでの開発効率化まで含めて解説します。

LangGraphとは何か、Claude Codeとどう組み合わせるか

LangGraphは、状態を持つ長時間実行のエージェントを構築・実行するための、低レベルなオーケストレーションフレームワークです。決定論的な手続き型のステップと、LLMが判断するエージェント的なステップを、同じグラフの中に混在させられる点が中核の特徴です。LangChain社が開発していますが、LangChain自体を使わなくても単独で利用できます。

Claude Codeはこのグラフを書くための開発環境として使えます。LangGraphのコードそのものはPythonのライブラリなので、Claude/Anthropicの機能ではありません。Claude Codeが担うのは、StateGraphの雛形を書く、ノード関数を実装する、テストを回すといった開発作業の自動化です。モデル側はlangchain-anthropicパッケージ経由でClaude Sonnetなどを接続します。

この記事では、公式クイックスタートに沿ってツール呼び出しループを持つエージェントを組み、状態設計・エッジ設計・永続化までを一通り実装します。あわせて、Claude Code自体をLangGraph開発にどう活かせるかも扱います。

前提条件を整える

Python環境と、Claudeの利用契約(APIキー)が必要です。パッケージは3つに分かれています。

pip install langchain_core langchain-anthropic langgraph

ANTHROPIC_API_KEY環境変数を設定します。Claudeコンソールでキーを発行し、シェルか.envファイルに設定してください。

export ANTHROPIC_API_KEY="sk-ant-..."

トレースを見たい場合はLangSmithの利用も選べますが、必須ではありません。ここではLangGraph単体の実装に絞ります。

手順1: モデルとツールを準備する

ChatAnthropic(またはinit_chat_model)でClaudeモデルを読み込み、@toolデコレータでツール関数を定義します。公式クイックスタートは四則演算ツールを例に使っています。

from langchain.tools import tool
from langchain.chat_models import init_chat_model
 
model = init_chat_model("claude-sonnet-4-6", temperature=0)
 
@tool
def add(a: int, b: int) -> int:
    """Adds `a` and `b`."""
    return a + b
 
@tool
def multiply(a: int, b: int) -> int:
    """Multiply `a` and `b`."""
    return a * b
 
tools = [add, multiply]
tools_by_name = {t.name: t for t in tools}
model_with_tools = model.bind_tools(tools)

手順2: 状態(State)スキーマを設計する

StateGraphは、ユーザー定義のStateオブジェクトでパラメータ化します。会話メッセージを扱うだけならMessagesStateを継承するのが最短です。

from langgraph.graph import MessagesState
from typing_extensions import TypedDict
 
class AgentState(MessagesState):
    llm_calls: int

ノードは辞書の一部だけを返し、グラフ側が既存の状態とマージします。マージのされ方を決めるのがreducerです。既定のreducerは新しい値で上書きしますが、Annotated[list, operator.add]のように指定すれば、リストへの追記に変わります。メッセージ履歴を上書きせず積み上げたいときは、この仕組みが必須です。MessagesStateはこの追記型reducerをあらかじめ組み込んだ状態スキーマです。

手順3: ノードを実装する

ノードはadd_nodeでグラフに登録する、状態を受け取り更新を返す関数です。モデルを呼ぶノードと、ツールを実行するノードの2つを用意します。

from langchain.messages import SystemMessage, ToolMessage
 
def llm_call(state: AgentState):
    return {
        "messages": [
            model_with_tools.invoke(
                [SystemMessage(content="あなたは計算を手伝うアシスタントです。")]
                + state["messages"]
            )
        ],
        "llm_calls": state.get("llm_calls", 0) + 1,
    }
 
def tool_node(state: AgentState):
    result = []
    for call in state["messages"][-1].tool_calls:
        tool = tools_by_name[call["name"]]
        observation = tool.invoke(call["args"])
        result.append(ToolMessage(content=observation, tool_call_id=call["id"]))
    return {"messages": result}

ツール実行を自分で書く代わりに、公式が提供するToolNodeも使えます。並列実行・エラーハンドリング・状態への注入を自動でこなす、プリビルドのノードです。

from langgraph.prebuilt import ToolNode
 
builder.add_node("tools", ToolNode(tools))

グラフ内で状態やcontextを読み書きしたいツールがある場合、Commandオブジェクトを返すツールとの組み合わせではToolNodeが推奨されます。自作のツール実行ノードでは、Commandの伝播を自分で書く必要があるためです。

手順4: エッジで状態遷移を定義する

エッジはノード間の遷移を決めます。種類は4つです。

種類書き方動作
通常のエッジ書き方add_edge("node_a", "node_b")動作常に次のノードへ進む
条件付きエッジ書き方add_conditional_edges("node_a", routing_fn)動作ルーティング関数の戻り値で次のノードを決める
エントリーポイント書き方add_edge(START, "node_a")動作最初に実行するノードを指定する
条件付きエントリーポイント書き方STARTから条件付きエッジを張る動作開始ノード自体を動的に決める

ツール呼び出しループでは、モデルがツールを呼んだかどうかで次を分岐させます。

from typing import Literal
from langgraph.graph import StateGraph, START, END
 
def should_continue(state: AgentState) -> Literal["tools", END]:
    if state["messages"][-1].tool_calls:
        return "tools"
    return END
 
builder = StateGraph(AgentState)
builder.add_node("llm_call", llm_call)
builder.add_node("tools", tool_node)
builder.add_edge(START, "llm_call")
builder.add_conditional_edges("llm_call", should_continue, ["tools", END])
builder.add_edge("tools", "llm_call")

1つのノードから通常のエッジと条件付きエッジ(またはCommand)を両方張ると、両方の経路が実行され得るため挙動を追いにくくなります。ノードごとにどちらか一方のルーティング方式に統一するのが安全です。

手順5: グラフをコンパイルして実行する

compile()でグラフを実行可能なオブジェクトに変換し、invokeで1回分の実行を回します。

agent = builder.compile()
 
from langchain.messages import HumanMessage
result = agent.invoke({"messages": [HumanMessage(content="3と4を足して")]})
for m in result["messages"]:
    m.pretty_print()

agent.get_graph(xray=True).draw_mermaid_png()でグラフの構造を画像として確認できます。ノードとエッジを図で見ながら設計を検証したいときに使います。

実行状態を永続化する

長時間動くエージェントや、中断から再開したいエージェントには、checkpointerが要ります。スレッド単位で状態をスナップショットとして保存し、会話の継続・人間による介入・障害からの復旧を支えます。

from langgraph.checkpoint.memory import InMemorySaver
 
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
 
graph.invoke(
    {"messages": [HumanMessage(content="Bobです、よろしく")]},
    {"configurable": {"thread_id": "thread-1"}},
)

checkpointerとstoreは役割が異なります。

項目checkpointerstore
保存対象checkpointerグラフ状態のスナップショットstoreアプリ定義のキー・バリューデータ
スコープcheckpointer1スレッド内storeスレッドをまたぐ
用途checkpointer会話の継続・人間介入・障害復旧storeユーザーの好み・共有知識

InMemorySaverはプロセスのメモリ上に保存するだけなので、プロセスが再起動すると消えます。本番運用ではPostgresSaverSqliteSaverのような永続ストレージに切り替えます。この状態設計と永続化の考え方は、AnthropicのContext Engineering論が扱う「長時間動くエージェントに何を持たせ、何を捨てるか」という論点とも重なります。

checkpointerを組み込むと、interrupt()関数でグラフの実行を任意の地点で止められます。ノード内でinterrupt("承認しますか?")のように呼ぶと、その時点の状態が保存されたまま実行が無期限に一時停止します。再開するときはCommand(resume=...)を渡して呼び出し直すと、その値がinterrupt()の戻り値としてノードに渡り、続きから処理が進みます。承認フローやレビューを挟みたいノードに使う仕組みです。

Claude Codeでの開発を効率化する具体的な方法

LangGraphの公式クイックスタートページには、Claude Codeへそのまま貼り付けて実行できる指示文(Prompt)が用意されています。この指示文を渡すと、Claude Codeが言語判定から依存パッケージのインストールまでを進めます。続けてANTHROPIC_API_KEYの確認と、四則演算エージェントの実装まで一気に進めます。手順1〜5を自分で書く前に、まずこの指示文で動くものを1つ作ってから読み替えると理解が早まります。

さらに、コーディングエージェント向けに2つの具体的な拡張も案内されています。1つ目は、LangChainのドキュメントMCPサーバーをClaude Codeに接続する方法です。

claude mcp add --transport http docs-langchain https://docs.langchain.com/mcp
claude mcp add --transport http reference-langchain https://reference.langchain.com/mcp

接続すると、Claude CodeがLangChain・LangGraph・LangSmithの最新ドキュメントとAPIリファレンスをその場で参照できるようになります。コーディングエージェントごとのMCP対応状況の違いはAIコーディングエージェントのMCP対応状況を比較するにまとめています。

2つ目は、LangChain公式のAgent Skillsパッケージの導入です。

npx skills add langchain-ai/langchain-skills --skill '*' --yes

LangGraph・LangChain・Deep Agents特有のタスクでの精度を上げる目的で配布されているスキル集です。Claude Codeのプロジェクトスコープにインストールすれば、以降のセッションで自動的に読み込まれます。

LangGraphが向く場面・向かない場面

LangGraphはあらゆる会話ボットに必要なものではありません。場面ごとの向き不向きは次のとおりです。

場面LangGraphが向くか理由
決定論的な処理とLLM判断を1つのフローに混在させたいLangGraphが向くか理由StateGraphで手続き型ノードとLLM駆動ノードを同居させられる
長時間実行・中断からの再開が必要LangGraphが向くか理由checkpointerでスレッド単位の状態を永続化できる
人間による承認・修正を挟みたいLangGraphが向くか理由interrupt機能で任意の時点の状態を検査・変更できる
単発の一問一答で完結するLangGraphが向くか理由オーケストレーション層を持ち込むほどの複雑さがない
Claude Code内の自動化だけで足りるLangGraphが向くか理由サブエージェントとhookの組み合わせで完結することが多い

モデルをどう割り当てるかという設計判断は、LangGraphのノード単位でも、Claude Codeのサブエージェント単位でも同じ発想で考えられます。役割ごとに単価と精度を天秤にかける具体的な基準はClaude Codeサブエージェントのモデル配分設計で扱っています。トレースや可観測性の面では、Anthropic純正のエージェント構築手段であるAgent SDKにもOpenTelemetryで可観測化するという別のアプローチがあります。

よくあるつまずき

PostgresSaver(やAsyncPostgresSaver)はthread_idを長さ制限のあるカラムに保存するため、255文字を超えると失敗します。UUIDやハッシュ値に短縮して使うと回避できます。

MemorySaverInMemorySaverはメモリ上にしか保存しないため、プロセスを再起動すると状態がすべて失われます。開発中の確認用と割り切るか、本番では永続ストレージへ切り替えます。

再帰上限に達する。LangGraphはグラフの実行を「スーパーステップ」単位で数えており、v1.0.6以降は既定で1000ステップが上限です。上限に達するとGraphRecursionErrorが送出されます。ループが終了条件を満たさない設計だと、この上限で強制停止します。invokestreamconfigrecursion_limitを渡せば、個別に調整できます。

通常のエッジと条件付きエッジを同じノードから両方張る。手順4で触れたとおり、両方の経路が同時に実行されうるため、意図しない並行実行が起きます。ノードごとにルーティング方式を1つに統一します。

まとめ

LangGraphは、決定論的なコードとLLM駆動の判断を同じグラフに同居させるためのオーケストレーション層です。State・Node・Edgeの3要素を設計し、ChatAnthropicでClaude Sonnetを接続します。これだけで、ツール呼び出しループを持つエージェントが組み上がります。

Claude Code自体は開発環境の役割で、LangChainのドキュメントMCPサーバーやAgent Skillsを組み込めば、実装作業そのものを効率化できます。長時間実行や再開が要る用途ではcheckpointerを、単発の会話で済む用途では素のグラフを、という切り分けが出発点になります。

この記事を共有:XはてブLinkedIn