チャットボットはツールを使用してユーザの質問に答えられるようになりましたが、以前のやり取りのコンテキストを記憶していません。これは一貫性のある、複数ターンの会話をする能力を制限しています。LangGraph は永続的なチェックポインティングを通してこの問題を解決します。
※ LangGraph 0.5 でドキュメント構成が大幅に変更されましたので、再翻訳しながら Google Colab ベースで動作するようにまとめ直します。モデルは可能な限り最新版を利用し、プロンプトは日本語を使用します。
LangGraph 0.5 on Colab : Get started : メモリの追加
作成 : クラスキャット・セールスインフォメーション
作成日時 : 06/30/2025
* 本記事は langchain-ai.github.io の以下のページを独自に翻訳した上で、補足説明を加えてまとめ直しています :
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
LangGraph 0.5 on Colab : Get started : メモリの追加
チャットボットはツールを使用してユーザの質問に答えられるようになりましたが、以前のやり取りのコンテキストを記憶していません。これは一貫性のある、複数ターンの会話をする能力を制限しています。
LangGraph は 永続的なチェックポインティング を通してこの問題を解決します。グラフのコンパイル時にチェックポインターを提供して、グラフを呼び出すときに thread_id を提供すれば、LangGraph は各ステップ後に状態を自動的にセーブします。同じ thread_id を使用してグラフを再度呼び出す場合、グラフはセーブされた状態をロードして、チャットボットが中断したところから再開することを可能にします。
チェックポインティングが単純なチャットメモリよりも遥かに強力であることを後で見ます – それは、エラーリカバリー、human-in-the-loop ワークフロー、タイムトラベル相互作用、等のためにいつでも複雑な状態を保存して再開させることができます。しかしまずは、チェックポインティングを追加して複数ターンの会話を可能にしましょう。
0. 準備
このチュートリアルは前回の Tavily Web 検索ツールの追加 のスニペットをベースに構築されていますので、必要なコードを下記にまとめておきます :
%%capture --no-stderr
%pip install -U --quiet langgraph "langchain[openai]"
%pip install -U --quiet langchain-tavily
import getpass
import os
def _set_env(key: str):
if key not in os.environ:
os.environ[key] = getpass.getpass(f"{key}:")
_set_env("TAVILY_API_KEY")
_set_env("OPENAI_API_KEY")
from langchain.chat_models import init_chat_model
llm = init_chat_model("openai:gpt-4.1")
from typing import Annotated
from langchain_tavily import TavilySearch
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
tool = TavilySearch(max_results=2)
tools = [tool]
llm_with_tools = llm.bind_tools(tools)
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
graph_builder.add_node("chatbot", chatbot)
tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)
graph_builder.add_conditional_edges(
"chatbot",
tools_condition,
)
# Any time a tool is called, we return to the chatbot to decide the next step
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
1. MemorySaver チェックポインターの作成
MemorySaver チェックポインターを作成します :
from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()
これは in-memory チェックポインターで、チュートリアル用には便利です。けれども production アプリケーションでは、これを SqliteSaver や PostgresSaver を使用してデータベースに接続するように変更する方が良いかもしれません。
2. グラフのコンパイル
提供されたチェックポインターでグラフをコンパイルします、これにより、グラフが各ノードを通過するときに State をチェックポイントします :
graph = graph_builder.compile(checkpointer=memory)
from IPython.display import Image, display
try:
display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
# This requires some extra dependencies and is optional
pass
3. チャットボットと相互作用する
ボットとやり取りできるようになりました!
- この会話のキーとして使用するスレッド (ID) を選択します。
config = {"configurable": {"thread_id": "1"}}
- チャットボットを呼び出します :
user_input = "こんにちは!私の名前は太郎です。" # The config is the **second positional argument** to stream() or invoke()! events = graph.stream( {"messages": [{"role": "user", "content": user_input}]}, config, stream_mode="values", ) for event in events: event["messages"][-1].pretty_print()
================================ Human Message ================================= こんにちは!私の名前は太郎です。 ================================== Ai Message ================================== こんにちは、太郎さん!お話しできてうれしいです。今日はどんなことをお手伝いしましょうか?
4. フォローアップの質問をする
フォローアップの質問をしてみてください :
user_input = "私の名前を覚えていますか?"
# The config is the **second positional argument** to stream() or invoke()!
events = graph.stream(
{"messages": [{"role": "user", "content": user_input}]},
config,
stream_mode="values",
)
for event in events:
event["messages"][-1].pretty_print()
================================ Human Message ================================= 私の名前を覚えていますか? ================================== Ai Message ================================== はい、覚えています!あなたの名前は太郎さんですよね。何か他にも覚えておいてほしいことがあれば、教えてくださいね。
メモリ用に外部のリストを使用していないことに注目してください : それはすべてチェックポインターにより処理されます!You can inspect the full execution in this LangSmith trace to see what’s going on.
Don’t believe me? 異なる config を使用してこれを試してみてください。
# The only difference is we change the `thread_id` here to "2" instead of "1"
events = graph.stream(
{"messages": [{"role": "user", "content": user_input}]},
{"configurable": {"thread_id": "2"}},
stream_mode="values",
)
for event in events:
event["messages"][-1].pretty_print()
================================ Human Message ================================= 私の名前を覚えていますか? ================================== Ai Message ================================== 申し訳ありませんが、前回のお名前を覚えていません。会話を終了すると、個人情報や進行中の内容はすべてリセットされるため、お名前などの情報は保持していません。もしよろしければ、改めてお名前を教えていただけますか?
Note : 行った唯一の変更点は、config の thread_id の変更だけであることに注意してください。See this call’s LangSmith trace for comparison.
5. 状態を調べる
これまでに、2 つの異なるスレッドに渡り、チェックポイントを作成しました。しかしチェックポイントには何が含まれるのでしょう?任意の時間について指定された config 用のグラフの状態を調べるには、get_state(config) を呼び出します。
snapshot = graph.get_state(config)
snapshot
StateSnapshot(values={'messages': [HumanMessage(content='こんにちは!私の名前は太郎です。', additional_kwargs={}, response_metadata={}, id='cabc68aa-d92b-4450-a41f-669d9f35abb4'), AIMessage(content='こんにちは、太郎さん!お話しできてうれしいです。今日はどんなことをお手伝いしましょうか?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 30, 'prompt_tokens': 777, 'total_tokens': 807, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-2025-04-14', 'system_fingerprint': 'fp_799e4ca3f1', 'id': 'chatcmpl-Bnz0Rtw3plgzzFI27JSsh85T8U25E', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--26f7b1b0-7616-4ac8-93f1-459b8c38461b-0', usage_metadata={'input_tokens': 777, 'output_tokens': 30, 'total_tokens': 807, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), HumanMessage(content='私の名前を覚えていますか?', additional_kwargs={}, response_metadata={}, id='5ffb0f90-d12c-40ae-8462-a1e33e3aa459'), AIMessage(content='はい、覚えています!あなたの名前は太郎さんですよね。何か他にも覚えておいてほしいことがあれば、教えてくださいね。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 37, 'prompt_tokens': 823, 'total_tokens': 860, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4.1-2025-04-14', 'system_fingerprint': 'fp_799e4ca3f1', 'id': 'chatcmpl-Bnz0rwXh49wXqUTBM8FwksBdxZXYl', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--4ca34958-1da9-4f2e-aefd-43cefc8bb727-0', usage_metadata={'input_tokens': 823, 'output_tokens': 37, 'total_tokens': 860, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f0555e1-7bb3-6e5f-8004-2c30a18eb88b'}}, metadata={'source': 'loop', 'step': 4, 'parents': {}, 'thread_id': '1'}, created_at='2025-06-30T02:58:26.191906+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f0555e1-70c3-643f-8003-729e87826686'}}, tasks=(), interrupts=())
snapshot.next # (since the graph ended this turn, `next` is empty. If you fetch a state from within a graph invocation, next tells which node will execute next)
()
Congratulations! チャットボットは LangGraph のチェックポインティング・システムにより、セッションに渡り会話の状態を維持できるようになりました。これは、より自然でコンテキストに基づいたインタラクションのワクワクするような可能性を広げます。LangGraph のチェックポインティングは任意に複雑なグラフ状態を処理します、これは単純なチャットメモリよりも遥かに表現力があり強力です。
以下のコードスニペットを確認して、このチュートリアルからのグラフをレビューしてください :
OpenAI
pip install -U "langchain[openai]"
import os
from langchain.chat_models import init_chat_model
os.environ["OPENAI_API_KEY"] = "sk-..."
llm = init_chat_model("openai:gpt-4.1")
Anthropic
pip install -U "langchain[anthropic]"
import os
from langchain.chat_models import init_chat_model
os.environ["ANTHROPIC_API_KEY"] = "sk-..."
llm = init_chat_model("anthropic:claude-3-5-sonnet-latest")
from typing import Annotated
from langchain.chat_models import init_chat_model
from langchain_tavily import TavilySearch
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
tool = TavilySearch(max_results=2)
tools = [tool]
llm_with_tools = llm.bind_tools(tools)
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
graph_builder.add_node("chatbot", chatbot)
tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)
graph_builder.add_conditional_edges(
"chatbot",
tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.set_entry_point("chatbot")
memory = MemorySaver()
graph = graph_builder.compile(checkpointer=memory)
以上