首页 › AI Agent › AI Agent 开发实战 › 第七节 Agent 内存管理进阶:如何实现长期记忆

第七节 Agent 内存管理进阶:如何实现长期记忆

​

飞书文档 - 图片

​

为什么 AI Agent 需要内存?​

默认情况下,大语言模型 (LLM) 是无状态的。它们根本不记得上一次 API 调用中发生了什么。​

​

🏖️

为了制造对话连续性的错觉,开发者会将聊天的历史记录(短期记忆)拼接在 Prompt 中。然而,随着上下文窗口被填满,API 成本会呈指数级飙升,延迟会显著增加,而且模型会严重受到"中间迷失 (Lost in the Middle)"现象的困扰。​

​

​

如果一个 AI Agent 要在几周甚至几个月的时间跨度内自主运行,它就必须具备长期记忆 (Long-Term Memory)​

​

Agent 内存的三大类型​

借鉴人类认知心理学,AI Agent 的内存通常被划分为三个层级:​

短期记忆 / 工作记忆 (Short-Term Memory)​

•

是什么:当前会话中活跃的上下文窗口。​

•

实现方式:传递给 LLM 的一个 Message 对象数组。​

•

生命周期:当会话结束或达到上下文窗口上限时被清空(通常通过滑动窗口或滚动总结来管理)。​

情景记忆 / 经验 (Episodic Memory)​

•

是什么:过去事件和对话的按时间顺序的记录。​

•

示例:"昨天,Agent 尝试编译代码,在第 42 行遇到了语法错误,并通过导入 json 模块修复了它。"​

•

实现方式:将完整的聊天日志或执行轨迹存储在关系型数据库(如 PostgreSQL)中,或存储在向量数据库中以便对历史对话进行语义搜索。​

语义记忆 / 知识与偏好 (Semantic Memory)​

•

是什么:关于世界或用户的提炼后的、客观的事实,与其被学到的具体时间解耦。​

•

示例:"用户的名字是 Alice。用户喜欢深色模式。用户的主要编程语言是 Python。"​

•

实现方式:通过后台 LLM 提取任务更新的知识图谱 (Knowledge Graph) 或专用的向量数据库。​

如何实现长期记忆 (Long-Term Memory)​

构建一个自定义的语义记忆系统,需要一个持续在后台运行的循环,它不断"阅读"短期记忆并提取事实。​

提取阶段 (后台 LLM)​

在用户发送消息后,一个次级的 LLM 进程在后台异步运行。它的 Prompt 类似这样:​

text​

​

代码块​

Plain Text

System: 从以下对话中提取客观事实、用户偏好和持久性知识。格式化为 JSON 列表。​

User: "我正在把我的后端从 Node 迁移到 Go。另外,请始终使用 4 个空格缩进输出代码。"​

Output:​

[​

"用户正在将后端从 Node.js 迁移到 Go。",​

"用户偏好使用 4 个空格的代码缩进。"​

]​

​

存储阶段​

这些提取出的字符串随后被 text-embedding-3-small 等模型向量化,并与关联的 user_id=123 元数据一起存储到向量数据库(如 Qdrant、Pinecone)中。​

检索阶段 (下一次会话)​

当用户一周后回来问:"你能写个脚本处理我的用户认证吗?"​

系统会执行以下操作:​

将用户的查询向量化。​

在向量数据库中搜索 user_id=123 的记录。​

检索到语义记忆:"用户正在将后端从 Node.js 迁移到 Go。用户偏好 4 个空格的代码缩进。"​

将这段记忆注入到当前对话的 System Prompt 中。​

最终,Agent 极其自然地回复了一个缩进了 4 个空格的 Go 语言脚本,表现出了完美的长期记忆能力。​

高阶实现方案:Mem0 与 Zep​

手动构建"提取-存储-检索"的循环既繁琐又容易出错。像 Mem0(前身为 Embedchain)和 Zep 这样的现代框架自动处理了这一切。​

使用 Mem0 的 Python 示例​

python​

​

代码块​

Python

import os​

from mem0 import Memory​

​

os.environ["OPENAI_API_KEY"] = "your-api-key"# 1. 初始化内存客户端​

m = Memory()​

​

# 2. 存储一个事实(信息提取和向量化在底层自动发生)​

m.add("我正在学习 AI Agent 开发,我非常喜欢 Python。", user_id="alice_123")​

​

# 3. 基于新的 Prompt 检索相关的记忆​

relevant_memories = m.search("我下一个项目应该用什么语言?", user_id="alice_123")​

​

print(relevant_memories)​

# 预期输出: [{'text': '用户喜欢 Python', 'score': 0.89...}]# 4. 将记忆无缝注入到你的 LLM Prompt 中​

system_prompt = f"你是一个得力的助手。关于用户的背景信息:{relevant_memories[0]['text']}"​

​

​

🔧 立即体验:使用我们的 JSON 格式化工具 在线检查并调试 Mem0 API 返回的复杂嵌套字典结构。​

​

内存管理的最佳实践​

实现记忆衰减 (Memory Decay) — 并非所有的记忆都同等重要。建立一套机制,将过时或极少访问的情景记忆归档或折叠总结,优先保留最近的语义事实。​

处理矛盾信息 — 如果用户之前说"我爱 Python",后来又说"我现在讨厌 Python,我只用 Rust",你的后台提取 Prompt 必须明确指示模型去更新或删除旧事实,而不是盲目追加相互冲突的信息。​

将系统指令与用户记忆隔离 — 确保注入到 Prompt 中的记忆被清晰地标记(例如放在 <user_memory>...</user_memory> 标签内),这样 LLM 就不会把检索到的用户事实误认为是核心系统指令。​

绝对尊重隐私 — 永远不要将 PII(个人身份信息)存储在共享的向量索引中。必须严格按照 user_id 或 tenant_id 对内存数据进行硬隔离。​

⚠️ 常见错误:​

•

完全依赖无限长的上下文窗口 → 解决方案:即使你能够一次性传入 100 万个 Token,如果每条消息都这么做,每次查询的成本将高达几美元,且延迟会增加到 30 秒以上。请务必使用基于 RAG 的内存检索。​

•

未对事实进行去重合并 → 解决方案:利用 LLM 在夜间定期整合记忆(例如,将"Alice 喜欢猫"和"Alice 养了一只狗"合并为一条语义更密集的"Alice 是一个宠物主人")。​

常见问题 (FAQ)​

Q1: LangGraph 的 Checkpointer 算作长期记忆吗?​

LangGraph 的 MemorySaver (Checkpointer) 主要设计用于短期/线程级记忆 (Thread-level memory) 和状态持久化。它保存的是图的精确执行状态,以便你能恢复某个特定的对话线程。它本身并不是一个用于跨线程事实检索的语义搜索引擎。​

Q2: 为什么用向量数据库做内存,而不用 SQL 数据库?​

虽然 SQL 数据库非常适合按时间顺序存储情景聊天日志,但它无法执行语义搜索。如果用户问"你之前给我的那个菜谱是什么来着?",向量数据库能在数学层面上将"菜谱"与过去的具体食材匹配起来,而 SQL 则需要死板的精确关键字匹配。​

Q3: 在检索内存时,如何处理 Token 限制?​

设置严格的 top_k 限制(例如,每次只检索最相关的前 5 条事实),并设置相关性分数阈值。永远不要把整个用户画像数据库一股脑地塞进 Prompt 里。​

总结​

内存机制将 AI Agent 从一个被动的工具,升华为了一个主动的、个性化的数字伙伴。通过将短期上下文与长期的情景记忆和语义知识解耦,并利用 Mem0 或自定义向量数据库等强大工具,你可以打造出真正"懂"用户的智能体系统。​