详情

首页手游攻略 AI Agent 工程实践(03):Memory 设计——Agent 究竟该记住什么

AI Agent 工程实践(03):Memory 设计——Agent 究竟该记住什么

佚名 2026-07-11 09:01:05

发布时间:2026-07-09

标签:AI Agent|LLM|Memory|上下文工程|工程实践

系列导航

上一篇:AI Agent 工程实践(02):为什么 Rules 要分层?

下一篇:待定(Review 机制 / Benchmark / Audit Trail)

本文是 AI Agent 工程实践 系列的第 03 篇。

本文你将学到

  • ✓ 为什么"给 Agent 加记忆"经常让它更笨,而不是更聪明
  • ✓ 短期 / 工作 / 长期 / 归档:记忆到底该分几层,各层边界在哪
  • ✓ 项目记忆 vs 用户记忆——为什么必须拆开,混在一起会怎样
  • ✓ Memory 的完整生命周期:什么时候写、什么时候晋升、什么时候清理

适合阅读

  • ✓ 正在给 Agent / Copilot 做"记忆"功能的开发者
  • ✓ 用 Claude Code / Cursor / LangGraph 且被"上下文越来越乱"困扰的人
  • ✓ 对"上下文工程(Context Engineering)"感兴趣的人

问题背景

在 系列第 02 篇 里,我讲了 Rules 怎么分层——那解决的是"规则该在什么时候出现"。这一篇讲一个更棘手的问题:记忆该记什么、记多久。

几乎所有 Agent 框架都在宣传"我们有 Memory"。教程也都在教你怎么"把历史对话存起来,下次再喂回去"。听起来很美好——Agent 有了记忆,就像人一样能积累经验。我照做了。然后遇到一连串问题:

上下文越来越臃肿。 每开一次对话,Agent 先"回忆"一大堆历史,token 消耗蹭蹭涨,响应越来越慢。

关键信息被稀释。 当前任务真正需要的那几条,淹没在几百条陈年记忆里。模型的注意力被无关信息分走,输出质量反而下降。

旧信息污染新决策。 上个项目用的是 FastAPI,这个项目用 Flask,可它"记得"FastAPI,于是不停给我推 FastAPI 的写法。

记忆之间开始打架。 我三个月前说"喜欢详细注释",上周又说"代码尽量精简"。两条都被记着,Agent 到底该听谁的?

一句话概括:加了记忆,Agent 没变聪明,反而被自己的记忆拖垮了。 这不是"有没有记忆"的问题,是"怎么管理记忆"的问题——和 Rules 分层一样,本质是规模问题。

错误尝试

第一次尝试:什么都记,全量回灌

最朴素的想法:把所有历史对话都存下来,下次对话时全部塞回上下文。"记得越多越聪明"嘛。

结果:上下文迅速膨胀到几千 token 起步。更糟的是,模型在长上下文里出现了典型的"迷失在中间"(Lost in the Middle)现象——放在开头和结尾的信息还记得,夹在中间的关键约束直接被忽略。

记得多 ≠ 用得上。

第二次尝试:只留最近 N 条(滑动窗口)

既然全量太重,那就只保留最近 20 条消息,老的丢掉。简单粗暴。

结果:短期是清爽了,但把"这个项目用红涨绿跌配色"这种说过一次、长期有效的关键约定也一起丢了。过几轮它又开始问"配色用什么"。滑动窗口砍掉的不是噪声,是按时间一刀切,而重要性和时间根本不成正比。

两次尝试指向同一个教训:记忆的问题,不能用"记多少"来解决,要用"怎么分类、怎么流转"来解决。

关键观察

我把 Agent 一周的上下文导出来做了个统计,按"这条信息在后续任务里被真正用到"来分类:

记忆类型举例后续被用到的比例
当前任务上下文正在改的文件、当前报错~95%
项目级约定技术栈、目录规范、命名~70%
用户长期偏好语言、代码风格、汇报格式~60%
一次性事实某次临时路径、搜索结果~3%
闲聊 / 情绪随口的玩笑、寒暄~1%

pie title Agent 上下文里的信息价值分布"高频复用(值得长期记)" : 20"当前会话有用(用完即弃)" : 25"噪声(记了反而添乱)" : 55

定位真正原因

问题不在"记得多少",而在所有记忆被平等对待、生命周期完全相同:一次性的临时路径和"这个项目用 Flask"这种长期约定,被存在同一个地方、同样长久地活着、同样默认加载。

正确思路是给记忆分层,让不同价值的信息走不同的"寿命"和"加载策略":短期记忆用完即弃,长期记忆按需晋升,噪声定期清理。

这其实和上一篇的 Rule RAG 是同一个哲学:不在推理时把所有东西硬塞给模型,而是让对的信息在对的时候出现。 Rules 管的是"规则何时出现",Memory 管的是"记忆何时出现"——两者都是**上下文工程(Context Engineering)**的一部分。

最终方案:四层记忆 + 生命周期

我把记忆拆成四层,每层有不同的存活时间和加载策略:

层级存什么生命周期是否默认加载
Conversation(对话层)当前对话的原始消息流会话内,session 结束即弃是(但滚动裁剪)
Working Memory(工作记忆)本次会话提炼的关键状态、当前任务进度单次会话是(精简版)
Long Memory(长期记忆)跨会话的持久约定:项目记忆 + 用户记忆长期,可更新按需检索
Archive(归档层)冷数据、历史日志、过期记忆永久但压缩否,仅显式检索

信息在这四层之间单向流动 + 定期回收:新信息先进对话层,被判定为"有长期价值"才晋升到长期记忆,长期记忆里过时的再降级到归档。

长期记忆为什么要拆成"项目"和"用户"

这是最容易被忽略、也最关键的一刀。很多 Agent 把两者混成一个大 memory,于是换个项目就开始串味。

  • 项目记忆(Project Memory):只在当前项目生效。技术栈、目录规范、命名约定、这个项目特有的坑。换项目就不该带过去。
  • 用户记忆(User Memory):跟着人走、跨所有项目。语言偏好、代码风格、汇报格式、工作习惯。

判据很简单:问一句"换个项目还成立吗?" 成立的进用户记忆,不成立的进项目记忆。"我喜欢中文回复"是用户记忆;"这个项目用红涨绿跌"是项目记忆。分开存,才不会拿 A 项目的约定去污染 B 项目。

实际收益

指标全量回灌分层 + 生命周期
每次对话默认加载的记忆量全部历史(数百条)精简工作记忆 + 3 条常驻(个位数)
换项目时的串味频繁(旧栈污染新项目)基本消除(项目记忆隔离)
关键长期约定的存活易被滑动窗口误删稳定(晋升到长期层)
上下文噪声占比~55%显著下降(噪声不晋升)

架构图 / 流程图

记忆的四层流动

关键点:越往下越持久、越精炼;只有对话层是易失的,其余都要落盘。晋升(promotion)是主动决策,不是自动全存。

记忆的生命周期(状态流转)

代码或配置示例

记忆的目录结构

我把落盘的记忆按"归属"组织,一眼就能看出哪些跟人走、哪些跟项目走:

# 用户级(跨项目,跟人走)~/.agent/└── MEMORY.md # 用户长期记忆:语言、风格、习惯项目级(仅当前项目)<project>/.agent/memory/├── MEMORY.md # 项目长期记忆:技术栈、规范、约定(精炼)├── 2026-07-09.md # 每日工作日志(append-only,短期→归档)└── archive/# 归档层:过期日志压缩沉淀

记忆管理策略(YAML 配置)

用配置显式声明每层的寿命和晋升条件,而不是让它们"自然生长":

# memory-policy.yaml — 记忆生命周期策略conversation:scope: session# 会话内有效max_messages: 30# 滚动裁剪,超出丢弃persist: false# 不落盘working_memory:scope: sessionpersist: falsedistill: true # 会话结束前蒸馏关键状态,决定是否晋升long_memory:project:# 项目记忆scope: project# 仅当前项目生效max_chars: 3000 # 超出触发蒸馏压缩user: # 用户记忆scope: global # 跨项目生效max_chars: 4000promotion:rule: "reused >= 2 OR user_explicit == true" # 复用≥2次 或 用户明确要求 才晋升archive:trigger: "not_hit_days > 30" # 30 天未命中 → 降级归档compress: truettl_days: 365# 归档超期清理

写入与晋升逻辑(伪代码)

def on_new_info(info: Info, ctx: Context) -> None:"""一条新信息进来时的处理"""# 1. 一律先进对话层ctx.conversation.append(info)# 2. 与当前任务相关 → 提炼进工作记忆if relevant_to_current_task(info, ctx.task):ctx.working_memory.upsert(info)def on_session_end(ctx: Context) -> None:"""会话结束时决定谁能"活下来" """for item in ctx.working_memory:if should_promote(item): # 复用≥2 或 用户明确要求target = "user" if is_cross_project(item) else "project"long_memory.write(item, scope=target) # 关键:分流到项目/用户# 对话层直接丢弃,不落盘def cleanup(long_memory: LongMemory) -> None:"""定期清理:去重、降级、蒸馏"""long_memory.dedup()# 合并重复/冲突项,保留最新for item in long_memory.stale(days=30):archive.move(item) # 长期未命中 → 归档if long_memory.over_limit():long_memory.distill()# 超出容量 → 蒸馏压缩

代码不多,但把三个动作说清楚了:分流写入、按条件晋升、定期清理。 这三个动作缺一个,记忆迟早失控。

设计权衡

候选方案优点缺点为什么不选
全量回灌历史实现最简单,"绝不遗漏"上下文爆炸、迷失在中间、噪声稀释注意力记得多不等于用得上,规模一大必崩
滑动窗口(只留最近 N 条)上下文恒定、实现简单按时间一刀切,误删长期约定重要性和时间不成正比,砍错了对象
全部塞向量库,用时检索容量无上限、语义召回召回噪声多、无生命周期、冷热不分只解决"存",没解决"该不该记、记多久"
四层 + 生命周期冷热分层、项目/用户隔离、可晋升可清理需设计晋升与清理策略选择理由:唯一同时管住"记什么、记多久、给谁"的方案

分层不是越多越好。 如果你的 Agent 只是个单轮问答机器人,压根不需要跨会话记忆——一个滑动窗口就够了。不要在不需要长期记忆的地方强行搭四层。 向量检索也不是对立面:它可以作为 Archive 层的召回手段,和分层是互补而非二选一。

总结

  • ✅ 记忆的问题不是"记多少",而是"怎么分类、怎么流转"——和 Rules 一样是规模问题。
  • ✅ 分四层:Conversation(易失)→ Working(会话)→ Long(持久)→ Archive(冷藏),越往下越精炼。
  • ✅ 长期记忆必须拆项目和用户——判据是"换个项目还成立吗?",否则必然串味。
  • ✅ 晋升是主动决策(复用≥2 或用户明确要求),不是自动全存。
  • ✅ 没有清理机制的记忆系统,最终都会被噪声拖垮——去重、降级、蒸馏一个都不能少。

参考资料

  • Lost in the Middle 论文(Liu et al., 2023) → 为什么长上下文里中间信息会被忽略,直接决定了"不能全量回灌"的结论
  • MemGPT: Towards LLMs as Operating Systems → 用分层记忆 + 分页调度管理超长上下文,本文四层模型的重要参照
  • Anthropic — Claude 上下文与记忆管理文档(待补链接)→ 工作记忆与长期记忆分离的工程实践基础
  • Generative Agents (Park et al., 2023) → 记忆的"重要性打分 + 检索 + 反思"机制,启发了本文的晋升与蒸馏设计
  • PARA / Zettelkasten 知识管理法 → 用"归属"而非"时间"组织信息,是项目/用户记忆拆分的方法论来源

系列导航

  • 上一篇:AI Agent 工程实践(02):为什么 Rules 要分层?
  • 下一篇:待定(Review 机制 / Benchmark / Audit Trail)
相关资讯
点击查看更多
游戏推荐
推荐专题
热门阅读
推荐下载