LangGraph基础知识(Store )(四)
背景
仅使用`checkpointer`,我们无法做到跨线程共享信息。LangGraph 提出了Store(仓库)概念。
Store介绍
`LangGraph`通过`BaseStore`接口提供内置文档存储。与通过线程 ID 保存状态的`checkpointer`不同,存储使用自定义命名空间来组织数据。常见用例包括存储用户配置文件、构建知识库以及管理所有线程的全局首选项。具体的**实现形式是:`LangGraph` 将长期记忆作为 `JSON` 文档存储在`Store`中,每个`memory`都组织在自定义`namespace`(类似于文件夹)和不同的`key` (例如文件名)下。命名空间通常包含用户或组织 ID 或其他标签,以便更轻松地组织信息。这种结构可以实现存储器的分层组织。然后通过内容过滤器支持跨命名空间搜索。
整体而言,`LangGraph` 中的长期记忆允许系统保留不同对话或会话中的信息。与线程范围的短期内存不同,长期内存保存在自定义“命名空间”中。
举个例子
from langgraph.store.memory import InMemoryStore
import uuidin_memory_store = InMemoryStore()
user_id = "1"
namespace_for_memory = (user_id, "memories")memory_id = str(uuid.uuid4())
memory = {"user" : "你好,我叫BUG"}
in_memory_store.put(namespace_for_memory, memory_id, memory)
memories = in_memory_store.search(namespace_for_memory)
memories[-1].dict()
#运行结果
{'namespace': ['1', 'memories'],'key': 'd792a784-0f52-4f91-b265-b5c80f794b86','value': {'user': '你好,我叫BUG'},'created_at': '2025-06-14T07:22:41.758736+00:00','updated_at': '2025-06-14T07:22:41.758736+00:00','score': None}
理解了上述过程后,就可以使用 `LangGraph` 中的`in_memory_store`方法了,当我们在编译图表时传递 `store` 对象,就会允许图中的每个节点访问 `store`,定义节点函数时的时候,就可以定义`store`关键字参数,`LangGraph` 会自动传递编译图时使用的 `store` 对象,代码如下所示:
import getpass
import os
from langchain_openai import ChatOpenAI
from typing import Annotated
from typing_extensions import TypedDict
from IPython.display import Image, display
from langgraph.graph import StateGraph, MessagesState, START, END
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage, ToolMessage
from langgraph.graph.message import add_messages
from langchain_core.runnables import RunnableConfig
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStorefrom langgraph.checkpoint.memory import MemorySaverfrom dotenv import load_dotenv
load_dotenv()
in_memory_store = InMemoryStore()
memory = MemorySaver()# if not os.environ.get("OPENAI_API_KEY"):
# os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")# 定义大模型实例
llm = ChatOpenAI(model="deepseek-chat")# 定义状态模式
class State(TypedDict):messages: Annotated[list, add_messages]# 定义对话节点, 访问记忆并在模型调用中使用它们。
def call_model(state: MessagesState, config: RunnableConfig, *, store: BaseStore):# 获取用户iduser_id = config["configurable"]["user_id"]# 定义命名空间namespace = ("memories", user_id)# 根据用户id检索记忆memories = store.search(namespace)print(f"memories: {memories}")info = "\n".join([d.value["data"] for d in memories])print(f"info: {info}")# # 存储记忆last_message = state["messages"][-1]store.put(namespace, str(uuid.uuid4()), {"data": last_message.content})system_msg = f"Answer the user's question in context: {info}"response = llm.invoke([{"type": "system", "content": system_msg}] + state["messages"])# 存储记忆store.put(namespace, str(uuid.uuid4()), {"data": response.content})return {"messages": response}# 构建状态图
builder = StateGraph(State)# 向图中添加节点
builder.add_node("call_model", call_model)# 构建边
builder.add_edge(START, "call_model")
builder.add_edge("call_model", END)# 编译图
graph = builder.compile(checkpointer=memory, store=in_memory_store)# 可视化
display(Image(graph.get_graph().draw_mermaid_png()))
进行测试:
config = {"configurable": {"thread_id": "10"}, "user_id": "6"}async for chunk in graph.astream({"messages": ["你好,我是BUG"]}, config, stream_mode="values"):chunk["messages"][-1].pretty_print()
================================ Human Message ================================= 你好,我是BUG memories: [] info: ================================== Ai Message ================================== 你好!看来你可能想表达自己是“BUG”(程序中的错误或漏洞),或者是想开个有趣的玩笑?如果是技术问题,比如遇到了代码bug或者系统故障,可以告诉我具体情况,我会尽力帮你解决! 如果是其他含义——比如昵称、游戏ID或者单纯想聊聊——也欢迎随时补充说明~ 😄 (中文里“BUG”通常指软件缺陷,但如果你有其他创意用法,我也很想知道!)
我们进行更改线程id再次进行测试:
config = {"configurable": {"thread_id": "20"}, "user_id": "6"}async for chunk in graph.astream({"messages": ["你知道我叫什么吗?"]}, config, stream_mode="values"):chunk["messages"][-1].pretty_print()#运行结果
================================ Human Message =================================你知道我叫什么吗?
memories: [Item(namespace=['memories', '6'], key='f1628098-c07c-4ca5-81fd-8704894eb757', value={'data': '你好,我是BUG'}, created_at='2025-06-14T07:37:26.910365+00:00', updated_at='2025-06-14T07:37:26.910365+00:00', score=None), Item(namespace=['memories', '6'], key='d5e75eb6-8205-4901-8560-827077e5223a', value={'data': '你好!看来你可能想表达自己是“BUG”(程序中的错误或漏洞),或者是想开个有趣的玩笑?如果是技术问题,比如遇到了代码bug或者系统故障,可以告诉我具体情况,我会尽力帮你解决! \n\n如果是其他含义——比如昵称、游戏ID或者单纯想聊聊——也欢迎随时补充说明~ 😄 \n\n(中文里“BUG”通常指软件缺陷,但如果你有其他创意用法,我也很想知道!)'}, created_at='2025-06-14T07:37:33.345231+00:00', updated_at='2025-06-14T07:37:33.345231+00:00', score=None)]
info: 你好,我是BUG
你好!看来你可能想表达自己是“BUG”(程序中的错误或漏洞),或者是想开个有趣的玩笑?如果是技术问题,比如遇到了代码bug或者系统故障,可以告诉我具体情况,我会尽力帮你解决! 如果是其他含义——比如昵称、游戏ID或者单纯想聊聊——也欢迎随时补充说明~ 😄 (中文里“BUG”通常指软件缺陷,但如果你有其他创意用法,我也很想知道!)
================================== Ai Message ==================================哈哈,当然知道啦!你的名字是 **“BUG”** —— 一个自带程序员幽默感的称呼! 不过悄悄问一句:
- 这是你的**隐藏身份**(比如深夜修代码时遇到的“第八号当铺Bug”)?
- 还是某种**神秘代号**(比如游戏里的终极Boss叫BUG)?
- 或者……你真的是个**会说话的漏洞**?(那我得赶紧通知CTO来抓你!🐛) 等你揭晓答案~ (或者继续用BUG的力量“折磨”我也行,我抗压能力很强✨)
能够发现,我们已经正确的实现了跨线程的记忆能力。而如果使用新的`user_id`,将会开启全新的交互。