AI Agent 记忆系统
背景
记忆系统可以使 AI Agent 像人类一样,在单次对话中保持上下文连贯性(短期记忆),同时能够跨会话记住用户偏好、历史交互和领域知识(长期记忆)。 记忆使它们能够记住之前的互动、从反馈中学习,并适应用户的偏好。
记忆的定义
习惯上,可以将会话级别的历史消息称为短期记忆,把可以跨会话共享的信息称为长期记忆。
- 会话级记忆:用户和智能体 Agent 在一个会话中的多轮交互(user-query & response)
- 跨会话记忆:从用户和智能体 Agent 的多个会话中抽取的通用信息,可以跨会话辅助 Agent 推理
本质上两者并不是通过简单的时间维度进行的划分,从实践层面上以是否跨 Session 会话来进行区分。 长期记忆的信息从短期记忆中抽取提炼而来,根据短期记忆中的信息实时地更新迭代,而其信息又会参与到短期记忆中辅助模型进行个性化推理。
Agent 框架集成记忆的通用模式
- 推理前加载- 根据当前 user-query 从长期记忆中加载相关信息
- 上下文注入- 从长期记忆中检索的信息加入当前短期记忆中辅助模型推理
- 记忆更新- 短期记忆在推理完成后加入到长期记忆中
- 信息处理- 长期记忆模块中结合 LLM+向量化模型进行信息提取和检索
flowchart LR
A[用户输入] --> B[短期记忆]
B --> C[LLM + 工具调用]
C --> D[短期记忆]
D --> E[用户回复]
D --> F[存入长期记忆]
G[长期记忆] -->|检索| B
F --> G
短期记忆
短期记忆存储会话中产生的各类消息,包括用户输入、模型回复、工具调用及其结果等。 这些消息直接参与模型推理,实时更新,并受模型的 maxToken 限制。 当消息累积导致上下文窗口超出限制时,需要通过上下文工程策略(压缩、卸载、摘要等)进行处理,这也是上下文工程主要处理的部分。
短期记忆的核心特点:
- 存储会话中的所有交互消息(用户输入、模型回复、工具调用等)
- 直接参与模型推理,作为 LLM 的输入上下文
- 实时更新,每次交互都会新增消息
- 受模型 maxToken 限制,需要上下文工程策略进行优化
短期记忆的上下文工程策略
短期记忆直接参与 Agent 和 LLM 的交互,随着对话历史增长,上下文窗口会面临 token 限制和成本压力。 上下文工程策略旨在通过智能化的压缩、卸载和摘要技术,在保持信息完整性的同时,有效控制上下文大小。
需要说明的是,各方对上下文工程的概念和理解存在些许差异:
- 狭义的上下文工程特指对短期记忆(会话历史)中各种压缩、摘要、卸载等处理机制,主要解决上下文窗口限制和 token 成本问题;
- 广义的上下文工程则包括更广泛的上下文优化策略,如非运行态的模型选择、Prompt 优化工程、知识库构建、工具集构建等。
这些都是在模型推理前对上下文进行优化的手段,且这些因素都对模型推理结果有重要影响。
针对短期记忆的上下文处理,主要有以下几种策略:
- 上下文缩减(Context Reduction) :上下文缩减通过减少上下文中的信息量来降低 token 消耗,主要有两种方法:
- 保留预览内容:对于大块内容,只保留前 N 个字符或关键片段作为预览,原始完整内容被移除
- 总结摘要:使用 LLM 对整段内容进行总结摘要,保留关键信息,丢弃细节
- 上下文卸载(Context Unloading) :主要解决被缩减的内容是否可恢复的问题。当内容被缩减后,原始完整内容被卸载到外部存储(如文件系统、数据库等),消息中只保留最小必要的引用(如文件路径、UUID 等)。当需要完整内容时,可以通过引用重新加载。优势在于上下文更干净,占用更小,信息不丢,随取随用。适用于网页搜索结果、超长工具输出、临时计划等占 token 较多的内容。
- 上下文隔离(Context Isolation):通过多智能体架构,将上下文拆分到不同的子智能体中(类似单体拆分称多个微服务)。主智能体编写任务指令,发送给子智能体,子智能体的整个上下文仅由该指令组成。子智能体完成任务后返回结果,主智能体不关心子智能体如何执行,只需要结果。优势在于上下文小、开销低、简单直接。适用于任务有清晰简短的指令,只有最终输出才重要,如代码库中搜索特定片段。
策略选择原则
- 时间远近:近期消息通常更重要,需要优先保留;历史消息可以优先进行缩减或卸载;
- 数据类型:不同类型的消息(用户输入、模型回复、工具调用结果等)重要性不同,需要采用不同的处理策略;
- 信息可恢复性:对于需要完整信息的内容,应优先使用卸载策略;对于可以接受信息丢失的内容,可以使用缩减策略
长期记忆
长期记忆与短期记忆形成双向交互:一方面,长期记忆从短期记忆中提取“事实”、“偏好”、“经验”等有效信息进行存储(Record); 另一方面,长期记忆中的信息会被检索并注入到短期记忆中,辅助模型进行个性化推理(Retrieve)。
要实现这两个核心流程,需要以下组件:
- LLM 大模型:提取短期记忆中的有效信息(记忆的语义理解、抽取、决策和生成);
- Embedder 向量化:将文本转换为语义向量,支持相似性计算;
- VectorStore 向量数据库:持久化存储记忆向量和元数据,支持高效语义检索;
- GraphStore 图数据库:存储实体-关系知识图谱,支持复杂关系推理;
- Reranker(重排序器):对初步检索结果按语义相关性重新排序;
- SQLite:记录所有记忆操作的审计日志,支持版本回溯;
Record
LLM 事实提取 → 信息向量化 → 向量存储 →(复杂关系存储)→ SQLite 操作日志
Retrieve
User query 向量化 → 向量数据库语义检索 → 图数据库关系补充 →(Reranker-LLM)→ 结果返回
与短期记忆的交互
- Record(写入):从短期记忆的会话消息中提取有效信息,通过LLM进行语义理解和抽取,存储到长期记忆中
- Retrieve(检索):根据当前用户查询,从长期记忆中检索相关信息,注入到短期记忆中作为上下文,辅助模型推理
实践中的实现方式
在 Agent 开发实践中,长期记忆通常是一个独立的第三方组件,因为其内部有相对比较复杂的流程(信息提取、向量化、存储、检索等)。 常见的长期记忆组件包括 Mem0、Zep、Memos、ReMe 等,这些组件提供了完整的 Record 和 Retrieve 能力,Agent 框架通过 API 集成这些组件。
信息组织维度
用户维度(个人记忆):面向用户维度组织的实时更新的个人知识库
- 用户画像分析报告
- 个性化推荐系统,千人千面
- 处理具体任务时加载至短期记忆中
业务领域维度:沉淀的经验(包括领域经验和工具使用经验)
- 可沉淀至领域知识库
- 可通过强化学习微调沉淀至模型
长期记忆与 RAG 的区别
| 对比条目 | RAG | 长期记忆 |
|---|---|---|
| 主要目的 | 为大模型提供外部知识,弥补训练数据的局限性(如时效性、专业性) | 为 AI Agent 记录并利用特定用户的历史交互信息,实现个性化、上下文连续的服务 |
| 服务对象 | 全体用户或任务 | 特定用户或会话主体(高度个性化) |
| 知识来源 | 结构化/非结构化文档(如 PDF、网页、数据库) | 用户与 Agent 的对话历史、行为日志等 |
技术层面的相似点:
- 向量化存储:都将文本内容通过 Embedding 模型转为向量,存入向量数据库;
- 相似性检索:在用户提问时,将当前 query 向量化,在向量库中检索 top-k 最相关的条目;
- 注入上下文生成:将检索到的内容注入到模型交互上下文中,辅助 LLM 生成最终回答;
长期记忆的关键问题
一、准确性
记忆的准确性包含两个层面:
- 有效的记忆管理:需要具备智能的巩固、更新和遗忘机制,这主要依赖于记忆系统中负责信息提取的模型能力和算法设计;
- 记忆相关性的检索准确度:主要依赖于向量化检索&重排的核心能力;
核心挑战:
- 记忆的建模:需要完善强大的用户画像模型;
- 记忆的管理:基于用户画像建模算法,提取有效信息,设计记忆更新机制;
- 向量化相关性检索能力:提升检索准确率和相关性;
二、安全和隐私
记忆系统记住了大量用户隐私信息,如何防止数据中毒等恶意攻击,并保障用户隐私,是必须解决的问题。
核心挑战:
- 数据加密与访问控制
- 防止恶意数据注入
- 透明的数据管理机制
- 用户对自身数据的掌控权
三、多模态记忆
支持文本记忆、视觉、语音仍被孤立处理,如何构建统一的“多模态记忆空间”仍是未解难题。
核心挑战:
- 跨模态关联
- 与检索统一的多模态记忆表示
- 毫秒级响应能力
长期记忆开源产品对比
| 产品 | 开源时间 | 功能(记忆类型) | LLM的作用 | 向量数据库 | 图数据库 |
|---|---|---|---|---|---|
| Mem0 | 2023年7月 | ✅ 用户画像 + 领域记忆 | 记忆创建/更新/检索 | ✅ pgvector, Qdrant等多种 | ✅ Neo4j, Memgraph |
| Zep | 2023年初 | ✅ 用户画像 + 领域记忆 | 记忆提取 + 实体识别 | ✅ 支持 | ✅ 时序知识图谱架构 |
| MemOS | 2025年7月 | ✅ 用户画像 + 领域记忆 | 记忆提取 + 推理 | ⚠️ 可选支持,非必需 | ✅ NebulaGraph, Neo4j |
| Memobase | 2025年1月 | ✅ 用户画像记忆 ❌ 领域记忆 | 画像理解 + 更新 | ❌ 不依赖向量数据库 | ❌ 不依赖图数据库 |
| AgentScope ReMe | 2025年6月 | ✅ 用户画像 + 领域记忆 | 记忆管理 + 优化 | ✅ Chroma, Qdrant等多种 | ❌ 未明确提及 |
mem0(“mem-zero”)
自托管
1、安装 Mem0
pip install mem0ai openai
2、一个小的 demo
from openai import OpenAI
from mem0 import Memory
openai_client = OpenAI()
memory = Memory()
def chat_with_memories(message: str, user_id: str = "default_user") -> str:
# 检索相关记忆
relevant_memories = memory.search(query=message, user_id=user_id, limit=3)
memories_str = "\n".join(f"- {entry['memory']}" for entry in relevant_memories["results"])
# 生成助手回复
system_prompt = f"你是我的私人助理,请根据我的记忆回答我的问题。\n我的记忆:\n{memories_str}"
messages = [{"role": "system", "content": system_prompt}, {"role": "user", "content": message}]
response = openai_client.chat.completions.create(model="gpt-4o-mini", messages=messages)
assistant_response = response.choices[0].message.content
# 从对话中创建新记忆
messages.append({"role": "assistant", "content": assistant_response})
memory.add(messages, user_id=user_id)
return assistant_response
def main():
while True:
user_input = input("用户:").strip()
if user_input.lower() == 'exit':
print("再见!")
break
print(f"系统:{chat_with_memories(user_input, "zhangsan")}")
if __name__ == "__main__":
main()
特点:
用户不再关注和拼接历史会话,每次用户请求进来后,先检索记忆,然后将记忆带到系统 Prompt 中回答用户问题,最后再将这次对话保存到记忆,以此循环往复。
注意:
上面这个记忆保存在内存里,只是临时的,重启程序后记忆就没有了。
平台托管
进入 Mem0 平台,注册后,根据提示步骤获取 API KEY。
修改 demo 代码:
from mem0 import MemoryClient
memory = MemoryClient(api_key=os.getenv("MEM0_API_KEY"))
这样就可以把记忆保存在 Mem0 服务中,就算程序重启,记忆也不会丢失。
可以在 Mem0 平台的 “Memories” 页面查看保存的记忆。
记忆存储原理
- 提取阶段(Extraction)
- 更新阶段(Update)
提取阶段
关注信息:
- 最新的一轮对话,通常由用户消息和助手响应组成;
- 滚动摘要,从向量数据库检索得到,代表整个历史对话的语义内容;
- 最近的 m 条消息,提供了细粒度的时间上下文,可能包含摘要中未整合的相关细节;
然后通过 LLM 从这些信息中提取出一组简明扼要的候选记忆,并在后台异步地刷新对话摘要,刷新过程不阻塞主流程。
更新阶段
针对新消息从向量数据库中检索出最相似的前 s 个条目进行比较,然后通过 LLM 的工具调用能力,选择四种操作之一:
- ADD - 在没有语义等效记忆存在时创建新记忆;
- UPDATE - 用补充信息增强现有记忆;
- DELETE - 删除和新信息所矛盾的记忆;
- NOOP - 当候选事实不需要对知识库进行修改时;
使得记忆存储保持一致、无冗余,并能立即准备好应对下一个查询。
记忆储存数据库
Mem0 的默认存储是 Qdrant 向量数据库,但是使用了本地文件,可以在 /tmp/qdrant 中找到,每次程序启动时都会删掉重建。
通过修改 Memory.from_config() 自定义记忆配置,可以指定数据库位置并可以开启持久化存储:
config = {
"vector_store": {
"provider": "qdrant",
"config": {
"path": "/tmp/qdrant_data",
"on_disk": True
}
},
}
memory = Memory.from_config(config)
也可以本地部署一个 Qdrant 数据库,并自定义配置:
config = {
"vector_store": {
"provider": "qdrant",
"config": {
"collection_name": "test",
"host": "localhost",
"port": 6333,
}
},
}
memory = Memory.from_config(config)
再进一步,我们可以配置自己的向量存储,格式如下:
config = {
"vector_store": {
"provider": "your_chosen_provider",
"config": {
# Provider-specific settings go here
}
}
}
备注:有关向量存储的自定义配置可以参考 https://docs.mem0.ai/components/vectordbs/config
其他配置
除了向量存储配置还可以配置包括:语言模型、嵌入模型、图存储 以及一些通用配置,如下:
config = {
"vector_store": {
"provider": "qdrant",
"config": {
"host": "localhost",
"port": 6333
}
},
"llm": {
"provider": "openai",
"config": {
"api_key": "your-api-key",
"model": "gpt-4"
}
},
"embedder": {
"provider": "openai",
"config": {
"api_key": "your-api-key",
"model": "text-embedding-3-small"
}
},
"graph_store": {
"provider": "neo4j",
"config": {
"url": "neo4j+s://your-instance",
"username": "neo4j",
"password": "password"
}
},
"history_db_path": "/path/to/history.db",
"version": "v1.1",
"custom_fact_extraction_prompt": "Optional custom prompt for fact extraction for memory",
"custom_update_memory_prompt": "Optional custom prompt for update memory"
}
- history_db_path:Mem0 每次生成新记忆时,不仅会保存在向量数据库里,而且还在本地数据库中留有一份副本。这个本地数据库使用的是 SQLite。
- version:目前支持 v1.0 和 v1.1 两个版本,默认使用的是最新的 v1.1 版本。
- custom_fact_extraction_prompt:事实提取的 Prompt。
- custom_update_memory_prompt:事实更新的 Prompt,默认使用 Mem0 提供的 Prompt。
还有一些系统提示词是系统定义好的:
- DEFAULT_UPDATE_MEMORY_PROMPT
- USER_MEMORY_EXTRACTION_PROMPT
- AGENT_MEMORY_EXTRACTION_PROMPT
DEFAULT_UPDATE_MEMORY_PROMPT
在 Prompt 中找到 DEFAULT_UPDATE_MEMORY_PROMPT 简单翻译一下:
DEFAULT_UPDATE_MEMORY_PROMPT = """你是一个智能记忆管理器,负责控制系统的记忆。
你可以执行四种操作:(1) 添加到记忆中,(2) 更新记忆,(3) 从记忆中删除,以及 (4) 不做改变。
基于上述四种操作,记忆将会发生变化。
将新提取到的事实与现有记忆进行比较。对于每个新事实,决定是否:
- 添加(ADD):将其作为新元素添加到记忆中
- 更新(UPDATE):更新现有的记忆元素
- 删除(DELETE):删除现有的记忆元素
- 无变化(NONE):不做任何改变(如果该事实已存在或不相关)
以下是选择执行哪种操作的具体指南:
1. **添加**:如果提取到的事实包含记忆中不存在的新信息,那么你必须通过在id字段中生成一个新ID来添加它。
2. **更新**:如果提取到的事实包含已经存在于记忆中但信息完全不同的内容,那么你必须更新它。
如果提取到的事实包含与记忆中现有元素表达相同内容的信息,那么你必须保留信息量最大的事实。
示例(a) -- 如果记忆中包含"用户喜欢打板球",而提取到的事实是"喜欢和朋友一起打板球",那么用提取到的事实更新记忆。
示例(b) -- 如果记忆中包含"喜欢奶酪披萨",而提取到的事实是"爱奶酪披萨",那么你不需要更新它,因为它们表达的是相同的信息。
如果指示是更新记忆,那么你必须更新它。
请记住在更新时保持相同的ID。
请注意只从输入ID中返回输出中的ID,不要生成任何新ID。
3. **删除**:如果提取到的事实包含与记忆中现有信息相矛盾的信息,那么你必须删除它。或者如果指示是删除记忆,那么你必须删除它。
请注意只从输入ID中返回输出中的ID,不要生成任何新ID。
4. **无变化**:如果提取到的事实包含已经存在于记忆中的信息,那么你不需要做任何改变。
"""
这段 Prompt 为每一种操作提供了对应示例,根据大模型返回的 event 类型,对旧记忆分别执行对应的操作:
- 新增记忆 _create_memory:在向量数据库新增一条记忆,同时在历史库新增一条 ADD 记录;
- 更新记忆 _update_memory:更新向量数据库中的已有记忆,同时在历史库新增一条 UPDATE 记录;
- 删除记忆 _delete_memory:删除向量数据库中的已有记忆,同时在历史库新增一条 DELETE 记录;
记忆类型
在 mem0/mem0/configs/enums.py 路径下定义了三种记忆类型:
| 维度 | SEMANTIC (语义) | EPISODIC (情景) | PROCEDURAL (程序) |
|---|---|---|---|
| 核心问题 | 是什么? (What) | 何时/何地? (When/Where) | 怎么做? (How) |
| 内容性质 | 事实、知识、偏好、定义 | 事件、经历、对话片段 | 技能、流程、规则、习惯 |
| 时效性 | 长期稳定,不易随时间改变 | 短期或中期,随新事件更新 | 长期稳定,除非重新训练/更新规则 |
| 依赖上下文 | 低(独立于具体时间) | 高(强依赖时间/地点) | 中(依赖任务触发) |
| AI 检索示例 | “用户喜欢红色” | “用户昨天买了红色的车” | “购车流程是先选颜色再付款” |
| 数据库策略 | 需去重、合并相似事实 | 按时间排序、保留原始细节 | 结构化存储、作为 System Prompt |
当用户向 Mem0 添加一条记忆时(memory.add(…)),框架内部会自动分析这条文本属于哪种类型,并打上标签:
- 输入:“我每天早上 8 点跑步。”
- LLM 判断:这既是一个事实(语义:用户有晨跑习惯),也是一个重复发生的事件(情景),甚至隐含了一种生活规则(程序:保持健康的生活方式)。
- Mem0 处理:可能会将其主要归类为 SEMANTIC(作为用户画像),或者如果强调了“今天早上”,则归为 EPISODIC。
- 输入:“每次处理退款请求时,必须先检查订单状态。”
- LLM 判断:这是一个操作规则。
- Mem0 处理:归类为 PROCEDURAL。当 Agent 下次遇到退款请求时,会优先检索此类记忆来指导行动。