首页
/ agentmemory 真实向量嵌入评测:BM25 + 本地 Embedding 混合检索的量化验证

agentmemory 真实向量嵌入评测:BM25 + 本地 Embedding 混合检索的量化验证

2026-09-10 13:44:00作者:柯茵沙

本篇文章围绕 agentmemory 的 benchmark/REAL-EMBEDDINGS.md 评测报告展开,完整还原 agentmemory v0.6.0 在 240 条观察记录、30 个会话、20 条带标注查询上的检索质量对比实验:纯 grep 全量扫描、BM25-only、BM25 + 本地向量(Dual-stream)、BM25 + 向量 + 知识图谱(Triple-stream)四种检索系统的 Recall / Precision / NDCG / MRR 全面对决。读完你可以掌握:真实向量嵌入相对关键词检索到底能带来多少可量化的召回提升、为什么本地 Embedding(Xenova/all-MiniLM-L6-v2)是零成本默认推荐、以及如何通过环境变量把 agentmemory 从关键词检索切换到语义检索并复现本评测。

评测背景:为什么关键词检索不够用

agentmemory 是一个面向 AI 编码 Agent 的持久化记忆系统。其记忆管线在每条观察(observation)被摄入时,会经历去重、隐私过滤、LLM 压缩为结构化事实(facts)+ 概念(concepts)+ 叙述(narrative),然后执行「向量嵌入 → 索引进 BM25 + 向量」的步骤(见 README.md 中的管线示意)。搜索时则采用「BM25 + Vector + Graph(RRF 融合)」的三流混合检索。

但关键词检索有一个众所周知的短板:它只能命中字面匹配。当 Agent 事后问「database performance optimization」时,纯关键词系统找不到标题为「Fix N+1 query in post listing」的记忆——因为两者没有一个词是相同的,而人类语义上它们强相关。真实向量嵌入的价值,正是让记忆检索从「字面匹配」升级为「语义匹配」。

评测设定:数据集、对照系统与指标

本次评测由 benchmark/real-embeddings-eval.ts 驱动,报告由同一脚本自动生成并写回 benchmark/REAL-EMBEDDINGS.md

数据集:由 benchmark/dataset.tsgenerateDataset() 生成——30 个会话、每会话 8 条观察,共 240 条 CompressedObservation,外加 20 条带 relevantObsIds 标注的查询。每条查询带有 category 分类:exact(精确匹配)、semantic(语义匹配)、temporal(时间)、cross-session(跨会话)、entity(实体)。数据集内容模拟了一个真实 webapp 项目从脚手架搭建、鉴权、数据库、API、测试到部署运维的完整生命周期,观察记录由 titlenarrativefactsconceptsfilesimportance 等字段构成。

四套对照系统(对应评测脚本中的四个阶段):

系统 构造方式(源码依据)
Built-in (grep all) 模拟「把全部记忆塞进上下文」的基线:逐条观察做子串匹配打分(见 evalBuiltinGrep
BM25-only SearchIndex + 词干化 + 同义词,权重 { bm25: 1.0, vector: 0, graph: 0 }
Dual-stream BM25 + 真实 Xenova 向量,权重 { bm25: 0.4, vector: 0.6, graph: 0 }
Triple-stream BM25 + 向量 + 图谱检索,权重 { bm25: 0.4, vector: 0.6, graph: 0.3 }

评测指标(脚本内实现于 benchmark/real-embeddings-eval.ts):Recall@5 / Recall@10(Top-K 中相关记忆占全部相关记忆的比例)、Precision@5NDCG@10(排序质量)、MRR(首个相关结果的倒数排名)、平均延迟、以及每次查询返回结果折算的 token 数(estimateTokens 按字符数 / 4 估算)。

Head-to-Head:真实向量嵌入 vs 关键词检索

报告核心结果如下(数据来自 benchmark/REAL-EMBEDDINGS.md):

系统 Recall@5 Recall@10 Precision@5 NDCG@10 MRR 平均延迟 Tokens/query
Built-in (grep all) 37.0% 55.8% 78.0% 80.3% 82.5% 0.44ms 19,462
BM25-only (stemmed+synonyms) 43.8% 55.9% 95.0% 82.7% 95.5% 0.26ms 1,571
Dual-stream (BM25+Xenova) 43.8% 64.1% 98.0% 94.9% 100.0% 2.39ms 1,571
Triple-stream (BM25+Xenova+Graph) 43.8% 64.1% 98.0% 94.9% 100.0% 2.07ms 1,571

几个关键读数:

  • 召回是差距所在:Recall@10 从 grep 的 55.8% 提升到向量增强后的 64.1%;而 Recall@5 稳定在 43.8%,说明前 5 条的结果差异不大,向量主要在更深的位置把「语义相关但字面不匹配」的记忆捞了回来。
  • MRR 提升显著:从 grep 的 82.5%、BM25-only 的 95.5% 提升到 100.0%——即加入向量后,20 条查询的首个相关结果全部排在第一位。
  • Token 成本是数量级差距:grep 基线平均每次查询要吞掉 19,462 tokens(相当于把 240 条记忆全量灌进上下文),而检索式方案仅返回 Top-K,每次查询折算约 1,571 tokens,节省 92%。这与 README.md 中「agentmemory 约 1,900 tokens(比全量加载少 92%)」的表述相互印证。
  • 延迟代价可忽略:向量检索平均 2.39ms(triple-stream 因并发图检索反而更快,2.07ms),相对 grep 的 0.44ms 和 BM25 的 0.26ms,对 Agent 会话注入场景完全无感。

向量嵌入带来的净收益

报告给出了两个量化结论:

  1. 在 BM25 之上叠加真实向量嵌入,Recall@10 提升 8.2 个百分点(55.9% → 64.1%)。
  2. 相对全量加载(grep),token 节省 92%:1,571 vs 19,462 tokens。

这里的核心洞察是:BM25 负责精准、向量负责召回。BM25 的词干化 + 同义词已经能覆盖精确/实体类查询,而向量弥补的是语义鸿沟——这是单靠关键词无法达到的。

Per-Query 分析:向量在哪些查询上赢

报告中列出了 Dual-stream(真实向量)显著优于 BM25-only 的查询:

查询 类别 BM25 Recall@10 +Vector Recall@10 增量
How did we set up authentication? semantic 25.0% 45.0% +20.0pp
Playwright test configuration exact 50.0% 90.0% +40.0pp
database performance optimization semantic 0.0% 40.0% +40.0pp
test infrastructure and factories exact 50.0% 80.0% +30.0pp
Prisma ORM configuration entity 14.3% 28.6% +14.3pp
CI/CD pipeline configuration exact 20.0% 40.0% +20.0pp

最典型的案例是 「database performance optimization」:BM25 的 Recall@10 是 0.0%,即一条相关记忆都找不到。因为数据集中相关记忆的标题是「Fix N+1 query in post listing」「Add Redis caching layer for expensive queries」等,与查询没有任何共享词。加入向量后,Recall@10 提升到 40.0%——向量理解到「performance optimization」与「N+1 query fix」「eager loading」「caching」之间的语义关联。这正是本报告最有力的论据:语义检索解决的是关键词检索的结构性盲区,而非边际优化

按类别对比:不同查询类型的最优解

类别 Built-in grep BM25 (stemmed) +Real Vectors +Graph
exact 48.0% 54.0% 72.0% 72.0%
semantic 35.5% 33.3% 41.9% 41.9%
cross-session 77.8% 77.8% 77.8% 77.8%
entity 79.0% 76.2% 79.0% 79.0%

(表格为 Recall@10,数据来自报告原文。)

结论分层清晰:

  • exact 类查询:向量带来最大增益(54.0% → 72.0%),原因在于精确查询往往是复合短语,如「Playwright test configuration」,BM25 只能部分命中,而向量能整体匹配语义;
  • semantic 类查询:从 33.3% 提升到 41.9%,是向量价值的主战场;
  • cross-session / entity 类查询:BM25 已能很好覆盖,向量增益有限(+0pp ~ +2.8pp)。这也印证了报告 Key Findings 的第 3 条:实体/精确查询由 BM25 + 词干化服务即可,向量是锦上添花;
  • 值得注意 BM25 在 entity 类别上反而略低于 grep(76.2% vs 79.0%),说明词干化对专有名词(如 Prisma、Terraform)存在过度归并的风险,而向量又把它拉了回来——多流融合的意义正在于此。

嵌入性能:一次性摄入成本

系统 嵌入耗时 模型 维度
Dual-stream (BM25+Xenova) 3.1s Xenova/all-MiniLM-L6-v2 384
Triple-stream (BM25+Xenova+Graph) 2.9s Xenova/all-MiniLM-L6-v2 384

嵌入是一次性摄入成本,索引完成后搜索为亚毫秒级。 评测脚本中嵌入以 32 条为一批调用 provider.embedBatch 分批进行(见 benchmark/real-embeddings-eval.tsbatchSize = 32),240 条观察全部嵌入仅需约 3 秒,且不需要任何 API key、不产生任何 API 调用费用。

关键结论回顾

报告总结的四条核心发现:

  1. 语义类查询受益最大:真实嵌入带来 8.6pp 的 Recall@10 提升;
  2. 最难的查询「database performance optimization」 从 BM25 的 0.0% 提升到向量增强后的 40.0%;
  3. 实体/精确查询已被 BM25 + 词干化良好服务,向量增益边际化;
  4. 本地嵌入(Xenova)无需 API key——零成本、零延迟顾虑。

推荐配置:开启本地 Embedding

报告的最终建议非常明确:默认启用本地嵌入——设置 EMBEDDING_PROVIDER=local,或安装 @huggingface/transformers 依赖。这能让 agentmemory 获得内置 Agent 记忆系统无法匹敌的语义检索能力。

环境变量与权重配置

agentmemory 的嵌入配置在 src/config.tsloadEmbeddingConfig()detectEmbeddingProvider() 中解析:

# 强制使用本地嵌入(无需任何 API key)
EMBEDDING_PROVIDER=local

# 可选:调整混合检索权重(默认值见下)
BM25_WEIGHT=0.4
VECTOR_WEIGHT=0.6
  • EMBEDDING_PROVIDER:显式指定提供方,可选 localgeminiopenaivoyagecohereopenrouter若未显式设置,则按 API key 存在与否自动探测GEMINI_API_KEYOPENAI_API_KEYVOYAGE_API_KEYCOHERE_API_KEYOPENROUTER_API_KEY),都没有则返回 null,系统退化为纯 BM25 检索;
  • BM25_WEIGHT(默认 0.4)、VECTOR_WEIGHT(默认 0.6):非法或负数时回退默认值,且上限截断为 1。评测中 Triple-stream 的图谱权重 graphWeight = 0.3benchmark/real-embeddings-eval.ts 的构造参数中直接传入。

Provider 的统一工厂在 src/providers/embedding/index.tscreateEmbeddingProvider() 中:每种 provider 都被 withDimensionGuard 包裹,防止「维度不匹配的向量静默写入索引导致记忆不可见」的坑(vector-index.ts 的余弦相似度在长度不一致时返回 0 而非抛错,守卫在边界拦截)。

本地嵌入的底层实现

EMBEDDING_PROVIDER=local 对应 src/providers/embedding/local.tsLocalEmbeddingProvider

  • 固定 dimensions = 384
  • 通过 @huggingface/transformers 加载 Xenova/all-MiniLM-L6-v2 模型,首次运行会下载约 80MB 模型文件;
  • pipeline 配置 { dtype: "q8" }(8-bit 量化,降低内存占用),提取参数 { pooling: "mean", normalize: true }(均值池化 + L2 归一化),输出 Float32Array 向量;
  • 未安装依赖时抛出清晰提示:npm install @huggingface/transformers(该行为由 test/local-embedding-provider.test.ts 的测试用例锁定)。

混合检索的融合原理

向量与 BM25、图谱如何融合?答案在 src/state/hybrid-search.tsHybridSearch.tripleStreamSearch()

  1. 三流并行检索:BM25 命中 + 向量余弦相似度命中 + 图谱实体检索命中,各自产出带排名的结果集;
  2. RRF(Reciprocal Rank Fusion)加权融合RRF_K = 60,每条结果按 weight * (1 / (RRF_K + rank)) 加权,多流同时命中的结果获得 AGREEMENT_BONUS = 0.05 的协同增益;
  3. 会话去重diversifyBySession 限制每会话最多 3 条,防止单一会话刷屏;
  4. 可选 LLM 重排:设置 RERANK_ENABLED=true 后,对 Top-20 窗口执行 src/state/reranker.ts 的重排;
  5. 向量检索由 src/state/vector-index.tsVectorIndex.search() 实现余弦相似度 Top-K,序列化时以 base64 存储 Float32Array(注意处理了 Node Buffer 池切片的「幻影 2048 维度」问题)。

这一设计与评测结论一致:BM25 保精准、向量保召回、图谱提供跨记忆的关系线索,三者融合后在 Recall@10(64.1%)、Precision@5(98.0%)、MRR(100.0%)上同时取得最优。

如何复现本评测

在仓库根目录执行(需要先安装依赖):

# 安装本地嵌入依赖(评测脚本首次加载模型需联网下载约 80MB)
npm install @huggingface/transformers

# 运行真实嵌入质量评测(等价于 npm run eval:real-embeddings 脚本入口)
node --import tsx benchmark/real-embeddings-eval.ts

脚本依次执行四阶段评测:grep 基线 → BM25-only → Dual-stream → Triple-stream,每阶段打印 Recall@10,最后将完整报告写入 benchmark/REAL-EMBEDDINGS.md。评测结果文件与其余基准(LongMemEval、质量评估、负载压测)的组织方式详见 benchmark/README.md。若模型加载失败,脚本会提示先执行 npm install @huggingface/transformers

小结

本次真实嵌入评测为 agentmemory 的混合检索架构提供了清晰的证据链:向量嵌入不是可选的锦上添花,而是语义检索能力的关键支撑——它把最难的一类查询(语义泛化)从「完全不可召回」提升到 40% 的 Recall@10,同时在 240 条记忆规模下把每次查询的 token 成本从 19,462 压缩到 1,571。而这一切通过一行 EMBEDDING_PROVIDER=local 即可启用,无需任何 API key 与外部服务,是一次零成本、可验证、收益显著的升级。


本评测全部测量基于 Xenova/all-MiniLM-L6-v2 本地嵌入(384 维,无 API 调用),原始数据与生成脚本见 benchmark/REAL-EMBEDDINGS.mdbenchmark/real-embeddings-eval.ts

热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.94 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
603
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
527