AI Agent记忆分层架构:上下文、工作记忆与长期记忆
2026/9/8 3:40:56 网站建设 项目流程

AI Agent 的记忆,正在成为比模型参数更让人头疼的瓶颈。你给它一份人物背景、一批项目资料和一个截止日期,第一轮它表现得像个资深同事;到第三轮,它已经开始忘记用户在第一轮强调过的约束条件。遇到过这个问题的开发者应该都有同感:模型本身没有错,指令写得也算清楚,上下文塞得足够满,但它就是记不住。

这不是上下文长度不够,而是记忆管理出了问题。

很多人在做 Agent 开发时,把记忆简单理解成“把聊天记录保存下来,下次再喂给模型”。这个做法在只有一个用户、一轮简单问答的小案例里能用,一旦任务变复杂、时间跨度变长、调用次数变多,就会立刻失效。我见过不少 AI Agent 项目,最后卡住不是因为模型不够聪明,而是因为系统不知道哪些信息该留在当前对话里,哪些该写进任务状态,哪些该沉淀到长期库。

真正需要的,不是更大的上下文,而是一套有清晰写入时机、存储形式、读取范围和淘汰策略的记忆架构。业内讨论比较多的是三层架构:上下文层、工作记忆层、长期记忆层。这篇文章就把这三层拆开讲透,顺带给出落地时可复用的管理思路。

1. 先想清楚一个问题:Agent“记不住”,到底缺的是什么

1.1 模型的上下文窗口不是记忆

这是初学者最容易踩的第一个误区:把上下文窗口当作记忆。

上下文窗口只是模型在一次请求里能看到的内容上限,本质上是一张有限宽的“草稿纸”,不是笔记本。一次请求结束后,模型对于这次请求里的大部分内容都不会主动保留。它输出完回复,这次计算就结束了,下一次调用又是一个新的开始。

也就是说,记忆这件事,模型能力只负责“理解当前看得见的内容”,而“让下一次还能看见”,是工程问题。

在简单的单轮问答里,你不需要考虑记忆。用户问一句,模型答一句,问答之间没有依赖。但只要任务被拆成多个步骤,或者需要跨多个轮次才能完成,记忆就成了必须回答的问题:哪些信息需要在下一次调用时继续保留,以什么形式保留,读多少回来。

1.2 记忆的本质是跨调用状态管理

从工程视角看,Agent 的记忆本质上是“跨调用状态管理”。

一个稍微复杂的 Agent 任务通常长这样:用户先给出背景和约束条件,Agent 先做信息收集,再调用工具或搜索,再整理分析,最后输出结果。过程中还可能遇到中间结果不对、需要重新执行某一步、需要向用户追问细节等情况。

这时候,Agent 必须在多次模型调用之间保持状态一致。它至少要回答四个问题:

  • 什么信息需要记住?
  • 存在哪里?
  • 什么时候重新读入上下文?
  • 什么时候更新、什么时候删除?

缺少任何一个答案,记忆系统都不完整。只把历史记录保存下来,只能解决一半问题;更麻烦的是,历史越长,噪声越多,模型越容易在无关信息上“分心”。

1.3 一种常见的错误做法:把对话历史当记忆

和上下文窗口误解并存的另一个弯路,是把记忆等同于对话历史拼接。

很多项目在最早期都会选择把所有历史消息按时间顺序拼在一起,重新发给模型。这个方案在 demo 阶段非常快,但进入真实任务后有三个问题:

第一,上下文窗口再大也会被填满。一旦超过限制,只能截断开头,或者压缩摘要。但摘要会丢失细节,而细节往往最值钱。

第二,噪声会越来越大。用户随口说的一句话、工具返回的冗长结果、模型自己生成的中间分析,都被无差别地塞进去。模型要花更多注意力分辨哪些内容真正重要。

第三,历史只能“重放”,不能“检索”。当历史长度超过一定规模,你无法精确回答“用户上次说的预算约束是什么”,只能靠模型从大量文本里重新找,这不仅慢,而且容易找错。

所以分层不是理论上的漂亮架构,而是工程上的必然选择。不是把记忆堆成一坨,而是让每类信息进入不同的保管方式。

2. 三层记忆架构:上下文层、工作记忆层、长期记忆层

2.1 第一层:上下文层,模型当前看到的一屏内容

上下文层是离模型最近的一层,也是最容易理解的一层。它保存的是当前这次请求需要马上用到的信息。

典型内容至少包括:

  • 系统提示(角色设定、任务目标、输出格式)
  • 用户最新输入
  • 当前请求相关的少量历史消息
  • 上一次模型输出的关键结果
  • Agent 上游模块传递过来的状态包
  • 工具返回的结果片段

这层的生命周期极短。正常情况下,一次请求结束后,上下文层里的内容就可以被清理或压缩,不需要持久化。它读取速度最快,因为模型每次请求都是直接读取。但容量也最小,受限于模型的上下文窗口长度。

设计这层的核心问题只有一个:当前这一次请求,哪些内容必须出现在模型的视野里,哪些内容可以放到底层,等需要时再取回来。

常见做法是“滚动窗口 + 摘要”。最近的几轮对话完整保留,更早的内容用摘要压缩,摘要再超过限制就进一步抽取关键事实。但摘要机制会影响 Agent 的行为一致性,所以需要额外设计,不是简单调一个压缩比例。

2.2 第二层:工作记忆层,任务进度和中间状态的保管箱

工作记忆层是很多人忽略的一层,但对多步任务来说,它才是决定 Agent 能不能真正“干活”的关键。

工作记忆保存的不是历史文本,而是任务状态。一个写报告的任务,工作记忆里应该是这样:

  • 当前任务的目标是什么
  • 已经完成到哪一步
  • 还有哪些子步骤没做
  • 哪些输入还没等到
  • 当前正在处理的文件路径或数据源
  • 用户这次任务里的临时约束

举个例子,Coding Agent 的工作记忆里可能需要记录:当前定位在哪个文件、已修改了哪个函数、上次运行测试的失败原因、下一步准备从哪开始。这些信息如果不保存,模型每调用一次,就相当于一个新手重新打开项目,什么都要重新摸索。

工作记忆的典型实现是 JSON 结构、内存对象、KV 存储或者小型数据库。它可以按任务 ID 组织,一个任务对应一份工作记忆。任务进行中,每完成一个子步骤就更新一次;任务完成后,工作记忆要么被清理,要么被整理成摘要沉淀到长期记忆。

为什么需要单独一层?因为上下文层放不下所有历史,长期记忆层又太重,读取得慢,而且容易带进无关内容。工作记忆处在中间,任务活着它就活着,任务结束它就退出。

2.3 第三层:长期记忆层,经验、事实和技能的资料库

长期记忆层的特征是生命周期最长、容量最大、写入和读取成本最高。它保存的是跨会话依然有价值的信息,通常可以分成三类:

情景记忆,记录某个用户过去的具体要求和发生过的事件。“用户上周说过预算控制在 5 万以内”“这个项目曾经因为缺少验证导致交付推迟”这类内容都属于情景记忆。

语义记忆,保存通用的领域知识和概念关系。“这个客户所在行业的合规要求是什么”“这个项目的技术栈是 Spring Boot + AI Agent 客户端”这类知识属于语义记忆。

程序记忆,沉淀经过验证的流程和技能。“每次生成报告前先拉取数据、再检查缺失值、最后写结论”“调用外部接口前先做参数校验”这类步骤属于程序记忆。

长期记忆的存储方式,也不像很多人想的那样只能放向量数据库。更稳妥的做法是“双通道”配合使用:

向量化通道负责语义相似度检索。适合模糊回忆,比如“用户之前提过类似的需求吗”,可以在没有精确关键词的情况下找到相关片段。

结构化通道负责精确读取。适合事实校验,比如“用户的姓名是什么”“上次对预算是怎么明确的”。这种信息放进关系表或 JSON 字段里,读取时更快,也更可靠。

只有向量检索,容易把相似但错误的内容调进来;只有结构化存储,又处理不了用户表达方式的变化。两者配合起来,才接近一个可用的长期记忆系统。

2.4 三层对比:存储形式、生命周期和读取成本

层级典型存储形式生命周期读写频率容量主要职责
上下文层模型输入、Prompt 模板、状态包一次请求最高当前轮理解与生成
工作记忆层JSON、内存对象、KV 存储、数据库表一个任务或会话任务进度、中间状态、临时决策
长期记忆层向量库、关系表、文件、记忆引擎跨会话长期保留用户偏好、领域知识、技能流程

表格里的“典型存储形式”不是唯一答案。具体用什么,取决于你的技术栈和数据量。但分层的大方向是确定的:越临时的内容,读取越快、存储越简单;越长期的内容,存储越稳、读取越看重检索质量。

3. 为什么分层比单层更实用:读写成本、遗忘机制、扩展边界

3.1 读写成本和容量差异决定了必须先分诊

如果把所有记忆都放在上下文层,最直接的问题是容量耗尽。调用次数一多,上下文窗口再大也撑不住。如果把所有记忆都放在向量库,每次请求都要先做一次检索,再决定取多少内容回来,整体延迟会明显上升,而且引入噪声的概率也更高。

所以记忆分层的本质,是给不同信息做“分诊”:

  • 马上要用的,直接放上下文层;
  • 当前任务过程需要的,放工作记忆层;
  • 以后可能反复用的,才沉淀到长期记忆层。

不分层,单层存储到一定规模后一定会遇到性能和数据质量的双重问题。分层不是为了让架构更复杂,而是为了让系统在规模变大之后依然可控。

3.2 记忆不是只增不减,遗忘机制很重要

很多 Agent 项目在写长期记忆时,永远只做 insert,不做 delete,也不做 update。这种“只增不减”的设计,很快会让长期记忆变成一堆噪声。

原因在于检索机制。向量检索通常是按相似度取 TopK。如果长期记忆里已经有 100 条关于用户偏好的记录,但其中 80 条来自三个月前,模型很可能会被旧偏好带偏。尤其当用户明确改变过态度时,新旧记录同时存在,模型很难判断以哪条为准。

一个真正可用的记忆系统,必须把遗忘当成一等公民。遗忘不一定是物理删除,可以有以下几种形式:

  • 显式删除:用户主动纠正后,旧内容标记为废弃,不再参与检索。
  • 时间衰减:很久没被访问且置信度低的内容,在检索排序中逐渐降权。
  • 归档:重要但不再高频使用的内容,移到冷存储,只在特定场景下搜索。
  • 冲突标记:新旧内容并存时,把版本状态标出来,提示后续调用方需要确认。

没有遗忘机制,记忆越多,模型越是不知道该信哪条。这反而是更大的不智能。

3.3 分层的额外价值:可观测、可定位、可维护

分层还有一个容易被忽略的价值:可以观测和定位问题。

不分层的记忆系统,一旦 Agent 行为异常,你几乎无法判断是哪一类信息出了问题。是历史消息里混入了噪声?是任务状态没有保存成功?还是长期记忆里存了错误事实?所有可能混在一起,排查成本极高。

分层之后,每个行为异常都有大致方向:

  • 当轮就忘了刚提到的事,大概率是上下文层被截断或摘要过度;
  • 任务中途丢失目标,大概率是工作记忆没有正确持久化;
  • 重启后完全不认识用户,大概率是长期记忆没有加载或写入失败;
  • 回答里出现无关知识,大概率是检索层召回太宽或排序不合理。

这种可观察性,对线上系统的长期维护来说,比任何算法优化都重要。

3.4 分层原则:重要内容放得稳,临时内容放得快

分层架构也要防止走进另一个极端:为了分层而分层,把简单问题复杂化。

如果只是一个客服问答机器人,没有多步任务,也没有跨会话长期需求,那么“上下文层 + 一个简单的会话存储”就够了。不需要一上来就搭向量数据库和记忆引擎。

更通用的设计原则是:内容越重要,存放得越稳;内容越临时,读取得越快。至少要把“一次请求内”和“跨请求”这两类分开,这已经是分层的底线。至于是否要把长期记忆再细分为情景、语义、程序三类,取决于你的业务复杂度。

4. 落地时最容易踩的坑:污染、过期、误读、丢失

4.1 记忆污染:模型推断被当成了用户事实

这是长期记忆系统里最隐蔽、也最危险的问题。

模型在推理过程中会生成大量“可能性判断”。比如它可能在用户没有明确表态的情况下,基于数据进行推断:“用户可能偏好极简风格”。如果这套推断被直接写入长期记忆,下一次任务里,模型就会把它当成既定事实使用,最终产出方向上出现偏差。

我在自己的项目里定过一条规则:长期记忆的写入,必须带有“来源”和“置信度”。

  • 用户明确说出的偏好,来源记为user_stated,置信度高,可以直接使用。
  • 模型根据上下文推断出的偏好,来源记为model_inferred,置信度低,只能作为临时候选,不能直接升为长期记忆。
  • 工具返回的可验证事实,来源记为tool_result,写入前还要做一次字段校验。

同时,写入动作不能完全交给模型自己决定,至少要在外层加一道过滤规则。比如当模型说“用户提到/用户要求/用户曾经”这类引用式表达时,才允许写长期记忆;当它说“我觉得/可能/大概”时,只允许写临时缓冲区。

注意:不要把模型的一次合理推断,直接当成用户的确定需求写进长期记忆。这种污染一旦发生,后面所有轮次都会被带偏。

4.2 记忆过期:旧版本没有时间戳和替换策略

第二个常见问题,是长期记忆里同时存在相互冲突的旧版本和新版本。

用户会改变想法。项目方向会调整。业务规则会更新。如果长期记忆里的每条记录没有时间戳,更新逻辑又没有版本控制,模型很可能检索到几天前甚至几个月前的旧规则,并把它当成当前规则。

从工程实践看,处理方式应该是:

  • 每条长期记忆记录至少带created_atupdated_at两个时间字段;
  • 写入新事实前,先按主题检索是否存在同类型旧记录;
  • 如果发现冲突,不直接物理删除旧记录,而是标记为archivedsuperseded
  • 在模型读取时,优先展示有superseded_by关联记录的最新版本。

只有把记忆当成一份会被反复修订的文档,而不是一堆追加写入的日志,才能避免旧事实长时间占用模型注意力。

4.3 读取误判:向量检索把“相似”当成“正确”

向量检索是一门模糊技术。它按语义相似度排序,不保证按逻辑正确性排序。两条信息可能表面措辞相似,语义指向却是完全不同的两件事。

如果你把向量检索结果直接拼进 Prompt,模型很容易把“看起来相关”的内容当成“事实正确”的内容。尤其在高风险场景里,比如医疗建议、法律判断、项目决策,这种误判会很危险。

因此,从长期记忆回来之后,不能直接使用。更稳妥的流程是:

  1. 先用向量检索召回候选内容;
  2. 再加一层过滤,可以是规则过滤,也可以让模型判断“候选内容与当前问题是否真的相关,时间是否过期,来源是否可靠”;
  3. 对于关键事实,优先从结构化记录里精确读取,而不是依赖模糊搜索。

检索的目标不是“召回的越多越好”,而是“召回的越准越好”。

4.4 重启失忆:工作记忆和长期记忆没有持久化

很多 Agent 项目在自己电脑上跑 demo 没问题,但放进真实环境就出现“失忆”:进程一重启,它什么都不记得了。原因通常是记忆只存在内存里,没有落盘。

工作记忆至少要支持序列化。常见做法是把任务状态同步到一个数据库,每次子步骤完成就更新一条记录。长期记忆则需要有稳定的持久化层,可以是 SQLite、PostgreSQL,也可以是文件加索引。

初始化 Agent 时,还应该有一个“记忆加载”过程:从持久化层读入长期记忆,根据当前任务 ID 恢复工作记忆,最后再组装到上下文层。可以把它理解为 Agent 的“开机恢复流程”。

如果缺少这步,即使存储层已经落盘,重启后 Agent 也不会主动加载,用户依然会感知到“失忆”。

5. 一个可复用的记忆管理框架:写入-存储-检索-更新-遗忘

5.1 写入:先判断值不值得记

每次准备写入记忆前,可以先问三个问题:

  • 这个信息在当前任务结束后,还会继续有用吗?
  • 它是用户明确表达的,还是模型主观推断的?
  • 它和已有记忆是否存在重叠或冲突?

如果三个问题都不明确,就不要急着写入。宁可在需要时重新问一次用户,也不要污染长期记忆。

对于临时任务产生的中间数据,可以直接进工作记忆;对于用户偏好、项目事实、已验证的技能,才考虑进长期记忆。

5.2 存储:双通道落位

写入之后,存储阶段要选择正确的通道。

工作记忆的存储建议使用结构化格式,以任务 ID 为 Key。这样即使并发处理多个任务,也不会互相覆盖。长期记忆则可以按类型分到两个通道:情景记忆和语义记忆进向量库进行语义检索;需要精确读取的事实,存关系表或结构化字段;程序记忆则以技能卡片或代码片段库的形式保存。

5.3 检索:按场景选择召回方式

检索不能只依赖一种方法。不同的场景,检索策略应该不同:

  • 需要精确事实,比如“用户的公司名是什么”,直接读结构化字段;
  • 需要模糊回忆,比如“用户之前提过哪些关于预算的偏好”,用向量检索召回,再做相关性过滤;
  • 需要执行步骤,比如“之前是怎么处理数据缺失的”,按技能名称和标签读程序记忆。

检索之后,结果还要经过一道重排。可以结合时间衰减、来源置信度和当前主题相似度,给每条记忆打分。

5.4 更新:先查重,再谨慎覆盖

更新操作最容易出错。

我一般不建议直接覆盖旧记录。因为模型并不知道“旧信息是否真的是错误信息”,很可能只是用户某个临时场景下的特殊表达。更安全的是:

  • 先按主题查重;
  • 如果冲突,给旧记录标记archived
  • 写入新记录,并保留旧记录的引用关系;
  • 等到用户或外部调用方确认新信息无误后,再真正废弃旧记录。

这样即使后续发现问题,也能回滚到旧版本,不至于因为一次错误的覆盖丢失全部历史。

5.5 遗忘:显式删除、时间衰减、归档

遗忘策略可以有三条路。

用户主动纠正后,要显式删除或标记废弃。这是最高优先级的遗忘动作,必须立即生效。很久未访问且置信度低的内容,通过时间衰减降低它在检索排序里的权重。大项目结束后,中间过程可以归档,只保留最终结论和关键决策依据。

遗忘不是记忆系统的失败,而是保持记忆质量的手段。没有遗忘,长期记忆最终会变成一锅粥。

5.6 一个最小记忆管理器的代码示意

这里给出一个不依赖具体框架的最小结构,帮助你理解各层之间如何协作。真实项目里会用它接数据库、接向量库,但核心接口基本一致。

class AgentMemory: def __init__(self): self.context = [] # 上下文层,本轮内容 self.working_memory = {} # 工作记忆层,按 task_id 组织 self.long_term = [] # 长期记忆层,含 metadata def write_context(self, message): self.context.append(message) def write_working_memory(self, task_id, key, value): if task_id not in self.working_memory: self.working_memory[task_id] = {} self.working_memory[task_id][key] = value def read_working_memory(self, task_id): return self.working_memory.get(task_id, {}) def write_long_term(self, content, source, confidence, timestamp): # 写入前先做查重 item = { "content": content, "source": source, # user_stated / model_inferred / tool_result "confidence": confidence, # 0.0 - 1.0 "ts": timestamp, "status": "active" } self.long_term.append(item) def retrieve_long_term(self, query, top_k=3): # 真实项目里这里会接向量检索,做相似度召回和重排 # 这里简化为返回前 top_k 条,仅作为结构示意 candidates = [item for item in self.long_term if item["status"] == "active"] return candidates[:top_k] def update_long_term(self, old_item, new_content, timestamp): # 标记旧记录归档,再写入新记录 old_item["status"] = "archived" self.write_long_term(new_content, "user_update", 1.0, timestamp) def forget(self, item): item["status"] = "deleted"

这个代码示例反映的是记忆管理的基础流程,实际项目中要替换的核心逻辑是retrieve_long_term:这里需要换成向量检索、相似度阈值、时间衰减和重排逻辑。但写入、更新、遗忘的边界,和上面这份示意是相通的。

6. 给不同场景的配置建议,以及出问题后的排查链路

6.1 不同场景的分层配置建议

不是所有 Agent 都需要完整的三层记忆,需要分层,但深度可以不同。

简单对话助手:上下文层 + 一个轻量会话记录即可。没有多步任务的情况下,工作记忆只需要保存会话 ID 和最后的摘要。长期记忆可以后置,等数据量增加后再接。

多步任务型 Agent:比如数据分析 Agent、文档写作 Agent、Coding Agent,工作记忆层需要重点设计。任务进度必须结构化,每完成一个子步骤都要更新状态。长期记忆里可以存项目规范和过往代码风格。如果你是在 Java 技术栈上开发 Spring Boot + AI Agent 客户端这类项目,特别要注意工作记忆的持久化,因为服务进程可能随时重启,任务状态不能只保存在内存里。

知识管理 / 个人助理型 Agent:比如 Obsidian + AI Agent 知识库这类场景,长期记忆是核心。它需要考虑知识跨会话长期存在、文件夹或标签的索引、双通道检索、定期整理归档。这类 Agent 最容易栽在长期记忆污染上,因为知识库内容来源杂,写入时更需要来源校验。

6.2 一个有用的排查链路:按现象定位记忆层

当 Agent 表现异常时,不要上来就怀疑模型能力,先按下面的链路定位是哪一层出了问题。

异常现象优先排查的记忆层检查点
当前对话里刚说的内容,下一轮就忘了上下文层是否发生了截断?摘要是否丢失关键细节?
任务进行到中途,丢失目标和进度工作记忆层任务状态是否持久化?每个子步骤完成后是否更新?恢复时是否重新注入上下文?
服务重启后完全不认识用户长期记忆层长期记忆是否落盘?启动时是否正确加载?工作记忆是否做了序列化?
回答里出现大量无关知识长期记忆层的检索链路检索 TopK 是否太大?相似度阈值是否过低?召回后是否做了相关性过滤?
记住过时或错误的信息写入与更新策略写入时是否带时间戳、来源和置信度?旧记录是否标记了废弃?

这个排查顺序,总体遵循“先看现象,再看输入,再看环境,再看参数,最后看工具边界”。多数记忆问题都不是模型不够好,而是工程链路里某一环没接上。

6.3 如果要长期运行,还需要补齐工程能力

记忆系统要长期稳定运行,除了三层架构本身,还需要几项工程能力:

  • 日志:每次记忆写入、读取、更新都要有清晰记录,方便回溯。
  • 权限:不同用户或不同项目的记忆要隔离,避免交叉污染。
  • 并发控制:多个请求同时更新同一条记忆时,要加版本号或最后写入时间,避免互相覆盖。
  • 重试和补偿:底层存储临时不可用时,写入操作要有失败重试,不能静默丢失。
  • 监控:长期记忆增长量和检索命中率要纳入监控,数据量增长到一定程度后,需要拆分或归档。

这些都是容易被忽略、但决定系统能否从 demo 走向生产的关键点。

7. 最后回到一个判断:记忆管理的终点不是存储,而是准确

7.1 记忆的准确比容量更稀缺

很多人会在 Agent 的记忆上做加法:更大上下文、更多向量条目、更完整的对话历史。但从我的经验看,记忆系统的瓶颈从来不是容量,而是准确度。

一条带有时间戳、明确来源、经过校验的记忆,比一万条模糊的、过期的、未经确认的片段更有价值。记忆不是给模型“喂得越多越好”,而是让模型“每次都能读到最该读的那几条”。

7.2 下一步最该做的第一件事

如果你正在开发一个 AI Agent,并且发现它在多轮任务里表现不稳定,我的建议是:不要急着换更大的模型,也不要急着调 Prompt。先把你自己的 Agent 跑一遍真实任务,把每一条信息按“上下文层、工作记忆层、长期记忆层”分一次类,看看哪一层缺失,哪一层混入了噪声。

先把最小可用的记忆管理器跑通,再逐步加入持久化、检索、更新和遗忘。当你能清楚地回答“某条信息为什么被记住、存在哪里、什么时候被读回、什么时候被删掉”这四个问题时,Agent 的记忆才算真正有了体系,而不是一个装满了聊天记录的垃圾箱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询