AI Agent 记忆系统:从原理到生产架构的完整指南
大多数人对 Agent 记忆的理解停留在"把聊天记录存到向量数据库"。这不叫记忆系统,这叫日志存储。
一个能在生产环境跑稳的 Agent,记忆是最关键的一环——记什么、怎么存、什么时候取、什么时候删,每一步都有讲究。这篇文章把 Agent 记忆系统从原理到工程完整拆解。
一、记忆类型:不只是短期和长期
只分"短期记忆"和"长期记忆"是不够的。生产级 Agent 的长期记忆至少要细分成三种:语义记忆、情景记忆、程序记忆——少一种,Agent 的能力就会有明显短板。
这套分类来自认知科学对人类记忆的研究(Tulving, 1972),被斯坦福 Generative Agents 论文和后续大量 Agent 研究直接采用。
短期记忆:上下文窗口
Agent 的短期记忆就是 LLM 当前的上下文窗口——模型能"看到"的所有信息。对话历史、系统 prompt、工具返回值,全部挤在这个窗口里。窗口满了,旧信息就得被截断或压缩。LangGraph 里叫 Checkpointer,用 MemorySaver 或 PostgresSaver 做会话级持久化。
工程瓶颈很明确:
- 容量有限:128K tokens 听起来很大,但 Agent 执行十几轮工具调用后,上下文很容易就满了
- 中间迷失(Lost in the Middle):LLM 对上下文中部的信息关注概率明显低于头部和尾部
长期记忆的三个子类型
语义记忆(Semantic Memory)——存事实和知识。
"用户是后端工程师"、"公司用的是 PostgreSQL"、"项目预算 50 万"。这些信息不绑定具体对话,是跨 session 复用的通用知识。存储方式通常是结构化的 key-value 或知识图谱。Mem0 的 factual memory 和 Letta 的 core memory blocks 都属于这类。
情景记忆(Episodic Memory)——存具体经历。
"上周二用户问了 RAG 检索不准的问题,我推荐了 reranker,用户说有效"。带时间戳、带上下文、带结果反馈,是 Agent 从经验中学习的基础。Generative Agents 论文里的 memory stream 就是典型的情景记忆。
程序记忆(Procedural Memory)——存技能和流程。
"当用户要求生成周报时,先查本周任务列表,再汇总进度,最后生成 Markdown 格式报告"。程序记忆决定 Agent 怎么做事,通常以 prompt 模板、工作流定义或学习到的策略形式存在。MemP 和 LEGOMem 专门研究如何从情景记忆中提炼出可复用的程序记忆。
协同工作
三种记忆的协同逻辑:
- 语义记忆提供事实基础
- 情景记忆提供经验参考
- 程序记忆指导执行流程
缺语义记忆,Agent 每次都要重新问用户基本信息;缺情景记忆,Agent 无法从成功或失败中学习;缺程序记忆,Agent 每次遇到同类任务都像第一次做。
记忆固化(Consolidation)
情景记忆可以通过后台 LLM 归纳,提炼成语义知识。比如 Agent 连续 5 次帮用户处理 CSV 数据时都用了 pandas,这个模式可以被提炼成语义记忆"该用户偏好 Python + pandas 做数据处理"。Letta 的 sleep-time agent 和 Mem0 的异步提取都在做这件事。
二、MemGPT:用操作系统的思路管理记忆
MemGPT 的核心洞察是:LLM 的上下文窗口就像 CPU 的物理内存——容量有限。操作系统用虚拟内存和分页机制解决了物理内存不够的问题,同样的思路可以用来管理 Agent 的记忆。
MemGPT(UC Berkeley,Packer 等人)提出了一个两层记忆架构:
第一层:In-Context Memory(类比 RAM)
在 LLM 的上下文窗口内,包含三部分:
- 系统指令(不可编辑)
- 可读写的记忆块(Memory Blocks)——核心创新
- 近期对话历史
Letta 的 ChatMemory 类默认提供 "human" 和 "persona" 两个记忆块,各 2K 字符。Agent 通过工具调用直接编辑这些块:
memory_insert——插入新信息
memory_replace——替换旧信息
memory_rethink——重新组织
第二层:Out-of-Context Memory(类比硬盘)
- Recall Memory:完整对话历史,支持文本搜索和日期搜索
- Archival Memory:长期存储,支持向量检索
上下文窗口快满时,旧对话被压缩成递归摘要,存入 Recall Memory。Agent 需要历史信息时,通过 conversation_search 或 archival_memory_search 工具主动检索。
关键设计:Agent 自己管理记忆
传统 RAG 是被动检索——系统帮你找文档塞进 prompt。MemGPT 是主动管理——Agent 自己决定记什么、忘什么、什么时候从"硬盘"读数据到"内存"。Agent 的每一个动作都是 tool call,包括给用户发消息(send_message)和记忆操作(core_memory_append、archival_memory_insert 等)。
Heartbeat 机制
MemGPT 在工具调用中注入 request_heartbeat 参数,设为 true 时,Agent 执行完当前工具后立刻获得下一轮执行机会,不需要等待用户输入。这实现了连续执行——一次用户请求可能触发多轮工具调用链,中间穿插记忆读写操作。
Sleep-Time Agent
原版 MemGPT 把记忆管理和对话响应放在同一个循环里,导致两个问题:记忆操作增加响应延迟,Agent 在对话压力下可能顾不上整理记忆。Letta V1 引入了 sleep-time agent,把记忆的整理、固化、清理放到异步后台处理,类似人类在睡眠中巩固记忆。
工程注意点
- 记忆块大小要控制。2K 字符的默认限制不是随意设的——块太大挤占上下文空间,块太小存不下关键信息。人均画像信息通常 1-2K 够用
- 对话压缩的时机。Letta 在上下文使用率到 75% 时触发压缩,阈值需要根据模型上下文长度和任务复杂度调整
- 自编辑记忆的幻觉风险。Agent 可能把错误信息写入 core memory,一旦写入就会持续影响后续所有交互。需要写入前做校验,或记录编辑历史以便回滚
三、记忆检索:三维打分框架
只用向量相似度,Agent 会频繁捞出"语义相关但实际没用"的记忆。生产级记忆检索至少需要三维打分:Recency(时效性)、Relevance(相关性)、Importance(重要性)。
这个框架来自斯坦福 Generative Agents 论文(Park 等人),现在已经成为 Agent 记忆检索的标准范式。
检索公式
score = α_recency × recency + α_relevance × relevance + α_importance × importance
三个权重 α 在原始论文中都设为 1,每个维度的分数归一化到 [0, 1],最终取 top-k。
Recency(时效性)
最近的记忆优先。采用指数衰减函数,每小时衰减 0.995:
- 24 小时没访问 → recency ≈ 0.89
- 一周没访问 → recency ≈ 0.71
- 被重新检索到时,recency 重置(间隔重复效应)
为什么需要 recency?同一个事实可能随时间变化。用户上个月说"我用的是 Python 3.8",本周说"刚升级到 3.12"。如果只靠向量相似度,两条记忆的语义距离几乎为零,但旧的那条已经过时了。
Relevance(相关性)
语义相关的记忆优先。对查询和每条记忆做 embedding,算 cosine similarity。这一步就是常规的向量检索。
Importance(重要性)
最容易被忽略的一维。"今天天气不错"和"用户刚被裁员"这两条记忆的重要性完全不同。做法是让 LLM 在存入时对每条记忆打 1-10 的重要性分数,高分记忆在检索时获得额外加权。
更先进的系统如 Mnemosyne(RAND)在此基础上引入了动态衰减和访问加成:recency 用 e^(-age/30) 衰减,每次被检索到加 0.1 分(上限 +2.0),图结构记忆还有邻居节点的接近度加成 +0.05。
前沿:从固定权重到可学习权重
原始论文的三个 α 都是手调的。人大和华为联合研究提出了用 MoE(Mixture of Experts)门控函数自适应调整权重——不同 query 类型、不同时间点,三个维度的权重比例动态变化。比如用户问"上周三发生了什么"时,recency 权重自动提高;用户问"我们的技术栈是什么"时,importance 权重自动提高。实验显示,自适应权重比固定权重在 LOCOMO benchmark 上提升了 8-12%。
检索不准时的排查顺序
- 先查 Relevance:embedding 模型选对了吗?中文场景用 bge-m3 或 gte-large,不要用纯英文模型
- 再查 Recency:衰减率是否合理?0.995/小时适合对话场景,知识库场景衰减要更慢(0.999)
- 最后查 Importance:重要性打分的 prompt 要明确定义什么算"重要",否则 LLM 容易给所有记忆打高分
- 检查 top-k 值:k 太小漏关键信息,k 太大引入噪声。对话场景 k=5-10,复杂推理场景 k=15-20
四、记忆遗忘:该记的长期记,该忘的及时忘
定期按时间删是最偷懒的做法,而且会删掉有价值的旧记忆。遗忘不是 bug,是 feature。
记忆膨胀(Memory Bloat)是生产级 Agent 最常见的性能杀手。症状:检索延迟从 100ms 涨到 1s+,Agent 回答开始跑偏(检索到太多不相关的记忆),token 成本直线上升。
研究数据:使用"全部保存"策略的 Agent,随着记忆积累性能持续下降。引入基于效用评估和检索历史的删除策略后,性能反而提升了 10%——低质量记忆稀释了检索精度。(来源:Xiong et al., 2025)
五种遗忘策略
1. 基于时间的衰减(Time-Based Decay)
每条记忆带时间戳,重要性分数随时间指数衰减。长时间未被访问的记忆自动降权,最终触发删除阈值(重要性分降到 0.1 以下就删除)。适合对话型 Agent,近期信息比远期信息重要的场景。
2. 基于访问频率的淘汰(LRU/LFU)
借鉴缓存策略:最近最少使用(LRU)或最不频繁使用(LFU)的记忆优先删除。存了 3 个月从来没被检索命中的记忆,大概率没有保留价值。实现简单,效果稳定。
3. 基于摘要的压缩(Summarization-Based Compression)
不删除,而是压缩。把 100 轮对话历史用 LLM 压缩成 500 字的摘要,细节丢失但核心信息保留。Letta 的递归摘要和 Mastra 的 Observational Memory 都用这个思路。Mastra 的数据:文本压缩比 3-6x,涉及工具调用的场景压缩比可达 5-40x。
4. 基于效用的清理(Utility-Based Pruning)
用专门的 LLM 评估每条记忆的效用分:对未来的交互有多大帮助?效用分最低的 20% 定期清理。实验数据:效用评估 + 检索历史结合的删除策略,比不删除的 baseline 性能提升 10%。(来源:Xiong et al., 2025)
5. 冲突解决(Conflict Resolution)
新记忆和旧记忆矛盾时,主动处理。Mem0 在 Update 阶段把新提取的记忆和已有的相似记忆做比对,通过 Tool Call 决定是更新、合并还是删除旧记忆。比如用户说"我换工作了,现在在字节",系统应该更新"用户在阿里"这条记忆,而不是同时保留两条矛盾信息。
分层清理建议
| 记忆类型 | 清理策略 |
|---------|---------|
| 语义记忆(事实) | 冲突解决来更新,不按时间删 |
| 情景记忆(对话经历) | 摘要压缩 + 时间衰减 |
| 程序记忆(技能) | 基本不删,只做版本更新 |
工程建议:
- 每个用户/session 的记忆条数设上限(比如 1000 条),超出时触发清理流程
- 不要在用户交互时同步做记忆清理,放到后台定时任务,每天低峰期跑一次全量清理
- 涉及用户数据的记忆必须支持 GDPR"被遗忘权"——用户要求删除时,必须能从向量库、图数据库、摘要中完全清除。Mem0 的 SOC 2 合规和加密删除协议是目前行业标杆
五、生产级记忆系统架构设计
"用 Mem0"不是架构设计,是工具选型。生产级记忆系统需要回答四个问题:记什么、怎么存、怎么取、怎么清。
Step 1:定义记忆策略(记什么)
不是所有对话内容都值得记忆:
记忆提取策略:
├─ 用户画像信息 → 语义记忆(姓名、职业、偏好、技术栈)
├─ 关键交互结果 → 情景记忆(成功/失败的方案、用户反馈)
├─ 重复出现的任务模式 → 程序记忆(工作流模板)
└─ 闲聊/寒暄/确认性回复 → 不存储
提取方式:
- Hot Path(同步提取):对话中实时提取,延迟 100-300ms,但立即可用
- Background(异步提取):对话后后台处理,延迟为零,但需要等处理完成
推荐组合:关键画像信息用 hot path(用户说"我是后端工程师"时立刻存),大量对话的模式总结用 background。
Step 2:存储架构(怎么存)
记忆存储分层:
├─ L1 热数据:Redis / In-Memory
│ └─ 当前会话状态、最近 10 轮对话
│ └─ 读延迟 < 5ms
├─ L2 温数据:PostgreSQL + pgvector
│ └─ 用户画像、近 30 天记忆
│ └─ 读延迟 < 50ms
└─ L3 冷数据:对象存储(S3)+ 向量索引
└─ 历史归档、压缩摘要
└─ 读延迟 < 500ms
百万用户级别选型:
- 向量检索:pgvector 百万级够用(HNSW 索引,recall > 95%),千万级以上考虑 Milvus 或 Qdrant
- 关系/图数据:用户间关系和实体关联用 Neo4j 或 PostgreSQL 的 graph 扩展。Mem0 的图记忆变体(Mem0^g)在多跳推理任务上比纯向量检索高约 2%
- 会话状态:LangGraph 的 PostgresSaver 做 checkpoint,配合 Redis 做缓存层
Step 3:检索策略(怎么取)
def retrieve_memories(query, user_id, k=10):
# 1. 先从 L1 热数据取当前会话上下文
session_context = redis.get(f"session:{user_id}")
# 2. 三维加权检索
candidates = vector_store.search(
query=query,
filter={"user_id": user_id},
top_k=k * 3 # 先取 3 倍候选
)
# 3. Rerank:recency + relevance + importance
scored = []
for mem in candidates:
score = (
0.3 * recency_score(mem.last_accessed) +
0.4 * mem.similarity +
0.3 * mem.importance / 10.0
)
scored.append((mem, score))
# 4. 取 top-k
return sorted(scored, key=lambda x: -x[1])[:k]
权重配比建议:
| Agent 类型 | Recency | Relevance | Importance |
|-----------|---------|-----------|------------|
| 对话型 | 0.4 | 0.3 | 0.3 |
| 知识型 | 0.2 | 0.5 | 0.3 |
Step 4:清理与运维(怎么清)
清理流水线(每日低峰期执行):
├─ Step 1:冲突检测 → 矛盾记忆合并/更新
├─ Step 2:效用评估 → 低效用记忆标记
├─ Step 3:压缩 → 30 天以上情景记忆摘要化
├─ Step 4:归档 → L2 → L3 迁移
└─ Step 5:删除 → 效用分 < 0.1 且 90 天未访问的记忆
六、工具选型
| 场景 | 推荐 | 理由 |
|------|------|------|
| 快速集成、已有 Agent 框架 | Mem0 | 框架无关,API 简单,SOC 2 合规 |
| 从零构建有状态 Agent | Letta | 自编辑记忆 + 完整 Agent Runtime,ADE 可视化调试 |
| 已在用 LangGraph | LangMem + Store | 原生集成,免额外依赖 |
| 需要图关系记忆 | Mem0^g 或 Cognee | 多跳推理、实体关系场景 |
| 重度定制需求 | 自研 | pgvector + Redis 自己搭,灵活但投入大 |
Benchmark 参考(LOCOMO 数据集,GPT-4o-mini):Letta 74.0%,Mem0 图记忆变体 68.5%,OpenAI 内置 memory 约 54%。单一 benchmark 结果,实际效果受业务场景影响很大。
七、常见踩坑
坑 1:把向量库当成完整记忆系统。 向量库解决检索,不解决记忆管理。冲突检测、压缩、清理、访问控制都需要额外建设。
坑 2:记忆提取过度。 每句话都提取记忆,记忆库很快膨胀到不可用。设置明确的提取规则,只记有长期价值的信息。Mem0 的经验:过度提取导致检索精度下降 15-20%。
坑 3:不做多租户隔离。 百万用户的记忆混在一起,检索时没有 user_id 过滤。轻则检索慢,重则信息泄露。用 namespace 做严格隔离。
参考资料
- Generative Agents: Interactive Simulacra of Human Behavior(Park et al., 2023)—— 三维检索框架
- MemGPT: Towards LLMs as Operating Systems(Packer et al., 2023)—— OS 级记忆管理
- Letta 官方文档 —— Sleep-Time Agent、Memory Blocks
- Mem0 论文与官方文档 —— 图记忆、SOC 2 合规
- LangChain LangMem 概念文档 —— 记忆固化机制
- Xiong et al., 2025 —— 记忆遗忘策略与效用评估
- Frontiers 2025、alphaXiv Learn to Memorize —— MoE 自适应权重