Agent Memory:为什么有记忆的 Agent 仍然会忘事?
从短期上下文到长期记忆,深入理解 AI Agent 的记忆架构、Memory Consolidation 与遗忘机制
一、一个奇怪的现象:Agent 明明有记忆,为什么还会反复犯错?
假设我们开发了一个 AI 编程 Agent。
第一天,用户告诉它:
“这个项目统一使用 PostgreSQL,不允许引入 MongoDB。”
Agent 理解了要求,顺利完成任务。
第二天,用户让它设计一个新的日志分析模块。
Agent 却给出了一套基于 MongoDB 的技术方案。
用户提醒:
“我昨天已经说过,这个项目不能使用 MongoDB。”
Agent 回答:
“抱歉,我忘记了。”
我们可能会觉得,这只是因为模型没有长期记忆。
于是给 Agent 增加一个 Memory 模块,把用户的要求保存到向量数据库。
问题似乎解决了。
但过了一段时间,新的问题出现了。
用户已经决定将项目从 PostgreSQL 迁移到 MySQL,Agent 却仍然反复推荐 PostgreSQL。
这次它并没有忘记,而是记住了错误的旧信息。
于是我们发现:
Agent 的问题不只是不会记忆,还包括不知道什么值得记、什么时候应该更新,以及什么时候必须忘记。
这正是 Agent Memory Engineering 要解决的核心问题。
上一篇文章讨论了 Context Engineering:如何让模型在当前推理中获得正确的信息。
今天我们进一步讨论:
当任务跨越多个会话、多个小时甚至多个星期时,Agent 怎样持续积累有用的信息,同时避免记忆污染?
二、什么是 Agent Memory?
很多开发者会把 Memory 理解为:
“把历史聊天记录存到数据库里,下次再查出来。”
这种方案确实能提供基本的历史检索能力,但距离完整的 Agent Memory 还很远。
我们可以把 Agent Memory 定义为:
Agent Memory 是一套对历史信息进行提取、组织、持久化、检索、更新和遗忘的机制,让 Agent 能够跨越当前上下文窗口,持续利用过去的知识与经验。
注意,这个定义包含六个动作:
-
Extract:从交互和执行结果中提取值得保存的信息。
-
Organize:对记忆进行分类和结构化。
-
Store:将记忆持久化保存。
-
Retrieve:根据当前任务检索相关记忆。
-
Update:当信息变化时修正已有记忆。
-
Forget:删除过期、错误或不再需要的记忆。
如果只实现 Store 和 Retrieve,得到的通常只是一个历史信息检索系统。
要真正提高 Agent 的长期可靠性,必须管理记忆的完整生命周期。
Agent Memory 的基本架构
用户与环境
│
▼
Agent Runtime
│
┌─────────┴─────────┐
│ │
▼ ▼
当前上下文 Memory Manager
│ │
│ ┌────────┴─────────┐
│ │ │
│ ▼ ▼
│ Memory Writer Memory Retriever
│ │ │
│ ▼ │
│ Validation Layer │
│ │ │
│ ▼ │
│ Memory Store ───────────┘
│ │
◄───────────────────┘
│
▼
LLM
│
▼
Tools / Actions
这里有一个很重要的设计原则:
Memory Store 不应该直接等于模型的全部历史上下文。
记忆需要根据当前任务动态检索、筛选,再进入 Context。
Memory 决定保存什么,Context Engineering 决定当前使用什么,Harness 决定如何执行。
三、Agent 到底需要哪些记忆?
在现代 Agent 架构中,一个很有用的分类来自认知科学。
LangGraph 的官方文档借鉴了人类记忆的分类方式,将长期记忆划分为:
-
Semantic Memory:语义记忆。
-
Episodic Memory:情景记忆。
-
Procedural Memory:程序性记忆。
此外,工程上还需要区分短期记忆与长期记忆的作用范围。
1. Short-term Memory:短期记忆
短期记忆主要保存当前会话和任务执行状态。
例如:
用户正在让 Agent 修复一个 Bug。
Agent 需要记住:
-
当前任务是什么。
-
已经检查了哪些文件。
-
修改过哪些代码。
-
上一次测试为什么失败。
-
下一步计划做什么。
这些信息对于当前任务非常重要,但不一定适合永久保存。
LangGraph 把这类记忆与 Thread State 联系起来,通过 Checkpointer 保存执行状态,让任务能够在同一个线程中恢复。
这里需要区分:
保存当前执行状态,不等于建立了长期知识记忆。
Checkpoint 更侧重恢复当前任务;长期记忆则侧重让未来任务利用过去的信息。
2. Semantic Memory:语义记忆
语义记忆保存相对稳定的事实、知识和偏好。
例如:
{
"type": "semantic",
"scope": "project:order-service",
"key": "primary_database",
"value": "PostgreSQL",
"source": "architecture_decision",
"status": "active"
}
它可以帮助 Agent 在未来设计系统时遵守已有架构约束。
但要注意,语义记忆并不意味着永远正确。
项目架构、用户偏好、组织结构和业务规则都可能改变。
因此,语义记忆应该支持来源追踪、版本管理和更新。
3. Episodic Memory:情景记忆
情景记忆保存的是过去发生过的事情。
例如:
“上次执行数据库迁移时,由于未处理空值字段,导致迁移脚本失败。”
这条记忆的价值不在于描述当前系统状态,而在于帮助 Agent 避免重复失败。
一个完整的情景记忆通常包含:
任务:执行数据库迁移
尝试:
直接修改字段约束
结果:
迁移失败
原因:
历史数据存在空值
有效解决方案:
先清理历史数据,再执行约束迁移
验证结果:
迁移测试通过
这类记忆对 Coding Agent、自动化运维 Agent 和长期研究 Agent 非常有价值。
但是,过去一次成功并不意味着方法可以无条件迁移到所有任务。
情景记忆应该附带适用条件。
4. Procedural Memory:程序性记忆
程序性记忆保存的是如何执行任务的知识。
例如:
“在这个项目中,修改API之前必须更新Schema,完成后执行集成测试。”
它更像执行规范、操作流程或技能。
在 Coding Agent 中,这类知识可能体现在:
-
AGENTS.md
-
CLAUDE.md
-
项目 Skills
-
团队开发规范
-
测试与部署流程
需要特别注意:程序性记忆并不应该允许 Agent 随意修改系统最高优先级的安全规则。
用户偏好可以更新,项目工作流程可以根据权限修改,但系统安全边界必须独立控制。
四类记忆的对比
| 记忆类型 | 保存什么 | 生命周期 | 典型用途 |
|---|---|---|---|
| 短期记忆 | 当前对话和任务状态 | 会话或任务级 | 任务恢复 |
| 语义记忆 | 事实、偏好、约束 | 跨会话 | 个性化与知识复用 |
| 情景记忆 | 历史行动与结果 | 跨任务 | 从过去成功和失败中学习 |
| 程序性记忆 | 工作方法与规则 | 长期、可版本化 | 执行规范与技能复用 |
一个成熟的 Agent 通常不是选择其中一种,而是根据任务组合使用。
四、Memory、RAG 和 Context Compaction 到底有什么区别?
这是 Agent 开发中非常容易混淆的三个概念。
RAG:从外部获取知识
例如:
用户问:“公司的最新报销政策是什么?”
Agent 从知识库中检索官方制度。
这里主要解决的是外部知识获取问题。
Memory:保存过去的经验和事实
例如:
用户之前已经告诉 Agent:
“我所在部门使用研发中心的差旅审批流程。”
下次处理相关任务时,Agent 可以取回这条偏好或业务上下文。
Context Compaction:压缩当前执行历史
例如:
Agent 已经执行了100轮工具调用,上下文接近窗口上限。
它可以将冗余的日志和历史消息压缩为一份结构化任务摘要,然后继续执行。
三者之间存在重要区别。
| 技术 | 核心问题 | 主要数据来源 | 是否天然跨会话 |
|---|---|---|---|
| RAG | 当前缺少什么知识? | 外部文档与知识库 | 不一定 |
| Memory | 过去有哪些信息值得复用? | 历史交互与执行经验 | 是其主要目标 |
| Compaction | 当前上下文太长怎么办? | 当前任务上下文 | 否,除非另行持久化 |
| Tool-result Clearing | 哪些旧工具输出不必继续占用窗口? | 可重新读取的工具结果 | 否 |
Anthropic 在2026年3月发表的工程实践中,专门比较了 Memory、Compaction 和 Tool-result Clearing。
其核心结论是:这三种技术解决的不是同一个问题,需要根据上下文膨胀、长期任务和跨会话记忆的实际需求组合使用。
OpenAI 的《Building Reliable Agents with Memory and Compaction》也通过合规证据分析 Agent 展示了一个类似思路:
Compaction 负责当前运行的延续,Memory 让后续运行复用过去的工作经验,而最终产出的、经过人工审查的文档仍是事实依据。
这说明一个重要问题:
Memory 不应该取代原始事实来源。
Agent 可以记住之前的分析结论,但当涉及金融数据、法律条文、软件版本或最新政策时,仍然应当重新核实权威来源。
五、记忆系统真正困难的地方:不是存储,而是写入
假设一个 Agent 每天和用户交互100次。
它应该保存多少条记忆?
如果把所有对话都存下来,数据会越来越多。
但大量信息可能只有一次性价值。
例如:
“今天下午帮我检查一个文件。”
这个任务可能不需要形成长期记忆。
而下面的信息可能很重要:
“以后这个项目的所有API都要使用OpenAPI规范。”
问题来了:
Agent 如何判断一条信息是否值得保存?
我建议采用 Memory Write Policy。
一个简单的记忆写入判断模型
每条候选记忆都从以下维度评估:
持久性: 这条信息未来还会有效吗?
复用价值: 未来任务是否可能需要它?
可信程度: 信息来自用户明确声明、已验证数据,还是模型自己的猜测?
敏感程度: 是否涉及个人隐私或金融、健康等敏感数据?
变化频率: 这条信息是否可能很快失效?
例如:
| 信息 | 是否建议长期保存 | 原因 |
|---|---|---|
| 项目使用PostgreSQL | 建议 | 长期架构约束 |
| 用户喜欢简洁的技术报告 | 视授权和使用需要 | 可复用偏好 |
| 今天北京下雨 | 通常不建议 | 时效性短 |
| 上次部署失败原因 | 建议保留结构化经验 | 可避免重复错误 |
| 用户的一次性验证码 | 禁止保存 | 高敏感、无长期价值 |
| 模型推测的用户身份 | 不应作为事实保存 | 缺少可靠依据 |
最重要的是:
不是所有用户说过的话,都应该变成永久记忆。
长期记忆的写入应该受到隐私策略、数据最小化原则和用户控制机制约束。
六、Memory Consolidation:为什么 Agent 需要整理记忆?
如果只是不断追加记忆,系统迟早会出现三个问题。
第一,重复记忆。
例如:
用户偏好Python
用户习惯使用Python
用户喜欢用Python开发后端
这三条信息可能表达相近的含义。
第二,冲突记忆。
2025年:项目使用PostgreSQL
2026年:项目已经迁移到MySQL
第三,过期记忆。
当前项目版本为1.2
一年之后,项目可能已经升级到3.0。
这就需要 Memory Consolidation,即记忆巩固和整理。
它主要完成:
-
合并重复信息。
-
从多次事件中提取稳定模式。
-
发现互相冲突的记忆。
-
更新已经失效的事实。
-
将重要经验转化为可复用的知识。
-
降低长期记忆检索的噪声。
可以把这一过程理解为:
历史对话与任务执行
│
▼
候选记忆提取
│
▼
可信度校验
│
▼
重复与冲突检测
│
┌────────┼────────┐
▼ ▼ ▼
新增 合并 替换
│ │ │
└────────┼────────┘
▼
Memory Store
│
▼
周期性复核与清理
Letta 的实践值得关注
Letta 延续了 MemGPT 对分层记忆的研究方向,让 Agent 能够管理可持久化的记忆内容。
其当前文档还介绍了基于记忆文件系统的管理方式,以及 Dreaming:通过后台任务分析已有对话,整理长期有价值的知识。
这里的 Dreaming 并不意味着模型真的像人类一样做梦。
它本质上是一种异步记忆维护流程。
不过,自动整理记忆存在一个风险:
如果 Agent 把错误推断反复写入并强化,记忆可能越来越自洽,却越来越偏离事实。
因此,Memory Consolidation 需要来源追踪和冲突处理,不能仅靠语言模型给自己生成总结。
七、Agent 应该如何遗忘?
这一节可能比“如何记忆”更重要。
传统数据库系统通常追求持久保存。
但 Agent Memory 不应该默认永久保留所有信息。
原因有三个。
1. 事实会过期
例如:
“公司现在使用 Kubernetes 1.30。”
几年之后,这条记忆可能不再准确。
2. 用户偏好会改变
例如:
用户以前喜欢详细回答,现在更喜欢精简结论。
Agent 不应该因为旧记忆而忽视新的明确要求。
3. 不必要的记忆会增加风险
个人信息、任务临时数据和敏感业务资料,保存得越久,潜在的隐私和安全成本越高。
因此,建议设计明确的 Memory Lifecycle。
Created
│
▼
Active
│
├────► Updated
│
├────► Superseded
│
├────► Expired
│
└────► Deleted
其中 Superseded 表示已被新事实取代。
Expired 表示超出有效期,需要重新验证。
Deleted 表示依据用户请求、保留策略或其他合法要求删除。
对于个人敏感信息,除了从当前数据库删除,还需要考虑索引、缓存、备份和日志中的副本。
“模型不再检索这条记忆”和“数据已经被彻底删除”不是同一件事。
给 Memory 增加时间维度
记忆不应该只有 Value。
建议至少保存:
{
"id": "project-db-001",
"scope": "project:order-service",
"type": "semantic",
"key": "primary_database",
"value": "MySQL",
"source": "architecture-decision-2026",
"observed_at": "2026-10-20T10:00:00Z",
"valid_until": null,
"status": "active",
"supersedes": "project-db-000"
}
这样,当 Agent 检索数据库技术栈时,就可以优先读取当前有效版本,而不是随机召回一条旧信息。
八、如何设计一个生产级 Memory Architecture?
我建议采用分层结构,而不是一开始就依赖向量数据库存储所有内容。
第一层:Session State
保存当前对话、工具执行和任务状态。
适合使用 LangGraph Checkpointer 或关系数据库实现。
第二层:Structured Memory
保存明确的用户偏好、项目约束、业务事实和任务经验。
建议使用 PostgreSQL 等关系数据库。
第三层:Semantic Retrieval
当记忆数量较多时,再引入向量检索。
例如 pgvector、Qdrant 或其他向量数据库。
向量检索适合语义相近内容的召回,但不能直接解决版本冲突、权限控制和事实有效性问题。
第四层:Memory Manager
负责:
-
记忆提取。
-
来源验证。
-
去重与冲突检测。
-
生命周期管理。
-
记忆检索与排序。
-
用户授权和删除。
推荐架构
User
│
▼
Agent Harness
│
┌─────────┴─────────┐
▼ ▼
Context Manager Memory Manager
│ │
│ ┌───────┼────────┐
│ ▼ ▼ ▼
│ Write Retrieve Consolidate
│ │ │ │
│ └───────┼────────┘
│ │
│ ┌───────┴────────┐
│ ▼ ▼
│ PostgreSQL Vector Index
│ │ │
│ └───────┬────────┘
│ │
◄───────────────────┘
│
▼
LLM
│
▼
Tools / Actions
这里我特别建议增加 Namespace,也就是记忆作用域。
例如:
user:123
project:order-service
team:backend
agent:coding-assistant
不同用户和项目的记忆不能混用。
即使向量检索结果高度相关,也必须先执行租户和权限过滤。
相关性不能取代授权。
九、动手实现:一个支持更新和过期的最小 Memory Store
下面我们不依赖复杂Agent框架,仅使用Python标准库中的SQLite,构建一个最小的结构化记忆系统。
它支持:
-
保存记忆。
-
按作用域隔离。
-
更新旧记忆。
-
设定有效期。
-
查询当前有效信息。
这段代码可以直接运行。
import sqlite3
import time
from datetime import datetime, timezone
class MemoryStore:
def __init__(self, db_path="memory.db"):
self.db = sqlite3.connect(db_path)
self.db.execute("""
CREATE TABLE IF NOT EXISTS memories (
scope TEXT NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
source TEXT NOT NULL,
updated_at REAL NOT NULL,
expires_at REAL,
PRIMARY KEY (scope, key)
)
""")
self.db.commit()
def remember(
self, scope, key, value,
source="user", ttl_seconds=None
):
now = time.time()
expires_at = (
now + ttl_seconds
if ttl_seconds is not None
else None
)
self.db.execute("""
INSERT INTO memories
(scope, key, value, source,
updated_at, expires_at)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(scope, key)
DO UPDATE SET
value=excluded.value,
source=excluded.source,
updated_at=excluded.updated_at,
expires_at=excluded.expires_at
""", (
scope, key, value, source,
now, expires_at
))
self.db.commit()
def recall(self, scope):
now = time.time()
cursor = self.db.execute("""
SELECT key, value, source
FROM memories
WHERE scope=?
AND (
expires_at IS NULL
OR expires_at > ?
)
ORDER BY updated_at DESC
""", (scope, now))
return [
{
"key": row[0],
"value": row[1],
"source": row[2]
}
for row in cursor.fetchall()
]
def forget(self, scope, key):
self.db.execute("""
DELETE FROM memories
WHERE scope=? AND key=?
""", (scope, key))
self.db.commit()
def close(self):
self.db.close()
if __name__ == "__main__":
store = MemoryStore(":memory:")
store.remember(
scope="project:order-service",
key="primary_database",
value="PostgreSQL",
source="architecture-doc-v1"
)
# 项目发生架构迁移
store.remember(
scope="project:order-service",
key="primary_database",
value="MySQL",
source="architecture-doc-v2"
)
print(store.recall("project:order-service"))
store.close()
预期输出:
[
{
"key": "primary_database",
"value": "MySQL",
"source": "architecture-doc-v2"
}
]
虽然代码不复杂,但已经体现了一个非常关键的思想:
长期记忆应该可以被更新,而不是无限追加。
不过,这只是一个教学示例。
它采用 Key-Value 结构,直接覆盖旧版本,因此不保留完整历史。
实际项目还需要:
-
版本表与审计日志。
-
来源真实性验证。
-
写入权限与用户授权。
-
冲突审批机制。
-
敏感信息加密。
-
租户隔离。
-
备份数据保留策略。
-
并发更新控制。
-
语义检索。
-
记忆质量评测。
另外,生产系统不能仅根据更新时间就自动认为一条新信息更可信。
例如,来自不可信网页的新信息,不能直接覆盖经过人工确认的项目架构规范。
十、什么时候应该让 Agent 自动写入记忆?
Memory Write 有两种典型方式。
Hot Path:在任务执行过程中写入
Agent 在当前任务中发现重要信息,立即更新记忆。
优点是信息可以马上被后续步骤利用。
缺点是可能增加延迟,也可能把尚未验证的中间推断写入长期存储。
例如:
Agent 调查服务故障,初步怀疑数据库连接池。
如果它立即把“数据库连接池有问题”写成长期事实,后续即使发现根因是网络故障,也可能留下错误记忆。
Background Consolidation:任务完成后整理
另一种方式是在任务结束后,根据已经验证的结果提取记忆。
例如:
任务执行
↓
测试验证
↓
生成任务总结
↓
筛选可复用经验
↓
校验来源
↓
写入长期记忆
这种方式可以降低把中间假设保存为事实的风险。
对于个人助手,可以根据用户授权在交互后整理偏好和长期计划。
对于 Coding Agent,则可以在测试通过、代码合并或任务验收后提取工程经验。
我更推荐:
任务中的临时发现保存在 Task State;经过验证、确实具有长期价值的信息,再进入 Long-term Memory。
对于高风险领域,关键记忆更新应该增加人工确认。
十一、如何证明 Memory 真的提高了 Agent 能力?
给 Agent 添加一个向量数据库,并不意味着 Agent 一定变得更好。
Memory 可能提高成功率,也可能引入错误和延迟。
因此,需要专门设计 Memory Evals。
我建议重点评测六个指标。
| 指标 | 关注的问题 |
|---|---|
| Memory Recall | 该记住的信息是否能找到? |
| Memory Precision | 找到的信息是否真正相关? |
| Staleness Rate | 使用了多少过期记忆? |
| Conflict Resolution | 新旧事实冲突时是否正确处理? |
| Task Success Rate | 记忆是否真正改善任务完成率? |
| Privacy Compliance | 是否错误保存或泄露敏感信息? |
例如,可以设计一组连续任务。
任务一: 用户指定项目采用PostgreSQL。
任务二: 在新会话中设计数据库模块。
任务三: 用户明确变更为MySQL。
任务四: 再次设计数据库模块。
任务五: 要求 Agent 删除这一项目记忆。
系统应该能够:
-
正确保存初始约束。
-
在第二次任务中使用正确的约束。
-
在约束改变后更新记忆。
-
不继续使用已经失效的信息。
-
在删除后停止检索该信息,并按数据保留策略处理存储副本。
还应该设置对照组:
A:没有长期记忆的Agent。
B:直接检索完整历史对话的Agent。
C:使用结构化记忆、来源验证和生命周期管理的Agent。
在相同模型、相同任务与工具配置下比较最终任务结果。
这比仅仅观察“Agent能
- 点赞
- 收藏
- 关注作者
评论(0)