你有没有遇到过这种令人抓狂的情况?和 AI 智能体聊了半天,它帮你计算了复杂数据、处理了多个文件,结果你下一句问 "刚才算的结果是多少?",它却一脸茫然地说 "抱歉,我不记得之前的对话了"。
这就是 AI 最让人头疼的 "先天性失忆症"------默认情况下,所有大模型和智能体都是无状态的。每次 API 调用都是一次完全独立的交互,模型不会保留任何之前的上下文信息,就像每次见面都是初次相遇一样。
动画视频在《25. AI 总是"失忆"?3 行代码让它记住你!》。
不过别担心,LangChain/LangGraph 提供了一套极其优雅的解决方案:checkpointer(检查点)机制。只需要 3 行核心代码,就能让你的智能体拥有持久化记忆,不仅能记住完整的对话历史,还能保存执行过程中的所有状态,下次接着聊,甚至程序重启后也能完美恢复之前的会话。
3 行代码实现内存记忆
我们先从最简单的内存记忆开始,把对话状态保存在程序内存中。这种方式速度极快,不需要任何额外的数据库依赖,非常适合开发测试和短期会话场景。
LangChain的 checkpointer 机制会自动拦截智能体的每一次状态变化,包括用户输入、AI 回复、工具调用请求和工具返回结果,并将这些状态保存下来。当你下次调用时,它会根据唯一的会话标识自动加载之前的状态,让智能体仿佛从未中断过对话。
import os
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain_core.tools import BaseTool
from langchain_community.tools import WriteFileTool, ReadFileTool, ListDirectoryTool
from langgraph.checkpoint.memory import MemorySaver
# 加载环境变量(配置通义千问API)
load_dotenv()
prefix = "QWEN"
model = init_chat_model(
model_provider="openai",
configurable_fields=["model", "api_key", "base_url"],
config_prefix=prefix
).with_config({
"configurable": {
f"{prefix}_model": os.getenv(f"{prefix}_MODEL"),
f"{prefix}_api_key": os.getenv(f"{prefix}_API_KEY"),
f"{prefix}_base_url": os.getenv(f"{prefix}_BASE_URL")
}
})
# 自定义计算工具
class CalculateTool(BaseTool):
name: str = "calculate"
description: str = "计算数学表达式的值"
def _run(self, expression: str) -> str:
try:
return f"计算结果: {eval(expression)}"
except Exception as e:
return f"计算错误: {str(e)}"
async def _arun(self, expression: str) -> str:
return self._run(expression)
# ========== 初始化工具 ==========
calculate = CalculateTool()
write_file = WriteFileTool()
read_file = ReadFileTool()
list_dir = ListDirectoryTool()
# ========== 关键代码1:创建内存检查点 ==========
checkpointer = MemorySaver()
# 创建带记忆功能的智能体
agent = create_agent(
model=model,
tools=[calculate, write_file, read_file, list_dir],
system_prompt="你是一个助手,会用工具计算、读写文件、列出目录。",
debug=True,
# ========== 关键代码2:将检查点传入智能体 ==========
checkpointer=checkpointer
)
# ========== 关键代码3:配置会话唯一标识 ==========
config = {"configurable": {"thread_id": "session-1"}}
# 测试对话(包含记忆验证)
queries = [
"计算 2024*12+500,然后把结果保存到 result.txt",
"读取 result.txt 的内容",
"列出当前目录文件",
"刚才计算的结果是多少?"
]
for q in queries:
print(f"\n问:{q}")
result = agent.invoke({"messages": [{"role": "user", "content": q}]}, config=config)
print(f"答:{result['messages'][-1].content}")
代码解释
创建 MemorySaver:这是内存版的检查点实现,负责在程序内存中保存所有的状态数据。
传入 create_agent:将 checkpointer 对象传递给智能体构造函数,这样智能体就会自动启用状态保存功能。
设置 thread_id:这是会话的唯一标识,checkpointer 通过它来关联不同的对话状态。同一thread_id下的所有调用会共享同一个记忆上下文,不同的thread_id则实现了多用户记忆隔离。
验证记忆是否生效
运行代码后,你会看到智能体依次完成计算、保存文件、读取文件和列出目录的任务。当最后问到 "刚才计算的结果是多少?" 时,它会准确回答24788(2024×12+500=24788),这说明它已经成功记住了之前的对话内容。
数据库持久化记忆
内存记忆虽然简单高效,但有一个致命的缺点:程序重启后所有记忆都会丢失。如果我们需要长期保存会话数据,或者在多进程 / 多线程环境中共享记忆,就需要把检查点保存到数据库中。
LangGraph 官方提供了SqliteSaver,使用轻量级的 SQLite 数据库来持久化检查点数据。SQLite 不需要单独安装服务器,一个文件就是一个数据库,非常适合中小型应用。
首先安装必要的依赖包:
pip install langgraph-checkpoint-sqlite
然后修改代码,将MemorySaver替换为SqliteSaver:
import os
import sqlite3
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain_core.tools import BaseTool
from langchain_community.tools import WriteFileTool, ReadFileTool, ListDirectoryTool
from langgraph.checkpoint.sqlite import SqliteSaver
# 加载环境变量(配置通义千问API)
load_dotenv()
prefix = "QWEN"
model = init_chat_model(
model_provider="openai",
configurable_fields=["model", "api_key", "base_url"],
config_prefix=prefix
).with_config({
"configurable": {
f"{prefix}_model": os.getenv(f"{prefix}_MODEL"),
f"{prefix}_api_key": os.getenv(f"{prefix}_API_KEY"),
f"{prefix}_base_url": os.getenv(f"{prefix}_BASE_URL")
}
})
# 自定义计算工具
class CalculateTool(BaseTool):
name: str = "calculate"
description: str = "计算数学表达式的值"
def _run(self, expression: str) -> str:
try:
return f"计算结果: {eval(expression)}"
except Exception as e:
return f"计算错误: {str(e)}"
async def _arun(self, expression: str) -> str:
return self._run(expression)
# ========== 初始化工具 ==========
calculate = CalculateTool()
write_file = WriteFileTool()
read_file = ReadFileTool()
list_dir = ListDirectoryTool()
# ========== 关键修改:创建SQLite检查点 ==========
# check_same_thread=False 是必须的,因为智能体可能会跨线程访问数据库
conn = sqlite3.connect("checkpoint.db", check_same_thread=False)
checkpointer = SqliteSaver(conn)
# 创建带持久化记忆功能的智能体
agent = create_agent(
model=model,
tools=[calculate, write_file, read_file, list_dir],
system_prompt="你是一个助手,会用工具计算、读写文件、列出目录。",
debug=True,
checkpointer=checkpointer
)
# 配置会话唯一标识
config = {"configurable": {"thread_id": "session-1"}}
# 测试对话
queries = [
"计算 2024*12+500,然后把结果保存到 result.txt",
"读取 result.txt 的内容",
"列出当前目录文件",
"刚才计算的结果是多少?"
]
for q in queries:
print(f"\n问:{q}")
result = agent.invoke({"messages": [{"role": "user", "content": q}]}, config=config)
print(f"答:{result['messages'][-1].content}")
# 使用完毕后关闭数据库连接
conn.close()
持久化效果验证
运行这段代码后,你会在当前目录下看到一个checkpoint.db文件,所有的对话状态都被保存在这个文件中。现在你可以:
- 完全关闭程序
- 重新运行程序
- 直接问 "刚才计算的结果是多少?"
智能体依然会准确回答24788,这说明记忆已经被成功持久化到数据库中了。