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

文章目录
- 导语
- 一、先分清 Context、State 和 Memory
- 二、什么信息值得记
- 用户偏好
- 项目事实
- 经过验证的工作约定
- 三、什么信息不应该直接记
- 一次性任务要求
- 未经验证的模型推断
- 敏感信息
- 很快过期的数据
- 四、Memory 必须有作用域
- 五、一条 Memory 至少要带哪些元数据
- 六、Memory 怎样写入
- 七、Memory 怎样进入下一次 Context
- 八、冲突和过期怎样处理
- 用户必须能纠正和删除
- 九、短期记忆、长期记忆只是相对说法
- 十、Memory 污染是怎样发生的
- 把错误结论记成事实
- 把局部偏好扩大作用域
- 把旧信息永久保留
- 检索太多无关记忆
- 十一、前端和后端各自要提供什么
- 前端:让记忆可见、可控
- 后端:保证隔离、版本和检索
- 十二、用四条候选信息检验理解
- 十三、这一篇真正要记住的事
- 下一篇预告
- 参考资料
导语
上一篇讲状态管理:一次 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
简单地把两条都交给模型,会把数据治理问题推给模型猜。
更稳的处理是:
- 根据作用域和事实键识别冲突;
- 新事实有可靠来源时,将旧记录标记为
superseded; - 来源不明确时,暂不覆盖,向用户或工具核实;
- 检索默认只返回 active 版本;
- 保留变更历史供审计和回滚。
动态事实还可以使用 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:什么时候需要计划,什么时候直接执行?
参考资料
- OpenAI Agents SDK — Running agents:
https://developers.openai.com/api/docs/guides/agents/running-agents - OpenAI Agents SDK — Persist memory across runs:
https://developers.openai.com/api/docs/guides/agents/sandboxes#persist-memory-across-runs - LangGraph — Memory overview:
https://docs.langchain.com/oss/python/concepts/memory