[控场AI]
· 9 分钟阅读· 4,951 字

笔记找不对,AI代理记忆系统差在哪?实测220条笔记的检索瓶颈

笔记找不对,AI代理记忆系统差在哪?实测220条笔记的检索瓶颈

用对话日志重建基准后发现:笔记结构没问题,真正的瓶颈是搜索无法把答案所在的笔记挑出来。

文章用真实对话日志构建基准,对比了"梯子"(按主题分组的笔记,当前状态置顶、历史按日期追加)与"碎片"(松散文本片段)两种记忆读取策略。给定正确笔记时,梯子以32%命中率、2.7k token成为最省策略,远超碎片的26%/8.7k;但真实路由下搜索只有三分之一时间能排对第一条笔记,梯子反而输给碎片。作者进一步发现,当前状态100%可被找到,日期化历史仅22%,而一半答案藏在历史里;此前的76%命中率测的是笔记是否被打开,而非事实是否到达。结论是笔记结构本身有效,问题出在前一步的笔记选择,且词法搜索在模糊指令下几乎无信号可用。

笔记没问题,找不到才是问题

一位开发者维护着一套给编程代理用的文本笔记系统:每个主题一条笔记,顶部是不断重写的当前状态,底部是只增不减的日期化历史记录。目前共有220条笔记、近百万token。每次对话时,基于词频的搜索先推荐可能相关的笔记,代理再请求各章节的地图及其开销,最后只取需要的那一条。

笔记检索流程

作者把这套机制称为"梯子"。在前一篇文章中,他将这套系统与Mem0、Zep、claude-mem和Letta进行了对比,结论是"什么都不缺"。但那只是一种"部件对部件"的比较——这个等价于那个。缺少的是另一种对比:给定一个问题,回答它的那行文字能否到达代理的视野?带来它需要多少token?

这篇文章就是那个测量。

核心发现速览

  • 在给定正确笔记的前提下,读取当前状态和一个章节,能在32%的问题中找到答案,花费2.7k token。松散碎片即使预算翻三倍也达不到这个水平。
  • 搜索只有三分之一的时间能把答案所在的笔记排在第一位。在这种真实路由下,梯子以微弱劣势输给碎片方案。
  • 当前状态总是能被找到;日期化历史只有五分之一的情况能找到。而一半的答案就藏在历史里。
  • 之前发布的76%命中率测量的是另一回事:是否有某条有用的笔记被打开,而不是事实是否到达。
  • 18/18的答案在位于当前状态时被找到;位于日期化历史时只有21/95。

记忆基准测试到底测什么

LongMemEval、LoCoMo以及Mem0发布的基准测试,衡量的对象是答案,而非文档:回答问题的证据是否到达了模型(或答案是否正确),以及消耗了多少上下文。Mem0会同时公布token消耗和命中率——两个数字一起看,因为全部加载总能命中,却毫无用处。

作者测量的是证据部分:回答问题时那行文字是否进入了被读取的内容中。前一次基准测试测的是另一回事:对于每条消息,查看哪些笔记被实际打开,以及搜索是否在前四名中推荐了它们,结果给出76%。但打开笔记不等于找到答案——笔记可能有300行,如果代理读了状态,而答案在上个月的某条记录里,那么推荐是正确的,事实却没有到达。

从对话记录构建基准

作者不想手写问题,因为人会写出自己系统擅长回答的问题。基准来自Claude Code保存在磁盘上的会话日志。在每一轮中,收集代理读取的内存行,如果某行中至少三个不常见词重新出现在代理回复中、且不在用户消息或之前三轮中,则算作答案。

第二个条件很关键:如果词已经在问题中出现过,那匹配是回声,不是回忆。移除代理写入内存的轮次,因为那里的匹配是刚起草的内容。也移除了每条笔记的标题,因为搜索建议已经包含了它。最终在70条笔记上得到191对匹配。抽样15个手工检查,约12个确实是答案——方法有噪声,但方向正确。

然后用每行在今天的笔记中逐字搜索。不再存在的被丢弃,这是第一个意外发现:372行曾经是答案的内容已不再以相同方式书写,几乎是留存数量的两倍。笔记被重写的速度比基准稳定的速度还快。在每天编辑的系统上,手工制作的记忆基准几周就会失效。

BM25 是一种经典的稀疏检索算法,也是 Elasticsearch、Lucene 等系统默认的相关性排序函数。它的核心思想是:一个词在当前文档中出现得越多,同时在整个语料中出现得越少,它对这篇文档的区分度就越高。具体而言,BM25 由词频饱和项(TF,出现的次数收益递减)和逆文档频率项(IDF,词越罕见权重越大)相乘构成,并对文档长度做归一化,避免长文档仅因篇幅大而获得高排名。

文中所谓的"碎片"方案,本质上就是把每条笔记切成一堆小片段,全部放进一个 BM25 索引里,再拿问题去检索。它和"梯子"的差异不在相关性算法本身——两者用的都是同一套词法搜索——而在于检索单元:碎片检索的是段落级短文本,梯子检索的是笔记级的标题与地图。检索单元越细,词法匹配的信号越集中,因此碎片在真实路由下能略微胜出;但代价是代理拿到的是脱离上下文的片段,缺乏"当前状态置顶"这类结构信息。

四种读取策略,同一预算

在这些配对上模拟了几种读取策略,都是对当前笔记的单遍读取:

策略命中率每题token
梯子+给定正确笔记32%2.7k
碎片(最佳40条)26%8.7k
碎片(最佳20条)19%4.3k
梯子+真实路由(2条笔记)17%6.1k
碎片(最佳10条)14%2.2k
梯子+真实路由(1条笔记)11%3.3k
碎片(最佳5条)7%1.1k
全部加载100%977k

两部分结论都在这里。给定正确笔记时,梯子是表格中最高效的策略:32%命中率仅需2.7k token,而碎片需要三倍以上token才达到26%。按主题分组知识、当前状态置顶,这是有效的。但在真实路由下,同一个梯子跌到了碎片曲线之下:11%使用3.3k token,对比碎片的14%使用2.2k token。

笔记的结构不是问题。问题在于前一步:选择打开哪一条。而我之前用一把看不见这个差异的尺子测量了那一步。

作者尝试了两个显而易见的修复方案。一是在笔记内搜索问题中最罕见的词,结果是10%和15%,没有变化。二是按段落路由,然后读取得分最高的笔记所在的完整笔记,结果是7%和14%。两者都无法改变局面,因为它们依赖同一个搜索。

这条 100% 命中、977k token 的"全部加载"基线值得单独理解:它相当于把整个 220 条笔记、近百万 token 一次性塞进上下文窗口,因此无论答案藏在哪一行,都必然出现在模型眼前。它的作用不是作为一个可用策略,而是作为命中率的理论上限和 token 开销的最坏参照。任何高效检索方案,本质上都是在"从 977k token 里必然命中"和"几乎不花 token 但可能漏掉"之间做取舍。

同时需要注意表格中"命中率"与"每题 token"的读法:它们必须成对比较,而不是各自单看。按纯命中率排序,全部加载永远第一;按纯 token 排序,碎片(最佳 5 条)最省。真正有意义的度量是达到某个命中率所需的最小 token,这也是文中"梯子在给定正确笔记时最高效、在真实路由下却跌到碎片曲线之下"这一结论得以成立的前提。

搜索为什么在这里失效

搜索在这些配对中只有32%的时间能把答案所在的笔记排在第一位,53%的时间进入前四推荐。而在上一个基准测试中,对所有消息的搜索给出76%。差异在于哪些消息最终导向一个事实——它们是最模糊的那些:"继续做你推荐的任何事""我们怎么推进?""检查一下是不是都没问题"。这些消息没有给搜索提供任何可用的信号。

代理之所以还能运转,是因为它拥有对话的其余上下文,如果第一条笔记不行可以再请求一条。但每一次往返都是一次额外调用,每次调用都会重新读取整个上下文。模拟的策略读一次就保留所读内容,而真实代理通过构造必然到达了所有这些答案——否则它们不会进入基准。模拟显示的是到达的代价,而不是能否到达。

**路由(routing)**在这里指的不是网络层的路由,而是"从一条用户消息决定该打开哪几条笔记"这一步。它是记忆系统流水线中先于读取的决策环节:只有选对了笔记,后续的读取结构(当前状态、正文、历史)才有机会发挥作用。文中反复强调的核心区分就在这里——笔记的结构决定"给定正确笔记后能多快找到答案",而路由决定"能不能拿到那条正确的笔记"。

这两者失效的机制完全不同。结构问题可以通过重写笔记、把高频答案提升到当前状态来修复;路由问题则源于词法搜索本身的局限:当用户消息是"继续做你推荐的任何事"这类模糊指令时,问题里几乎不含任何可用的检索信号,BM25 再强也无从下手。文中的两个"明显修复"(笔记内搜最罕见词、按段落路由后读整条笔记)之所以都无效,正是因为它们仍然依赖同一个送进搜索的查询——若查询本身没信号,换检索粒度只是换个位置失败而已。

历史记录是最薄弱的区域

给定正确笔记时,梯子能否找到答案取决于答案在笔记中的位置:

  • 当前状态:100%(18/18)
  • 笔记正文:28%(22/78)
  • 日期化历史:22%(21/95)

当前状态总是被读取,所以总能命中。历史是按章节读取的,而问题几乎从不提及日期或具体事件:它说"上周那个事"或者什么都不说。LongMemEval有一个类别叫时间推理,是评估系统中最难的类别之一。在这份数据中,一半的答案就住在那里。

这改变了作者对笔记的思考方式。当前状态之所以有效,是因为它短且被重写:如果历史中的某些内容经常被问及,它应该被提升到状态中,而不是留在一个需要被猜测的日期化条目里。历史是用于审计决策原因的,不是用于快速回答的。

需要修正的结论

作者曾写道系统"已经与记忆项目持平",基于词的搜索"已经达到75%命中率"。第一个在设计上成立,在结果上只对了一半:给定正确笔记时它赢,在选择笔记时它输。第二个测量的是是否有有用的笔记被建议,而不是事实是否到达。以基准测试的尺子衡量,搜索只有三分之一的时间能把笔记排在第一。

作者不因此更换系统。安装Mem0或向量数据库会在每次会话开始时增加固定文本,这是最频繁付费的部分,而碎片在真实路由下的优势只有几个百分点。教训更小也更有用:在说某事持平之前,用其他人问自己的同一个问题来测量它。

局限性说明

  • 词法对词法:碎片使用BM25而非嵌入向量。语义搜索可能同时提升碎片曲线和梯子的路由。
  • 命中不等于正确答案:测量的是那行文字是否进入了被读取的内容,而不是模型是否用好它。
  • 单遍读取:真实代理用未被计费的调用来补偿。
  • 精确匹配:用其他措辞重写的行算作丢失,基准丢弃了实践中可能被回答的案例。
  • 单一个人:这是作者的笔记、消息和提问方式,在对话已有上下文时是模糊的。

词法检索 vs 语义检索是理解整篇基准结论边界的关键。词法检索(如 BM25)靠字面共现匹配,只有当问题和答案"用了相似的词"时才可能命中;语义检索(嵌入向量、向量数据库)则把文本映射到高维空间,用余弦相似度匹配含义相近但措辞不同的内容,因此能召回"上周那个事"与"3 月 14 日部署回滚"这类字面无交集却语义相关的文本。

文中明确只用 BM25 做对照,是一个刻意收紧变量的选择:它保证了梯子和碎片用的是同一把词法尺子,从而隔离出"检索单元粗细"这一差异。但这也意味着结论不能直接外推到向量方案——正如作者自己在局限性中所承认的,语义搜索有可能同时抬升碎片曲线和梯子的路由命中率,甚至可能让路由这一步不再是最薄弱的环节。换句话说,"搜索只有三分之一时间能排对第一"这一发现,是特定于词法检索的,而非记忆系统结构的普遍宿命。

常见问题

给代理松散的记忆碎片好,还是整条笔记好?

在这份数据中取决于笔记是否被选对。给定正确笔记时,读取当前状态和最匹配的章节,32%的问题能找到答案,花费2.7k token;最佳40条碎片达到26%,花费8.7k token。在真实路由下——只有三分之一的时间能把笔记排第一——碎片在相同预算下以微弱优势胜出。

如何不手写问题来构建记忆测试基准?

从对话记录中构建。每轮取代理读取的内存行,标记那些至少三个不常见词重新出现在代理回复中、且不在问题中的行为答案。丢弃代理写入内存的轮次。

为什么笔记的历史部分最难找?

因为问题几乎从不提及日期或具体事件。给定正确笔记时,当前状态18/18被找到,日期化历史只有21/95。这就是LongMemEval所称的时间推理,而在数据中一半的答案住在那里。

#AI代理记忆系统#记忆检索基准测试#Claude Code#token消耗优化#LongMemEval#RAG检索#笔记路由#上下文管理
分享:

相关推荐