记忆系统
记忆系统是 AI Agent 的重要组成部分,它让 Agent 能够记住对话历史、用户偏好和任务状态,从而提供连贯、个性化的服务。LangGraph 提供了一套完整的记忆系统解决方案,包括短期记忆和长期记忆。
为什么需要记忆系统
没有记忆系统的 Agent 就像一个"健忘"的助手,每次对话都从零开始:
用户:我叫张三
Agent:你好,张三!
用户:我叫什么名字?
Agent:抱歉,我不知道您的名字。
有了记忆系统,Agent 能够记住之前的对话内容:
用户:我叫张三
Agent:你好,张三!
用户:我叫什么名字?
Agent:您叫张三。
记忆系统解决了以下问题:
- 上下文连贯性:让对话保持连贯,避免重复询问
- 用户个性化:记住用户偏好,提供个性化服务
- 任务连续性:在多轮对话中持续推进任务
- 知识积累:存储和检索知识信息
LangGraph 记忆架构
LangGraph 将记忆系统分为两个层次:
短期记忆 vs 长期记忆
| 特性 | 短期记忆 | 长期记忆 |
|---|---|---|
| 作用域 | 单个会话(thread) | 跨会话、跨用户 |
| 存储内容 | 对话历史、临时状态 | 用户偏好、知识事实 |
| 持久化 | 通过 checkpointer | 通过 store |
| 检索方式 | 按 thread_id 加载 | 语义搜索、过滤查询 |
| 生命周期 | 会话期间 | 永久保存 |
短期记忆
短期记忆(Short-term Memory)用于跟踪当前会话中的对话历史。LangGraph 通过 checkpointer 实现短期记忆的持久化。
基本使用
使用 MemorySaver 作为最简单的 checkpointer:
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
# 创建 checkpointer
checkpointer = MemorySaver()
# 创建模型
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 创建 Agent
agent = create_react_agent(
model=llm,
tools=[], # 可添加工具
prompt="你是一个友好的助手。",
checkpointer=checkpointer
)
# 配置会话
config = {"configurable": {"thread_id": "session-001"}}
# 第一轮对话
result1 = agent.invoke(
{"messages": [{"role": "user", "content": "你好,我叫张三"}]},
config=config
)
print(result1["messages"][-1].content)
# 第二轮对话(Agent 会记住之前的内容)
result2 = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?"}]},
config=config
)
print(result2["messages"][-1].content)
输出:
你好张三!很高兴认识你。有什么我可以帮助你的吗?
根据我们之前的对话,你告诉我你的名字是张三。
生产级 Checkpointer
在生产环境中,需要使用数据库支持的 checkpointer:
PostgreSQL Checkpointer:
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
# 配置数据库连接
DB_URI = "postgresql://user:password@localhost:5432/mydb"
# 创建 checkpointer
async with AsyncPostgresSaver.from_conn_string(DB_URI) as checkpointer:
# 首次使用需要初始化表结构
await checkpointer.setup()
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[...],
checkpointer=checkpointer
)
result = await agent.ainvoke(
{"messages": [...]},
config={"configurable": {"thread_id": "session-001"}}
)
SQLite Checkpointer:
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
import aiosqlite
async def main():
async with aiosqlite.connect("checkpoints.db") as conn:
checkpointer = AsyncSqliteSaver(conn)
await checkpointer.setup()
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[...],
checkpointer=checkpointer
)
Redis Checkpointer:
from langgraph.checkpoint.redis.aio import AsyncRedisSaver
DB_URI = "redis://localhost:6379"
async with AsyncRedisSaver.from_conn_string(DB_URI) as checkpointer:
await checkpointer.setup()
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[...],
checkpointer=checkpointer
)
管理对话历史
当对话历史过长时,需要主动管理以避免超出 LLM 上下文限制:
裁剪消息:
from langchain_core.messages import trim_messages
from langgraph.prebuilt import create_react_agent
# 在自定义节点中裁剪消息
def trim_messages_node(state):
messages = state["messages"]
# 保留最后 10 条消息
trimmed = trim_messages(
messages,
max_tokens=4000,
strategy="last",
token_counter=len, # 或使用实际的 token 计数器
start_on="human",
end_on=("human", "tool"),
)
return {"messages": trimmed}
总结历史:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
async def summarize_if_needed(state):
"""如果对话过长,总结旧消息"""
messages = state["messages"]
if len(messages) > 10:
# 提取需要总结的消息
old_messages = messages[:-5]
recent_messages = messages[-5:]
# 生成总结
summary_prompt = f"""请总结以下对话内容,保留关键信息:
{chr(10).join([f"{m.type}: {m.content}" for m in old_messages])}
"""
summary = await llm.ainvoke([{"role": "user", "content": summary_prompt}])
# 用总结替换旧消息
from langchain_core.messages import SystemMessage
return {
"messages": [
SystemMessage(content=f"[历史摘要] {summary.content}"),
*recent_messages
]
}
return state
长期记忆
长期记忆(Long-term Memory)用于跨会话存储用户偏好、事实知识等信息。LangGraph 通过 Store 实现。
基本使用
使用 InMemoryStore 作为最简单的 store:
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore
from langchain_core.tools import tool
from langchain_core.runnables import RunnableConfig
from langgraph.config import get_store
import uuid
# 创建 store
store = InMemoryStore()
# 定义工具
@tool
def save_memory(content: str, config: RunnableConfig) -> str:
"""保存重要信息到长期记忆"""
memory_store = get_store()
user_id = config.get("configurable", {}).get("user_id", "default")
memory_store.put(
("memories", user_id),
str(uuid.uuid4()),
{"content": content}
)
return f"已保存:{content}"
@tool
def recall_memories(config: RunnableConfig) -> str:
"""从长期记忆中检索信息"""
memory_store = get_store()
user_id = config.get("configurable", {}).get("user_id", "default")
memories = memory_store.search(("memories", user_id))
if not memories:
return "暂无记忆"
return "\n".join([f"- {m.value['content']}" for m in memories])
# 创建 Agent
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[save_memory, recall_memories],
store=store,
prompt="你是一个智能助手,可以记住用户的信息。"
)
# 用户 A 的会话
config_a = {"configurable": {"user_id": "user-a", "thread_id": "session-1"}}
agent.invoke(
{"messages": [{"role": "user", "content": "请记住:我喜欢喝咖啡"}]},
config=config_a
)
# 用户 A 的新会话(仍能回忆起记忆)
config_a2 = {"configurable": {"user_id": "user-a", "thread_id": "session-2"}}
result = agent.invoke(
{"messages": [{"role": "user", "content": "我记得什么?"}]},
config=config_a2
)
print(result["messages"][-1].content) # 包含"喜欢喝咖啡"
语义搜索
启用语义搜索可以更智能地检索记忆:
from langgraph.store.memory import InMemoryStore
from langchain_openai import OpenAIEmbeddings
# 创建带语义搜索的 store
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
def embed_texts(texts: list) -> list:
return embeddings.embed_documents(texts)
store = InMemoryStore(
index={
"embed": embed_texts,
"dims": 1536, # embedding 维度
}
)
# 保存记忆
store.put(("user_123", "memories"), "1", {"text": "用户喜欢吃披萨"})
store.put(("user_123", "memories"), "2", {"text": "用户是一名软件工程师"})
store.put(("user_123", "memories"), "3", {"text": "用户的生日是3月15日"})
# 语义搜索
results = store.search(
("user_123", "memories"),
query="关于用户工作的信息",
limit=2
)
for item in results:
print(f"记忆: {item.value['text']}")
print(f"相似度: {item.score}")
生产级 Store
PostgreSQL Store:
from langgraph.store.postgres import PostgresStore
DB_URI = "postgresql://user:password@localhost:5432/mydb"
store = PostgresStore.from_conn_string(DB_URI)
store.setup() # 首次使用
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[...],
store=store
)
MongoDB Store:
from langgraph.store.mongodb import MongoDBStore
DB_URI = "mongodb://localhost:27017"
store = MongoDBStore.from_conn_string(DB_URI)
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[...],
store=store
)
记忆类型
根据认知心理学,记忆可分为三种类型:
| 类型 | 说明 | Agent 应用 |
|---|---|---|
| 语义记忆 | 存储事实和知识 | 用户偏好、个人信息 |
| 情景记忆 | 存储经历和事件 | 过去的对话、完成的任务 |
| 程序记忆 | 存储规则和技能 | Agent 的行为模式、提示词 |
语义记忆实现
语义记忆用于存储关于用户的事实信息:
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore
from langchain_core.tools import tool
from langchain_core.runnables import RunnableConfig
from langgraph.config import get_store
from pydantic import BaseModel
store = InMemoryStore()
class UserProfile(BaseModel):
"""用户档案"""
name: str = ""
preferences: list = []
facts: list = []
@tool
def update_profile(key: str, value: str, config: RunnableConfig) -> str:
"""更新用户档案"""
memory_store = get_store()
user_id = config.get("configurable", {}).get("user_id", "default")
namespace = ("profiles", user_id)
# 获取现有档案
existing = memory_store.get(namespace, "profile")
if existing:
profile = UserProfile(**existing.value)
else:
profile = UserProfile()
# 更新字段
if key == "name":
profile.name = value
elif key == "preference":
profile.preferences.append(value)
elif key == "fact":
profile.facts.append(value)
# 保存更新后的档案
memory_store.put(namespace, "profile", profile.model_dump())
return f"已更新档案:{key} = {value}"
@tool
def get_profile(config: RunnableConfig) -> str:
"""获取用户档案"""
memory_store = get_store()
user_id = config.get("configurable", {}).get("user_id", "default")
existing = memory_store.get(("profiles", user_id), "profile")
if existing:
return str(existing.value)
return "暂无档案信息"
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[update_profile, get_profile],
store=store,
prompt="你是一个智能助手,可以记住用户的信息。"
)
情景记忆实现
情景记忆用于存储过去的经历,常用于 few-shot 学习:
from langgraph.store.memory import InMemoryStore
from langchain_openai import ChatOpenAI
from datetime import datetime
import uuid
store = InMemoryStore()
llm = ChatOpenAI(model="gpt-4o-mini")
async def save_episode(user_id: str, task: str, approach: str, success: bool):
"""保存情景记忆"""
namespace = ("episodes", user_id)
episode = {
"task": task,
"approach": approach,
"success": success,
"timestamp": datetime.now().isoformat()
}
store.put(namespace, str(uuid.uuid4()), episode)
async def retrieve_episodes(user_id: str, current_task: str, limit: int = 3):
"""检索相关情景记忆作为 few-shot 示例"""
namespace = ("episodes", user_id)
# 可以使用语义搜索
episodes = store.search(namespace, query=current_task, limit=limit)
return [
{
"task": e.value["task"],
"approach": e.value["approach"],
"success": e.value["success"]
}
for e in episodes
]
async def agent_with_episodes(user_input: str, user_id: str):
"""使用情景记忆的 Agent"""
# 检索相关经历
episodes = await retrieve_episodes(user_id, user_input)
# 构建 few-shot 示例
examples_text = ""
if episodes:
examples_text = "\n\n相关的成功案例:\n"
for i, ep in enumerate(episodes, 1):
examples_text += f"{i}. 任务:{ep['task']}\n 方法:{ep['approach']}\n"
system_msg = f"""你是一个智能助手。
{examples_text}
请参考过去的成功经验来处理当前任务。"""
response = await llm.ainvoke([
{"role": "system", "content": system_msg},
{"role": "user", "content": user_input}
])
return response.content
程序记忆实现
程序记忆用于存储 Agent 的行为规则,可以通过"反思"机制动态优化:
from langgraph.store.memory import InMemoryStore
from langchain_openai import ChatOpenAI
store = InMemoryStore()
llm = ChatOpenAI(model="gpt-4o-mini")
async def update_instructions(user_id: str, feedback: str) -> str:
"""根据反馈更新程序记忆(指令)"""
namespace = ("instructions", user_id)
# 获取当前指令
existing = store.get(namespace, "system_prompt")
current_instructions = existing.value["text"] if existing else "你是一个友好的助手。"
reflection_prompt = f"""当前指令:
{current_instructions}
用户反馈:
{feedback}
请根据反馈优化指令,使 Agent 能更好地服务用户。
返回优化后的完整指令。"""
response = await llm.ainvoke([{"role": "user", "content": reflection_prompt}])
# 保存更新后的指令
store.put(namespace, "system_prompt", {"text": response.content})
return response.content
async def agent_with_procedural_memory(user_input: str, user_id: str) -> str:
"""使用程序记忆的 Agent"""
namespace = ("instructions", user_id)
# 获取自定义指令
existing = store.get(namespace, "system_prompt")
system_prompt = existing.value["text"] if existing else "你是一个友好的助手。"
response = await llm.ainvoke([
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_input}
])
return response.content
记忆更新策略
热路径更新(In the Hot Path)
在对话过程中实时更新记忆:
from langgraph.prebuilt import create_react_agent
from langchain_core.tools import tool
from langchain_core.runnables import RunnableConfig
from langgraph.config import get_store
import uuid
@tool
def remember(content: str, config: RunnableConfig) -> str:
"""记住重要信息(实时保存)"""
store = get_store()
user_id = config.get("configurable", {}).get("user_id", "default")
store.put(
("memories", user_id),
str(uuid.uuid4()),
{"content": content}
)
return f"我会记住:{content}"
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[remember],
prompt="如果用户告诉你重要的信息,使用 remember 工具保存。"
)
优点:
- 实时性强,新记忆立即可用
- 用户可以知道记忆何时被保存
缺点:
- 增加 Agent 响应延迟
- Agent 需要同时处理任务和记忆管理
后台更新(Background)
在后台异步更新记忆:
import asyncio
from datetime import datetime
async def background_memory_updater(user_id: str, messages: list, store):
"""后台记忆更新任务"""
namespace = ("memories", user_id)
# 分析对话,提取记忆
extraction_prompt = f"""从以下对话中提取重要信息:
{chr(10).join([f"{m['role']}: {m['content']}" for m in messages])}
返回需要记住的信息列表。"""
llm = ChatOpenAI(model="gpt-4o-mini")
response = await llm.ainvoke([{"role": "user", "content": extraction_prompt}])
# 保存提取的记忆
store.put(namespace, str(uuid.uuid4()), {
"content": response.content,
"timestamp": datetime.now().isoformat()
})
优点:
- 不影响主流程响应时间
- 可以进行更复杂的记忆分析
缺点:
- 新记忆不会立即可用
- 需要额外的调度机制
完整示例:带记忆的智能助手
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.memory import InMemoryStore
from langchain_core.tools import tool
from langchain_core.runnables import RunnableConfig
from langgraph.config import get_store
import uuid
from datetime import datetime
# ========== 配置 ==========
# 创建 checkpointer(短期记忆)
checkpointer = MemorySaver()
# 创建 store(长期记忆)
store = InMemoryStore()
# ========== 工具定义 ==========
@tool
def save_memory(content: str, config: RunnableConfig) -> str:
"""保存重要信息到长期记忆"""
memory_store = get_store()
user_id = config.get("configurable", {}).get("user_id", "default")
memory_store.put(
("memories", user_id),
str(uuid.uuid4()),
{
"content": content,
"timestamp": datetime.now().isoformat()
}
)
return f"已保存:{content}"
@tool
def recall_memories(config: RunnableConfig) -> str:
"""检索长期记忆"""
memory_store = get_store()
user_id = config.get("configurable", {}).get("user_id", "default")
memories = memory_store.search(("memories", user_id))
if not memories:
return "暂无记忆"
return "\n".join([
f"- {m.value['content']} ({m.value.get('timestamp', 'unknown')})"
for m in memories
])
# ========== 创建 Agent ==========
agent = create_react_agent(
model="openai:gpt-4o-mini",
tools=[save_memory, recall_memories],
prompt="""你是一个智能助手,能够记住用户的信息和偏好。
如果用户告诉你一些值得记住的信息(如偏好、个人信息等),
请使用 save_memory 工具保存。
如果用户询问之前的信息,请使用 recall_memories 工具检索。""",
checkpointer=checkpointer,
store=store
)
# ========== 使用示例 ==========
def chat(user_input: str, user_id: str, thread_id: str) -> str:
"""与助手对话"""
config = {
"configurable": {
"user_id": user_id,
"thread_id": thread_id
}
}
result = agent.invoke(
{"messages": [{"role": "user", "content": user_input}]},
config=config
)
return result["messages"][-1].content
# 会话 1:告诉 Agent 一些信息
print("=== 会话 1 ===")
print(chat("你好,我叫张三,我是一名软件工程师", "user-001", "session-001"))
# 会话 2:继续对话(短期记忆)
print("\n=== 会话 2 ===")
print(chat("我刚才说我叫什么?", "user-001", "session-001"))
# 会话 3:新会话,但长期记忆保留
print("\n=== 会话 3(新会话)===")
print(chat("你知道关于我的什么信息?", "user-001", "session-002"))
最佳实践
1. 选择合适的存储后端
| 场景 | 推荐后端 |
|---|---|
| 开发/测试 | MemorySaver / InMemoryStore |
| 单机生产 | SQLite |
| 分布式生产 | PostgreSQL / Redis |
| 高并发 | Redis |
2. 合理设置命名空间
# 按用户隔离
namespace = ("user", user_id, "memories")
# 按类型隔离
namespace = ("user", user_id, "preferences") # 偏好
namespace = ("user", user_id, "facts") # 事实
namespace = ("user", user_id, "episodes") # 经历
3. 定期清理过期记忆
from datetime import datetime, timedelta
async def cleanup_old_memories(store, user_id: str, max_age_days: int = 90):
"""清理过期的记忆"""
namespace = ("memories", user_id)
# 获取所有记忆
all_memories = store.search(namespace, limit=1000)
cutoff_date = datetime.now() - timedelta(days=max_age_days)
for memory in all_memories:
timestamp = memory.value.get("timestamp")
if timestamp:
memory_date = datetime.fromisoformat(timestamp)
if memory_date < cutoff_date:
# 删除过期记忆
store.delete(namespace, memory.key)
4. 隐私保护
import re
def sanitize_memory(content: str) -> str:
"""脱敏处理敏感信息"""
patterns = [
(r'\b\d{17}[\dXx]\b', '[身份证号]'),
(r'\b1[3-9]\d{9}\b', '[手机号]'),
(r'\b[\w\.-]+@[\w\.-]+\.\w+\b', '[邮箱]'),
(r'\b\d{16,19}\b', '[银行卡号]'),
]
for pattern, replacement in patterns:
content = re.sub(pattern, replacement, content)
return content
小结
LangGraph 的记忆系统提供了完整的解决方案:
- 短期记忆:通过 checkpointer 实现会话级状态持久化
- 长期记忆:通过 store 实现跨会话数据存储和语义检索
- 三种记忆类型:语义、情景、程序记忆,满足不同场景需求
- 灵活的更新策略:热路径或后台更新
- 生产级支持:PostgreSQL、Redis、MongoDB 等多种后端
设计良好的记忆系统是构建智能 Agent 的关键,它让 Agent 能够提供连贯、个性化的服务体验。