ai-memory LongMemEval-S 检索基准全解析:zero-llm 基线、评分口径与复现指南
【免费下载链接】ai-memorySolution for long term memory for agent coding CLIs and to facilitate handoff between different agent vendors项目地址: https://gitcode.com/GitHub_Trending/ai/ai-memory
导读
本文以 ai-memory 仓库发布的 docs/benchmarks/longmemeval-s-2026-09-01.md 为骨架,完整解读这份检索质量基线报告:它记录的是 ai-memory 在 2.0 检索重构之前、纯确定性(zero-llm)模式下的 LongMemEval-S(v1)检索成绩。读完本文,你将掌握:该基准的 slice 划分与每项指标的确切含义、hit@k 与 recall@k 在会话级评分的实现差异、数据从注入(hook 回放)到查询(MCP memory_query)再到打分的完整链路,以及如何在本地用仓库自带的ai-memory-eval工具复现这份数字,并通过同一套 harness 对比 FTS 与本地 embedding 两条检索路径的差距。
一、这份基准报告记录了什么
docs/benchmarks/longmemeval-s-2026-09-01.md是 ai-memory 检索质量的公开基线之一,报告头部完整保留了四项溯源信息:
| 溯源字段 | 值 | 含义 |
|---|---|---|
| commit | 496e419ff60ece21e1c7dfc76a960c92043e75c8 | 跑分时检出的代码版本,保证数字可回溯 |
| dataset | longmemeval_s(sha25608d8dad4be43ee20…) | LongMemEval-S v1 数据集及其校验和 |
| mode | zero-llm | 完全离线确定性模式,无任何 LLM 参与 |
| hardware | AMD Ryzen 9 7950X3D(32 线程) | 运行环境 |
| questions scored | 470(30 个 abstention 题除外) | 计分题量与排除规则 |
这份报告属于三份同日(2026-09-01)基准中的最早期基线。对照 docs/benchmarks/README.md 中的 Baselines 表可以看到完整的演进脉络:
| date | mode | overall hit@5 | 对应报告 |
|---|---|---|---|
| 2026-09-01 | zero-llm(pre-2.0 FTS) | 0.617 | longmemeval-s-2026-09-01.md |
| 2026-09-01 | zero-llm、stopword-filtered FTS | 0.668 | longmemeval-s-2026-09-01-fts.md |
| 2026-09-01 | local embeddings(2.0 默认) | 0.823 | longmemeval-s-2026-09-01-local.md |
也就是说,这份文档是 2.0 检索重构的起点基线(floor):它测量的不是带 embedding 的混合检索,而是 FTS5 + entity/graph 的纯确定性检索栈。
二、逐项解读 470 道题的切片成绩
报告按 LongMemEval 官方的 question type 切成 6 个 slice,全部成绩如下:
| slice | n | hit@1 | hit@3 | hit@5 | hit@10 | recall@1 | recall@3 | recall@5 | recall@10 |
|---|---|---|---|---|---|---|---|---|---|
| knowledge-update | 72 | 0.528 | 0.653 | 0.708 | 0.806 | 0.264 | 0.451 | 0.493 | 0.604 |
| multi-session | 121 | 0.388 | 0.504 | 0.570 | 0.669 | 0.168 | 0.290 | 0.333 | 0.448 |
| overall | 470 | 0.449 | 0.566 | 0.617 | 0.713 | 0.297 | 0.430 | 0.472 | 0.570 |
| single-session-assistant | 56 | 0.696 | 0.750 | 0.804 | 0.875 | 0.696 | 0.750 | 0.804 | 0.875 |
| single-session-preference | 30 | 0.367 | 0.567 | 0.600 | 0.733 | 0.367 | 0.567 | 0.600 | 0.733 |
| single-session-user | 64 | 0.406 | 0.453 | 0.484 | 0.547 | 0.406 | 0.453 | 0.484 | 0.547 |
| temporal-reasoning | 127 | 0.394 | 0.551 | 0.598 | 0.709 | 0.190 | 0.364 | 0.410 | 0.507 |
解读这份表需要注意三点:
- slice 规模差异:
temporal-reasoning(127 题)和multi-session(121 题)是最大的两个分类,也是纯 FTS 模式下相对最弱的两个——前者考验时间推理,后者要求跨多个会话召回证据,recall@1 分别只有 0.190 和 0.168,说明"第一个返回结果就命中全部证据会话"在这两类题上极难。 - hit@k 与 recall@k 的差距:
multi-session的 hit@10 为 0.669 但 recall@10 仅 0.448,temporal-reasoning的 hit@10 为 0.709 但 recall@10 仅 0.507。差距大说明"能找到至少一个证据会话"与"找全所有证据会话"之间仍有明显鸿沟——这正是多会话问题的本质难点。 - single-session 系列:
single-session-assistant表现最好(hit@1 已达 0.696,hit@10 达 0.875),且其 hit@k 与 recall@k 完全相同——因为单会话题只有一个证据会话,命中即满分、未命中即零分。
三、指标口径:hit@k 与 recall@k 的准确定义
报告末尾的 Notes 明确了两套指标的定义,这是理解一切成绩的前提:
- hit@k= 任意一个证据会话出现在 top k 中(即大多数记忆系统对外发布的 "Recall@k");
- recall@k= 找到的证据会话数占该题全部证据会话数的比例(对多会话题更严格);
- 会话归属(session attribution)来自
sessions/<id>.md页面与原始 observation 命中,无法归属到会话的页面永不参与计分; - 捕获是生产形态的:excerpt 被限制在 2 KB 隐私边界内。
3.1 评分器的源码实现
在 score.rs 中可以精确看到这两套指标如何落地:
let top: HashSet<&uuid::Uuid> = ranked_sessions.iter().take(k).collect(); let found = evidence.iter().filter(|e| top.contains(e)).count(); hit_at.insert(k, if found > 0 { 1.0 } else { 0.0 }); recall_at.insert(k, found as f64 / evidence.len() as f64);关键实现细节:
- 会话去重:检索结果先按"会话"折叠,同一会话的多个 chunk 只占据一个排名槽位。
score.rs中duplicate_session_chunks_fill_one_slot_not_three测试验证了这一点——3 个噪声会话 chunk 只能占 rank 0,随后出现的证据会话即命中 hit@2。 - abstention 题排除:没有证据会话的题目既不参与 hit@k 也不参与 recall@k(
score_question中 evidence 为空时 recall@k 记 0,但这类题在进入评分前已被剔除,见retrieval/mod.rs的questions.retain(|q| !q.is_abstention()))。 - 聚合方式:
aggregate按 question_type 与 overall 分组,对每题成绩做宏平均(macro-average)。 - 控制测试:
random_sessions_score_zero测试保证随机检索结果计零分——评分器不会给任意命中以错误加分。
3.2 不可归属页面的处理
query.rs中session_uuid_from_path只把sessions/<uuid>.md路径解析为会话 UUID,其余页面(如gotchas/build.md)归属为None,unattributed_pages_never_score测试验证了这些命中不会得分。这意味着报告度量的是"会话级召回",而非"页面级检索质量"。
四、zero-llm 模式的完整评测链路
这份报告的数字不是理论推演,而是由仓库自带的 evals 工具通过真实服务栈端到端跑出来的。评测链路共四步,没有任何环节被 mock:
4.1 启动真实服务子进程
retrieval/mod.rs中的EvalServer::launch会在一个全新的临时数据目录上启动真实的ai-memory serve子进程。zero-llm 模式意味着:
- 无 consolidation LLM;
- 无 embedder;
- 无 reranker;
- 完全确定性与离线可复现。
该模式的等价生产配置是embedding_provider = "none"(见 config.default.toml 中关于 2.0 默认 embedding 与显式 opt-out 的注释),或任何模型无法加载的主机。
4.2 通过 /hook/batch 回放 haystack
每道题对应一个私有项目(lme-<question_id>),其 ~50 个会话的 haystack 通过POST /hook/batch按生产 hook 节奏回放(见 ingest.rs):
session-start → (user-prompt-submit + stop) × 每轮对话 → session-end其中stop事件携带 opt-in 的助手摘要_ai_memory_assistant.excerpt(capture_assistant=1)。回放忠实继承生产捕获的两条约束:
- excerpt 上限 ~2 KB:客户端
cap_excerpt在 UTF-8 字符边界截断,服务器端同样限流——证据若深埋在单个超长 turn 中,索引确实够不到,这是被度量的系统本身的一部分,而非 harness 伪影; - 会话日期前缀:每个 turn 文本前注入
[session date: …],补偿重放历史缺少真实时间戳的问题(官方 LongMemEval harness 同样把时间戳暴露给检索器)。
注入还有一层健壮性保障:ingest_question会重试被服务器限流跳过的 batch item,直到全部接受——静默丢失 haystack 数据会直接污染基准,因此这种失败被设计为"硬失败"而非"软跳过"。
4.3 通过 MCP memory_query 查询
查询走的是真实 MCP Streamable-HTTP 接口tools/call memory_query(见 query.rs),与真实 Agent 完全同一条路径,并显式传入workspace=longmemeval与project=lme-<id>做作用域隔离。响应同时解析结构化页面命中(hits)与原始 observation 命中(raw_hits),并按"编译页面优先、原始命中随后"的顺序扁平化(pages_rank_before_raw_hits_and_order_is_preserved测试保障)。
4.4 会话级打分与报告
打分在 score.rs 完成,之后report.rs输出带完整溯源(commit、数据集 sha、硬件、逐题分数)的report.{json,md},落盘到evals/runs/<timestamp>-retrieval/。发布基线时只需把 markdown 复制进docs/benchmarks/——本系列三份报告正是这样产生的。
五、如何复现这份基准
5.1 准备与运行
按照 evals/README.md 与 docs/benchmarks/README.md,复现命令如下:
# 1. 构建被测服务端(ai-memory-cli 的 release 二进制) cargo build --release -p ai-memory-cli # 2. 下载数据集并跑完整 500 题(278 MB,校验和固定,上游漂移会响亮报错) cargo run --release -p ai-memory-eval -- retrieval --fetch # 3. 快速冒烟(前 10 题,用于迭代) cargo run -p ai-memory-eval -- retrieval --sample 10retrieval子命令支持的参数(见RetrievalArgs)包括:
| 参数 | 默认值 | 说明 |
|---|---|---|
--dataset | evals/datasets/longmemeval_s.json | 数据集路径 |
--fetch | 关 | 缺失时先下载 |
--server-bin | target/release/ai-memory | 被测服务端二进制 |
--sample N | 无 | 只跑前 N 题(确定性前缀) |
--ks 1,3,5,10 | 1,3,5,10 | hit@k / recall@k 的截断点 |
--concurrency | 8 | 并发题数 |
--out | evals/runs | 输出根目录 |
--embeddings none\|local | none | none = zero-llm;local = 进程内 all-MiniLM-L6-v2 |
5.2 复现三种模式
- 本报告的 zero-llm(pre-2.0 FTS):
--embeddings none在当时的 commit 上运行,即 0.617 基线; - stopword-filtered FTS:对 bare-query 的 FTS OR-join 去掉停用词后重跑,hit@5 提升至 0.668(+5.1 点),hit@1 提升 0.449 → 0.534(+8.5 点),对应 longmemeval-s-2026-09-01-fts.md;
- local embeddings(2.0 默认):
--embeddings local,进程内 all-MiniLM-L6-v2(384 维)嵌入器,模型首次使用时拉取约 87 MB 到evals/models/(校验和锁定),整体 hit@5 达 0.823。
5.3 诚实数字的边界
README 明确给出可比性说明:
- 外部可比口径:agentmemory 0.967 R@5(hybrid + reranking)、doobidoo/mcp-memory-service 0.804 R@5 均为 embedding-based 检索在原始聊天日志上的成绩;本项目对外可比的口径是 hit@5。
- 不可忽略的成本:本流水线额外承担了生产形态捕获的开销(2 KB excerpt 隐私边界),因此长 turn 深处的证据确实在索引可达范围之外——基准度量的是交付的系统,而不是理想化的检索器。
- 回归门槛:Roadmap 2-6 项会重跑此基准,任何使某个 slice 明显下降的改动都被视为需要修复的回归,而非可发布的注记。
六、从 0.617 到 0.823:两份姊妹报告串起 2.0 检索升级路径
将三份报告并排阅读,可以看到 2.0 检索重构在同一个 harness 上实测的两次跃迁:
- 停用词过滤(zero-llm 内部):把 bare-query FTS OR-join 中的停用词剔除,overall hit@5 从 0.617 升至 0.668,hit@1 从 0.449 升至 0.534——一次零模型成本、完全确定性的改进;
- 本地 embedding 混合检索:进程内 all-MiniLM-L6-v2 嵌入器(正确 masked-mean pooling)与 FTS5 + entity + graph 融合,overall hit@5 进一步升至 0.823,recall@5 达 0.680。
注意 docs/benchmarks/README.md 特别指出:仅 pooling 修复一项就值约 6.6 个点(对比 padded-attention 实现),且是被 calibration 测试捕获的——这解释了为什么仓库强调"每个中间数字都在该改动落地前于同一 harness 上实测"。从 slice 视角看,local embedding 对multi-session和knowledge-update的提升最为显著(hit@5 分别 0.570 → 0.876、0.708 → 0.903),说明向量检索对跨会话、知识更新类问题的增益最大。
七、延伸阅读与关键证据路径
- 基准总览与可比性说明:docs/benchmarks/README.md
- 另外两份同日基准:longmemeval-s-2026-09-01-fts.md(stopword-filtered FTS)、longmemeval-s-2026-09-01-local.md(local embeddings)
- 评测 harness 使用手册(含完整命令行示例):evals/README.md
- 评测入口与参数定义:evals/src/main.rs、evals/src/retrieval/mod.rs
- 会话级评分器及全部单元测试:evals/src/retrieval/score.rs
- hook 回放注入(生产节奏、2 KB excerpt、日期前缀):evals/src/retrieval/ingest.rs
- MCP 查询与页面归属:evals/src/retrieval/query.rs
- 2.0 默认 embedding 配置说明:crates/ai-memory-cli/templates/config.default.toml(unset = 进程内 all-MiniLM-L6-v2,384 维;
embedding_provider = "none"即回到本文 zero-llm 的确定性检索栈) - 本地嵌入的详细设计:docs/local-embeddings.md
【免费下载链接】ai-memorySolution for long term memory for agent coding CLIs and to facilitate handoff between different agent vendors项目地址: https://gitcode.com/GitHub_Trending/ai/ai-memory
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考