如何为你的图添加跨线程持久化功能¶
在上一篇指南中,你学习了如何在单个线程上的多次交互中持久化图状态。LangGraph 还允许你在**多个线程**之间持久化数据。例如,你可以将用户信息(他们的姓名或偏好)存储在共享内存中,并在新的对话线程中重用这些信息。
在本指南中,我们将展示如何构建和使用一个图,该图具有使用 Store 接口实现的共享内存。
注意
本指南中使用的 Store
API 支持是在 LangGraph v0.2.32
版本中添加的。
本指南中使用的 Store
API 的 index 和 query 参数支持是在 LangGraph v0.2.54
版本中添加的。
环境设置¶
首先,让我们安装所需的包并设置我们的 API 密钥
import getpass
import os
def _set_env(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"{var}: ")
_set_env("ANTHROPIC_API_KEY")
_set_env("OPENAI_API_KEY")
为 LangGraph 开发设置 LangSmith
注册 LangSmith 以快速发现问题并提升你的 LangGraph 项目的性能。LangSmith 允许你使用跟踪数据来调试、测试和监控使用 LangGraph 构建的大语言模型应用程序 —— 点击此处了解更多关于如何开始使用的信息。
定义存储¶
在这个示例中,我们将创建一个图,该图能够检索有关用户偏好的信息。我们将通过定义一个 InMemoryStore
来实现这一点,InMemoryStore
是一个可以在内存中存储数据并查询这些数据的对象。然后,我们将在编译图时传递该存储对象。这使得图中的每个节点都可以访问该存储:当你定义节点函数时,可以定义 store
关键字参数,LangGraph 会自动传递你编译图时使用的存储对象。
使用 Store
接口存储对象时,需要定义两件事:
- 对象的命名空间,一个元组(类似于目录)
- 对象键(类似于文件名)
在我们的示例中,我们将使用 ("memories", <user_id>)
作为命名空间,并为每个新的记忆使用随机 UUID 作为键。
重要的是,为了确定用户,我们将通过节点函数的配置关键字参数传递 user_id
。
让我们首先定义一个已经填充了一些关于用户记忆的 InMemoryStore
。
from langgraph.store.memory import InMemoryStore
from langchain_openai import OpenAIEmbeddings
in_memory_store = InMemoryStore(
index={
"embed": OpenAIEmbeddings(model="text-embedding-3-small"),
"dims": 1536,
}
)
创建图表¶
import uuid
from typing import Annotated
from typing_extensions import TypedDict
from langchain_anthropic import ChatAnthropic
from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.base import BaseStore
model = ChatAnthropic(model="claude-3-5-sonnet-20240620")
# NOTE: we're passing the Store param to the node --
# this is the Store we compile the graph with
def call_model(state: MessagesState, config: RunnableConfig, *, store: BaseStore):
user_id = config["configurable"]["user_id"]
namespace = ("memories", user_id)
memories = store.search(namespace, query=str(state["messages"][-1].content))
info = "\n".join([d.value["data"] for d in memories])
system_msg = f"You are a helpful assistant talking to the user. User info: {info}"
# Store new memories if the user asks the model to remember
last_message = state["messages"][-1]
if "remember" in last_message.content.lower():
memory = "User name is Bob"
store.put(namespace, str(uuid.uuid4()), {"data": memory})
response = model.invoke(
[{"role": "system", "content": system_msg}] + state["messages"]
)
return {"messages": response}
builder = StateGraph(MessagesState)
builder.add_node("call_model", call_model)
builder.add_edge(START, "call_model")
# NOTE: we're passing the store object here when compiling the graph
graph = builder.compile(checkpointer=MemorySaver(), store=in_memory_store)
# If you're using LangGraph Cloud or LangGraph Studio, you don't need to pass the store or checkpointer when compiling the graph, since it's done automatically.
API Reference: RunnableConfig
注意
如果您正在使用 LangGraph Cloud 或 LangGraph Studio,在编译图时无需传递存储,因为这会自动完成。
运行图表!¶
现在让我们在配置中指定一个用户 ID,并告诉模型我们的姓名:
config = {"configurable": {"thread_id": "1", "user_id": "1"}}
input_message = {"role": "user", "content": "Hi! Remember: my name is Bob"}
for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
chunk["messages"][-1].pretty_print()
================================[1m Human Message [0m=================================
Hi! Remember: my name is Bob
==================================[1m Ai Message [0m==================================
Hello Bob! It's nice to meet you. I'll remember that your name is Bob. How can I assist you today?
config = {"configurable": {"thread_id": "2", "user_id": "1"}}
input_message = {"role": "user", "content": "what is my name?"}
for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
chunk["messages"][-1].pretty_print()
================================[1m Human Message [0m=================================
what is my name?
==================================[1m Ai Message [0m==================================
Your name is Bob.
config = {"configurable": {"thread_id": "3", "user_id": "2"}}
input_message = {"role": "user", "content": "what is my name?"}
for chunk in graph.stream({"messages": [input_message]}, config, stream_mode="values"):
chunk["messages"][-1].pretty_print()
================================[1m Human Message [0m=================================
what is my name?
==================================[1m Ai Message [0m==================================
I apologize, but I don't have any information about your name. As an AI assistant, I don't have access to personal information about users unless it has been specifically shared in our conversation. If you'd like, you can tell me your name and I'll be happy to use it in our discussion.