チャットボットはツールを使用してユーザの質問に答えられるようになりましたが、以前のやり取りのコンテキストを記憶していません。これは一貫性のある、複数ターンの会話をする能力を制限しています。LangGraph は永続的なチェックポインティングを通してこの問題を解決します。
LangGraph : Get started : 基本 – メモリの追加
作成 : クラスキャット・セールスインフォメーション
作成日時 : 06/02/2025
* 本記事は langchain-ai.github.io の以下のページを独自に翻訳した上で、補足説明を加えてまとめ直しています :
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
◆ お問合せ : 下記までお願いします。
- クラスキャット セールス・インフォメーション
- sales-info@classcat.com
- ClassCatJP
LangGraph : Get started : 基本 – メモリの追加
チャットボットはツールを使用してユーザの質問に答えられるようになりましたが、以前のやり取りのコンテキストを記憶していません。これは一貫性のある、複数ターンの会話をする能力を制限しています。
LangGraph は 永続的なチェックポインティング を通してこの問題を解決します。グラフのコンパイル時にチェックポインターを提供して、グラフを呼び出すときに thread_id を提供すれば、LangGraph は各ステップ後に状態を自動的にセーブします。同じ thread_id を使用してグラフを再度呼び出す場合、グラフはセーブされた状態をロードして、チャットボットが中断したところから再開することを可能にします。
チェックポインティングが単純なチャットメモリよりも遥かに強力であることを後で見ます – それは、エラーリカバリー、人間参加型ワークフロー、タイムトラベル相互作用、等のためにいつでも複雑な状態を保存して再開させることができます。しかしまずは、チェックポインティングを追加して複数ターンの会話を可能にしましょう。
1. MemorySaver チェックポインターの作成
MemorySaver チェックポインターを作成します :
from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()
これは in-memory チェックポインターで、チュートリアル用には便利です。けれども production アプリケーションでは、これを SqliteSaver or PostgresSaver を使用してデータベースに接続するように多分変更するでしょう。
グラフのコンパイル
提供されたチェックポインターでグラフをコンパイルします、これにより、グラフが各ノードを通過するときに 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. チャットボットと相互作用する
ボットとやり取りできるようになりました!
- この会話のキーとして使用するスレッドを選択します。
config = {"configurable": {"thread_id": "1"}}
- チャットボットを呼び出します :
user_input = "Hi there! My name is Will." # 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 ================================= Hi there! My name is Will. ================================== Ai Message ================================== Hello Will! It's nice to meet you. How can I assist you today? Is there anything specific you'd like to know or discuss?
4. フォローアップの質問をする
フォローアップの質問をしてみてください :
user_input = "Remember my name?"
# 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 ================================= Remember my name? ================================== Ai Message ================================== Of course, I remember your name, Will. I always try to pay attention to important details that users share with me. Is there anything else you'd like to talk about or any questions you have? I'm here to help with a wide range of topics or tasks.
Notice that we aren’t using an external list for memory: it’s all handled by the checkpointer! 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 ================================= Remember my name? ================================== Ai Message ================================== I apologize, but I don't have any previous context or memory of your name. As an AI assistant, I don't retain information from past conversations. Each interaction starts fresh. Could you please tell me your name so I can address you properly in this conversation?
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='Hi there! My name is Will.', additional_kwargs={}, response_metadata={}, id='8c1ca919-c553-4ebf-95d4-b59a2d61e078'), AIMessage(content="Hello Will! It's nice to meet you. How can I assist you today? Is there anything specific you'd like to know or discuss?", additional_kwargs={}, response_metadata={'id': 'msg_01WTQebPhNwmMrmmWojJ9KXJ', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 405, 'output_tokens': 32}}, id='run-58587b77-8c82-41e6-8a90-d62c444a261d-0', usage_metadata={'input_tokens': 405, 'output_tokens': 32, 'total_tokens': 437}), HumanMessage(content='Remember my name?', additional_kwargs={}, response_metadata={}, id='daba7df6-ad75-4d6b-8057-745881cea1ca'), AIMessage(content="Of course, I remember your name, Will. I always try to pay attention to important details that users share with me. Is there anything else you'd like to talk about or any questions you have? I'm here to help with a wide range of topics or tasks.", additional_kwargs={}, response_metadata={'id': 'msg_01E41KitY74HpENRgXx94vag', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 444, 'output_tokens': 58}}, id='run-ffeaae5c-4d2d-4ddb-bd59-5d5cbf2a5af8-0', usage_metadata={'input_tokens': 444, 'output_tokens': 58, 'total_tokens': 502})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7d06e-93e0-6acc-8004-f2ac846575d2'}}, metadata={'source': 'loop', 'writes': {'chatbot': {'messages': [AIMessage(content="Of course, I remember your name, Will. I always try to pay attention to important details that users share with me. Is there anything else you'd like to talk about or any questions you have? I'm here to help with a wide range of topics or tasks.", additional_kwargs={}, response_metadata={'id': 'msg_01E41KitY74HpENRgXx94vag', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 444, 'output_tokens': 58}}, id='run-ffeaae5c-4d2d-4ddb-bd59-5d5cbf2a5af8-0', usage_metadata={'input_tokens': 444, 'output_tokens': 58, 'total_tokens': 502})]}}, 'step': 4, 'parents': {}}, created_at='2024-09-27T19:30:10.820758+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7d06e-859f-6206-8003-e1bd3c264b8f'}}, tasks=())
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)
以上