Agent 工程 · 第 4 章|记忆系统:分类学、实现模式、生命周期管理

第 4 章 · 记忆系统

上下文工程(第 3 章)解决"一次会话内"的问题,记忆系统解决"跨会话"的问题:用户上周说的话、Agent 昨天犯的错、三个月积累的领域知识——这些必须活在窗口之外,按需召回。

4.1 记忆分类学

按内容性质分四类,每一类的读写模式完全不同:

类型 存什么 例子 写入方式
工作记忆 当前任务的活跃状态 笔记、计划、最近几轮 循环内自动维护
情景记忆(episodic) 发生过的具体事件 "用户 3 月 2 日抱怨过响应慢" 会话结束或事件发生时提取
语义记忆(semantic) 提炼出的事实与偏好 "用户偏好简洁回答""部署目标是 k8s" 从情景中归纳(可多次修正)
程序性记忆(procedural) 怎么做事的知识 技能、SOP、代码模板 显式编写或 PR 审核

两种最容易犯的分类错误:

  • 把语义记忆当情景记忆存:存了一万条"某天用户说了某话"的原始记录,召回时全靠运气。正确做法是两层:情景层存事件(可追溯),语义层存归纳(可检索)——归纳有更新的机会,事件只有追加;
  • 把程序性记忆当语义记忆存:把"怎么部署"写成一条条事实散落在记忆库里。程序性知识是流程,应该是结构化的技能文档(可版本化、可测试、可 PR 审),不是记忆检索的对象。

4.2 三种实现模式

文件式(file-based)

记忆 = 一个有结构的目录树。目录即分类,文件即条目,_meta.json 之类的索引文件记录描述与统计。

memory/
├── _index.md                 # 顶层目录索引(供渐进披露)
├── 用户偏好/
│   ├── _meta.json            # 子目录元数据:描述、更新时间、条目数
│   └── 输出风格.md
├── 项目-x/
│   ├── _meta.json
│   ├── 架构决策.md
│   └── 已知问题.md
└── 归档/

Agent 通过文件工具(列目录 → 读索引 → 读具体文件)访问记忆,天然支持渐进披露(第 3 章):上下文里只放 _index.md,需要哪个分支再深入。

优点:结构清晰、可人审可 git 管理、零额外基础设施。缺点:检索靠目录导航(Agent 要多几次工具调用),规模大了之后依赖索引质量。这是多数产品记忆系统的最佳起点——Claude Code 的 CLAUDE.md/Memory 机制、大量个人助手的记忆实现都属此类。

向量式(embedding-based)

记忆条目 → embedding 向量 → 向量库;查询时把当前上下文 embed 成查询向量,召回最近邻。

def remember(text: str, meta: dict):
    vec = embed(text)
    store.upsert(vector=vec, payload={**meta, "text": text, "ts": now()})

def recall(query: str, k=5, min_score=0.75):
    hits = store.search(embed(query), k=k)
    return [h for h in hits if h.score >= min_score]   # 相似度低宁可不召回

优点:语义召回("部署的事"能召回"上线流程"),规模无压力。缺点与纪律:

  • 召回质量是玄学高发区:相似 ≠ 相关。必须设相似度阈值,宁可漏召回也不要塞进不相关记忆污染上下文;
  • 写入门禁:Agent 自动写入的记忆必须去重与冲突处理("用户喜欢详细回答"和"用户喜欢简洁回答"并存是灾难),写入前先检索相似条目做 merge;
  • 来源可追溯:每条记忆记着来源会话,用户要求"删除关于我的记忆"时要能删干净(合规硬要求)。

结构化式(structured / graph)

把记忆建成实体与关系(用户 —偏好→ 简洁;项目-x —使用→ k8s),支持多跳查询("用户在用过框架 X 的那个项目里踩过什么坑")。实现可以是图数据库,也可以是关系表 + 查询工具。

优点:精确、可推理。缺点:抽取(从对话里抽实体关系)与 schema 维护成本高。适用:实体关系本身是核心资产的场景(CRM 类助手、长期项目协作者)。

务实结论:文件式打底 + 向量检索增强是当前的最佳实践组合;结构化式只在实体关系密集的场景引入。

4.3 生命周期:写入、召回、遗忘、巩固

记忆系统是完整的数据生命周期管理,四个环节都要设计:

写入(write)。三个过滤器:

  1. 值得记吗——一次性上下文("帮我看下这个文件")不记;持久事实("我们生产环境是 k8s")记;
  2. 属于哪一层——按 4.1 分类;
  3. 与已有记忆冲突吗——先检索再写入,冲突时更新而非追加(并保留更新时间)。

召回(recall)。两条路径并行:

  • 主动召回:每次会话开始,用当前任务做查询拉取 Top-K 相关记忆注入上下文(注意 3.1 的预算,注入 500-1500 token 为宜);
  • 被动召回:给 Agent 一个 search_memory 工具,它在需要时自己查。两条腿缺一不可——主动召回解决"该想起没想起",被动召回解决"临时需要"。

遗忘(forgetting)。没有遗忘的记忆库会单调膨胀,检索质量随规模下降,旧记忆还会与新风控冲突。工程手段:

  • 时间衰减:每条记忆带权重 w = base * exp(-λ * days_since_last_access),召回排序时叠加权重;
  • 归档:低权重超过阈值的移入"归档"分区(不再自动召回,但可显式检索)——删除是危险操作,归档是安全操作;
  • 显式失效:用户说"我搬去上海了"时应触发对旧地址记忆的失效标记,而不是静默并存两条矛盾记忆。

巩固(consolidation)。类比人类睡眠记忆巩固:后台任务周期性把情景层的高频/高价值内容归纳升级为语义层(三条"用户在周会前都要求提前看材料"→ 一条"用户习惯周会前预审材料"),并合并重复条目。巩固是让记忆库从"流水账"进化为"知识库"的关键环节,也是用户感知"这个助手越来越懂我"的直接来源。

4.4 记忆的失败模式

失败模式 现象 根因与防御
记忆污染 错误记忆反复出现,越纠正越多 写入无冲突检测;防御:写入前查重 + 归纳式更新
记忆幻觉 Agent 声称记得从未发生的事 召回结果与本次生成混淆;防御:注入时标注"以下是历史记忆"
过度召回 每次回复都牵扯八竿子打不着的旧事 无阈值/无权重排序;防御:min_score + 时间衰减
隐私事故 用户 A 的记忆出现在用户 B 的上下文 共享存储无租户隔离;防御:记忆按用户硬隔离,工具层强制过滤
记忆僵化 环境变了但 Agent 用旧认知行事 无遗忘/巩固;防御:时间衰减 + 用户可查看可删除记忆

记忆幻觉值得特别强调:记忆是注入上下文的"别人的话",模型可能把它当成自己此刻的认知。注入时的包装格式很重要:

[以下是与当前任务相关的历史记忆,供参考,可能过时:
- (2026-08-12, 来源:会话#a1b2) 用户的生产环境是 k8s
]

标注时间与来源,模型才能正确处理"记忆与现实冲突"的情况(以现实/用户最新陈述为准)。

4.5 评测你的记忆系统

记忆系统没有评测就没有迭代方向。三层指标:

  1. 召回指标:构造"记忆问答对"(写入记忆 X 后,N 轮对话外提相关问题),测命中率与误召回率;
  2. 端到端指标:带记忆 vs 不带记忆的对话质量对比(同任务双跑);长期一致性(跨会话约束遵守率);
  3. 健康指标:记忆库规模增长曲线、冲突检测触发率、衰减归档量——异常增长通常意味着写入过滤器失效。

实现作业

给第 2-3 章的 Agent 加一个文件式 + 向量混合的记忆系统:

  1. 会话结束时用 LLM 抽取"值得跨会话记住的事实"(写入过滤器),语义层落文件、情景层落向量库;
  2. 新会话开始时主动召回 Top-5 注入上下文(带时间来源标注),并提供 search_memory 工具;
  3. 实现时间衰减排序与"更新代替追加"的冲突处理;
  4. 写 10 个"隔轮问答对"验证召回命中率,并故意制造一条冲突记忆验证更新逻辑。

深入材料

  • MemGPT / Letta 论文:arXiv 2310.08560(把 OS 虚存思想引入 LLM 记忆的鼻祖)
  • Generative Agents(arXiv 2304.03442,斯坦福小镇:记忆流 + 反思 + 计划的完整实现,巩固思想的来源)
  • Anthropic Claude Memory 功能说明(产品级文件式记忆的取舍)
← 返回资讯列表

读者留言

COMMENTS 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?