AI Agent 为什么需要记忆?
欢迎来到啾啾的博客🐱。
记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。
目录
- 引言
- 1 记忆的存储架构:信息存放在哪里?
- 2 记忆的管理策略:如何读写和筛选信息?
- 3 实践与选型:如何选择和组合记忆系统?
- 4 部分关键技术实现深度剖析
- 4.1 摘要压缩 (Summarization / Compression)
- 4.2 分层记忆 (Tiered Memory / MemGPT)
- 4.3 知识图谱 (Knowledge Graph)
引言
AI Agent 为什么需要记忆?
核心瓶颈在于大语言模型(LLM)的 “固定上下文窗口” (Fixed Context Window) 是有限的,无法在一次处理中容纳长期的、跨会-话的全部信息。且应用需要控制成本、响应速度。
为了让 Agent 能够执行复杂、长期的任务,就必须构建一个超越这个有限窗口的记忆系统。
基于此,我们可以构建一个清晰的认知框架,从 “存哪里”(存储架构) 和 “怎么用”(管理策略) 两个基本维度来解构 Agent 的记忆系统。
记忆机制的本质是信息完整性与计算效率的动态平衡。
资料参考:
https://mp.weixin.qq.com/s/29SXiWyRgIZNGgpY3E0jdw
https://arxiv.org/abs/2506.21605
1 记忆的存储架构:信息存放在哪里?
这是记忆系统的物理基础,决定了信息的组织形式和访问效率。
存储架构 | 核心思想 | 代表系统 | 优点 | 缺点 |
---|---|---|---|---|
1. 全量式内存 | 无差别存储:将所有历史信息不加区分地放入一个线性序列中,作为 LLM 的输入。 | Full-Context | 信息无损,短期推理能力强。 | 成本高、延迟大(如26k token延迟17秒),信息冗余度高达90%。 |
2. 分层式虚拟内存 | 模仿操作系统:将记忆分为快速但昂贵的“主上下文”(RAM)和慢速但廉价的“外部存储”(硬盘)。 | MemGPT | 容量近乎无限,成本显著降低。 | 存在数据换入/换出的延迟,管理机制复杂。 |
3. 结构化知识库 | 格式化存储:将信息转化为结构化数据,如数据库的表或知识图谱的三元组。 | CHATDB, Knowledge Graph | 支持精准查询和复杂关系推理,存储效率高。 | 对非结构化信息处理不佳,需要预先定义数据模式(Schema)。 |
2 记忆的管理策略:如何读写和筛选信息?
这是记忆系统的大脑,决定了在有限的“工作记忆”中,哪些信息应该被优先调入、更新或遗忘。
管理策略 | 核心思想 | 代表系统/技术 | 优点 | 适用场景 |
---|---|---|---|---|
1. 窗口式管理 | “近因效应”:只保留最近的 N 轮对话历史。 | Sliding Window | 实现简单,延迟极低。 | 客服机器人等短期、上下文依赖轻的任务。 |
2. 检索式管理 | “按需调取”:将记忆向量化,根据当前查询的语义相似度,动态检索最相关的信息注入上下文。 | Retrieval Memory | 精准高效,能从海量信息中召回关键记忆。 | 依赖 Embedding 模型的质量,对时序性问题处理较弱。 |
3. 压缩式管理 | “总结归纳”:当信息过载时,调用 LLM 对早期或冗长的记忆进行总结,用凝练的摘要替换原文。 | GenerativeAgent | 极大节省空间,保留核心语义。 | 压缩过程可能丢失细节,并产生额外计算开销。 |
4. 拟人化管理 | “模拟遗忘”:模仿人脑的记忆曲线(如艾宾浩斯遗忘曲线),对记忆赋予权重,随时间推移降低不重要记忆的权重。 | MemoryBank | 模拟人类认知,能更好地管理情感、偏好等长期个性化信息。 | 机制复杂,权重计算需要精细调整。 |
注:最佳实践常采用架构+策略组合方案(如MemGPT+摘要压缩)
3 实践与选型:如何选择和组合记忆系统?
在实际应用中,单一的架构或策略往往不够,最佳实践是采用 “架构 + 策略” 的组合方案。
1. 性能与成本考量
机制组合示例 | 记忆容量 | 延迟 | 推理能力 | 成本 |
---|---|---|---|---|
Full-Context | 有限 | 极高 | ★★★★☆ | 高 |
MemGPT (分层+检索) | 无限 | 中 | ★★★☆☆ | 低 |
MemoryBank (全量+拟人) | 大 | 高 | ★★★★☆ | 中 |
知识图谱 (结构化+检索) | 中 | 低 | ★★★★★ | 中 |
![[突破上下文限制:AI记忆机制全景解析与技术选型指南-2.png]]
2. 黄金组合方案(决策树的应用)
根据任务需求,可以选择不同的黄金组合:
-
目标:实时、低延迟对话
- 方案:MemGPT 架构 + 滑动窗口策略
- 逻辑:MemGPT 提供无限记忆容量的底座,滑动窗口则保证了最近对话的极低延迟响应。
-
目标:深度情感交互与个性化(如心理治疗机器人)
- 方案:MemoryBank 拟人策略 + 摘要压缩策略
- 逻辑:MemoryBank 模拟情感记忆的淡忘与强化,摘要压缩则定期提炼冗长的对话,形成关键的“病人画像”。
-
目标:企业知识库问答
- 方案:知识图谱/向量数据库架构 + 语义检索策略
- 逻辑:知识图谱负责精准的关系推理(如组织架构查询),向量检索则负责从海量文档中召回语义相关的段落。
4 部分关键技术实现深度剖析
4.1 摘要压缩 (Summarization / Compression)
“信息有密度差异,并非所有细节都同等重要。” 随着时间推移,大量对话的细节会变得冗余,但其核心主旨(如“用户确诊了糖尿病”)却至关重要。为了在有限的上下文窗口中容纳更长时间跨度的信息,必须“去粗取精”。
- 工作原理拆解:
- 触发 (Trigger): 系统监控到上下文窗口即将被填满。
- 隔离 (Isolate): 锁定一部分需要被压缩的记忆,通常是最早期的、最久远的对话记录。
- 委托 (Delegate): 将这部分隔离的原文,连同一个明确的指令(例如:“请将以下对话总结成一段包含关键信息的摘要”),发送给一个 LLM。
- 替换 (Replace): 用 LLM 生成的、信息密度极高的简短摘要,替换掉原来冗长、信息密度低的原文。
关键代码逻辑:
if ctx_tokens > THRESHOLD:old_events = extract_early_dialogs() summary = llm_summarize(old_events) # LLM生成摘要memory.replace(old_events, summary) # 替换为摘要
- 优势: 以极高的压缩比节省了宝贵的上下文空间。
- 劣势: 压缩是一个有损过程,可能丢失关键细节,甚至在总结过程中产生“幻觉”(即生成不准确的摘要)。同时,调用 LLM 进行总结本身会带来额外的计算成本和延迟。
4.2 分层记忆 (Tiered Memory / MemGPT)
“信息的访问频率决定了其存储位置。” 就像我们把常用工具放在手边的桌上,而把不常用的工具收进仓库的箱子里一样。AI 的记忆也应该根据其重要性和即时性,存放在不同成本和速度的“位置”。
- 工作原理拆解:
- 定义层级:
- 主上下文 (RAM): 这是 LLM 的“工作台”,容量小(如 4k-8k tokens),访问快,成本高。所有即时思考和推理都在这里发生。
- 外部存储 (Disk): 这是“仓库”,通常是向量数据库,容量近乎无限,访问慢,成本低。
- 自治管理 (The Controller): MemGPT 的核心创新在于,LLM 本身被赋予了“内存管理员”的职能。它能自主判断:
- 换出 (Page Out): “主上下文快满了,这段对话看起来暂时不重要了,我先总结一下,然后把它存到外部存储里去。”
- 换入 (Page In): “用户现在问到了他三个月前提到的过敏原,我需要去外部存储里搜索‘过敏原’相关的记忆,然后把它调入主上下文来回答问题。”
- 定义层级:
详细流程示例:
# ---------------------------------------------------
# 分层记忆 (MemGPT) 关键代码逻辑
# ---------------------------------------------------
import json# --- 模拟外部依赖 ---
def llm_summarize(text: str) -> str:"""(占位符) 模拟调用LLM来生成摘要"""return f"这是一段关于'{text[:20]}...'的摘要。"def semantic_search_in_disk(query: str, disk: dict) -> list:"""(占位符) 模拟在外部存储中进行语义搜索"""results = []for key, content in disk.items():if query in content or query in key:results.append(content)return results# --- MemGPT 核心架构模拟 ---# 1. 定义记忆层级
MAIN_CONTEXT_LIMIT = 100 # RAM容量上限(以字符数简化模拟)
main_context = [] # RAM: 快速工作区,存储最近的对话
external_storage = {} # Disk: 长期向量数据库,存储归档的记忆def process_new_interaction(user_query: str, response: str):"""处理一次新的用户与AI的交互"""interaction = {"user": user_query, "ai": response}main_context.append(interaction)print(f"\n[新交互] 添加到主上下文 (RAM)")def get_current_context_size():"""计算当前主上下文的大小"""return len(json.dumps(main_context))def autonomous_memory_management(user_query: str):"""核心功能1:自治内存管理循环这是MemGPT的“大脑”,决定何时进行换入换出。"""# --- 换出 (Page Out) 逻辑 ---# 检查RAM是否已满if get_current_context_size() > MAIN_CONTEXT_LIMIT:print(f"[警告] 主上下文 (RAM) 超出限制 ({get_current_context_size()}/{MAIN_CONTEXT_LIMIT}),触发换出 (Page Out)。")# 1. 识别最旧的记忆stale_memories = main_context[:len(main_context)//2] # 简单策略:移出前一半# 2. 压缩记忆summary = llm_summarize(json.dumps(stale_memories))# 3. 存入外部存储 (Disk)summary_id = f"summary_{len(external_storage)}"external_storage[summary_id] = summary# 4. 从主上下文 (RAM) 中移除旧记忆del main_context[:len(main_context)//2]print(f"[换出成功] '{summary_id}' 已存入外部存储 (Disk)。RAM 已清理。")# --- 换入 (Page In) 逻辑 ---# 检查查询是否需要历史知识 (简化为关键词检查)if "记住" in user_query or "之前" in user_query or "过敏原" in user_query:print(f"[信息需求] 查询 '{user_query}' 可能需要历史知识,触发换入 (Page In)。")# 1. 在外部存储中搜索相关记忆relevant_memories = semantic_search_in_disk(user_query, external_storage)# 2. 将相关记忆加载回主上下文 (RAM)if relevant_memories:for mem in relevant_memories:main_context.insert(0, {"retrieved_memory": mem}) # 插入到最前面print(f"[换入成功] {len(relevant_memories)} 条相关记忆已加载回主上下文 (RAM)。")# --- 模拟运行 ---
print("--- Agent 开始运行 ---")
# 交互1
process_new_interaction("你好,我叫Alex。", "你好Alex,很高兴认识你。")
autonomous_memory_management("你好,我叫Alex。")# 交互2 (导致RAM溢出)
long_text = "我最近在研究AI Agent的记忆机制,特别是关于分层记忆和知识图谱的部分,我觉得非常有趣。"
process_new_interaction(long_text, "这确实是一个前沿领域,我们可以深入探讨。")
autonomous_memory_management(long_text)# 交互3 (需要历史知识)
process_new_interaction("请记住我的过敏原是花生。", "好的,我已经记住了,你的过敏原是花生。")
autonomous_memory_management("请记住我的过敏原是花生。") # 这次交互本身也会被压缩# 交互4 (触发换入)
process_new_interaction("我之前提到的过敏原是什么?", "让我想想...")
autonomous_memory_management("我之前提到的过敏原是什么?")print("\n--- Agent 状态最终检查 ---")
print("主上下文 (RAM):", json.dumps(main_context, indent=2))
print("外部存储 (Disk):", json.dumps(external_storage, indent=2))
- 优势: 理论上实现了无限的记忆容量,同时将运行成本控制在极低水平。
- 劣势: 管理过程本身消耗 LLM 的计算资源和时间,导致一定的延迟。LLM 的判断力决定了内存管理的效率,如果判断失误,可能导致关键信息未能及时调入。
4.3 知识图谱 (Knowledge Graph)
“孤立的事实价值有限,事实之间的关系构成了真正的知识。” 一段文本“爱因斯坦提出了相对论”只是一个事实。但如果能将其结构化为 (爱因斯坦) -提出-> (相对论),并与其他事实如 (相对论) -属于-> (物理学) 关联,就形成了一个可供推理的知识网络。
- 工作原理拆解:
- 结构定义: 核心是“三元组”(Subject, Predicate, Object),即 (实体, 关系, 实体)。这是构建知识的基本原子。
- 信息抽取 (Extraction): 使用 LLM 读取大量非结构化文本(如维基百科),并从中抽取出这些三元组。
- 建图 (Construction): 将抽取出的三元组存入图数据库。每个实体是一个节点(Node),每个关系是一条带方向的边(Edge)。
- 查询与推理 (Query & Inference): 用户提问时,问题被翻译成图查询语言。例如,“爱因斯坦提出了什么理论?”会被翻译成寻找从“爱因斯坦”节点出发,经过“提出”这条边,所能到达的所有节点。
详细流程示例:
# ---------------------------------------------------
# 知识图谱 (Knowledge Graph) 关键代码逻辑
# ---------------------------------------------------class SimpleKnowledgeGraph:"""一个简单的知识图谱实现,用于演示核心逻辑。使用字典来模拟图结构:- key: 实体(如 "Eddy")- value: 一个关系字典,其中 key 是关系(如 "创作"),value 是关联的实体列表"""def __init__(self):self.graph = {}def add_triple(self, subject: str, predicate: str, obj: str):"""核心功能1:添加三元组 (Subject, Predicate, Object)这就像是在图中添加一个节点和一条指向另一个节点的有向边。"""# 如果主体不存在,则在图中创建它if subject not in self.graph:self.graph[subject] = {}# 如果关系不存在,则为该主体创建该关系if predicate not in self.graph[subject]:self.graph[subject][predicate] = []# 添加关系指向的对象self.graph[subject][predicate].append(obj)print(f"知识添加成功: ({subject}) -[{predicate}]-> ({obj})")def query(self, subject: str, predicate: str) -> list:"""核心功能2:执行单步查询查询从一个实体出发,通过一个特定关系能到达哪些实体。"""if subject in self.graph and predicate in self.graph[subject]:return self.graph[subject][predicate]return []# --- 演示 ---# 1. 构建知识图谱
kg = SimpleKnowledgeGraph()
kg.add_triple("Eddy", "创作", "交响乐")
kg.add_triple("交响乐", "风格", "古典")
kg.add_triple("Eddy", "职业", "作曲家")
kg.add_triple("交响乐", "乐器", "小提琴")# 2. 进行推理查询
# 问题:“Eddy创作的作品是什么风格?”
# 这需要一个“多跳查询”(Multi-hop Query)# 第1跳:查询 Eddy 创作了什么?
works = kg.query("Eddy", "创作") # -> ["交响乐"]
print(f"\n第1跳查询 'Eddy' 的 '创作' -> {works}")# 第2跳:遍历第1跳的结果,查询这些作品的风格
if works:for work in works:styles = kg.query(work, "风格") # -> ["古典"]if styles:print(f"第2跳查询 '{work}' 的 '风格' -> {styles}")print(f"\n推理结果:Eddy 创作的作品风格是 '{styles[0]}'")
- 优势: 极高的查询精度和强大的逻辑推理能力,信息存储非常高效。
- 劣势: 结构僵化,难以存储模糊、主观或程序性的知识(如“如何烤蛋糕”)。知识抽取的准确性是其构建的瓶颈。