Agent 记忆机制设计:你必须要回答的11个问题
很多人一提到 Agent 记忆,第一反应是“把历史对话存起来,下次再塞进
Prompt”。但真正做项目后会发现,这种做法很快会失控:历史越来越长,token
不够;旧偏好和新偏好冲突;一次性要求被误认为长期偏好;RAG 内容、工具结果、用户隐私混在一起,最后 Agent
不是更聪明,而是更容易被噪声污染。
所以我理解的 Agent 记忆机制,本质不是“让 Agent 什么都记住”,而是回答四个问题:
- 什么值得记?
- 记在哪里?
- 什么时候召回?
- 什么时候更新或遗忘?
一个比较可靠的设计,是把记忆分成 短期记忆、长期记忆、上下文编译、记忆写回治理 四层。
1. 为什么需要长期记忆?
短期记忆解决的是“当前会话能不能接上”。比如上一轮用户问了什么、刚刚工具返回了什么、当前任务进行到哪一步。
但真实用户不是只用一次系统。他会持续地用系统做资料问答、总结、PPT 生成、笔记整理。这个过程中会出现很多跨会话仍然有价值的信息:
用户喜欢简洁回答
用户做 PPT 偏正式风格
用户长期在准备某个项目
用户希望总结时使用固定结构
用户明确说“以后都按这个格式”
如果没有长期记忆,Agent 每次都像第一次见到用户。用户要不断重复自己的偏好,体验会很差。
所以长期记忆解决的是:
跨会话个性化
稳定事实复用
减少重复说明
让 Agent 的行为逐渐贴合用户
但长期记忆不是“所有对话都长期保存”。它只应该保存跨时间仍然有价值、稳定、可复用的信息。
2. 短期记忆和长期记忆有什么区别?
短期记忆面向当前会话,生命周期短,主要用于维持上下文连续性。
典型内容:
当前用户输入
最近 N 轮消息
会话摘要
本轮工具调用结果
本轮子 Agent 结果
长期记忆面向用户维度,跨会话存在,主要用于个性化和长期事实复用。
典型内容:
用户偏好
长期目标
常用输出格式
稳定项目背景
反复出现的事实
用户明确要求记住的信息
不能进入长期记忆的内容:
一次性要求:比如“这次写正式一点”
临时任务上下文
未经确认的模型推测
外部 RAG 检索内容
工具返回的原始网页内容
已经注入 Prompt 的旧记忆
敏感信息,除非用户明确授权
这里有一个关键原则:
短期记忆可以宽一点,因为它只影响当前会话;长期记忆必须严一点,因为它会影响未来很多次对话。
3. 长期记忆应该存在向量库还是关系数据库?
比较合理的设计是两者都用,但职责不同:
MySQL:长期记忆事实源
向量数据库:长期记忆语义索引
MySQL 里保存可信事实和治理字段:
user_memories
- id
- user_id
- content
- memory_type
- status
- confidence
- source_message_id
- evidence
- version
- superseded_by
- created_at
- updated_at
向量库里保存召回索引:
memory_vectors
- memory_id
- user_id
- embedding
- content_snapshot
- version
两者通过 memory_id 关联。
召回流程是:
当前问题
→ 生成 query embedding
→ 向量库语义检索
→ 返回 memory_id
→ 回查 MySQL
→ 判断状态、权限、版本、敏感度
→ 通过后才允许进入上下文候选
为什么不能只用向量库?
因为向量库擅长相似度召回,但不擅长做事实治理。长期记忆需要更新、删除、失效、冲突处理、用户可见可编辑、审计和权限控制,这些更适合关系数据库。
所以:
向量库是索引,可以重建;MySQL 是事实源,不能丢。
4. 长期记忆怎么召回?
长期记忆不能每次全量塞进 Prompt,而是按当前问题召回。
流程是:
用户当前问题
→ MemoryProvider 生成检索请求
→ 向量库召回候选 memory_id
→ 回查 MySQL 事实源
→ 过滤无效记忆
→ 计算相关性和优先级
→ 交给 ContextCompiler
→ 在 token 预算内选择是否注入
召回后不能直接放进 Prompt,还要过滤:
是否属于当前用户
是否 active
是否被用户删除
是否和当前任务相关
是否敏感
是否和其他记忆冲突
是否允许当前 Agent 访问
是否超过 token 预算
这一步很重要。因为“被召回”只代表语义相似,不代表一定应该被使用。
5. 不同 Agent 是否都能访问长期记忆?
不应该。
Agent 记忆访问必须按角色隔离。
比如:
Chat Agent:
可以读取用户偏好、会话摘要、最近历史。
Main Agent:
可以读取必要的用户偏好和任务背景,用于规划和工具选择。
Search Agent:
默认不读取长期记忆和完整聊天历史,只接收结构化 SearchTask。
Generation Agent:
只读取和生成任务相关的格式偏好,比如 PPT 风格、总结格式。
这样做有两个好处:
减少隐私泄露
减少上下文污染
例如 Search Agent 的职责只是搜索,不应该知道用户完整的长期偏好和聊天历史。否则不仅浪费 token,也会扩大隐私暴露面。
6. 长期记忆什么时候写入?
长期记忆写入应该发生在本轮 Agent 执行完成之后,也就是 finalization 阶段。
原因是:
工具调用过程中信息还不稳定
子 Agent 可能失败
用户可能取消
中途写入容易产生脏记忆
最终回答完成后输入输出边界最清楚
写入流程一般是:
Agent 生成最终回答
→ MemoryWriter 读取用户原始输入和真实输出
→ 提取候选记忆
→ 输出结构化结果
→ 系统规则校验
→ 新增、更新、降权或失效
注意:MemoryWriter 不应该从已经注入 Prompt 的旧记忆、RAG 内容、工具结果里直接提取长期记忆,否则容易产生反馈循环。
也就是:
可以从用户原始输入提取
可以从本轮真实输出提取
不要从旧记忆再次提取
不要从外部检索内容直接提取
7. 怎么避免模型乱记?
不能只靠系统 Prompt。
更稳的方式是:
模型负责提取候选
系统负责治理和落库
模型输出结构化候选:
{
"type": "preference",
"content": "用户喜欢简洁回答",
"evidence": "以后回答尽量简短",
"scope": "global",
"confidence": 0.92
}
系统再做规则过滤:
包含“这次、本次、临时” → 不写长期记忆
没有用户输入证据 → 不写
来自 RAG 或工具结果 → 不直接写
敏感信息 → 默认不写
置信度低 → pending
和已有记忆冲突 → 进入冲突处理
所以长期记忆写回不是一次模型调用就结束,而是一个治理流程。
8. 新旧记忆冲突怎么办?
冲突是长期记忆里最常见的问题。
比如旧记忆是:
用户喜欢详细解释
新输入是:
以后回答尽量简短
不能把两条都同等优先级放进 Prompt。否则模型会收到互相矛盾的指令。
更合理的处理是:
以用户最近明确表达为准
新记忆设为 active
旧记忆标记为 superseded 或 inactive
保留版本关系
召回时只注入 active 记忆
这样既不会丢失审计历史,也不会污染当前上下文。
9. 长期记忆怎么避免越存越多?
长期记忆不能只增不减,否则会出现三个问题:
召回变慢
冲突变多
质量下降
治理策略可以包括:
相似记忆合并
低置信度记忆 pending
长期未使用记忆降权
冲突记忆失效
用户可手动删除
按类型设置容量上限
定期清理低价值记忆
一个成熟的长期记忆系统,不是看它能记多少,而是看它能不能保持高质量。
10. 用户要求忘记怎么办?
如果用户说“忘记我之前说过的偏好”,系统应该支持真正的遗忘。
流程是:
定位相关记忆
→ MySQL 标记 deleted / inactive
→ 删除或失效向量索引
→ 后续召回时即使命中也丢弃
→ 不再注入 Prompt
这里有一个关键点:
向量库召回结果不能直接信任,必须回查 MySQL 状态。
因为向量索引可能延迟删除或暂时不一致。只要 MySQL 里已经标记 deleted,召回阶段就必须过滤掉。
11. 这个机制的核心取舍是什么?
记忆机制的难点不在“怎么存”,而在“怎么控制”。
一个差的记忆系统会:
什么都记
什么都召回
召回就注入
冲突不处理
删除不彻底
一个好的记忆系统应该是:
该记的记住
不该记的不写
不确定的先 pending
冲突的能降权或失效
删除的不会再使用
召回的还要经过治理
最终可以总结成一句话:
Agent 记忆机制不是无限扩大 Prompt,而是把用户历史信息变成可检索、可更新、可遗忘、可治理的上下文资产。
面试时可以这样回答:
我会把 Agent 记忆分成短期记忆和长期记忆。短期记忆负责当前会话连续性,用最近消息和会话摘要实现;长期记忆负责跨会话个性化,保存用户偏好、长期目标和稳定事实。长期记忆不会只存在向量库里,MySQL 是事实源,向量库只是语义索引。召回时先通过向量库拿到 memory_id,再回查 MySQL,过滤状态、权限、敏感度和冲突,最后交给 ContextCompiler 按 token 预算决定是否注入。写回时也不是所有内容都记,而是在本轮回答完成后由 MemoryWriter 提取候选,再经过规则校验、置信度判断和冲突处理。这样 Agent 才能既记住用户,又不会被错误记忆和历史噪声污染。
更多推荐



所有评论(0)