AI Agent 记忆系统详解 — 短期记忆、长期记忆与 RAG
30秒结论
- 解决什么问题:Agent 没有记忆就无法跨会话工作。三层记忆模型让 Agent 记住对话历史、用户偏好和海量知识。
- 核心方法:短期记忆(消息列表+窗口管理)、长期记忆(JSON/SQLite 持久化)、RAG 记忆(向量数据库+嵌入检索)。
- 关键结论:绝大多数场景用"滑动窗口+定期摘要+JSON 文件长期记忆"就够了。RAG 只在有大量文档需要检索时才引入。
- 读完能做什么:为你的 Agent 实现跨会话记忆,理解何时用哪种记忆层。
Agent 和人的一个关键相似点:没有记忆,什么都做不了。
想象一个 Agent 在帮你做一个为期三天的数据分析项目。第一天你告诉它数据源在哪、偏好什么图表风格。第二天你让它继续——如果它完全不记得第一天的事,你要把所有指令重新说一遍。
这就是记忆系统要解决的问题。本文拆解三种 Agent 记忆机制,从原理到代码。
三层记忆模型
| 类型 |
类比 |
时效 |
实现 |
| 短期记忆 |
工作记忆 |
当前会话 |
消息列表 |
| 长期记忆 |
笔记本 |
跨会话持久 |
数据库/文件 |
| RAG 记忆 |
图书馆 |
按需检索 |
向量数据库 |
第一层:短期记忆(对话窗口)
短期记忆就是 messages 列表。我们前面写的 Agent 已经在用了:
messages = [
{"role": "system", "content": "你是…"},
{"role": "user", "content": user_input},
{"role": "assistant", "content": "我来搜索一下…"},
{"role": "tool", "content": "搜索结果…"},
{"role": "assistant", "content": "根据搜索结果…"}
]
问题是:模型的上下文窗口有限。如果对话太长,超出窗口怎么办?
窗口管理策略
1. 滑动窗口——只保留最近 N 条消息。简单粗暴,但会丢掉早期关键信息。
2. 智能摘要——用模型定期压缩历史对话,把「用户喜欢蓝色图表、数据在 data/ 目录」浓缩成一段系统提示词,替换掉冗长的原始对话:
def compress_history(messages, client):
"""将历史对话压缩为一句话摘要。"""
summary_prompt = "将以下对话的关键信息压缩成一段摘要:\n" + \
"\n".join([f"{m['role']}: {m['content'][:200]}" for m in messages[-20:]])
summary = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": summary_prompt}]
).choices[0].message.content
return summary
3. 分层窗口——最近的保留原始消息,稍远的只保留摘要,更远的丢掉。兼顾上下文和效率。
📌 实践建议:绝大多数场景用「滑动窗口 + 定期摘要」就够了。只有当你需要精确引用历史细节时才需要更复杂的方案。
第二层:长期记忆(持久化存储)
长期记忆让 Agent 记住跨会话的信息——你的偏好、项目结构、上次任务的结果。
最简单的实现:文件存储
import json, os
MEMORY_FILE = "agent_memory.json"
def load_memory() -> dict:
if os.path.exists(MEMORY_FILE):
with open(MEMORY_FILE) as f:
return json.load(f)
return {"facts": [], "preferences": {}}
def save_fact(key: str, value: str):
memory = load_memory()
memory["facts"].append({"key": key, "value": value, "time": datetime.now().isoformat()})
with open(MEMORY_FILE, "w") as f:
json.dump(memory, f, indent=2)
def get_relevant_context(query: str) -> str:
"""启动时,将记忆注入系统提示词。"""
memory = load_memory()
facts = "\n".join([f"- {f['key']}: {f['value']}" for f in memory["facts"]])
return f"用户已知信息:\n{facts}"
使用时,在每次对话开始时加载记忆注入 system 消息:
system_prompt = f"""你是一个有用的助手。
{get_relevant_context()}
请根据已知信息回答用户的问题。"""
进阶:结构化记忆
对于更复杂的场景,可以用 SQLite 存储分类型的记忆:
CREATE TABLE memory (
id INTEGER PRIMARY KEY,
category TEXT, -- 'preference', 'project', 'person', 'fact'
key TEXT,
value TEXT,
importance REAL, -- 0.0 到 1.0,决定是否注入上下文
created_at TIMESTAMP,
last_accessed TIMESTAMP
);
注入时只选高重要度或最近访问的记忆,避免上下文膨胀。
第三层:RAG 记忆(检索增强生成)
前两层适合记录「元信息」——偏好、状态、事实。但如果你有大量文档——代码库、手册、研究报告——需要的是 RAG。
RAG 的核心流程:
- 索引:把文档切成小块,用嵌入模型转成向量,存入向量数据库
- 检索:用户提问时,用同样模型把问题转成向量,搜最相似的文档块
- 生成:把搜到的文档块注入提示词,让模型基于这些信息回答
import chromadb
from openai import OpenAI
client = OpenAI(base_url="...", api_key="...")
db = chromadb.PersistentClient(path="./agent_rag_db")
collection = db.get_or_create_collection("knowledge_base")
# 1. 索引 — 文档入库
def index_document(doc_id: str, content: str):
chunks = [content[i:i+500] for i in range(0, len(content), 500)]
for i, chunk in enumerate(chunks):
embedding = client.embeddings.create(
model="text-embedding-3-small", input=chunk
).data[0].embedding
collection.add(
ids=[f"{doc_id}_{i}"],
embeddings=[embedding],
documents=[chunk]
)
# 2. 检索 — 找到相关片段
def retrieve(query: str, top_k: int = 5) -> str:
query_embedding = client.embeddings.create(
model="text-embedding-3-small", input=query
).data[0].embedding
results = collection.query(query_embeddings=[query_embedding], n_results=top_k)
return "\n\n".join(results["documents"][0])
# 3. 注入 Agent — 在 system prompt 中追加检索结果
rag_context = retrieve(user_input)
system_prompt += f"\n\n参考知识库:\n{rag_context}"
三层怎么配合
实际项目中三层不是互斥的,而是各司其职:
| 场景 |
用哪层 |
| 记住用户这次说了什么 |
短期记忆(消息列表) |
| 记住用户偏好、项目路径 |
长期记忆(JSON/SQLite) |
| 查技术文档、研究资料 |
RAG(向量数据库) |
| 代码库里找相关函数 |
RAG + 目录树索引 |
| 总结已完成的任务 |
长期记忆 + 定期摘要 |
一个完整的记忆感知 Agent
把三层整合进我们的 Agent 循环:
def run_agent_with_memory(user_input: str, user_id: str = "default"):
# 加载长期记忆
long_term = load_memory(user_id)
# RAG 检索
rag_context = retrieve(user_input)
# 构建系统提示词(三层信息融合)
system = f"""你是用户的 AI 助手。
## 用户偏好
{long_term}
## 知识库参考
{rag_context}
## 对话指南
- 优先使用知识库中的信息
- 记住用户的偏好用于后续对话"""
messages = [{"role": "system", "content": system}]
messages.extend(load_recent_history(user_id)[-20:]) # 短期记忆
messages.append({"role": "user", "content": user_input})
# ReAct 循环(不变)
for turn in range(10):
response = client.chat.completions.create(
model="gpt-4o", messages=messages, tools=TOOLS
)
msg = response.choices[0].message
if not msg.tool_calls:
# 主动保存新学到的事实
extract_and_save_facts(msg.content, user_id)
return msg.content
# ... 执行工具 ...
return "达到最大轮次"
常见陷阱
- 记忆膨胀——不加选择地保存所有信息,上下文越来越长。解决:加 importance 评分,只注入高分记忆。
- 记忆过时——旧偏好不再适用但仍在注入。解决:加 TTL 过期时间,或定期要求模型重新确认。
- 隐私泄露——敏感信息被存入记忆后在后续对话中暴露。解决:敏感信息标记不持久化,或加密存储。
- RAG 噪音——检索到不相关内容反而误导模型。解决:加相关性阈值过滤,或让模型自行判断检索结果是否有用。
📖 下一篇:Agent 错误恢复与自我纠错 — 让 Agent 在犯错后自己修好
常见问题
Q: 短期记忆和长期记忆的核心区别是什么?
A: 短期记忆存在于单次会话的消息列表中,会话结束即消失。长期记忆持久化到文件或数据库,跨会话保留。
Q: 什么时候该用 RAG 而不是简单的长期记忆?
A: 长期记忆适合存储结构化的事实和偏好。RAG 适合需要从大量非结构化文档中检索信息时。数据量在一千条以内用长期记忆,超过一万条用 RAG。
Q: 向量数据库该选什么?
A: ChromaDB 开源、本地部署、零配置,适合原型。Pinecone 是全托管云服务,适合生产环境。起步建议用 ChromaDB。
Q: 记忆膨胀怎么解决?
A: 三种策略:① 重要性评分只注入高分记忆;② TTL 过期自动清理;③ 用摘要压缩替代原始消息。