← 全部文章

Agent Memory:什么时候该记,什么时候不该记?

Memory 不是把所有历史永久塞回上下文,而是把经过验证、具有明确作用域和生命周期的信息,按需带入未来任务。

Agent Memory:什么时候该记,什么时候不该记?
文章目录
  1. 导语
  2. 一、先分清 Context、State 和 Memory
  3. 二、什么信息值得记
  4. 用户偏好
  5. 项目事实
  6. 经过验证的工作约定
  7. 三、什么信息不应该直接记
  8. 一次性任务要求
  9. 未经验证的模型推断
  10. 敏感信息
  11. 很快过期的数据
  12. 四、Memory 必须有作用域
  13. 五、一条 Memory 至少要带哪些元数据
  14. 六、Memory 怎样写入
  15. 七、Memory 怎样进入下一次 Context
  16. 八、冲突和过期怎样处理
  17. 用户必须能纠正和删除
  18. 九、短期记忆、长期记忆只是相对说法
  19. 十、Memory 污染是怎样发生的
  20. 把错误结论记成事实
  21. 把局部偏好扩大作用域
  22. 把旧信息永久保留
  23. 检索太多无关记忆
  24. 十一、前端和后端各自要提供什么
  25. 前端:让记忆可见、可控
  26. 后端:保证隔离、版本和检索
  27. 十二、用四条候选信息检验理解
  28. 十三、这一篇真正要记住的事
  29. 下一篇预告
  30. 参考资料

导语

上一篇讲状态管理:一次 Run 做到哪里、调用过什么工具、为何暂停,以及怎样恢复。

但用户下周创建一个全新任务时,系统还应该知道什么?

例如用户曾经说:

这个项目统一用 pnpm。

下次运行测试时,记住它很有价值。

如果用户说:

这次先不要改代码。

把它永久记住就会出问题。下一次用户明确要求修复 Bug,Agent 可能仍然拒绝写文件。

Memory 的难点从来不是“怎样存一段文本”,而是:什么值得跨任务保留,应该对谁生效,什么时候过期,以及怎样纠正。

本文会把 Context、State 和 Memory 分开,并设计一条可控的记忆写入与读取流程。


一、先分清 Context、State 和 Memory

三个概念都可能包含文字,但解决的问题不同。

概念 回答的问题 例子
Context 这一轮模型能看到什么 当前任务、相关消息、工具结果
State 这次运行进行到哪里 当前 Step、等待确认、预算使用
Memory 未来任务可能复用什么 用户偏好、项目稳定事实、已验证规则

Memory 不等于 Context。

数据库里存了 10 万条记忆,并不代表每一轮都要全部塞给模型。检索、筛选后真正进入模型输入的那一小部分,才成为本轮 Context。

Memory 也不等于 State。

“工具调用到第 7 步”只对当前 Run 有意义;“这个仓库使用 pnpm”可能在未来多个 Run 中复用。

这条边界能避免把一次任务的临时限制污染成长期规则。


二、什么信息值得记

一个实用判断是:如果下次任务不知道这条信息,会不会反复询问、做错选择或破坏稳定约定?

常见的可记信息有三类。

用户偏好

代码示例优先使用 TypeScript
生成报告时保留详细引用
写操作默认先给预览

偏好不是绝对命令。它应在当前用户作用域内生效,并允许本次任务覆盖。

项目事实

包管理器是 pnpm
测试命令是 pnpm test
前端入口位于 apps/web
生产分支是 main

项目事实最好由配置文件、仓库内容或用户确认支持,而不是从一次失败尝试随便推断。

经过验证的工作约定

数据库迁移必须先在 staging 演练
生成 API 客户端后要运行 pnpm generate:check
发布前必须通过 security-smoke 测试

这类规则通常属于团队或项目作用域,不应写进全局用户偏好。


三、什么信息不应该直接记

一次性任务要求

这次只分析,不修改
只看最近 30 分钟日志
先不要运行测试

它们属于当前 Run 或 Thread,任务结束后通常失效。

未经验证的模型推断

这个项目可能使用 Yarn
用户大概不喜欢单元测试
支付问题应该是 Redis 导致的

模型推测可以留在当前任务的待验证项中,不能直接升级为长期事实。

敏感信息

访问令牌、密码、Cookie、银行卡号和完整个人隐私不应作为自然语言 Memory 保存。凭证要进入专门密钥系统,并通过权限受控的引用使用。

很快过期的数据

当前部署版本是 v1.2.3
今天值班的人是小王
生产接口现在响应 500

如果确实需要缓存,应带来源、过期时间和刷新策略,而不是伪装成永久事实。


四、Memory 必须有作用域

“记住”不能只有一个全局存储桶。

type MemoryScope =
  | { type: 'user'; userId: string }
  | { type: 'project'; projectId: string }
  | { type: 'team'; teamId: string }
  | { type: 'agent'; agentId: string }

同一条信息放错作用域,会产生完全不同的后果。

“我喜欢简短回答” → user
“此仓库使用 pnpm” → project
“生产发布需要双人批准” → team / project
“该 Agent 的工具失败后最多重试两次” → agent 配置或 Runtime policy

最后一条甚至未必应该进入 Memory。能由确定性配置表达的运行规则,优先放配置或 Policy,而不是自然语言记忆。

多租户系统还必须把 tenantId 纳入物理查询条件,不能只依赖向量相似度“刚好没搜到别人数据”。


五、一条 Memory 至少要带哪些元数据

只保存:

项目使用 pnpm

不够。系统还要知道它从哪里来、是否仍可信。

type MemoryRecord = {
  id: string
  scope: MemoryScope
  kind: 'preference' | 'fact' | 'rule' | 'lesson'
  content: string
  source: {
    type: 'user_statement' | 'file' | 'tool_result' | 'human_review'
    ref?: string
  }
  confidence: 'confirmed' | 'derived'
  createdAt: string
  updatedAt: string
  expiresAt?: string
  supersedesId?: string
  status: 'active' | 'superseded' | 'deleted'
}

confidence 不是模型随意生成的小数分数。这里用有限状态区分“用户明确确认”与“系统从证据推导”。不同类型可以采用不同写入门槛。

supersedesId 用于保留纠正历史。例如项目从 npm 迁移到 pnpm,新事实替代旧事实,而不是让两条冲突记录同时活跃。


六、Memory 怎样写入

最危险的实现是:每轮结束后,让模型自由总结并永久保存所有“有用信息”。

更可控的写入流程是:

流程图

候选信息可以由模型提取,但应用要做确定性检查:

  • 作用域是否明确;
  • 来源是否可追踪;
  • 是否包含凭证或隐私;
  • 是否与现有记忆冲突;
  • 是否需要用户确认;
  • 是否应该设置过期时间。

高影响规则,例如“生产部署无需审批”,不能因为聊天中出现一句类似话就自动写入。它应该来自受信任配置或明确授权。


七、Memory 怎样进入下一次 Context

记忆检索不是只做向量相似度搜索。

假设当前任务是:

给 web-app 升级依赖并运行测试。

检索至少要先做硬过滤:

const candidates = await memoryStore.search({
  tenantId,
  scopes: [
    { type: 'user', userId },
    { type: 'project', projectId }
  ],
  status: 'active',
  notExpiredAt: now,
  query: userTask
})

再根据相关性、来源和冲突情况挑选少量记录:

项目使用 pnpm
升级依赖后运行 pnpm test
用户希望写操作先展示 diff

“用户喜欢 Markdown 表格”可能是有效记忆,但与当前操作无关,就不应进入本轮 Context。

检索出来也不是无条件服从。当前用户明确说“这次使用 npm 做兼容测试”时,本次指令可以覆盖默认偏好;组织安全策略则不能被普通用户指令覆盖。

Memory 是辅助上下文,不是最高权限的规则源。


八、冲突和过期怎样处理

假设 Memory 中同时存在:

2026-05:项目使用 npm
2026-08:项目使用 pnpm

简单地把两条都交给模型,会把数据治理问题推给模型猜。

更稳的处理是:

  1. 根据作用域和事实键识别冲突;
  2. 新事实有可靠来源时,将旧记录标记为 superseded;
  3. 来源不明确时,暂不覆盖,向用户或工具核实;
  4. 检索默认只返回 active 版本;
  5. 保留变更历史供审计和回滚。

动态事实还可以使用 expiresAt。过期后不是立即当成错误事实,而是不再自动注入,并在需要时重新查询。

用户必须能纠正和删除

Memory 会影响未来行为,因此产品至少要支持:

  • 查看系统记住了什么;
  • 修改错误内容;
  • 删除某条记忆;
  • 清除某个作用域;
  • 关闭某类自动记忆。

这不只是体验问题,也是隐私和治理边界。

deleted 状态只能表示这条记忆不再参与检索。如果用户要求删除数据,后端还要按保留策略清理正文、向量索引和缓存,不能只更新一个状态字段。


九、短期记忆、长期记忆只是相对说法

例如,LangGraph 把 Thread 作用域内的历史和状态称为 short-term memory,把通过 Store 跨 Thread 保留的信息称为 long-term memory。OpenAI Agents SDK 的 Session 会管理持久的对话历史,而 Sandbox Agent Memory 则把工作区运行中可复用的经验保存到文件中;官方文档明确将两者分开。

这说明 “Memory” 在不同框架里可能指:

  • 会话历史持久化;
  • 当前线程的执行状态;
  • 跨任务的长期知识与偏好;
  • Agent 在工作区中沉淀的经验文件。

阅读文档时不要只看名词,要看三个问题:

保存多久
跨越什么边界
下一次怎样被取回并使用

本文为了课程清晰,把当前 Run/Thread 的执行信息归入 State,把未来独立任务仍可能复用的信息称为 Memory。


十、Memory 污染是怎样发生的

把错误结论记成事实

一次排障中模型误判 Redis 是根因,系统却把它写成项目事实。以后每次故障都优先检查 Redis,形成自我强化。

把局部偏好扩大作用域

某个项目要求用 JavaScript,系统却写入全局用户偏好,导致其他 TypeScript 项目也被改写。

把旧信息永久保留

项目已经迁移到新目录,Agent 仍不断读取旧路径。

检索太多无关记忆

把几十条历史经验塞进 Context,不仅增加成本,还可能压过当前任务里的新事实。

Memory 质量不能只用“召回了多少”衡量。更重要的是相关性、正确性、作用域、时效和最终任务是否改善。


十一、前端和后端各自要提供什么

前端:让记忆可见、可控

可以把记忆展示成:

已记住:此项目使用 pnpm
来源:package.json
作用范围:web-app 项目
可操作:编辑 / 删除

自动写入高影响信息前,前端可以请求确认。用户纠正后,应明确告诉他从什么时候开始生效。

后端:保证隔离、版本和检索

后端要处理:

  • tenant / user / project 作用域过滤;
  • 内容脱敏和访问控制;
  • 冲突检测与版本替代;
  • 过期和清理任务;
  • 向量检索与结构化过滤组合;
  • 写入与命中日志;
  • 对最终效果做 Eval。

向量数据库只是可能的检索实现,不等于完整 Memory 系统。


十二、用四条候选信息检验理解

一次任务中出现:

A. 用户说:以后这个项目统一使用 pnpm
B. 用户说:这次不要运行测试
C. 工具返回:当前生产版本为 v2.4.1
D. 模型推测:用户可能更喜欢函数式编程

你会怎样处理?

一种合理答案是:

  • A:可作为 project Memory 候选,最好结合项目文件或用户确认;
  • B:只属于当前 Run,不写长期 Memory;
  • C:如果未来需要,可带来源和短过期时间缓存,否则按需查询;
  • D:未经验证,不写入。

如果能说明每条信息的作用域、来源和生命周期,而不是只回答“记/不记”,就真正掌握了 Memory 设计。


十三、这一篇真正要记住的事

Memory 的目标不是让 Agent 永远记住一切,而是让它在未来任务中少问一次、少犯一次已经验证过的错误。

每条长期记忆至少要回答:

它是什么类型
对谁和哪个项目生效
来源是什么
是否经过确认
什么时候过期
如何纠正或删除

写入时宁缺毋滥,读取时按作用域和相关性筛选。当前指令、组织策略和新证据仍可能覆盖旧记忆。

下一篇进入 Planning:当任务变长时,是否应该先生成计划?计划与 Agent Loop、Run State 又是什么关系?


下一篇预告

下一篇:Agent Planning:什么时候需要计划,什么时候直接执行?


参考资料