AI Agent 的记忆系统怎么设计?从短期记忆、长期记忆到 RAG,讲透 Memory 工程化

AI Agent 的记忆系统怎么设计?从短期记忆、长期记忆到 RAG,讲透 Memory 工程化

作者:王仕宇(JavaPub)

现在做 AI Agent,大家都很喜欢讲一个词:Memory,也就是记忆。

一个 Agent 如果能记住用户的偏好、项目的技术栈、之前做过的事情,确实会比每次都从零开始的聊天机器人好用很多。

但真正开始开发以后,你会发现事情没那么简单。

明明已经给 Agent 接入了向量数据库,它还是会忘记之前的要求。明明项目已经升级到 MySQL 8,它却还在按照旧版本分析问题。甚至用户已经明确修改了需求,Agent 过几轮对话又把旧需求拿出来了。

所以我觉得,Agent 的记忆问题,不是简单接入一个向量数据库就能解决的。

真正要做的是一套完整的记忆系统。

一、上下文不等于记忆

先说一个很多人容易混淆的问题。

假设用户说:

"帮我开发一个 Go 项目,使用 Gin、GORM 和 MySQL。"

模型在当前对话里当然记得。

因为这句话就在上下文中。

但如果对话越来越长,早期内容被压缩,或者用户明天重新打开一个会话,模型可能就不知道这个项目使用什么技术栈了。

这时候,我们通常会考虑把信息保存起来。

但这里有两种完全不同的数据。

第一种是会话上下文:

text 复制代码
用户刚才问了什么?
Agent 刚才调用了什么工具?
最近一次执行结果是什么?

第二种是长期记忆:

text 复制代码
项目使用 Go + Gin。
ORM 使用 GORM。
数据库使用 MySQL 8。
项目采用 Docker Compose 部署。

前者服务于当前任务。

后者可以跨会话复用。

所以我更喜欢把它们分开管理。

Context 负责当前推理,Memory 负责跨时间保留有价值的信息。

二、不是所有聊天记录都值得保存

这是我觉得做 Memory 最容易踩的坑。

有些 Agent 的记忆系统非常简单:

用户说一句话,就存进向量数据库。

Agent 回答一句话,也存进去。

执行一次工具,再存进去。

最后数据库里有几万条记录。

看起来记忆很多,实际上检索质量可能越来越差。

比如:

text 复制代码
用户:数据库使用 MySQL 5.7。

三个月后:

用户:我们已经升级到 MySQL 8 了。

如果直接把两句话都存进去,Agent 以后检索时就可能同时找到:

text 复制代码
MySQL 5.7
MySQL 8

然后随机选择一个。

这显然不合理。

因为这里发生的不是新增知识,而是:

已有事实发生了变化。

所以 Memory 系统不能只会 Insert。

还必须支持:

text 复制代码
Create
Update
Invalidate
Delete

也就是记忆的创建、更新、失效和删除。

三、我更建议把 Agent 记忆分成四类

如果让我设计一个 Agent Memory 系统,我会先分成四种。

1. Working Memory:工作记忆

保存当前任务直接需要的信息。

例如:

json 复制代码
{
  "task_id": "task_10001",
  "current_step": "fix_api",
  "current_file": "handler/user.go",
  "last_error": "invalid request payload"
}

这类信息变化很快。

任务结束后,未必需要长期保留。

2. Episodic Memory:事件记忆

保存过去发生过的重要事情。

例如:

text 复制代码
2026-10-20:

用户排查过一次 Docker OOM。

最终原因:
容器 memory limit 配置过小。

解决方式:
调整资源限制并优化应用内存。

这类记忆的价值在于:

下次出现相似问题时,Agent 可以参考历史经验。

但历史经验不能直接当作当前故障的根因。

仍然需要重新检查证据。

3. Semantic Memory:事实记忆

保存相对稳定的知识。

例如:

text 复制代码
项目使用 Go。
数据库版本是 MySQL 8。
部署使用 Docker Compose。
API 使用 RESTful 风格。

这类信息可以跨任务复用。

4. Procedural Memory:过程记忆

保存工作流程和操作规范。

例如:

text 复制代码
修改 Go 代码以后,
必须执行 go test ./...

修改 Docker Compose 以后,
需要执行 docker compose config。

生产环境数据库变更,
必须经过人工审批。

这类记忆不只是告诉 Agent"知道什么"。

而是告诉它:

做事情时应该遵循什么流程。

不过需要注意,真正重要的安全规则和权限边界,仍然必须由程序强制执行,不能只放在可检索的 Memory 里。

四、一个 Memory 记录应该包含哪些字段?

很多人设计记忆表,可能只有:

text 复制代码
id
content
embedding

这对于简单语义搜索够用,但对于真正的 Agent,我觉得还不够。

至少还应该考虑:

json 复制代码
{
  "memory_id": "mem_10001",
  "scope": "project",
  "subject": "project_backend",
  "key": "database_version",
  "value": "MySQL 8",
  "source": "user_confirmed",
  "confidence": 1.0,
  "status": "active",
  "created_at": "2026-10-20T10:00:00Z",
  "updated_at": "2026-10-28T10:00:00Z"
}

这里几个字段非常重要。

scope 表示作用范围。

比如是用户级、项目级还是某个任务级。

source 表示信息来源。

是用户明确确认的,还是 Agent 从日志里推测的?

confidence 表示证据可信程度。

但它不能被误认为模型生成的百分数就是真实概率。

status 表示记忆是否仍然有效。

如果数据库从 MySQL 5.7 升级到了 MySQL 8,旧记忆就应该失效,而不是继续参与普通检索。

五、Memory 的作用域必须设计清楚

这是非常重要的一点。

假设同一个用户维护三个项目:

text 复制代码
项目 A:
Go + Gin + MySQL

项目 B:
Java + Spring Boot + PostgreSQL

项目 C:
Python + FastAPI + Redis

如果所有记忆都放在一个集合里,没有明确作用域,Agent 就很容易混淆。

比如正在开发项目 A,却检索到项目 B 的 PostgreSQL 配置。

所以我更建议至少区分:

text 复制代码
User Memory
Project Memory
Task Memory
Session Memory

检索时先做权限和作用域过滤,再做语义检索。

例如:

text 复制代码
当前用户:
user_1001

当前项目:
project_A

当前任务:
task_2001

那么 Agent 只应该读取当前任务需要、且当前用户有权限访问的记忆。

Memory 检索首先是权限问题,其次才是相似度问题。

这一点在多租户 SaaS 系统里尤其重要。

六、向量数据库到底负责什么?

向量数据库主要解决的是:

语义相似性检索。

比如用户问:

"之前那个容器内存问题是怎么解决的?"

记忆里保存的是:

text 复制代码
Docker 容器因为 cgroup memory limit 导致 OOMKilled。

虽然用户没有直接说:

text 复制代码
cgroup
OOMKilled

但 Embedding 可以帮助找到语义相近的记录。

大致流程:

text 复制代码
用户问题
    ↓
Embedding
    ↓
Vector Search
    ↓
候选记忆
    ↓
过滤与重排
    ↓
注入 Agent 上下文

但需要注意:

向量数据库解决的是"可能相关"。

并不天然解决:

text 复制代码
哪条记忆最新?
哪条记忆已经失效?
哪条记忆来自用户确认?
哪条记忆属于当前项目?

这些问题必须由业务层处理。

所以我认为:

Vector Search 是 Memory 系统的一个组件,不是 Memory 系统本身。

七、RAG 和 Memory 也不是一回事

现在很多人会把 RAG 和 Memory 混在一起。

但两者关注点并不完全相同。

RAG 更像:

text 复制代码
用户提出问题
    ↓
检索相关知识
    ↓
将知识提供给模型
    ↓
生成回答

它经常检索:

text 复制代码
技术文档
产品手册
代码仓库
企业知识库

Memory 更关注:

text 复制代码
过去发生了什么?
用户已经确认了什么?
当前任务进行到哪里?
哪些信息发生了变化?

比如:

text 复制代码
项目 README

可以作为 RAG 的知识来源。

而:

text 复制代码
用户昨天明确要求不能修改数据库结构

更适合作为项目或任务记忆中的约束。

两者可以结合,但最好不要在数据模型上完全混为一谈。

八、真正困难的是 Memory Write

很多人以为记忆系统最难的是检索。

但我反而觉得:

真正难的是决定什么值得记住。

比如用户说:

"今天这个接口又报 500 了。"

这句话需要长期保存吗?

不一定。

但如果后面经过排查,确认:

text 复制代码
某个服务的 HTTP 500
是因为连接池没有正确释放连接。

那么这个经过验证的故障结论就可能值得保存。

所以我更喜欢这种写入流程:

text 复制代码
对话 / 工具结果
       ↓
提取候选记忆
       ↓
判断是否值得长期保存
       ↓
识别作用域
       ↓
检查是否已有同类事实
       ↓
更新或新增
       ↓
保存来源和时间

而不是:

text 复制代码
所有消息
    ↓
全部 Embedding
    ↓
直接入库

尤其用户没有要求持久记忆时,还需要遵循产品的隐私设置和数据保存规则。

不是任何用户信息都适合自动存储。

九、Memory Update 比 Memory Insert 更重要

继续看数据库版本这个例子。

旧记忆:

json 复制代码
{
  "key": "database_version",
  "value": "MySQL 5.7",
  "status": "active"
}

用户后来明确说:

"项目已经升级到 MySQL 8 了。"

系统不应该简单新增一条:

json 复制代码
{
  "key": "database_version",
  "value": "MySQL 8"
}

而应该根据同一项目、同一事实键以及新的确认信息,更新当前有效版本。

可以保留历史记录:

text 复制代码
MySQL 5.7
状态:superseded

MySQL 8
状态:active

这样既保留变更历史,也不会让旧配置继续干扰当前回答。

不过并不是所有冲突都意味着旧记忆应该被覆盖。

比如:

text 复制代码
开发环境使用 MySQL 8。

生产环境使用 MySQL 5.7。

这两句话可能同时正确。

所以更新记忆前,必须先检查作用域、时间和语义。

不能仅仅因为内容相似,就互相覆盖。

十、Agent 应该什么时候检索记忆?

我不建议每次模型调用都把所有长期记忆取出来。

比如用户只问:

"1 + 1 等于几?"

根本没有必要检索整个项目知识库。

但如果用户说:

"继续昨天的用户管理模块。"

那 Memory 就非常重要。

所以我更喜欢:

text 复制代码
用户问题
    ↓
判断是否需要历史上下文
    ↓
识别用户 / 项目 / 任务
    ↓
检索候选记忆
    ↓
过滤过期和无关内容
    ↓
注入必要记忆

检索也可以按类型进行。

比如:

text 复制代码
继续项目开发

优先检索任务状态、项目规范和最近决策。

而:

text 复制代码
这个错误以前发生过吗?

优先检索历史事件记忆。

这样比无差别向量搜索更准确,也更省 Token。

十一、记忆太多,也会污染上下文

假设系统从数据库里检索了 100 条记忆。

然后全部塞给模型。

可能出现:

text 复制代码
用户偏好
旧项目配置
历史错误
临时调试信息
无关日志

真正有用的只有三条。

这不但浪费 Token,还可能降低判断质量。

所以 Memory Retrieval 应该有明确的预算。

例如:

yaml 复制代码
memory:
  max_candidates: 30
  max_selected: 8
  max_context_tokens: 3000

这些数字只是示例。

真正使用时,还需要根据模型上下文和任务复杂度调整。

更重要的是重排逻辑。

我一般会考虑:

text 复制代码
相关性
时效性
来源可靠性
作用域
当前有效状态

而不是单纯按向量相似度排序。

最相似的记忆,不一定是最应该相信的记忆。

十二、Memory 必须允许"不确定"

比如 Agent 从日志中发现:

text 复制代码
Redis timeout

于是推断:

text 复制代码
可能是 Redis 网络不稳定。

这个结论还没有经过验证。

如果 Memory 系统直接保存成:

text 复制代码
Redis 网络存在问题。

以后 Agent 可能反复引用它。

这样一次不可靠的推断,就逐渐变成系统里的"既定事实"。

我觉得这是非常危险的。

所以最好区分:

text 复制代码
Observed
Inferred
Confirmed

例如:

json 复制代码
{
  "fact": "Redis 网络可能不稳定",
  "evidence_type": "inferred",
  "status": "unverified"
}

当后续检查确认:

text 复制代码
真正原因是磁盘 IO 导致 Redis 延迟。

就应该纠正原来的推断。

不能让未经验证的假设长期污染 Agent 的知识库。

十三、一个实际的 Agent Memory 架构

如果让我基于 Go 或 Python 做一个 Memory 服务,我会先设计成:

text 复制代码
                  AI Agent
                     │
          ┌──────────┴──────────┐
          │                     │
          ▼                     ▼
     Memory Reader         Memory Writer
          │                     │
          ▼                     ▼
     Scope Filter          Fact Extractor
          │                     │
          ▼                     ▼
     Hybrid Search         Conflict Checker
          │                     │
          ▼                     ▼
       Reranker            Memory Updater
          │                     │
          └──────────┬──────────┘
                     │
                     ▼
                Memory Store
                     │
          ┌──────────┼──────────┐
          │          │          │
          ▼          ▼          ▼
        MySQL      Vector DB   Object Store

MySQL 可以负责:

text 复制代码
记忆元数据
作用域
状态
来源
更新时间

向量数据库负责:

text 复制代码
语义检索

对象存储可以保存:

text 复制代码
大型日志
任务产物
历史快照

当然,这只是一个可扩展的架构。

对于小项目,完全可以先使用 PostgreSQL 加 pgvector,或者一个关系数据库配合简单的全文搜索。

甚至刚开始用 SQLite 都可以。

不要一做 Memory 就上复杂的分布式系统。

先把数据模型和更新规则设计清楚,比选哪一个向量数据库更重要。

十四、Memory 需要一个"来源引用"

这是我觉得很值得做的能力。

比如 Agent 回答:

"你的项目使用 MySQL 8。"

它最好能够知道:

这个信息来自哪里?

例如:

text 复制代码
来源:
用户在项目讨论中确认。

确认时间:
2026-10-28。

作用域:
project_A。

如果来源是:

text 复制代码
Agent 根据旧配置推断。

那可信程度就不一样。

对于一些重要信息,我甚至建议在 Agent 内部保留:

text 复制代码
source_id
source_type
verified_at

这样以后发生冲突,可以回到原始来源核对。

这和我们前面讲的 Agent 证据链,其实是同一个思路。

记忆不只是保存结论,还应该保存结论从哪里来。

十五、长期记忆需要遗忘机制

很多人希望 Agent:

"永远记住所有事情。"

但从工程角度看,我觉得不一定是好事。

比如:

text 复制代码
一次性的临时调试端口
已经失效的测试环境地址
三个月前的错误日志
旧版本的部署参数

这些信息如果长期保留,反而可能干扰 Agent。

所以我更喜欢给 Memory 增加生命周期管理。

例如:

text 复制代码
临时任务状态:
任务结束后归档。

临时故障推断:
一段时间后复核或失效。

稳定项目规范:
长期保留,变更时更新。

历史事故记录:
归档保存,需要时检索。

注意,这里的遗忘不是简单定期删除所有旧数据。

而是根据记忆类型,决定保留、归档、失效或删除。

对于个人数据,还需要支持用户查看、更正和删除的权限。

这才是一个真正可用的 Memory 系统。

十六、Agent 的 Memory 也需要评测

前面写过 AI Agent 的评测体系。

Memory 同样需要测试。

比如准备一组场景:

测试场景 预期行为
用户确认新技术栈 更新当前有效记忆
旧版本信息仍然存在 不把旧配置当作当前事实
两个项目使用不同数据库 不跨项目混淆
信息来自未经验证的推断 保留不确定性
用户要求继续昨日任务 找到正确任务状态
检索结果与当前文件冲突 核对来源和时间
用户无权访问其他项目 不返回其他项目记忆
用户要求删除个人记忆 按产品的数据删除机制处理

我觉得这里尤其要测试:

记忆冲突、过期信息和跨项目隔离。

因为真正危险的 Memory 错误,很多不是"完全没记住"。

而是:

记住了错误的事情,还特别自信地拿出来用。

十七、王仕宇的观点:Memory 最难的不是记住,而是知道什么该相信

我是王仕宇,JavaPub。

我现在越来越觉得,Agent 的记忆系统和传统数据库其实有很多相似之处。

比如:

text 复制代码
Memory Write
≈
数据写入

Memory Update
≈
数据更新

Memory Scope
≈
租户隔离

Memory Conflict
≈
数据一致性

Memory Expiration
≈
生命周期管理

Memory Retrieval
≈
查询优化

这些问题本来就是后端工程里经常处理的。

但 Agent 又多了一层难度:

模型可能会把推测当成事实。

也可能把过期事实当成当前事实。

所以真正成熟的 Memory 系统,不应该只是:

text 复制代码
Embedding
+
Vector Database
+
Top K

而应该是:

text 复制代码
事实提取
+
来源管理
+
作用域隔离
+
冲突处理
+
状态更新
+
语义检索
+
时效控制

这一整套机制。

我觉得对于后端工程师来说,这反而是一个很值得深入的方向。

因为它不只是 Prompt Engineering。

而是真正涉及数据模型、检索、权限、状态和一致性的系统设计。

最后

我是王仕宇,JavaPub。

AI Agent 想要长期工作,就绕不开 Memory。

但一个好的记忆系统,不是把用户所有聊天记录都存进向量数据库。

而是能够区分:

哪些信息只是当前任务的临时上下文,哪些是已经确认的项目事实,哪些是可以复用的历史经验,哪些又是需要严格遵守的工作流程。

更重要的是,它需要知道:

什么信息已经过期,什么结论没有得到验证,哪些记忆只属于某个项目,以及什么时候应该更新或删除。

如果只记住一句话:

AI Agent 的记忆能力,不取决于它存了多少条数据,而取决于它能否在正确的时间、正确的作用域内,找到当前有效、来源可靠、真正有用的信息,并且知道什么时候应该修正旧记忆。

本文由王仕宇(JavaPub)整理与分享,持续分享后端开发、AI Agent 和工程化实践。

相关推荐
智恒百亿1 小时前
一台 5090 八卡服务器怎么分给一个团队用?多模型多任务的显存分配实战
java·大数据·人工智能
MCT1236541 小时前
AI VRM 高频设计|功率电感磁材选型与电源损耗、效率关系
人工智能·dc-dc电源·功率电感·一体成型电感
汤姆宝库1 小时前
安全深拆:越狱、Sleeper与可解释性
人工智能
skywalk81631 小时前
lightmem记忆系统实战存取.小浣熊一句话:从lightmem记忆中,取出FreeBSD linux仿真环境安装启动workbuddy的信息,整理成文档
人工智能·硬件架构·记忆·lightmem
Solange__1 小时前
在线3D预览器、Blender和游戏引擎怎么选?按5类任务比较格式、隐私与编辑性
人工智能·游戏·3d·游戏引擎·blender·1024程序员节·关卡设计
镜子AI2 小时前
镜子AI 内容方法论:GEO 到底怎么起作用,以及一份四步操作清单
人工智能·dubbo
小oo呆2 小时前
【自然语言处理与大模型】自然语言处理的漫漫长路之文本向量化
人工智能·自然语言处理