一个 Agent 可以准确回答“项目以前用什么运行时”,却在今天发布时拿出已经失效的配置。它也可能记得你的限制,只是你没有明确问起时,它从未把那条记忆取回来。
这比“上下文窗口不够大”更难处理。我最看好的路线,是把记忆组织、主动检索和证据检查连成一个过程:当前任务缺什么,就去取什么;取到以后,还要确认它现在适用。
本文核查截至 2026 年 9 月 8 日 可见的论文与作者仓库,选取五类方法和两项近期评测。这里的“效果突出”指论文在明确条件下给出了可检查的收益,不是跨榜单选一个永久冠军。下面既有研究结果,也有我实际运行的版本检查小实验和成本复算。
先分清:记忆、知识库与当前上下文
假设 Agent 要“把这个项目升级后发布”。昨天的排错经历属于记忆,今天的官方迁移文档属于外部知识;这次推理真正看到的那几段内容,才是当前上下文。
三者不能互相替代。记忆告诉它去哪里看、以前为什么失败;实时查询告诉它现在是什么情况;上下文决定这一次能用哪些证据。如果这些概念还混在一起,可以先读知识库、RAG 和 Agent 记忆的区别。
| 名词 | 本文中的意思 |
|---|---|
| 长期记忆 | 跨任务保留的事实、经历或操作经验,不保证仍然有效 |
| 工作记忆 | 当前任务暂存的目标、约束、证据和未解决问题 |
| RAG | Retrieval-Augmented Generation:先取外部资料,再据此生成 |
| 混合检索 | 用语义相似、关键词、关系和时间等不同方式共同找候选 |
| 证据缺口 | 想得出结论,还缺哪一条事实或哪一段关系 |
| 重排 | 对初步候选重新排序,把有限上下文留给更有用的资料 |
| 消融实验 | 移除一个组件,看效果如何变化,用来判断收益来自哪里 |
| LLM Judge | 让另一个模型按规则评分;分数会受裁判和评分提示影响 |
我会盯住四个部件,而不是先纠结选哪种数据库。
| 部件 | 控制什么 | 改动的影响 | 缺了会怎样 |
|---|---|---|---|
| 写入 | 原文、事实、推断、经验怎样保存 | 抽取得更细有利于定位,但会增加处理成本 | 丢失条件,把猜测存成事实 |
| 获取 | 搜什么、去哪里搜、要不要继续搜 | 多轮搜索能补关系,代价是延迟 | 找到相关内容却缺决定性证据 |
| 装配 | 哪些信息常驻,哪些按需加载 | 常驻约束更容易被看见,但持续占预算 | 明明记得,关键时刻没想起 |
| 更新 | 何时失效、被替代、重新核验 | 更积极核验提高新鲜度,也增加调用 | 旧事实反复影响新行动 |
这些设计之所以出现,是因为未来任务未知,而每次决策可见的信息和读取预算有限。如果完整历史总能以零成本、无干扰地被可靠使用,选择、压缩和检索就没有今天这么重要。再加上事实会变化,“保存下来”自然不等于“以后可直接使用”。
玩法一:分层保存,用多条路径找回——Hindsight
可以把记忆库想成有原始凭据的档案室:事实、经历、归纳和判断各有位置。对“某人现在负责什么”这类问题,关键词可能找出姓名,关系能串起职位变化,时间条件再排除旧任职。
Hindsight 论文把记忆分成世界事实、Agent 经历、综合观察和意见四类,提供 retain、recall、reflect 三种操作。ACL 2026 系统论文说明其检索结合语义、关键词、图关系与时间信息,底层使用 PostgreSQL 和 pgvector。
最值得看的是同底座对照:原论文表 3 的 LongMemEval-S、500 道题中,GPT-OSS-20B 全上下文基线为 39.0%,Hindsight 搭配相同底座为 83.6%。这是完整系统的准确率收益,不能归因成“换了向量数据库就涨 44.6 个百分点”,也不是对所有业务的保证。
我的落地取舍是:先保存“事实 + 原文定位 + 生效时间”,再考虑复杂图结构。例如保留“配置在变更单 A 中被改成 B”,通常比一句“项目已经升级”更容易支持后续判断。写入时可以做归纳,但原文要能重新打开。
这里复用的是数据库的老经验:原始记录用于追溯,派生视图用于加速。归纳错了可以重建;只留下归纳,就很难恢复被压掉的限制条件。
玩法二:搜索由“缺什么证据”驱动——EviMem
问“升级后可以发布吗”,查到“已改为新版本”只能完成一半。还需要同一次变更的测试、构建或验收结果。把原问题换几种措辞反复搜,不如直接搜“这次升级对应的验证结果”。
EviMem把检索做成循环:检查已有证据,描述缺口,再定向补查。其 LaceMem 分为可搜索的原子事实、关系边和原始对话;IRIS 判断证据是直接充分、可推得,还是仅部分相关,并保留原始问题路径以减少搜索跑偏。
它的对照值得细读。论文表 1 使用 GPT-4o 生成答案、GPT-4o-mini 执行辅助步骤;单次检索与 EviMem 使用相同的 LaceMem 记忆。LoCoMo 时间题从 58.8% 到 81.6%,多跳题从 81.4% 到 85.2%。但相对外部基线 MIRIX,总体 Judge Accuracy 只是 75.9% 到 76.5%,单跳、开放域和对抗题还低于 MIRIX。
因此,我会优先把它用在跨时间、跨实体的证据拼接,先测难题收益。简单事实查询不必默认进入多轮循环。
flowchart TB
A[任务与有效约束] --> B[检索候选并读取原文]
B --> C{证据足够且适用吗}
C -->|是| D[回答或执行]
C -->|否且有预算| E[明确缺口再补查]
E --> B
C -->|预算耗尽| F[报告缺项或冲突]
图中是我的工程化组合,并非论文算法的逐行复制。特别是“适用”还要由业务定义:配置对应哪个提交、测试覆盖哪个环境、结论允许用于什么动作。检索器无法仅靠相似度替你补齐这些定义。
玩法三:让 Agent 操作长材料——RLM
如果问题是“从整库记录中找出所有满足条件的组合”,取最相似的几段天然不够。任务可能要求遍历、过滤、计数和交叉核对,像在分析一个数据集。
Recursive Language Models,v3把长输入放在模型可操作的外部环境里。Agent 在 Python 交互环境中查看、切分材料,必要时递归调用子模型处理片段,最后汇总。材料留在环境,主模型不必一次吞下全文。
论文表 1 中,GPT-5 为主模型、GPT-5-mini 为子调用模型,OOLONG-Pairs 的 F1 从直接调用的 0.1 提升到深度 1 的 58.0、深度 3 的 76.0。但同表中 Qwen3-Coder 的部分任务随递归加深反而下降。递归深度不是越大越好,效果依赖任务和底座。
这条路线解决的是“怎样读取和计算上下文”,跨会话保存与过期更新还得另外实现。更完整的背景可接着读把推理交给程序的三种方式。
对知识获取,我会先做一个便宜的路由:
| 任务形态 | 优先的读取方式 | 要留下的证据 |
|---|---|---|
| 精确错误码、符号名、配置项 | 关键词搜索、代码搜索、结构化查询 | 文件位置与版本 |
| 概念相近、用词不同 | 语义与关键词混合召回,再重排 | 原文片段与来源 |
| 关系横跨多处 | 分解问题,按缺口迭代检索 | 每一跳的来源 |
| 全量统计、枚举与比较 | 数据库查询、程序遍历,必要时子模型分类 | 覆盖范围与中间结果 |
| 随时变化的外部状态 | 查询当前 API 或正式文档 | 获取时间与有效范围 |
这是我的选型建议,不是 RLM 的五项评测结果。固定模式的数据处理能交给确定性程序,就先交给程序;需要语义判断的部分,再让模型参与。
玩法四:学习“怎么写记忆”——MemSkill
“提炼重要信息”太宽泛。一个经历可能要保留时间线,另一个要保存失败原因,还有一个要明确标记“尚未确认”。只用同一段摘要提示,很容易将它们压成相似的句子。
MemSkill,v2将记忆操作组织成可选技能:控制器选技能,执行器按技能生成记忆,设计器根据难例修改或增加技能,并支持退回较好的技能快照。它优化的是记忆加工方式,而不只是把更多记录塞进库里。
论文表 1 的 LLaMA-3.3-70B-Instruct 组中,LoCoMo 的 LLM Judge 分数是 53.82,同组 A-MEM 为 49.71;表 2 去掉设计器后降至 46.50。这里有学习和进化流程,不能只复制几个技能提示就声称复现了收益。
我更愿意先借它的失败驱动方法:收集“忘了日期”“漏了否定条件”“总结把两个人混在一起”的样本,再逐类调整写入规则。每次规则变更都重跑未参与修改的样本,避免只把已经见过的题修好。
玩法五:一起学习写入、检索与遗忘——AgeMem
AgeMem,v3进一步让 Agent 学习何时添加、更新、删除长期记忆,以及何时检索、压缩、过滤当前上下文。它在 HotpotQA 上强化学习训练,再评测知识问答和交互环境。
表 2 中,Qwen3-4B 的 ALFWorld 成功率从不做强化学习的 AgeMem-noRL 38.02% 到完整 AgeMem 48.97%;但 HotpotQA 的对应分数为 54.49 到 55.49。它证明了统一策略有潜力,收益并不在所有任务上等幅出现。
对普通应用团队,我的顺序是:先把操作日志和结果反馈收集起来,再决定是否训练。每次删记忆都不知道后来有没有用,每次检索都不知道是否改变结果,就缺少训练策略所需的反馈。
短期可以人工调整规则;当失败样本足够、结果可评测且任务分布相对稳定时,再考虑学习策略。要优化的是后续任务表现,不能只奖励“摘要更短”或“工具调用更多”。
七月至九月的新信号:用户没问,记忆还会被用上吗
InMind,2026 年 7 月 27 日专测隐式关联。它的 125 个任务中,把关键记忆直接放进上下文,GPT-5-mini 的间接任务表现是 84.0%;要求系统自己找回时,六个记忆系统最高 14.4%,另列的朴素 RAG 对照最高 16.0%。这不是 Hindsight、EviMem 或所有记忆系统的联合排名。
这个区别非常实际:直接问“之前有哪些限制”,与给出一个需要遵守限制的新任务,检索线索完全不同。模型可能具备连接两者的常识,却没有机会看到那条记忆。
我会据此尝试一小层常驻约束,并在每次加载时检查是否仍适用。例如项目的当前发布目标、必须满足的验收条件,先于详细历史进入工作记忆。其余信息按需取回。这是受论文启发的设计假设,常驻条目的选择仍需评测;把整个用户档案常驻也会带来干扰。
LoCoMo-Conv,2026 年 9 月 3 日又把原问答改成直接对话、隐式、错误前提和组合请求。表 4 中,AnchorMem 加多角度查询改写后,隐式检索召回从 0.368 到 0.524;抽象记忆系统 mem0 的对应值却从 0.456 到 0.453。相同查询技巧,并不适配所有记忆表示。
这项刚发布的预印本还不足以决定产品选型,但它提醒我:验收要包含自然请求和纠错场景,不能全是“请回忆第几天说过什么”。
我跑的小实验:相关记录为什么仍然不能直接用
为了把上面的讨论落到具体判断,我构造了一个发布准备场景。项目第 1 天使用 Node20,第 7 天切到 Node22,第 8 天测试通过。另有一条第 9 天的模型推断:“可能该用 Node24”。这些都是合成记录,不代表真实项目或运行时推荐。
我固定检索候选,不调用模型,单独检查读取后的决策契约。这里故意将“当前版本”“历史版本”“验证结果”“模型推断”分开,看看接收层是否能拒绝不适用的证据。
下面是完整脚本,Python 3.10+、无需第三方库。start、end 是简化的生效日编号,区间包含起点、不包含终点;None 表示没有记录终止日。needed 是调用方要求的事实类型列表。
import json
from dataclasses import dataclass, replace
@dataclass(frozen=True)
class Fact:
id: str
key: str
value: str
start: int
end: int | None = None
kind: str = 'evidence'
source: str = 'change-log'
old = Fact('v1', 'runtime', 'Node20', 1, 7)
new = Fact('v2', 'runtime', 'Node22', 7)
check = Fact('t1', 'tests', 'pass@Node22', 8)
guess = Fact('g1', 'runtime', 'Node24', 9, kind='inference')
def decide(candidates, day, needed):
valid = [m for m in candidates if m.kind == 'evidence'
and m.source and m.start <= day
and (m.end is None or day < m.end)]
selected = {}
for key in needed:
hits = [m for m in valid if m.key == key]
if not hits:
return 'NEED_EVIDENCE:' + key
if len({m.value for m in hits}) > 1:
return 'CONFLICT:' + key
selected[key] = hits[0].value
return '|'.join(selected[k] for k in needed)
cases = [
('current', [old, new], 9, ['runtime'], 'Node22'),
('historical', [old, new], 3, ['runtime'], 'Node20'),
('stale_only', [old], 9, ['runtime'], 'NEED_EVIDENCE:runtime'),
('missing_test', [new], 9, ['runtime', 'tests'], 'NEED_EVIDENCE:tests'),
('complete', [new, check], 9, ['runtime', 'tests'], 'Node22|pass@Node22'),
('inference_only', [guess], 9, ['runtime'], 'NEED_EVIDENCE:runtime'),
('no_source', [replace(new, source='')], 9, ['runtime'], 'NEED_EVIDENCE:runtime'),
('conflict', [new, replace(old, end=None)], 9, ['runtime'], 'CONFLICT:runtime'),
]
for name, facts, day, needed, expected in cases:
actual = decide(facts, day, needed)
print(json.dumps({'case': name, 'result': actual, 'ok': actual == expected}))
assert actual == expected
保存为 check_memory.py 后运行:
python3 check_memory.py > results.jsonl
wc -l results.jsonl
cat results.jsonl
我的原始输出中,wc -l 为 8,八个 ok 均为 true。结果逐条如下:
| 场景 | 输出 | 它检查的事情 |
|---|---|---|
| current | Node22 | 旧记录不能支配当前决策 |
| historical | Node20 | 历史问题不能一律回答最新版 |
| stale_only | NEED_EVIDENCE:runtime | 只有过期候选时继续找证据 |
| missing_test | NEED_EVIDENCE:tests | 改了配置不等于测试通过 |
| complete | Node22|pass@Node22 | 返回已具备的两类事实 |
| inference_only | NEED_EVIDENCE:runtime | 推断不能冒充已证实事实 |
| no_source | NEED_EVIDENCE:runtime | 缺来源的候选不进入此证据通道 |
| conflict | CONFLICT:runtime | 生效区间冲突时不擅自选一个 |
我最在意 historical 和 conflict。只按“最新一条”排序,看起来解决了过期问题,却会答错历史查询;两条都有效时,机械选新的又掩盖了数据矛盾。
这不是记忆框架对比实验,更不是端到端准确率。它只验证我写下的接收规则;事实抽取是否正确、生效时间如何得来、检索会不会遗漏,均不在这八个测试内。source 这里只检查非空,生产系统还须验证来源确实存在、可信且允许访问。
另一个不能省的扩展是绑定对象:pass@Node22 还不足以许可发布,实际应绑定提交、测试范围和目标环境。实验中的 complete 仅表示两类字段齐全,没有执行发布动作。
从这个小实验反推记忆结构
我会把一条可用于行动的记忆最少组织成以下几部分。这是从失效情况推导的业务设计,不是任何论文声称的统一标准。
| 内容 | 一个例子 | 为什么要保留 |
|---|---|---|
| 事实与类型 | runtime=Node22,已证实 | 与猜测、计划区分 |
| 适用对象 | 某项目、某分支或提交 | 防止一个环境的结果套到另一个 |
| 时间与版本 | 生效日、失效日、替代记录 | 同时支持当前和历史问题 |
| 依据 | 配置片段、变更单、测试报告 | 能回原文验证 |
| 推导依赖 | 这个经验来自哪次失败与修复 | 前提变化后知道该撤销什么 |
这会改变“压缩”的目标。一次失败可以被浓缩成“遇到这个错误先检查 X”,但必须保留触发条件和适用版本。否则 Agent 记住的是口号,下一次换了环境仍会照做。
这里与可验证环境如何影响 Agent 学习相接:只奖励一次回答看起来正确,可能把错误经验长期固化。记忆更新的反馈应尽量来自后续真实结果。
再算一笔账:索引记忆何时值得提前加工
很多“节省上下文”的方案先在写入阶段花了大量工作。只看最后一次问答输入,会漏掉记忆抽取、关系构建和更新成本。
我做了一个简化预算例子,以下数字都是假设,不是任何产品报价或论文实测:
- 直接读取路线:每次读取 30,000 个输入 token。
- 预加工路线:一次性消耗 1,000,000 个输入 token,此后每次读取 3,000 个。
- 暂时假设输入 token 单价相同,忽略输出、缓存折扣、存储、更新和失败重试。
Token 是模型处理文本的计量单位,不等于汉字数。在这些假设下,每次查询节省 27,000 个输入 token;需要 1,000,000 ÷ 27,000 ≈ 37.04 次查询,才能摊平预加工量,因此从第 38 次开始低于直接读取。
| 查询次数 | 直接读取累计输入 | 预加工后累计输入 | 预加工路线的输入量节省 |
|---|---|---|---|
| 10 | 300,000 | 1,030,000 | −243.33%,反而更多 |
| 38 | 1,140,000 | 1,114,000 | 2.28% |
| 100 | 3,000,000 | 1,300,000 | 56.67% |
复算代码如下,本文表格已用同样计算核验:
from decimal import Decimal as D
build, direct, indexed = D('1000000'), D('30000'), D('3000')
print('break_even =', build / (direct - indexed))
for count in (10, 38, 100):
before = direct * count
after = build + indexed * count
print(count, before, after, round((1 - after / before) * 100, 2))
这笔账解释了我的选择:长期重复服务同一批资料,值得考虑精细记忆加工;一次性读取且资料频繁更新,按需查询可能更合算。现实中加入缓存、输出和更新以后,临界点会移动。
更重要的是先设质量底线。便宜但老拿错版本的记忆不能进入候选方案。通过质量验收后,再比较每个成功任务的总成本与延迟。
真要落地,我会按这个顺序做
第一步是保留可回读的原始材料,搭一个足够强的基线。精确字段走结构化查询,关键词和语义检索共同找候选,按实际输入预算比较,不用相同的 top-k 冒充相同成本。十条长摘要与十条短事实并不占用相同上下文。
第二步是把失效原因记下来。每个失败样本判断:没写进去、没搜出来、搜出了旧内容、原文不支持摘要、证据齐了但模型没用好。只测最终答案,会把这些问题都误判成“底座不够强”。
第三步才加针对性的机制。漏关系就尝试证据缺口循环,漏隐式约束就试常驻小摘要与查询展开,丢全量信息就试程序遍历,写入反复出错再探索 MemSkill 式规则进化或 AgeMem 式训练。
可以用下面这组请求验收同一条记忆,避免“背诵题”独占测试集:
| 请求形式 | 合成示例 | 主要观察 |
|---|---|---|
| 直接问事实 | “当前运行时是什么?” | 能否取回正确版本 |
| 自然任务 | “帮我准备这次发布。” | 没被点名的约束能否生效 |
| 错误前提 | “既然仍是 Node20,就照旧发吧。” | 能否识别并纠正前提 |
| 多证据 | “升级与验证是否都完成?” | 能否找全对应证据 |
| 历史回看 | “第 3 天的配置是什么?” | 能否按时点回答 |
| 不可回答 | “未运行的测试是不是通过了?” | 能否明确说缺证据 |
做对照时固定底座、资料版本、写入时点、裁判和任务集,同时记录写入成本、每次读取量、搜索轮数与端到端耗时。将参考答案放在评测侧;若让记忆设计器接触测试答案,再报告这些题的提升,就失去了泛化意义。
小结:让正确证据出现在正确的决策里
我会优先尝试分层记忆、混合检索与有预算的缺口补查;全量分析交给可执行环境;记忆策略学习留给有可靠反馈的场景。
真正的约束是:任务尚未出现时就得决定保留什么,任务出现后又只能读取一部分。最朴素的方案甚至不用撑到百万 token 才崩——两条互相冲突的版本记录就能让它拿错依据。
花 5 分钟复制上面的 Python 脚本,把 runtime 和 tests 改成你业务里的两个关键条件,再补一个真实失败样本。先观察它为什么应该继续查、为什么应该停,比马上替换整个记忆框架更容易找到有用的改进点。
参考来源
论文与评测
- Hindsight:记忆结构与 LongMemEval 对照,重点表 3;ACL 2026 系统演示论文。
- EviMem:证据缺口与迭代检索,重点 §4.1、表 1–3。
- Recursive Language Models v3:环境化上下文读取,重点表 1。
- MemSkill v2:技能学习与进化,重点表 1、表 2。
- AgeMem v3:统一长短期记忆管理,重点表 1、表 2。
- InMind:隐式关联盲点,2026-07-27,重点表 1 与对照设计。
- LoCoMo-Conv:自然对话中的记忆获取,2026-09-03,重点表 4。
作者开源实现
- Hindsight:完整记忆服务与客户端入口。
- EviMem:README 提供记忆库构建、检索 runner 与消融参数。
- RLM:递归语言模型推理库。
- MemSkill:技能选择与进化的研究实现。
- AgeMem:训练与评测流程。
以上入口已核查;本文本地运行的是正文中的契约实验与复算,没有把这些仓库的全量评测重新跑一遍。