封面
返回上一级

FluxMem:把记忆看成一张会不断改写的图

2026.05.29
27
📂 Research
# Agent Memory
# LLM

论文链接:Rethinking Memory as Continuously Evolving Connectivity

这篇论文带给我的启发

最近读 Agent Memory 的论文时,我越来越觉得,很多工作虽然都在谈"记忆",实际做的仍然是检索:把历史内容存起来,等新任务到来后再找几段相似文本塞进 Prompt。检索得准不准当然重要,但它默认了一件事:记忆本身已经整理好了,我们只需要把它找回来。

FluxMem 换了一个角度。它不把记忆看成一排等待检索的文档,而是看成一张会随任务持续变化的图。哪些事实应该连到当前步骤,哪些历史经验值得复用,某条技能是不是写得太粗,这些都不是一次写死的。Agent 会根据执行反馈去加边、删边,必要时直接改写节点内容。

我读完后的第一反应是,论文把讨论往前推了一步:开始检索之前,记忆结构本身真的整理好了吗?

静态记忆的问题不只在"记不住"

静态记忆系统中的连接错误与粒度错位

Figure 1:静态记忆系统的主要问题。

传统流程通常是 Extract -> Compress -> Index -> Retrieve。工程上很好理解,也方便拆模块,但到了长链路任务里,它会暴露两类问题。

第一类是连接不准。系统可能漏掉真正有用的历史经验,也可能召回一堆看起来相关、实际会干扰当前决策的内容。前者造成上下文缺失,后者占用 Context Window,还可能把模型带向错误答案。

第二类是记忆粒度不合适。只保存"任务和答案"太粗,API 调用、状态变化和失败原因都丢了;把完整轨迹和全部日志原样塞进去又太细,可复用的操作规律反而埋在细节里。

这里有个我很认同的判断:记忆失效不一定是因为没存,也可能是因为连错了,或者存成了不适合当前任务的形状。

FluxMem 如何表示记忆

FluxMem 用异构图 G=(V,E)\mathcal{G}=(\mathcal{V},\mathcal{E}) 表示记忆。节点分成三层:

  1. 语义知识层 Vsem\mathcal{V}_{sem} 存放事实性内容,例如对话历史、知识文档和工具 API 说明。

  2. 情景经验层 Vepi\mathcal{V}_{epi} 记录具体任务里的状态和动作轨迹:

    τq={(ot,at)}t=1T\tau_q=\{(o_t,a_t)\}_{t=1}^{T}

    每个任务都会产生自己的情景节点。调试日志、工具调用顺序、环境返回结果都属于这一层。

  3. 过程技能层 Vproc\mathcal{V}_{proc} 保存从多次经验中归纳出的操作模板,例如多步规划策略或统计分析流程。这些节点由后面的长期巩固阶段生成。

三层之间有两类主要连接。Eground\mathcal{E}_{ground} 把语义知识连到情景经验,表示某条事实为任务中的一步提供证据;Edistill\mathcal{E}_{distill} 把情景经验连到过程技能,表示某项技能是从哪些历史轨迹中提炼出来的。

每执行一步,FluxMem 都会激活其中一小部分节点,得到当前任务的局部子图 Gt(q)\mathcal{G}_t(q),然后把它序列化为工作上下文:

St(q)=Concat(q,Obst,Vtsem,Vtepi,Vtproc)S_t(q)=\operatorname{Concat} \left(q,\operatorname{Obs}_t, \mathcal{V}^{sem}_t, \mathcal{V}^{epi}_t, \mathcal{V}^{proc}_t\right)

于是,Prompt 里出现什么内容,取决于当前子图有哪些节点和边。优化上下文也就变成了修改这张局部图。

三个阶段,一张不断变化的局部图

FluxMem 三阶段架构与三种拓扑编辑

Figure 2:Stage I 和 Stage II 在线运行,Stage III 离线巩固长期技能。

这张图基本把整篇论文讲完了。前两个阶段发生在任务执行过程中,第三个阶段放在任务结束后离线进行。

Stage I:先搭出一个能用的上下文

Stage I 做初始连接。系统先从语义层召回事实,再从情景层找相似任务,最后沿着已有的提炼边继承相关技能。

语义检索除了向量相似度,还会结合 BM25 和 LLM verification 打分。情景经验按向量相似度取 Top-K,过程技能则从这些情景节点已有的 Edistill\mathcal{E}_{distill} 连接中继承。三部分合起来,形成当前步骤的初始上下文 St(q)S_t(q)

这个上下文只是起点。它可能漏信息,也可能带进噪声,所以论文没有把第一次检索当成最终答案。

Stage II:根据反馈修连接、改内容

Agent 用 St(q)S_t(q) 执行一步后,会收到环境反馈或自我验证结果。Stage II 根据失败原因做三种编辑:

  • 缺少信息时执行 Link Expand,激活原本没有连进来的节点,并建立新边。
  • 出现噪声或幻觉时执行 Link Prune,切断干扰当前任务的连接。
  • 连接本身没错,但节点太粗或太细时执行 Node Reshape,直接改写记忆单元的内容。

前两种操作改的是"该不该连",第三种改的是"节点里应该写什么"。我觉得这个区分很实用。工具选错和思路写错看起来都会导致任务失败,但需要修的地方其实不同。

Stage II 会反复执行,直到任务成功,或者达到预设的最大修正轮数 TT

Stage III:把反复成功的经验沉淀成技能

任务完成后,系统把新的轨迹写入情景层,并按语义相似度对历史情景做聚类。每个簇里的共同模式会被归纳成一个过程技能节点,再通过 Edistill\mathcal{E}_{distill} 连回产生它的情景经验。

这一阶段解决的是另一个问题:相似任务每次都重新检索、重新试错,等于只把旧流程又跑了一遍,谈不上学会。Stage III 希望把反复有效的路径固定下来,让以后遇到类似任务时可以直接激活成熟技能。

不过,第一次总结出来的技能未必可靠。它可能有逻辑错误,也可能只是把原始轨迹换了一种说法。于是论文又加了一轮测试和改写。

PEMS:技能什么时候才算学稳了

PEMS 的全称是 Procedure Evolution Maturity Score。第 kk 轮演化时,系统会拿当前技能重新运行生成它的源任务,再计算:

PEMS(k)=η(Vproc(k))log(Vproc(k))(1δ(Gcons(k),Gcons(k1)))\operatorname{PEMS}^{(k)} = \frac{\eta\left(\mathcal{V}_{proc}^{(k)}\right)} {\log \ell\left(\mathcal{V}_{proc}^{(k)}\right)} \left( 1-\delta\left( \mathcal{G}_{cons}^{(k)}, \mathcal{G}_{cons}^{(k-1)} \right) \right)

这里同时考虑了三件事:

  • η(k)\eta^{(k)} 是源任务的平均成功率。技能首先得真的能把任务做对。
  • (k)\ell^{(k)} 是技能文本的 Token 长度。描述越冗长,长度惩罚越明显。
  • δ(k)\delta^{(k)} 是相邻两轮技能表示的差异。差异持续很大,说明技能还没有稳定下来。

低分技能会根据执行结果继续重写。相邻两轮的提升 ΔPEMS\Delta\operatorname{PEMS} 小于阈值 ϵ\epsilon 后,系统停止离线演化。

我对这个指标的好感主要来自它给长期巩固设了一个停止条件。它没有完整定义什么是"好技能",但能阻止 LLM 一直总结下去,越改越长,最后还偏离原任务。

三组实验分别在测什么

论文选了 LoCoMo、Mind2Web 和 GAIA三个Benchmark。三者放在一起并不是为了凑三个榜单,它们对记忆的要求差别很大。

LoCoMo:长上下文里找对证据

LoCoMo 测长对话中的单跳、多跳、时间和开放域问题。下面是 GPT-4.1-mini 作为底座时的平均 LMJ 分数:

方法 LoCoMo 平均分
Full Context 81.23
EverMemOS 93.05
FluxMem 95.06

把全部上下文直接交给模型只得到 81.23,FluxMem 达到 95.06。这里最直观的收益来自连接修正:答案本来就在历史文本里,系统需要做的是把正确证据接到当前问题上,同时别让无关内容挤进来。

Mind2Web:网页导航更依赖可复用技能

Mind2Web 的网页里有大量候选元素。论文同时测试了人工过滤元素和不做过滤的设置。我更关注后者,因为它更接近真实网页。下面是 GPT-4.1-mini 在无人工元素过滤时的 Cross-Task 成功率:

方法 Cross-Task SR
AWM 3.6
FluxMem 8.1

这个 8.1 仍然不算高,但相对 AWM 的 3.6 已经有明显变化。网页任务不是只找一句事实,它要求 Agent 连续选择元素、判断页面状态,还要把过去的操作经验变成下一次能复用的流程。这里 Stage III 的价值比 LoCoMo 更明显。

GAIA:复杂任务中的整体收益

GAIA 包含检索、工具调用和多步推理。使用 Kimi K2 时,论文报告的平均成功率如下:

方法 GAIA 平均成功率
Flash-Searcher 52.12
FluxMem 64.85

绝对提升是 12.73 个百分点。这个结果说明 FluxMem 不只适合对话记忆。只要任务过程中会反复出现工具选择、失败反馈和可复用步骤,它的图编辑机制就有发挥空间。

消融实验里我最关心的两件事

FluxMem 阶段消融、修正轮数与 PEMS 收敛

Figure 3:LoCoMo、Mind2Web 上的阶段消融,以及修正和演化轮数分析。

先看图 (a) 和图 (b)。LoCoMo 上去掉 Stage II 后下降最明显。GPT-4.1-mini 的平均分从 95.06 降到 85.32,Qwen3-30B-A3B 也从 93.44 降到 84.74。长上下文问答的证据已经存在,结果主要取决于系统能否根据反馈补齐缺失连接、剪掉噪声连接。

图 (c) 换成 Mind2Web 后,Stage III 的影响更突出。网页导航需要跨多个步骤复用操作规律,只有短期检索修正还不够。没有长期巩固,Agent 每次都得重新摸索页面。

第二件事是修正到底要做多少轮。图 (d) 中,LoCoMo 的平均分从 0 轮时的 85.32 上升到 5 轮时的 95.06,但后面的增益越来越小。图 (e) 里的 PEMS 也在第 4 轮后趋于平缓。反馈循环有效,但不值得无限运行。PEMS 在这里更像一个刹车,不是另一个需要追高的榜单分数。

Case Study:工具选错和思路算错是两种问题

FluxMem 在 GAIA 表格推理任务中的修正过程

Figure 4:一个 GAIA 表格推理任务中的 Link Prune、Link Expand 和 Node Reshape。

这个案例很适合把三种编辑放到具体任务里看。题目要求读取 olympic_athletes.csv,找出平均每名运动员获得奖牌数最高的国家。

任务开始时,Stage I 激活了 CSV Reader、Spreadsheet Viewer 和 Python Data Analysis 三份工具知识,也找到了一个处理 GDP 排名的历史任务。继承到的表格问答技能很粗,只写着 load csv -> inspect columns -> answer

第一轮执行中,Agent 成功读到表头,却调用 Spreadsheet Viewer 打开整张表。文件太大,环境返回 render timeout。这个失败来自工具连接不合适,所以 FluxMem 剪掉 Spreadsheet Viewer 的边,再把当前任务连到 Python Data Analysis API。

换成 Pandas 后,代码能运行了,但 Agent 根据奖牌总数选出了 country1。自我验证发现题目要比较的是人均奖牌数,不是总数。这次工具没有问题,错误来自技能粒度太粗。FluxMem 因此执行 Node Reshape,把原来的表格问答技能改成:

group by entity
-> derive metric
-> normalize
-> compare

下一轮按国家分组,计算 avg_medal 后再取 argmax,最终答案变成正确的 country2

同样是答错,第一次需要换工具,第二次需要改思路。FluxMem 没把所有失败都归结成"再检索一次",这一点比案例最后答对本身更值得看。

读完之后,如何理解 FluxMem

论文读完后,我更愿意把 Agent 记忆理解成一个持续整理的过程。内容存下来只是第一步,后面还要反复调整它与任务、经验和技能之间的关系。

这套设计最吸引我的地方,是它把上下文错误拆成了连接问题和节点内容问题。缺信息就扩边,噪声太多就剪边,抽象层次不对就重写节点。等相似任务积累到一定程度,再把成功路径压成技能。整套逻辑能和 Agent 实际遇到的失败对应起来,不只是换一个更复杂的向量数据库。

当然,它的代价也很清楚。Stage II 会增加在线执行轮数,Stage III 需要离线重跑源任务;Top-K、最大修正轮数和 PEMS 阈值都会影响成本。论文的实验仍然基于静态数据集,开放环境里的记忆衰减、任务边界模糊和巩固调度仍没有答案。

所以我暂时不会把 FluxMem 看成一套已经完成的长期记忆方案。它更像是在提醒我,做 Agent Memory 时不能只问"存什么、怎么搜",还要问:这段记忆现在和谁相连,为什么相连,失败以后又该改哪里。