所有在 AI 裡做記憶的人都預設需要 embeddings。我們也是。然後我們跑了 benchmark。
FTS5 —— SQLite 內建的全文搜尋 —— 在 LongMemEval-S 上達到 95.40% R@5。 加上 384 維 ONNX 向量管線做加權融合之後,掉到 82.40%。這是用公開 ICLR 2025 dataset 的 500 道題跑出來的,任何人都可以獨立驗證。
還有一個更出乎意料的發現:改用 max-fusion(兩個訊號取最大值而非加權平均)之後,成績維持在 95.40%。逐題比對命中集合:Mode A 與 Mode B 命中的 477 個 question ID 完全相同。向量搜尋在 top-5 沒有帶來任何額外命中。
| Mode | 說明 | R@5 | 跑完 500 題耗時 |
|---|---|---|---|
| A | 純 FTS5 | 95.40% | 10s |
| B | FTS5 + ONNX MiniLM-L6(max fusion) | 95.40% | ~25 分鐘 |
| C | FTS5 + ONNX(60/40 加權) | 82.40% | ~13 分鐘 |
memesh 以 300 KB 的 npm package 出貨,完全跑在使用者自己的機器上 —— 沒有雲端 round-trip、不需要 API key、沒有向量索引。Mode A 距離 MemPalace 廠商自報的上限 96.6%(向量 + reranker stack)只差 1.2 個百分點。
我們怎麼測的
LongMemEval 模擬的是真實情境:你跟 AI 對話了好幾週,現在問一個答案藏在很久之前對話裡的問題。500 道題,每題都有一個 haystack,包含約 50 個歷史對話 session —— 有些是使用者自己的,有些是泛用公開 Q&A 的干擾項(ultrachat_*、sharegpt_*)。
指標是 R@5:正確答案 session 出現在 top-5 retrieval 結果裡的題目比例。Dataset SHA256:08d8dad4be43ee2049a22ff5674eb86725d0ce5ff434cde2627e5e8e7e117894。
這是 retrieval test,不是 end-to-end LLM 測試。我們量的是 memory layer 能不能浮出對的 session。LLM 拿到之後怎麼用,不在範圍內。
Mode A pipeline —— 全部就這些:
- 把 haystack 每個 session 灌進全新 SQLite 資料庫。每個 session 存成一個 entity,完整文字截斷在 8,000 字元。FTS5 用
unicode61 remove_diacriticstokenization 建索引。 - 斷詞 query。去掉非英數字字元,丟掉長度 ≤ 2 的 token,最多取 20 個,OR 串接成引號 quoted terms。
- FTS5 MATCH 配 default BM25 ranker,取前 20 名。
- 依排名位置打分:
score = 1 − (rank / n_results)。 - 回傳排序清單,計算 R@5。
沒有 embedding。沒有 API call。沒有向量倉。
純 FTS5 為什麼能到 95%
大家的直覺是「語意搜尋一定要用向量」—— 對跨詞彙 retrieval 是對的,對個人記憶不是(或者沒那麼對)。
在公開語料庫裡,問「我的焦慮要怎麼處理」可能要 match 到「應對恐慌症」—— 詞彙不同,意圖相同。Vector embeddings 跨越這個落差。
在個人記憶裡,haystack 是使用者自己過去寫的東西。他們現在問問題用的詞彙,和當時記下記憶用的詞彙,統計上很接近。BM25 加關鍵字,意外地常常就是對的工具。
從 dataset 實際打開 session 內容驗證過的案例:
- Question(
e47becba,類型 single-session-user):“What degree did I graduate with?” - Haystack:54 個 sessions,涵蓋生產力 app、健行、約會、副業,加上 26 個泛用公開 Q&A 干擾項。
- Answer session(
answer_280352e9,haystack 索引 52):一段 8 輪、關於任務管理 app 的對話。學位是使用者在第三輪裡若無其事提到的:“I graduated with a degree in Business Administration, which has definitely helped me in my new role.” 整段對話主要聊 Todoist、Trello、meal-prep。學位只出現一句。 - Mode A 結果:答案 session 排名第 2。Top-5 回傳:
02bd2b90_3, answer_280352e9, sharegpt_Cr2tc1f_0, ultrachat_214101, f6859b48_2。
FTS5 把一個冗長且離題的 session 裡唯一一次出現的關鍵字「graduated」,排在另外 53 個 session 之上 —— 包括泛論教育但缺乏使用者詞彙指紋的干擾項。這就是詞彙指紋的優勢:使用者自己的特定用詞是比語意相似度更強的訊號。
Mode A 仍然失敗的 23 道題(4.6%)集中在:
- 時間推理(8 題):“What’s the order of the three trips I took in the past three months?” FTS5 沒有時間戳概念,排名只看詞彙相關度。
- 跨 session 聚合(7 題):“How many different doctors did I visit?” 需要合併多個 session 的證據,而不是取出單一答案 session。
- 單一 session 偏好(5 題):相關過去 session 用了領域詞彙,而現在的問題沒有直接呼應。
- 其他(3 題):兩題 abstention、一題 knowledge-update。
三分之二的失敗(15/23)在時間推理與跨 session 聚合。正解不是換更好的 embedder,而是不同的 retrieval 模式:時間戳感知排名,或在原始 retrieval 之上加 graph/aggregation 層。
為什麼加向量讓結果更差
Mode C 是更有意思的發現。直覺:「FTS5 給你 95.40%,向量再給一個獨立訊號,60/40 混合應該 ≥ 95.40%,甚至更高。」實測:82.40%,退步 13 個百分點。
根本原因:LongMemEval 的 haystack 裡有 ultrachat_*、sharegpt_* 這類泛用公開 Q&A session,對幾乎任何 query 都有高 cosine 相似度。它們是廣義 chatbot 訓練對話,語意上和所有東西都有重疊。它們不是使用者的個人記憶。
Mode A 正確地把它們排在後面:它們沒和 query 共享使用者特定的關鍵字。
Mode B(max-fusion)保留了 FTS5 主導性:FTS5 把對的答案排在前面,向量訊號擠不掉它。命中集和 Mode A 完全相同。
Mode C(加權平均)用弱的向量訊號稀釋了強的 FTS5 訊號。泛用 Q&A 干擾項在排名上被推上來,把真正屬於使用者個人記憶的 session 擠下去。
教訓:融合機制比是否加入特徵更重要。 弱訊號用 max-fusion 加進來無傷大雅;同一個訊號用加權平均加進來,在 haystack 有對抗性 distractor 時會主動造成傷害。
這不是 memesh 特有的發現,而是任何在個人資料之上做 retrieval 的系統都適用的架構教訓。Mode C 的退步結果是與其他 mode 一起發表、沒有省略,因為這個退步就是這篇文章要說的事。
對你的架構決策意味著什麼
對個人記憶 retrieval(單一使用者、自己的對話歷史、幾千條 entity 以內):
從 FTS5 開始。 它已經在你的 SQLite 裡。不需要 embedding pipeline、向量倉,寫入時也不需要付 API 成本。大概可以直接到 90%+ R@5。
把向量當 tie-breaker,不要當主訊號。 Max-fusion 配 FTS5 主導是安全的。加權混合不是 —— 只要你的 haystack 裡有任何不屬於特定使用者的內容就不行。
用公開 methodology 驗證廠商數字。 MemPalace 的 96.6% 是廠商自報,可能用了 cleaned 版 dataset。Mem0 和 Zep 是 LongMemEval 原論文在同一個 dataset 變體上公佈的數字,可以直接比。Supermemory 是廠商估計。把這幾種不同來源的數字放在同一張表,每行都要標清楚。
| 系統 | R@5 | 來源 |
|---|---|---|
| MemPalace | 96.6% | 廠商自報;用了 reranker;可能是 longmemeval-cleaned 變體 |
| memesh(Mode A) | 95.40% | 本 benchmark,跑在 longmemeval_s |
| Supermemory | ~82% | 廠商估計 |
| Zep | 63.8% | LongMemEval 論文 arXiv:2410.10813,跑在 longmemeval_s |
| Mem0 | 49.0% | LongMemEval 論文 arXiv:2410.10813,跑在 longmemeval_s |
對全語料規模 retrieval(數百萬筆、多使用者、跨語言),trade-off 反過來,向量搜尋就是必要的。memesh 不是要取代 pgvector 或 Pinecone,而是另一種押注:對個人記憶,簡單架構是更好的架構。
95.40% 沒有包含的東西
這個 benchmark 只跑 retrieval pipeline。生產版 plugin 還包含以下功能,在 benchmark 裡關掉以保持比較公平:
- 多因子評分 —— recency、frequency、confidence、impact 一起加權,不只是 retrieval 相關度。
- LLM query expansion(「Smart Mode」)—— FTS5 之前先用 LLM 把問題擴展同義詞。
- 跨 entity 圖譜遍歷 —— 支援需要追隨 entity 關係的問題。
- 自動衰減 —— 過久沒用的記憶在排名上淡出但不刪除。
生產版 R@5 很可能高於 95.40%。我們還沒把這些功能單獨切出一個發佈數字,這裡也不宣稱。
8 步重現
npm install 大約需要 30–60 秒(視網速而定)。整個流程包含下載 dataset,全部跑完預計 10–15 分鐘。
git clone https://github.com/PCIRCLE-AI/memesh-llm-memory.git
cd memesh-llm-memory
git checkout bench/longmemeval-public-r1
npm install
curl -L "https://huggingface.co/datasets/xiaowu0162/longmemeval/resolve/main/longmemeval_s" \
-o /tmp/longmemeval_s.json
shasum -a 256 /tmp/longmemeval_s.json # 預期 08d8dad4be43ee2049a22ff5674eb86725d0ce5ff434cde2627e5e8e7e117894
node benchmarks/longmemeval/run.mjs --mode A --dataset /tmp/longmemeval_s.json
node -e "const d=require('./benchmarks/longmemeval/results/mode-A-2026-05-03T12-31-26.json'); console.log('R@5:', (d.overall_metrics.r_at_5 * 100).toFixed(2) + '%')"
預期輸出:R@5: 95.40%。Mode B 和 Mode C 因為要下載並執行 ONNX MiniLM-L6 模型,分別需要約 25 分鐘和 13 分鐘。
Repository:github.com/PCIRCLE-AI/memesh-llm-memory
Benchmark 分支:bench/longmemeval-public-r1
License:MIT
延伸問題
- FTS5-only 配置從每題約 50 個 sessions 擴展到單一使用者 5,000+ entities 時還能維持嗎?
- 更小、更貼合領域的 embedder(而非 384 維 MiniLM-L6)是否真的能帶來新增的 top-5 命中?還是 FTS5 上限對個人記憶任務來說是結構性的?