ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

智能体持久化记忆架构:三层存储与自主学习闭环实践

智能体持久化记忆架构:三层存储与自主学习闭环实践

1. 从"每轮清零"到"带着记忆工作:这个智能体要解决什么问题

去年我在做智能体的时候,反复遇到一个让人哭笑不得的场景:同一个用户,同一类需求,隔了一周再来问,我的智能体完全想不起上次怎么处理的,又把同样的坑踩一遍。市面上的很多智能体Demo,单看一轮对话确实聪明,但本质上是"每轮清零"——上一轮学到的东西、得到的纠正、留下的偏好,下一轮全都不作数。所以我动手做了一个代号叫"小龙虾"的智能体项目,核心就两个词:自主学习、持久化记忆。选这个名字没有太复杂的理由,就是觉得小龙虾生命力强,壳硬、钳子掉了还能再长出来,有点像我希望这个智能体具备的特质——在真实交互中不断适应、修复和进化,而不是被写死在一套固定规则里。

这类需求其实很普遍。个人助手、客服机器人、知识库问答系统,凡是需要长期服务的场景,都会撞上同一堵墙:模型再强,也记不住三天前的事。这篇文章不聊花哨的Agent理论,就把我实现"小龙虾"时用到的三层记忆架构、自主学习闭环、持久化落地方案,以及上线后踩过的坑完整拆开来讲。适合正在做智能体、特别是想让自己的Agent"越用越懂你"的开发者参考。

1.1 无状态智能体的三个尴尬时刻

先说我遇到的三类典型问题,如果你也在做Agent,大概率会觉得眼熟。第一类是偏好丢失:用户交代过"以后报告都用表格形式",过了两周再让我生成报告,我还是按默认的段落式输出,用户只能再纠正一遍。第二类是错误重犯:某个工具调用方式明明在上一轮已经出错并纠正过,换个会话又用错误方式去调,像是历史从未发生。第三类是积累缺失:用户连续问了几个相关主题,我不但没能连起来理解,反而每次都当全新问题处理,回答质量完全依赖单次上下文的长度。

这三个问题其实指向同一个根因:没有把"发生了什么"沉淀下来。上下文窗口只是短期工作台,会话结束就清空;真正需要的是一个跨会话、可检索、能演化的记忆层,让智能体带着上一次的经验继续工作。

1.2 小龙虾的定位与设计目标

我把"小龙虾"定位成一个长期服务的通用型智能体:既能聊天答疑,也能调用工具完成任务,关键在于它应当"越用越懂用户"。在设计时我给自己定了五个目标,后面所有架构决策基本都在为这五条服务。

  • 跨会话持久:记忆不能只活在当前上下文里,重启、换会话、换设备都要能恢复。
  • 自主学习:不需要人工整理知识库,智能体从日常交互中自行提炼经验。
  • 成本可控:记忆增长不能导致每次请求的模型调用成本线性爆炸。
  • 结果可解释:用户可以查看智能体记住了什么,也能删除不想要的记忆。
  • 行为稳定:记忆系统带来的能力提升,不能以回答飘忽不定为代价。

这五条里最难平衡的是最后一条。记忆越多,检索结果越杂,模型反而容易被带偏。后面我会专门讲怎么用"遗忘策略"和"一致性校验"来压住这个风险。

1.3 为什么记忆和学习必须一起做

这里想先纠正一个常见的理解偏差:很多人以为持久化记忆就是给Agent加一个数据库,把聊天记录存下来,下次调出来当上下文。这其实是"记录",不是"记忆"。真正的记忆系统要做三件额外的事:判断什么值得记、把零散记录整合成可复用的知识、在合适的时候主动遗忘。这三件事合起来,就是我理解的"自主学习"。

打个比方:日记本是记录,把每一天原样写下来;但人的记忆不是日记本,它会把一次次相似经历压缩成经验,把教训提炼成原则,还会顺手忘掉无关细节。小龙虾的记忆系统走的就是后一条路:原始日志保留在情景层,经过定期反思和巩固,变成语义层的知识卡片,再到推理时被检索调用。学习负责"写什么进记忆",记忆负责"让学习有积累",二者互为前提。没有学习的记忆,存再多也只是硬盘;没有记忆的学习,学再多也只是过眼云烟。

2. 三层记忆架构:会话、情景与语义记忆的分工

如果只用一个巨型向量库把所有历史问答都塞进去,检索结果必然会又乱又贵。我在小龙虾里参考了一点认知科学的分层思路,把记忆拆成三个层次:会话记忆、情景记忆、语义记忆。每一层解决一类问题,存储方式和访问模式完全不同。

2.1 会话记忆:智能体的工作台

会话记忆相当于人脑的"工作记忆",容量小、速度快、会话结束就清空。它负责当前对话的上下文组织,包括最近几轮消息、工具调用的中间结果、待办状态。这层没有太多新鲜事,但要注意两个工程细节。

一个是预算管理:我会给会话记忆设定一个token预算,比如总上下文窗口的40%留给检索回来的内容,40%留给对话记录,剩下的给系统提示词和工具输出。超过预算就按时间倒序丢弃最旧的轮次,或者用摘要替代。另一个是状态标记:工作台上要能标识"这个用户正在等某个任务结果"这类进行中状态,否则多轮协作很容易断片。这些都控制在单次请求内部,不落盘。

2.2 情景记忆:智能体的日记本

情景记忆是"发生了什么"的原始记录,只追加、不修改。它把每次交互的关键事件结构化存下来,包括时间、用户请求、智能体动作、工具返回、最终结果、用户反馈。别小看这层,它是整个学习闭环的数据源,没有它,后面所有反思和提炼都无从谈起。

事件的结构我建议尽量统一,方便后面批量处理。实际落库时,我会把事件写进关系表,字段大概是:

{ "event_id": "evt_2025xxxxxx", "session_id": "sess_xxxx", "timestamp": "2025-01-12T09:30:00Z", "actor": "user", "action": "request", "content": "帮我把销售周报转成图表", "tool_used": "chart_gen", "tool_status": "failed", "feedback": "user_corrected", "reflection_id": null }

情景记忆的特点决定了它必须用追加式的增量写入。它只负责"记住",至于"怎么看"是语义层和检索层的事。我还会在事件上打一个reflection_id字段,记录这条事件是否已经被反思任务处理过,避免重复消费。

2.3 语义记忆:智能体的知识卡片

语义记忆是最接近"学会了"的一层。它由反思任务从情景事件中提炼而来,不再关心具体某一次对话,而是沉淀出能跨场景复用的知识。我把语义记忆分成三类:偏好类(用户喜欢什么、不喜欢什么)、事实类(用户所在行业的术语、特定实体的属性)、方法类(某类任务应该怎么做、哪些路走不通)。

每条语义记忆都设计成一张卡片,带有来源、重要度、置信度和最近确认时间。重要度用于检索排序,置信度决定它会不会被实际采用,最近确认时间用于处理偏好变更。这类记忆不能无限堆叠,我会限制单个用户可以长期保留的卡片数量,超过阈值就要合并或淘汰。

2.4 分层存储的选型逻辑,以及一张关系对照表

三层记忆的存取模式差异很大,硬用一个存储方案会很别扭。我的选型逻辑是:按访问模式决定存储技术。会话记忆不落盘,原文保存在关系库里,便于按人、按时间、按会话批量离线分析;语义记忆需要按语义相似度召回,放进向量检索组件,同时用标签和实体字段做结构化过滤;偏好类这种高频点查的小数据,用键值存储最省事。

记忆层典型存储访问模式更新频率生命周期
会话记忆内存/会话缓存顺序读写每轮改写单会话结束即清空
情景记忆关系库追加日志按时间和会话查询只追加长期保留,定期归档
语义记忆向量库+键值存储相似度检索+点查反思任务批量更新长期保留,可删除/合并

这套分工让每个存储都只干自己最擅长的事:追加日志不锁库,向量检索不做全量扫描,偏好点查不会有慢查询。后面讲到反思和巩固时,你会发现这个分层还顺带解决了一个大难题——模型读文档的时候不会被几十万条原始日志糊住,因为需要被咀嚼的信息,早就被提炼成几百张卡片了。

3. 自主学习闭环:从交互记录里提炼经验的四个环节

架构搭好了,接下来就是核心机制:智能体怎么从一堆原始日志里自主学习。我把它抽象成四个环节——采集、反思、巩固、应用。听起来有点学术,其实拆开就是:把交互过程记下来、定期让模型复盘、把复盘结果写入记忆、在下次推理前把相关记忆拿出来用。

3.1 采集:把"用户纠正"当成最重要的信号

学习质量高度依赖采集质量。日志埋点越细,后面提炼越准。我的做法是在所有流程里统一打点:用户消息、模型回复、工具调用参数、工具返回状态、用户后续反馈,全部进入情景记忆。别只记录成功路径,失败的调用、报错信息、用户撤回的消息同样有价值,它们往往是学习信号最密集的地方。

在所有信号里,我最看重"用户纠正"。它几乎是免费的标注数据:用户在聊天里说"不对,应该是这样""别用表格,用图表""上次不是说过吗",都是明确告诉智能体哪里错了、哪里需要改。我会把这类消息单独打标,让反思任务优先处理。同样值得打标的还有用户的明确表扬、主动重复某类请求,以及一段时间内反复出现的相似问题——这些都能反映真实偏好。

3.2 反思:定时让模型复盘最近的交互

反思任务是小龙虾自主学习的发动机。它按调度触发,对一段窗口内的情景事件做复盘,输出候选的语义记忆卡片。触发时机我后来定为"会话结束后半小时,或事件积累到一定数量",而不是实时。这样做有三个好处:等一个完整会话结束再复盘,能看到事情的全貌;批量处理能摊薄大模型调用的成本;延迟触发还能避开用户还在交互时抢资源。

反思的本质是一次有结构化输出要求的大模型调用。输入是最近N条相关事件加一套复盘指令,输出是JSON格式的记忆候选。我整理了一套复盘指令框架,概略如下:

你正在为一个长期服务的智能体做记忆复盘。 请阅读以下交互事件,提炼出值得长期记忆的内容。 只输出JSON数组,每项包含: - type: preference | fact | procedure - summary: 一句话描述这条知识 - importance: 1-5分 - confidence: 0-1 - evidence_ids: 支撑这条知识的事件ID列表 要求: 1. 同一类偏好至少出现两次或伴随明确纠正才提炼 2. 不要把单次偶然行为当成长期偏好 3. 不输出已经存在且未被推翻的重复知识

注意第1条和第3条,这两条是我在踩了记忆污染和重复积累的坑之后加进去的硬性约束,后面会细说。反思输出不能直接进语义库,先进入一个候选区,等待下一步巩固。

3.3 巩固:去重、合并、更新、归档

巩固环节负责把候选记忆变成正式记忆,同时维护记忆库的健康。我把它做成一个定时批处理任务,干四件事。第一,去重:用向量相似度和关键字双重判断,候选卡片和已有卡片语义重复就丢弃或合并。第二,合并:多张描述同一主题的低重要度卡片拼成一张更完整的高层卡片,保留它们的证据ID便于追溯。第三,更新:如果新卡片与旧卡片冲突,根据时间戳和用户反馈决定谁覆盖谁,并用supersedes字段指向被取代的旧卡片。第四,归档:把所有已被取代、长期未命中、重要度衰减到阈值以下的卡片移到冷存储,不再参与常规检索。

这四步做完,语义记忆库才算真正被"维护"过,而不是只进不出。我一直把巩固任务比喻成"睡前整理日记":白天记录大量琐碎事件,晚上把它们压缩成几条能指导第二天行动的原则。这是整套系统从"能记"走向"会学"的关键。

3.4 应用:在推理前把相关记忆带回上下文

学习最终要服务于推理。每次用户发起请求,我会先做一轮记忆召回:把用户ID、当前消息和任务类型作为检索条件,从语义记忆里取回相关的偏好卡片、事实卡片和方法卡片,和会话记忆一起拼进提示词。召回不是越多越好,我设置了一个上限,并且按照"重要度置信度时间衰减"排序,只取最相关的前几条。

这里有个容易忽略的点:记忆应用不能只靠往提示词里塞文本。有些记忆适合隐式生效,比如用户偏好里的"报告默认用表格",我会直接把它转成任务参数,而不是讲给模型听。检索策略也要考虑请求类型:闲聊场景多召回偏好类记忆,任务场景多召回方法类记忆,避免把不相关信息混进去干扰判断。

4. 持久化的工程落地:向量检索、知识压缩与遗忘策略

前两章讲的是机制,这一章落到工程实现。持久化记忆的四个核心问题分别是:知识怎么写进向量库、怎么检索才准、怎么防止记忆无限膨胀、怎么处理过期记忆。每一个都有坑,我按实际配置逐一说。

4.1 知识怎么写进向量库:嵌入、分块与元数据

语义记忆卡片入库前要经过嵌入处理。这里的关键不是选哪个大模型的嵌入式服务,而是入库策略:我会把每张卡片拼上一个"概念前缀",再整体生成一个向量。比如方法类卡片加"这描述了如何完成某类任务",偏好类卡片加"这是用户的长期偏好"。这个看似多余的前缀能显著提升召回效果,因为原始卡片往往是一句很短的结论,直接嵌入会缺少检索语义的上下文。

分块策略也要跟着记忆类型走。短卡片整条入库,长卡片按语义断点切块,每块保留相同的元数据:用户ID、记忆类型、创建时间、来源事件ID、重要度等。元数据非常重要,后面做过滤检索全靠它。向量库的索引参数我没用默认值,检索数会放大两倍再在应用层重新打分过滤,这样做牺牲一点性能,但大大减少了漏召回。

4.2 检索不能只靠相似度:混合检索与重排

刚开始我只做纯向量相似度检索,结果发现一个很打脸的问题:用户用词和记忆卡片表述差异大时,明明存过的记忆就是召回不到。比如记忆里写的是"用户偏好简洁回复",用户问的是"能不能别啰嗦",两句话语义能对上,但向量距离往往不够近。后来我改成混合检索:向量相似度召回一批,经典的关键词统计算法(如BM25这类方案)召回一批,再用标签和实体过滤掉不适用于当前场景的记忆,最后合流重排。

重排的评分公式我调了好几版,目前用的是加权组合:相似度得分占大头,记忆的置信度、重要度、新鲜度各占小头,用户反馈级别作为修正项。线上测试下来,混合检索比纯向量检索的命中率提高了很多,尤其在偏好类和事实类记忆上提升最明显。

4.3 给记忆定期"做减法":分级压缩机制

记忆库最怕无脑增长。每个用户跑几个月就是几万条情景事件,几十张语义卡片,如果不做压缩,反思任务读不完、检索任务跑不动、成本也撑不住。我实现的是三级压缩:日常压缩把细碎卡片合并成主题卡片,每周压缩把同主题卡片再合成月度总结,每月压缩生成季度画像,同时把原始事件进一步沉淀。每压缩一次,原始级数据就向归档层推进一步。

这个机制有点像金字塔:底层是海量原始事件,中间层是主题卡片和状态知识,顶层是极少量用户画像。绝大多数检索只命中中间层和顶层,底层日志仅在做深度反思或人工审计时才启用。压缩不是简单删减,而是让每一层都保留足够的证据链——上层卡片始终挂着下层来源的ID,出问题能一路追回去。

4.4 遗忘与归档:让记忆系统"能放下"

大约半年后我意识到,记忆系统最难设计的不是记住什么,而是忘掉什么。正确答案是设计一个显式的遗忘机制,而不是让记忆库无限膨胀后自然拖垮系统。我给每条记忆算一个"衰退分数",基础分来自重要度和置信度,每隔一段时间按半衰期衰减,每次被成功召回用于回答且用户给正反馈时又会回升。低于阈值的记忆自动进入待归档区,连续多周期未回升就正式归档。

给记忆设置活跃度分数,一是控制检索噪声:太老的记忆反复混进提示词,只会让回答越来越散;二是为巩固任务提供决策依据:低活跃记忆可以优先作为被合并、覆盖的对象。我还留了手动入口,用户可以在记忆管理界面看到"这个Agent记得关于你的哪些事",想删哪条删哪条。这一条不是可选项,长期服务的智能体一定要给用户这个控制权。

我在线上跑了一套参考参数,供起步时照着调,具体值肯定要根据你的业务场景再压测调整:

配置项参考值说明
向量检索 top-k20召回条数,可放大后重排
语义相关度阈值0.72低于此处不进入候选
反思触发的累计事件数50达到即触发一轮反思
语义卡片单用户上限300超限触发合并与淘汰
记忆半衰期30天活跃度衰减到一半所需天数
归档阈值活跃度低于20且连续3个周期未回升自动归档

这些参数我专门建了个配置中心管理,方便做对照实验。大家起步阶段不用追求最优,先把链路跑通,再用真实数据慢慢调。

5. 上线后的实测与踩坑:五个必须提前知道的问题

光讲理想架构没说服力,"小龙虾"从Demo到可用的过程,有一半时间花在修问题上。我把踩过的坑挑最重要的五个写出来,每个都附上排查链路和最终解法,希望能帮后来者绕开。

5.1 记忆污染:一次偶然请求被当成长期偏好

第一次上线没多久我就发现,智能体开始变得"话多"。查日志才知道,某位用户有一次提问附带了一句"能不能解释详细一点",反思任务就提炼出了"用户偏好详细解释"的卡片,之后每次回答都长篇大论。这就是典型的记忆污染:单次偶然行为被泛化成了长期偏好。

排查链路我复述一下:先看行为异常点(话变多),再倒查合成提示词里的记忆片段,找到"用户偏好详细解释"这张卡片,然后沿着卡片上的证据ID追到原始事件,发现只有一条支撑事件,且用户并没有明确表达长期偏好。到这里根因就清楚了——反思指令缺少"频次约束"。修复方式是在复盘要求里明确加上"同类偏好至少出现两次且有支撑事件,才允许提炼"。同时我给候选区加了置信度门槛,低置信度卡片必须等第二次确认才能转正,宁可漏记,不可乱记。

5.2 新旧记忆互搏:矛盾偏好导致行为漂移

第二个坑比第一个更难发现。某个用户的偏好从"喜欢长邮件"慢慢变成了"喜欢短消息",两句话本身都被记进了语义库。之后智能体的行为开始随机漂移,有时回长文、有时回短句,用户被搞得莫名其妙。我在日志里看到同一个请求前后几轮的回答风格差异巨大,才追踪到是检索结果里同时出现了两张互相矛盾的偏好卡片。

根因是记忆系统没有处理"版本更替"。修正方案是给每张语义卡片加supersedes字段,新人换旧人时,用显式的引用关系标记旧卡片已被取代,检索时默认排除被取代的记忆。更进一步,我在巩固任务里加了冲突检测:新卡片和现存卡片语义极性相反时,先不写入,而是按时间戳和用户反馈决出胜负。上线这段修复后,行为漂移基本消失了。

5.3 反思任务的成本失控

有一阵子我的模型账单涨得飞快。定位到反思任务的日志才发现问题:巩固任务为了追求覆盖面,把过去三十天的原始事件全部灌进提示词让模型复盘,单次调用token数大得离谱,而且每天跑一次。反思是内部任务,不直接产收入,成本必须压住。

我的修复分了三步。第一步给反思窗口加滑动限制,每次只复盘最近新产生的N条事件,而不是全量重放;第二步对跨天需求用增量摘要衔接,昨天的复盘结论直接作为今天复盘的背景,不用重读原始日志;第三步把大批量生成合并卡片的任务拆到低峰时段批处理。改完以后,反思成本降了大约七成,提炼质量没有明显下降,因为增量摘要反而让模型更聚焦于增量信息。

5.4 语义检索"找不到明明存过的记忆"

混合检索是在一次线上事故后下决心做的。用户投诉智能体记错了他的偏好,我沿证据链一查,发现偏好卡片明明存在,就是没被召回到提示词里。单独跑一次向量检索,相关度只有0.58,低于0.72的阈值,被过滤掉了。原因是用户这句请求的表达和记忆卡片的原始表述几乎不同词,向量相似度就是不够。

后来我把检索改成三段式:先用向量召回一批,再用经典的关键词统计算法召回一批,两个集合合并后按个性化重排,最后才用相关度阈值过滤。同时扩展了关键词命中策略,对记忆卡片里的核心实体做别名扩展,用户说"图表"能关联上"可视化""图形"等表达。这个改动之后,同类"明明存过却找不到"的问题,出现频率显著下降。

5.5 评估缺失:怎么知道记忆系统没变蠢

最后一个坑其实不是功能缺陷,而是方法缺陷:早期我全凭主观感受评估记忆效果,结果就是每次改动都像开盲盒。后来我造了一份"记忆行为评测集":先定义一组应该稳定的用户画像和偏好场景,再准备对应的提问方式,每次改动完就跑一遍回归,看智能体是否按照预期偏好回答。

评测集规模不用大,二三十条就能拦住大部分回归问题,关键是每一条场景要真实来自往期会话,而不是我自己编的理想化模板。我还加了一个"无记忆对照组":同一批问题,让没有记忆的版本和带记忆的版本各跑一遍,对比答案质量。这样一来,每次调参、改代码都能有量化依据。建议所有做记忆系统的团队,第一天就把这套评测搭起来,别等上线后再补。

6. 这套架构还能怎么长:扩展思路与最后的建议

最后聊聊我下一步准备做的事,以及几件实在的忠告。小龙虾这套记忆架构做到现在,只是一个单智能体的记忆系统,真正的想象空间在它之上的延伸。

6.1 多智能体共享记忆与个性记忆的分离

我在规划的下一个功能,是让多个智能体在同一个记忆底座上协作:共享一个经脱敏处理的高层知识池,每个智能体各自保留一套私有个性记忆。这样既能避免重复建设常识,又能让不同角色的Agent保持各自风格。实现思路不算复杂,本质上只需在记忆卡片上增加可见性字段,巩固任务根据可见性决定写入哪个知识池,检索时按当前智能体的角色过滤。

难点在于共享知识池的污染控制:一个智能体的错误经验如果写进共享池,会被所有智能体学走,放大错误。我的策略是共享池只接受高置信度、多来源交叉验证的知识,个性池则放宽标准。这套规则还在验证中,等跑出更多数据我再单独写一篇。

6.2 把记忆控制权交还给用户

这一节是我最想强调的设计原则。记忆是用户的资产,不是智能体的资产。我在系统里做了一个完整的记忆管理界面:用户能看到所有偏好卡片,能删除、能冻结某类偏好不再自动更新,还能一键"失忆"。别觉得这是负担,恰恰相反,可控性才是用户敢长期使用智能体的前提。没有这个界面之前,用户对智能体自发产生的任何"自作主张"都会非常警惕。

技术实现上,删除记忆要连坐处理:删语义卡片的同时,把关联的情景事件标记为不可用,避免下次反思又从旧日志里把同一条知识再提炼出来。这一步不做干净,删了等于白删。

6.3 给后来者的三条建议

最后给准备动手做类似系统的朋友三条建议,都是我实际走弯路换来的。

第一,先做记忆层,再做学习层。不要一上来就追逐"自主学习"这个概念。先把情景日志、语义卡片、检索应用这整套记忆基础设施跑通,让智能体能够稳定记得用户告诉它的事,再逐步加入反思和巩固任务。这样每加一块都有清晰的正向反馈,出了错也容易定位。

第二,从第一天起就把日志埋好。很多团队做到一半想加反思任务,回头发现之前的交互没有结构化记录,只能重新开始积累。情景记忆是廉价但不可再生的资产,越早设计好事件结构,后面越省力。

第三,把遗忘当成一等公民。记住什么重要,忘掉什么同样重要。没有一个显式的衰减、合并、归档机制,记忆库迟早会变成一个又贵又乱、反而拉低回答质量的杂物间。宁可最开始写得保守一点,也好过后面手动清洗一堆噪声数据。

我自己的体会是,智能体的长期价值不在于单次回答多么惊艳,而在于第二次、第三次、第十次打交道时,它是不是真的比上一次更懂你。所谓终极进化,从来不是某一次重构带来的飞跃,而是每一次交互都在往记忆里沉淀一点点、在下一次调用时多想起一点点。小龙虾这个项目离完美还很远,但至少它证明了一件事:把记忆和学习做进架构里,而不是靠提示词临时拼凑,这条路是走得通的。如果你也在做类似的Agent,不妨从今天开始,给它装一套自己的日记本和知识卡片,剩下的进化,交给时间。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进