LangChain クロスセッションストレージ —— Store
Checkpointer は「単一の会話内の記憶」の問題を解決しました。しかし、異なる会話間でデータを共有する必要がある場合——例えばユーザーの好みや学習進捗——には、使用する必要がありますStore。
Checkpointer vs Store
| 次元 | Checkpointer | Store |
|---|---|---|
| スコープ | 単一の会話スレッド(thread_id) | すべての会話スレッドにわたる |
| データ型 | Agent 状態のスナップショット(自動管理) | 任意のキーバリューデータ(手動管理) |
| 典型的な用途 | マルチターン会話メモリ | ユーザーの好み、ナレッジベース、構成 |
| データの編成 | thread_id → checkpoint | (namespace, key) → value |
Store の基本操作
Store の使用名前空間 + キーの階層構造を使用してデータを編成します:
例
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
# データの書き込み:put(namespace, key, value)
# namespace はタプル、key は文字列、value は辞書です
store.put(
("users", "user_001"), # 名前空間
"preferences", # キー
{ # 値
"theme": "dark",
"language": "zh-CN",
"level": "入門",
}
)
store.put(
("users", "user_001"),
"progress",
{
"completed_courses": ["HTML 基礎", "Python 基礎"],
"total_hours": 35,
}
)
# データの読み取り:get(namespace, key)
prefs = store.get(("users", "user_001"), "preferences")
print(f"ユーザー設定: {prefs.value}")
progress = store.get(("users", "user_001"), "progress")
print(f"学習進捗: {progress.value}")
# データの検索:search(namespace)
all_user_data = store.search(("users", "user_001"))
print(f"\nユーザーのすべてのデータ ({len(all_user_data)} 件):")
for item in all_user_data:
print(f" {item.key}: {item.value}")
# データの削除:delete(namespace, key)
store.delete(("users", "user_001"), "preferences")
deleted = store.get(("users", "user_001"), "preferences")
print(f"\n削除後: {deleted}") # None
store = InMemoryStore()
# データの書き込み:put(namespace, key, value)
# namespace はタプル、key は文字列、value は辞書です
store.put(
("users", "user_001"), # 名前空間
"preferences", # キー
{ # 値
"theme": "dark",
"language": "zh-CN",
"level": "入門",
}
)
store.put(
("users", "user_001"),
"progress",
{
"completed_courses": ["HTML 基礎", "Python 基礎"],
"total_hours": 35,
}
)
# データの読み取り:get(namespace, key)
prefs = store.get(("users", "user_001"), "preferences")
print(f"ユーザー設定: {prefs.value}")
progress = store.get(("users", "user_001"), "progress")
print(f"学習進捗: {progress.value}")
# データの検索:search(namespace)
all_user_data = store.search(("users", "user_001"))
print(f"\nユーザーのすべてのデータ ({len(all_user_data)} 件):")
for item in all_user_data:
print(f" {item.key}: {item.value}")
# データの削除:delete(namespace, key)
store.delete(("users", "user_001"), "preferences")
deleted = store.get(("users", "user_001"), "preferences")
print(f"\n削除後: {deleted}") # None
実行結果:
設定: {'theme': 'dark', 'language': 'zh-CN', 'level': '入门'}
学習進捗: {'completed_courses': ['HTML 基础', 'Python 基础'], 'total_hours': 35}
用户的所有数据 (2 项):
preferences: {'theme': 'dark', 'language': 'zh-CN', 'level': '入门'}
progress: {'completed_courses': ['HTML 基础', 'Python 基础'], 'total_hours': 35}
削除後: None
Agent で Store を使用する
Store を create_agent() に渡すと、Agent 内のすべてのツールが InjectedStore を介してアクセスできます:
例
from dotenv import load_dotenv
load_dotenv()
from typing import Annotated
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore
from langchain.tools import tool, InjectedStore
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
# Store を作成し、データをプリセットする
store = InMemoryStore()
store.put(("サンプル", "courses"), "catalog", {
"Python3 基礎チュートリアル": {"price": "無料", "hours": 20, "level": "入門"},
"Python データ分析": {"price": "会員", "hours": 30, "level": "上級"},
"Java オブジェクト指向": {"price": "無料", "hours": 25, "level": "上級"},
})
store.put(("サンプル", "users"), "user_vip_001", {
"name": "小明",
"membership": "VIP",
"joined": "2024-01-15",
})
@tool
def query_course_info(
course_name: str,
store: Annotated[BaseStore, InjectedStore()],
) -> str:
"""サンプル のコースの詳細情報を照会します。
Args:
course_name: コース名
"""
item = store.get(("サンプル", "courses"), "catalog")
catalog = item.value if item else {}
if course_name in catalog:
info = catalog[course_name]
return (
f"《{course_name}》 - 価格:{info['price']}、"
f"時間:{info['hours']}時間、難易度:{info['level']}"
)
return f"コース《{course_name}》が見つかりません"
@tool
def get_user_membership(
user_id: str,
store: Annotated[BaseStore, InjectedStore()],
) -> str:
"""ユーザーの会員情報を照会します。
Args:
user_id: ユーザー ID
"""
item = store.get(("サンプル", "users"), user_id)
if item is None:
return f"ユーザー {user_id} が見つかりません"
user = item.value
return (
f"ユーザー {user['name']}、{user['membership']} 会員、"
f"登録日 {user['joined']}"
)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
model=model,
tools=[query_course_info, get_user_membership],
store=store,
system_prompt="あなたは サンプル のコースアドバイザーです。",
)
# コース情報を照会(データは Store から)
result = agent.invoke({
"messages": [HumanMessage(content="Python3 基礎チュートリアルはいくらですか?")]
})
print(f"コース照会: {result['messages'][-1].content}")
# ユーザー情報を照会(データは Store から)
result = agent.invoke({
"messages": [HumanMessage(content="ユーザー user_vip_001 の情報を調べてください")]
})
print(f"ユーザー照会: {result['messages'][-1].content}")
load_dotenv()
from typing import Annotated
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore
from langchain.tools import tool, InjectedStore
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
# Store を作成し、データをプリセットする
store = InMemoryStore()
store.put(("サンプル", "courses"), "catalog", {
"Python3 基礎チュートリアル": {"price": "無料", "hours": 20, "level": "入門"},
"Python データ分析": {"price": "会員", "hours": 30, "level": "上級"},
"Java オブジェクト指向": {"price": "無料", "hours": 25, "level": "上級"},
})
store.put(("サンプル", "users"), "user_vip_001", {
"name": "小明",
"membership": "VIP",
"joined": "2024-01-15",
})
@tool
def query_course_info(
course_name: str,
store: Annotated[BaseStore, InjectedStore()],
) -> str:
"""サンプル のコースの詳細情報を照会します。
Args:
course_name: コース名
"""
item = store.get(("サンプル", "courses"), "catalog")
catalog = item.value if item else {}
if course_name in catalog:
info = catalog[course_name]
return (
f"《{course_name}》 - 価格:{info['price']}、"
f"時間:{info['hours']}時間、難易度:{info['level']}"
)
return f"コース《{course_name}》が見つかりません"
@tool
def get_user_membership(
user_id: str,
store: Annotated[BaseStore, InjectedStore()],
) -> str:
"""ユーザーの会員情報を照会します。
Args:
user_id: ユーザー ID
"""
item = store.get(("サンプル", "users"), user_id)
if item is None:
return f"ユーザー {user_id} が見つかりません"
user = item.value
return (
f"ユーザー {user['name']}、{user['membership']} 会員、"
f"登録日 {user['joined']}"
)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
model=model,
tools=[query_course_info, get_user_membership],
store=store,
system_prompt="あなたは サンプル のコースアドバイザーです。",
)
# コース情報を照会(データは Store から)
result = agent.invoke({
"messages": [HumanMessage(content="Python3 基礎チュートリアルはいくらですか?")]
})
print(f"コース照会: {result['messages'][-1].content}")
# ユーザー情報を照会(データは Store から)
result = agent.invoke({
"messages": [HumanMessage(content="ユーザー user_vip_001 の情報を調べてください")]
})
print(f"ユーザー照会: {result['messages'][-1].content}")
実行結果:
コース照会: 《Python3 基础教程》是免费的,学习时长约20小时,难度为入门级别。 ユーザー照会: 用户小明是 VIP 会员,注册日期为 2024年1月15日。
Store の永続化
InMemoryStore のデータはプログラム再起動後に失われます。本番環境では PostgresStore などの永続化ソリューションを使用できます:
例
# 開発段階
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
# 本番環境(PostgreSQL が必要)
# from langgraph.store.postgres import PostgresStore
# store = PostgresStore.from_conn_string("postgresql://...")
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
# 本番環境(PostgreSQL が必要)
# from langgraph.store.postgres import PostgresStore
# store = PostgresStore.from_conn_string("postgresql://...")
Store 使用のアドバイス
| シナリオ | namespace の例 | key の例 | 説明 |
|---|---|---|---|
| ユーザーの好み | ("users", user_id) | preferences | テーマ、言語、通知設定 |
| 学習進捗 | ("users", user_id) | progress | 完了したコース、学習時間 |
| ナレッジベース | ("kb", collection) | doc_id | ドキュメント、FAQ、製品情報 |
| 会話サマリー | ("sessions", thread_id) | summary | 長い会話の要約、Checkpointer 以外のシナリオで使用します |
その他の拡張Checkpointer は「会話がどこまで進んだか」を担当し、Store は「ユーザーが誰か、何ができるか、何を好むか」を担当します。両者を組み合わせて使用することで、持続的な記憶を持つインテリジェントな Agent を構築できます。