文章讲述了LangGraph框架中AI Agent记忆机制的底层逻辑和解决方案。通过代码示例展示了AI"失忆"的原因——State在每次运行后会重置,介绍了Checkpointer机制(包括MemorySaver和SqliteSaver)来解决这个问题,并探讨了如何使用Store实现跨对话的长期记忆。文章提供了实战案例和最佳实践,帮助开发者构建真正具有记忆能力的AI助手,让AI能够记住用户信息并在后续对话中有效利用。
你是否也遇到过这样的尴尬场景?
刚告诉AI助手你的名字,下一秒它就像失忆了一样反问:“请问怎么称呼您?”
没错,就连我用着号称“智能体神器”的LangGraph框架时,也被测试同学当场抓包:“你这聊天机器人,是不是有健忘症?”
表面上,LangGraph的State里明明记录着所有对话历史;但实际上,AI Agent的“记忆迷宫”,远不止存个聊天记录那么简单。
为什么消息列表满满当当,AI却依然“转头就忘”?
我啃了两天源码和文档,终于搞懂了LangGraph记忆机制的底层逻辑——不是数据没存,而是记忆不会用。
一、问题重现 - 为什么Agent会"失忆"?
先来重现一下这个问题。下面是一个最基础的LangGraph聊天机器人:
import os
from langchain_openai import ChatOpenAI
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from IPython.display import Image, display
# 设置OpenAI API Key
os.environ["OPENAI_API_KEY"] = "你的API密钥"
# 初始化模型
llm = ChatOpenAI(model="gpt-4o")
# 定义状态结构
class State(TypedDict):
messages: Annotated[list, add_messages]
# 定义对话节点
def call_model(state: State):
response = llm.invoke(state["messages"])
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)
# 编译
simple_graph = builder.compile()
# 可视化(可选)
display(Image(simple_graph.get_graph().draw_mermaid_png()))
看起来很完美对吧?我们来测试一下:
# 第一轮对话
print("=" * 50)
print("第一轮对话:")
print("=" * 50)
for chunk in simple_graph.stream(
input={"messages": ["你好,我叫张三"]},
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
# 第二轮对话
print("\n" + "=" * 50)
print("第二轮对话:")
print("=" * 50)
for chunk in simple_graph.stream(
input={"messages": ["你知道我叫什么吗?"]},
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
运行结果:
==================================================
第一轮对话:
==================================================
================================ Human Message =================================
你好,我叫张三
================================== Ai Message ==================================
你好张三!很高兴认识你,有什么我可以帮你的吗?
==================================================
第二轮对话:
==================================================
================================ Human Message =================================
你知道我叫什么吗?
================================== Ai Message ==================================
抱歉,我不知道您的名字。您可以告诉我吗?
看到了吗?第二轮对话完全忘记了第一轮的内容!
为什么会这样?
我一开始也很困惑,State里不是有add_messages这个Reducer函数吗?它不是应该维护消息列表吗?
后来我打开debug模式仔细看了一下:
print("第一轮对话的State状态:")
for chunk in simple_graph.stream(
{"messages": ["你好,我叫张三"]},
stream_mode="debug"
):
if chunk["type"] == "task":
print(f"输入消息: {chunk['payload']['input']['messages']}")
if chunk["type"] == "task_result":
print(f"输出消息: {chunk['payload']['result'][0][1].content}")
print("\n第二轮对话的State状态:")
for chunk in simple_graph.stream(
{"messages": ["你知道我叫什么吗?"]},
stream_mode="debug"
):
if chunk["type"] == "task":
print(f"输入消息: {chunk['payload']['input']['messages']}")
结果发现:每次调用stream方法,State都会重新初始化!
原来State的设计逻辑是这样的:
- 在单次运行期间,State可以在各个节点间传递和累积消息
- 但每次运行结束后,State就重置了
- 下次运行时,又是一个全新的State
这就像你每次做数学题都用一张新草稿纸,上一道题的计算过程完全没保留。

二、解决方案 - Checkpointer登场

既然State不能跨运行保存数据,那我们需要一个额外的机制来存储历史信息。LangGraph提供的方案就是Checkpointer(检查点)。
核心概念理解
在深入代码之前,先理解三个关键概念:
- Checkpointer:负责存储和读取历史状态的管理器
- Thread(线程):一个独立的对话会话,用
thread_id标识 - Config:配置对象,包含
thread_id等信息
它们的关系是这样的:
用户A的对话1 (thread_id="user_a_chat_1") → Checkpointer存储
用户A的对话2 (thread_id="user_a_chat_2") → Checkpointer存储
用户B的对话1 (thread_id="user_b_chat_1") → Checkpointer存储
每个thread_id对应一条独立的对话历史,就像微信里的不同聊天窗口。
方案一:MemorySaver(内存存储)

最简单的实现方式是MemorySaver,它把历史数据存在内存里。适合开发测试阶段使用。
完整可运行代码:
import os
from langchain_openai import ChatOpenAI
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.memory import MemorySaver
from IPython.display import Image, display
# API配置
os.environ["OPENAI_API_KEY"] = "你的API密钥"
llm = ChatOpenAI(model="gpt-4o")
# 状态定义
class State(TypedDict):
messages: Annotated[list, add_messages]
# 节点函数
def call_model(state: State):
response = llm.invoke(state["messages"])
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)
# 关键:添加Checkpointer
memory = MemorySaver()
graph_with_memory = builder.compile(checkpointer=memory)
# 可视化
display(Image(graph_with_memory.get_graph().draw_mermaid_png()))
现在来测试一下,注意这次必须提供
config
参数:
# 定义配置(指定线程ID)
config = {"configurable": {"thread_id": "conversation_1"}}
# 第一轮对话
print("=" * 50)
print("第一轮对话:")
print("=" * 50)
for chunk in graph_with_memory.stream(
{"messages": ["你好,我叫张三"]},
config, # 注意这里传入config
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
# 第二轮对话(使用相同的thread_id)
print("\n" + "=" * 50)
print("第二轮对话:")
print("=" * 50)
for chunk in graph_with_memory.stream(
{"messages": ["你知道我叫什么吗?"]},
config, # 相同的config
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
# 第三轮对话
print("\n" + "=" * 50)
print("第三轮对话:")
print("=" * 50)
for chunk in graph_with_memory.stream(
{"messages": ["我刚才问了你什么问题?"]},
config,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
运行结果:
==================================================
第一轮对话:
==================================================
================================ Human Message =================================
你好,我叫张三
================================== Ai Message ==================================
你好张三!很高兴认识你。
==================================================
第二轮对话:
==================================================
================================ Human Message =================================
你知道我叫什么吗?
================================== Ai Message ==================================
你叫张三!
==================================================
第三轮对话:
==================================================
================================ Human Message =================================
我刚才问了你什么问题?
================================== Ai Message ==================================
你问我是否知道你叫什么。
成功了!现在Agent有记忆了!
测试不同线程的隔离性
我们再测试一下不同thread_id的隔离效果:
# 使用新的thread_id
new_config = {"configurable": {"thread_id": "conversation_2"}}
print("=" * 50)
print("新的对话线程:")
print("=" * 50)
for chunk in graph_with_memory.stream(
{"messages": ["你知道我叫什么吗?"]},
new_config, # 新的thread_id
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
结果:
==================================================
新的对话线程:
==================================================
================================ Human Message =================================
你知道我叫什么吗?
================================== Ai Message ==================================
抱歉,我不知道您的名字。
完美!不同的thread_id之间完全隔离,互不影响。
方案二:SqliteSaver(持久化存储)

MemorySaver有个致命缺点:程序一重启,所有记忆就没了。生产环境必须用持久化存储。
首先安装依赖:
pip install langgraph-checkpoint-sqlite
然后使用SqliteSaver:
from langgraph.checkpoint.sqlite import SqliteSaver
from contextlib import ExitStack
# 创建持久化存储(数据保存在文件里)
stack = ExitStack()
checkpointer = stack.enter_context(
SqliteSaver.from_conn_string("chat_history.sqlite")
)
# 重新编译图
graph_persistent = builder.compile(checkpointer=checkpointer)
# 测试
config = {"configurable": {"thread_id": "user_001"}}
print("第一次运行程序:")
for chunk in graph_persistent.stream(
{"messages": ["你好,我叫李四,今年25岁"]},
config,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
现在你可以关闭Python程序,重新启动,然后运行:
from langgraph.checkpoint.sqlite import SqliteSaver
from contextlib import ExitStack
# 重新连接到同一个数据库文件
stack = ExitStack()
checkpointer = stack.enter_context(
SqliteSaver.from_conn_string("chat_history.sqlite")
)
graph_persistent = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "user_001"}}
print("程序重启后:")
for chunk in graph_persistent.stream(
{"messages": ["我叫什么?多大?"]},
config,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
结果:
程序重启后:
================================ Human Message =================================
我叫什么?多大?
================================== Ai Message ==================================
你叫李四,今年25岁。
数据成功持久化了!
查看数据库内容,如果你好奇数据是怎么存储的,可以用sqlite3查看:
import sqlite3
# 连接数据库
conn = sqlite3.connect("chat_history.sqlite")
cursor = conn.cursor()
# 查看所有表
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
print("数据库中的表:", cursor.fetchall())
# 查看checkpoints表的数据
cursor.execute("SELECT * FROM checkpoints LIMIT 5;")
print("\n最近5条记录:")
for row in cursor.fetchall():
print(row)
conn.close()
三、实战案例 - 带记忆的天气查询Agent
理论说完了,来个实际的例子。我们做一个能记住用户偏好的天气查询Agent。
完整代码
import os
import json
import requests
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.sqlite import SqliteSaver
from contextlib import ExitStack
from pydantic import BaseModel, Field
# API配置
os.environ["OPENAI_API_KEY"] = "你的OpenAI密钥"
# 定义天气查询工具
class WeatherInput(BaseModel):
location: str = Field(description="城市名称,如Beijing、Shanghai、ChangSha")
@tool(args_schema=WeatherInput)
def get_weather(location: str):
"""
查询指定城市的当前天气
"""
url = "https://api.openweathermap.org/data/2.5/weather"
params = {
"q": location,
"appid": "你的OpenWeather密钥", # 需要去openweathermap.org注册
"units": "metric",
"lang": "zh_cn"
}
try:
response = requests.get(url, params=params)
data = response.json()
if response.status_code == 200:
weather = data["weather"][0]["description"]
temp = data["main"]["temp"]
return f"{location}当前天气:{weather},温度:{temp}°C"
else:
return f"查询失败:{data.get('message', '未知错误')}"
except Exception as e:
return f"查询出错:{str(e)}"
# 创建Agent
llm = ChatOpenAI(model="gpt-4o")
tools = [get_weather]
# 持久化存储
stack = ExitStack()
checkpointer = stack.enter_context(
SqliteSaver.from_conn_string("weather_agent.sqlite")
)
# 编译Agent
agent = create_react_agent(
llm,
tools=tools,
checkpointer=checkpointer
)
# 测试场景
print("=" * 60)
print("场景1:用户首次查询")
print("=" * 60)
config = {"configurable": {"thread_id": "user_zhang"}}
for chunk in agent.stream(
{"messages": ["你好,我住在长沙,帮我查一下天气"]},
config,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
print("\n" + "=" * 60)
print("场景2:用户再次查询(测试记忆)")
print("=" * 60)
for chunk in agent.stream(
{"messages": ["我现在该穿什么衣服?"]}, # 没说城市
config,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
print("\n" + "=" * 60)
print("场景3:询问历史")
print("=" * 60)
for chunk in agent.stream(
{"messages": ["我住在哪里?"]},
config,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
运行效果:
============================================================
场景1:用户首次查询
============================================================
[工具调用] get_weather(location="ChangSha")
长沙当前天气:晴,温度:15°C
============================================================
场景2:用户再次查询(测试记忆)
============================================================
根据之前查询,长沙当前15°C,建议穿长袖外套。
============================================================
场景3:询问历史
============================================================
根据对话记录,您住在长沙。
Agent不仅记住了用户的城市,还能在后续对话中主动使用这个信息!
四、进阶 - 跨对话的长期记忆
现在我们遇到一个新需求:用户可能会开启多个对话窗口,但我们希望某些信息(比如用户偏好)能在所有对话中共享。
Checkpointer做不到这一点,因为它是按thread_id隔离的。这时候需要用到Store。

Store的设计思路
Store引入了namespace(命名空间)的概念:
用户A的记忆
├── (user_a, "profile") # 个人信息
├── (user_a, "preferences") # 偏好设置
└── (user_a, "memories") # 对话记忆
用户B的记忆
├── (user_b, "profile")
├── (user_b, "preferences")
└── (user_b, "memories")
不同thread_id的对话可以访问同一个namespace,实现信息共享。
完整实现
import os
import uuid
from langchain_openai import ChatOpenAI
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.memory import InMemoryStore
from langgraph.store.base import BaseStore
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import SystemMessage
# API配置
os.environ["OPENAI_API_KEY"] = "你的API密钥"
llm = ChatOpenAI(model="gpt-4o")
# 创建Store和Checkpointer
store = InMemoryStore()
checkpointer = MemorySaver()
# 状态定义
class State(TypedDict):
messages: Annotated[list, add_messages]
# 节点函数(关键:使用store参数)
def call_model(state: State, config: RunnableConfig, *, store: BaseStore):
# 获取用户ID
user_id = config["configurable"]["user_id"]
namespace = ("user_memories", user_id)
# 读取用户的长期记忆
memories = store.search(namespace)
memory_text = "\n".join([
f"- {m.value['content']}"
for m in memories
])
# 构建系统提示
system_prompt = f"""你是一个智能助手。
用户的历史信息:
{memory_text if memory_text else "暂无历史信息"}
请基于这些信息回答用户的问题。"""
# 调用模型
messages = [SystemMessage(content=system_prompt)] + state["messages"]
response = llm.invoke(messages)
# 保存重要信息到长期记忆
last_msg = state["messages"][-1].content
if any(keyword in last_msg for keyword in ["我叫", "我是", "我住在", "我喜欢"]):
memory_id = str(uuid.uuid4())
store.put(namespace, memory_id, {"content": last_msg})
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)
# 编译(同时传入checkpointer和store)
graph = builder.compile(checkpointer=checkpointer, store=store)
# 测试:对话1
print("=" * 60)
print("对话窗口1:")
print("=" * 60)
config1 = {
"configurable": {
"thread_id": "chat_window_1",
"user_id": "user_wang"
}
}
for chunk in graph.stream(
{"messages": ["你好,我叫老谭,我住在长沙,喜欢吃火锅"]},
config1,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
# 测试:对话2(不同的thread_id,但相同的user_id)
print("\n" + "=" * 60)
print("对话窗口2(新窗口):")
print("=" * 60)
config2 = {
"configurable": {
"thread_id": "chat_window_2", # 不同的对话
"user_id": "user_wang" # 相同的用户
}
}
for chunk in graph.stream(
{"messages": ["你知道我的信息吗?"]},
config2,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
# 测试:对话3(不同用户)
print("\n" + "=" * 60)
print("其他用户的对话:")
print("=" * 60)
config3 = {
"configurable": {
"thread_id": "chat_window_3",
"user_id": "user_zhao" # 完全不同的用户
}
}
for chunk in graph.stream(
{"messages": ["你知道老谭的信息吗?"]},
config3,
stream_mode="values"
):
chunk["messages"][-1].pretty_print()
# 查看Store中的数据
print("\n" + "=" * 60)
print("Store中存储的数据:")
print("=" * 60)
for memory in store.search(("user_memories", "user_wang")):
print(f"- {memory.value}")
运行结果:
============================================================
对话窗口1:
============================================================
你好老谭!我记住了,你住在长沙,喜欢吃火锅。
============================================================
对话窗口2(新窗口):
============================================================
知道的!你叫老谭,住在长沙,喜欢吃火锅。
============================================================
其他用户的对话:
============================================================
抱歉,我不知道老谭的信息,每个用户的信息是保密的。
============================================================
Store中存储的数据:
============================================================
- {'content': '你好,我叫老谭,我住在长沙,喜欢吃火锅'}
完美!现在我们实现了:
- ✅ 同一用户的不同对话窗口共享记忆
- ✅ 不同用户的记忆完全隔离
- ✅ 记忆可以持久化存储
五、最佳实践
经过这么多实践,我总结了一套可以直接用的最佳实践:
1、记忆选择决策树
需要跨程序重启保存吗?
├─ 不需要 → MemorySaver
└─ 需要
├─ 小项目/测试环境 → SqliteSaver
└─ 生产环境 → PostgresSaver
需要跨对话共享信息吗?
├─ 不需要 → 只用Checkpointer
└─ 需要 → Checkpointer + Store组合
2、生产环境模板
import os
from contextlib import ExitStack
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.store.memory import InMemoryStore
from langgraph.prebuilt import create_react_agent
# 配置
DB_PATH = "production_chat.sqlite"
os.environ["OPENAI_API_KEY"] = "your-key"
# 初始化
stack = ExitStack()
checkpointer = stack.enter_context(
SqliteSaver.from_conn_string(DB_PATH)
)
store = InMemoryStore() # 生产环境建议用Redis等
# 创建Agent
agent = create_react_agent(
llm,
tools=your_tools,
checkpointer=checkpointer,
store=store
)
# 使用
def chat(user_id: str, thread_id: str, message: str):
config = {
"configurable": {
"user_id": user_id,
"thread_id": thread_id
}
}
for chunk in agent.stream(
{"messages": [message]},
config,
stream_mode="values"
):
yield chunk["messages"][-1].content
# 清理
try:
# 你的业务逻辑
pass
finally:
stack.close()
3、性能优化建议
- 定期清理历史
# 只保留最近N条消息
def trim_messages(messages, max_count=20):
if len(messages) > max_count:
return messages[-max_count:]
return messages
- 异步处理
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
async_stack = AsyncExitStack()
async_checkpointer = await async_stack.enter_async_context(
AsyncSqliteSaver.from_conn_string(DB_PATH)
)
- 添加日志
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def call_model(state, config, *, store):
logger.info(f"User {config['configurable']['user_id']} - Processing")
# 你的逻辑
六、总结
LangGraph的记忆机制就像给AI建造一座记忆宫殿——Checkpointer负责记录单次对话的“短期记忆”,Store则构建跨对话的“长期记忆”。
两者结合,才能让AI真正“记住”你是谁、你喜欢什么、上次聊到了哪里。
记忆,从来不只是存储问题,更是使用艺术。下次当你的AI助手再次“失忆”时,不妨检查一下:是用错了thread_id导致记忆隔离?还是忘了配置Checkpointer让State每次重置?亦或是需要Store来实现跨对话记忆共享?
最后
我在一线科技企业深耕十二载,见证过太多因技术卡位而跃迁的案例。那些率先拥抱 AI 的同事,早已在效率与薪资上形成代际优势,我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在大模型的学习中的很多困惑。
我整理出这套 AI 大模型突围资料包:
- ✅AI大模型学习路线图
- ✅Agent行业报告
- ✅100集大模型视频教程
- ✅大模型书籍PDF
- ✅DeepSeek教程
- ✅AI产品经理入门资料
完整的大模型学习和面试资料已经上传带到优快云的官方了,有需要的朋友可以扫描下方二维码免费领取【保证100%免费】👇👇

为什么说现在普通人就业/升职加薪的首选是AI大模型?
人工智能技术的爆发式增长,正以不可逆转之势重塑就业市场版图。从DeepSeek等国产大模型引发的科技圈热议,到全国两会关于AI产业发展的政策聚焦,再到招聘会上排起的长队,AI的热度已从技术领域渗透到就业市场的每一个角落。

智联招聘的最新数据给出了最直观的印证:2025年2月,AI领域求职人数同比增幅突破200% ,远超其他行业平均水平;整个人工智能行业的求职增速达到33.4%,位居各行业榜首,其中人工智能工程师岗位的求职热度更是飙升69.6%。
AI产业的快速扩张,也让人才供需矛盾愈发突出。麦肯锡报告明确预测,到2030年中国AI专业人才需求将达600万人,人才缺口可能高达400万人,这一缺口不仅存在于核心技术领域,更蔓延至产业应用的各个环节。


资料包有什么?
①从入门到精通的全套视频教程⑤⑥
包含提示词工程、RAG、Agent等技术点

② AI大模型学习路线图(还有视频解说)
全过程AI大模型学习路线

③学习电子书籍和技术文档
市面上的大模型书籍确实太多了,这些是我精选出来的

④各大厂大模型面试题目详解

⑤ 这些资料真的有用吗?
这份资料由我和鲁为民博士共同整理,鲁为民博士先后获得了北京清华大学学士和美国加州理工学院博士学位,在包括IEEE Transactions等学术期刊和诸多国际会议上发表了超过50篇学术论文、取得了多项美国和中国发明专利,同时还斩获了吴文俊人工智能科学技术奖。目前我正在和鲁博士共同进行人工智能的研究。
所有的视频教程由智泊AI老师录制,且资料与智泊AI共享,相互补充。这份学习大礼包应该算是现在最全面的大模型学习资料了。
资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。


智泊AI始终秉持着“让每个人平等享受到优质教育资源”的育人理念,通过动态追踪大模型开发、数据标注伦理等前沿技术趋势,构建起"前沿课程+智能实训+精准就业"的高效培养体系。
课堂上不光教理论,还带着学员做了十多个真实项目。学员要亲自上手搞数据清洗、模型调优这些硬核操作,把课本知识变成真本事!


如果说你是以下人群中的其中一类,都可以来智泊AI学习人工智能,找到高薪工作,一次小小的“投资”换来的是终身受益!
应届毕业生:无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。
零基础转型:非技术背景但关注AI应用场景,计划通过低代码工具实现“AI+行业”跨界。
业务赋能 突破瓶颈:传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型。
👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓**

307

被折叠的 条评论
为什么被折叠?



