Memory 记忆系统——让 Agent 记住用户
Memory 记忆系统——让 Agent 记住用户
这是「Agent 工程化」系列的第五篇。上一篇我们讲了上下文管理,但那只解决"一次会话内"的问题:会话一关,上下文清空,什么都留不下。这篇讲跨会话的记忆——Memory 记忆系统,让 Agent 真正"记住"用户。
失忆现场 2.0:换个会话,什么都不记得了
先看一个比第四篇更气人的场景:
昨天下午
用户:帮我订一张下周五去北京的机票
助手:好的,需要靠窗还是过道?
用户:靠窗吧
(会话结束,上下文清空)
今天上午
用户:下周五去北京的机票,帮我再看看
助手:好的,请问需要靠窗还是过道?
用户:???我昨天刚说了靠窗啊!
第四篇的"失忆"发生在一个长会话内部;这次的"失忆"发生在两个会话之间——昨天聊得再好,今天开新会话,一切归零。
这不是 bug。上下文就是一块"便签纸",会话结束就扔。要让 Agent 记住昨天的事,得把信息从便签纸抄进"笔记本"——这就是记忆系统。
Memory 的本质:三件事
记忆系统听起来高大上,拆开就三个问题:
| 问题 | 通俗说法 | 对应能力 |
|---|---|---|
| 存储 | 记在哪 | 数据库 / 向量库 |
| 写入 | 什么值得记 | 写入门槛 |
| 检索 | 怎么想起来 | 检索注入 |
一句话:对话时把值得记的存下来,下次会话开始时捞回来用。 就这么简单。
记忆分层:短期、工作、长期、语义
“记忆"不是一种,业界通常分四层——从"马上忘"到"记得最久”:
| 层级 | 生命周期 | 内容 | 存哪 | 谁负责 |
|---|---|---|---|---|
| 短期记忆 | 本次会话 | 刚说的酒店名、当前问题 | 上下文窗口 | 上下文管理(第 4 篇) |
| 工作记忆 | 当前任务 | 5 日游行程进行到哪一步 | 任务状态 | Agent 内部状态 |
| 长期记忆 | 跨会话 | 用户偏好:靠窗、怕红眼航班 | 数据库 | Memory 系统(本篇) |
| 语义记忆 | 长期沉淀 | 抽象出的经验规则 | 向量库/数据库 | Memory 系统 |
对比一下人类记忆,很好理解:
- 短期记忆:正在看的一段话——对应上下文窗口
- 工作记忆:正在做的一件事进行到哪——对应任务状态
- 长期记忆:去年去过三亚,喜欢靠窗——对应持久化存储
- 语义记忆:总结出的规律"这位用户不喜欢红眼航班"——对应沉淀的经验
注意前两层(短期、工作)跟第四篇是一回事,后两层(长期、语义)才是本篇的主角。 很多人把"记忆系统"理解成"把对话存下来",那是存对话原文,不是记忆——记忆是从对话里提炼出来的、跨会话还有用的结论。
核心机制一:什么该记(写入门槛)
最大的坑是"全都记"。如果每句话都存,记忆库很快就塞满几百条没用的内容——这比不记还糟(后面踩坑部分细讲)。
写入门槛:只有"这次对话结束后,下次还有用"的信息才值得存。判断标准很简单,看两类:
| 该记(长期有用) | 不该记(一次性/临时) |
|---|---|
| 明确偏好:靠窗、不要辣、怕红眼航班 | 临时闲聊:今天天气不错 |
| 重要事实:已订 MU5101、行程在哪儿 | 一次性问题:现在几点 |
| 重复出现的行为:每次都选经济舱 | 过期信息:这周三去出差 |
一个简单的实现示意:
import json, time
DB = {} # user_id -> list of memories
def remember(user_id: str, content: str, importance: int = 1):
"""写入门槛:只存有长期价值的信息"""
DB.setdefault(user_id, []).append({
"content": content,
"ts": time.time(),
"importance": importance, # 1-5,越高越重要
})
实际生产里,“提炼记忆"这个动作可以交给 LLM 做——每轮对话结束后,让模型判断一句"本轮有没有值得长期记住的用户偏好或事实?有就输出,没有就输出 None”。
核心机制二:怎么想起来(检索注入)
存了记忆,新会话开始时要把相关的捞回来。不能把几百条记忆全塞给模型(会淹没重点),要检索最相关的 Top-K 条。
def recall(user_id: str, query: str, top_k: int = 5) -> list:
"""简单实现:关键词匹配 + 重要度排序"""
mems = DB.get(user_id, [])
scored = []
for m in mems:
score = m["importance"]
if query and query in m["content"]:
score += 10 # 命中关键词,加权重
scored.append((score, m))
scored.sort(reverse=True)
return [m for _, m in scored[:top_k]]
def build_context(user_id: str, user_input: str) -> list:
mems = recall(user_id, user_input)
block = "关于这位用户,我记得:" + ";".join(m["content"] for m in mems)
return [{"role": "system", "content": block},
{"role": "user", "content": user_input}]
注入时机在会话开始:
检索质量决定记忆效果:上面是关键词匹配(够 Demo 用),生产里常用向量相似度(把记忆和提问都转成向量,算语义相近程度),这个后面 RAG 篇会详细讲——记忆检索和知识库检索用的是同一套技术。
生产级考虑:记忆的维护
记忆不是存进去就完了,要维护,三个生产级问题:
1. 记忆过期清理
不是所有记忆都永久有效。要有生命周期:
def cleanup(user_id: str, max_age_days: int = 90):
"""清理:过期的、用户撤销的、太久没用的"""
now = time.time()
keep = []
for m in DB.get(user_id, []):
if m["ts"] + max_age_days * 86400 < now:
continue # 过期
if m.get("revoked"):
continue # 用户明确让忘掉
keep.append(m)
DB[user_id] = keep
2. 用户有"遗忘权"
用户说"以后别记我的偏好"“删掉我昨天的记录”,必须能执行。这既是产品体验,也是合规要求。记忆系统必须支持删除,这个能力越早留好越省事。
3. 重要度动态调整
记忆的"重要度"不是写死一次就完:一条记忆被反复命中,说明它越来越重要(权重可上调);很久没被用到,权重下调直至清理。这就是"记忆会自己进化"的雏形,也是第 7 篇 Skill 系统的前奏。
踩坑:全记 = 全忘
坑一:什么都记,等于什么都没用。
想象记忆库里有 300 条记录,新会话开始全塞进上下文——模型面对一堆信息,反而抓不住重点,甚至被不相关记忆带偏。记忆的价值在于精选:
| 做法 | 结果 |
|---|---|
| 全记全注入 | 噪声淹没关键信息,效果比不记还差 |
| 门槛过滤 + Top-K 检索 | 注入的都是最相关的,记忆才真正起作用 |
一句话:记忆系统是"过滤器",不是"仓库"。 宁可少存、必存精品。
坑二:记忆和上下文的边界不清。
两者分工明确,别混用:
| 上下文管理(第 4 篇) | Memory 记忆系统(本篇) | |
|---|---|---|
| 管什么 | 会话内的过程记录 | 跨会话的沉淀结论 |
| 生命周期 | 本次会话 | 长期 |
| 存什么 | 对话原文、工具结果 | 提炼出的偏好、事实 |
| 解决的问题 | 长对话不"失忆" | 换会话不"失忆" |
常见错误:用记忆系统去存对话原文(那是上下文的事,存原文又占空间又没提炼);或者把该沉淀的偏好只留在上下文里(会话一关就丢)。
小结
- Memory = 存储 + 写入门槛 + 检索注入,把会话里值得记的提炼出来,跨会话复用
- 记忆分四层:短期/工作归上下文管,长期/语义才是记忆系统的主场
- 写入门槛:只有"下次还有用"的才记,宁缺毋滥
- 检索注入:新会话用 Top-K 检索捞回最相关的记忆,控制数量
- 要维护:过期清理、遗忘权、重要度动态调整
- 记忆系统是过滤器,不是仓库
下篇预告
到这篇为止,Agent 能记住用户了(Memory),也能动手了(工具)。但还有个空白:用户问"机票怎么退改签",这个知识不在模型训练数据里,也不在用户记忆里——它属于业务知识库。
下一篇讲 RAG 与向量知识库:怎么把"退改签政策"这种文档变成 Agent 能查、能引用、能答对的知识。如果说 Memory 让 Agent 记住用户,那 RAG 让 Agent 懂业务。
本系列路线(从 0 到 1): Agent 是什么 → 手写最小 ReAct → Function Calling 与工具设计 → 上下文管理 → Memory 记忆系统 → RAG 知识库 → Skill 自学习 → 编排模式与多 Agent → 给 Agent 装护栏 → 生产部署与可观测 → 评测与回归
更多推荐


所有评论(0)