為什麼 multi-agent 記憶系統不是 mem0 的形狀——4 個結構差別
2026 Q2 社群在比誰的 recall 分數高——Chronicle、Hindsight、Gigabrain。我用自建 memhall 跑七位一體 agent stack 5 天的體感是:recall 分數贏 40 個百分點,解不了真正的問題。真正的問題是 shape。
4/20 下午三點,我打開 Claude Code 想查一下之前寫過的一個決定,memhall search 回了,分數很怪。我心想 Ollama 又在發神經了。
打 /v1/health——degraded。已經 6 小時。沒人發現。
然後我做了一件事後看超蠢的決定:想說修 embedder 問題,順手把 docker run 改成 docker compose up。Compose 很熱心地告訴我一切都 healthy——然後 47 筆資料消失。
那天晚上我坐在電腦前,一邊啃鹹酥雞一邊想:Chronicle 每月 100 鎂,Hindsight 在 LongMemEval 91.4%,Gigabrain 用 typed memory + provenance 吊打所有人——我剛踩的這兩個坑,跟 recall 分數有一毛錢關係嗎?
沒有。
社群現在討論 AI memory 基本上就在比「誰記得最準」。但我用 memhall 跑七位一體 agent stack(Claude / Codex / Gemini / gemma4 / Perplexity Max / SuperGrok / 我)5 天的體感是:recall 分數贏 40 個百分點,解不了我真正的問題。
真正的問題是:當你有 N 個 agent 要共用一個記憶層時,mem0 的 schema 在幾個結構點上就是不夠。
這不是 mem0 做錯什麼。是它本來就是為「一個 user 的 chatbot」設計的。N 個 agent 共用是另一個物種。
下面 4 點寫給想做 multi-agent 的人——你在選 memory layer 時,別只看 recall,先看 shape。
1. 你要的是「user 的記憶」還是「agent 寫了什麼」?
上禮拜三下午,memhall 開了 Token auth。我寫了一筆公告 entry,然後 Codex 和 Gemini 各自在自己的 agent:codex / agent:gemini namespace 寫了一筆 ack:
我是 codex,已收到 memhall auth rollout 公告(2026-04-23)。後續 HTTP client 會帶 Authorization: Bearer...
我是 Gemini,已收到 memhall auth rollout 公告
蠢萌儀式,但它有用——我兩小時內就確認「三方都對齊了、誰看過誰還沒看過」。
這個儀式在 mem0 上做不出來。
mem0 的 primary key 是 user_id。所有記憶掛在一個 user 下。你可以在 metadata 硬塞 {"agent": "codex"}——但那是 string 欄位,不是 schema 層級的東西。不能 index、不能快速 filter per-agent、dedup 不會 per-agent 分開、audit log 看不到是誰寫的。
memhall 的 entry schema 第一個 required field 就是 agent_id:
entry {
entry_id
agent_id ← required
namespace ← 可以是 agent:codex / shared / project:X
type ← decision / observation / note / ...
content
...
}
每筆明確知道是哪個 agent 寫的。per-agent namespace 是零成本查詢。公告 + ack 這種儀式是結構 support 的,不是 application-layer hack。
聽起來沒什麼了不起,但 5 天裡我們做了兩次這種儀式(04-18 memhall 啟用公告、04-23 auth rollout 公告)——在 mem0 上我得在每個 entry content 硬塞「我是 X」當 prefix,query 時 grep 字串湊結果。醜,而且錯一個字就搜不到。
memhall 輸的地方:對一人用的 chatbot 過度。mem0 不需要強制 agent_id 是合理的——它本來就只有 user。
2. dedup 是「寫兩次就是同一筆」還是「LLM 覺得像就合併」
我剛開始用 mem0 時,天真地以為「同一段 content 寫兩次會被 dedup」。結果實際上 mem0 會先把你送進去的 content 過 LLM 抽成「結構化 fact」,再跟庫裡比 similarity,類似就 merge。
聽起來很聰明,直到你想寫 /wrap-up skill——session 結束時把 summary 寫進 memory,希望「同一個 session 如果重複呼叫 wrap-up 不要灌出一堆重複 entry」。
問題是 mem0 的 dedup 邏輯是非決定性的:
- 同一段 content 兩次,可能存成一筆、兩筆、或一筆加一個 update event
- depend on LLM 當下的 extraction 心情
- 你事後 audit 看不到原始 content,只看到「extracted fact」
- dedup 結果沒有 stable identifier——LLM 換版本後同一筆 fact 可能長得完全不同
我最後放棄了,自己在 skill 裡做 hash key。多做了一層東西,就為了繞過 mem0 的「聰明」。
memhall 的 dedup 很笨但很乾脆:
content_hash = sha256(agent_id + namespace + type + content)
entry_id = deterministic(content_hash)
同一個 (agent_id, namespace, type, content) 寫兩次 → 拿回同一個 entry_id,伺服端告訴你 "created": false。就這樣。
skill 可以無腦 curl,server 幫去重。我沒寫半行 idempotency 邏輯在 call site。
memhall 輸的地方:沒 fact extraction = 你寫進去什麼就是什麼。mem0 會把「我喜歡用 vim 不用 emacs」自動抽成 preference: vim over emacs 讓未來對話能 recall——這對 chatbot preference 場景是真實價值。memhall 你寫的就是 raw 字串,不會幫你抽。
3. embedder 掛掉的時候,你的 agent 也一起掛嗎?
回到 4/20 那天。Ollama 被大 LLM 擠 VRAM,bge-m3 embedder 進入 evict/load 迴圈,每次 embed request timeout 30 秒。
如果我用的是 mem0,我現在就是在電腦前哭。
mem0 是同步 write。POST 一筆 memory,request 裡面跑 fact extraction + embedding + 寫 vector DB + 寫 metadata DB,全部成功才回 200。任何一步慢——request 跟著慢。
那天如果是 mem0,我所有 agent 的寫入都會卡:/wrap-up 會 timeout、/handoff 會 timeout、每個背景 writer 都卡住。整個 seven-agent stack 在 embedder 恢復前基本廢了。
memhall 不會。它的 write contract 長這樣:
POST /v1/memory/write
├─ Step 1: 寫 SQLite metadata(必須成功)
└─ Step 2: 呼叫 embedder
├─ 成功 → sync_status=embedded, indexed_at=<now>
└─ 失敗 → sync_status=pending, indexed_at=null(背景 job 之後補)
回:202 Accepted, entry_id, embedded=<bool>
Embedder 掛,write 照通。entry 落盤 SQLite,只是暫時沒進向量索引。等 embedder 恢復,背景 reindex job 補齊。
4/20 那 6 小時 degraded,我的寫入完全沒斷。47 筆新 entry 都進庫了,只是那期間 semantic search 暫時退化成 lexical-only。我 session 該跑還是跑,沒有任何 agent 卡住。
那天的災難不在「寫入失敗」——是「讀取的 semantic search 分數崩了」+「Docker volume 等下會自己消失」。但這是兩件事,不要混。write path 本身的高可用是 memhall 給我的。
memhall 輸的地方:這個設計複雜度不低。你看 memhall 原始碼會發現很大一塊是處理 pending → embedded state machine 跟背景 reindex loop。mem0 的同步 write 寫起來乾淨多了。這是 memhall 為這個 contract 付的代價。
4. 有網路才能用嗎?
Codex 某些 sandbox 配置下 localhost TCP 是被擋的。不是它想擋,是 sandbox 本身的安全政策。
這種情況你 agent 怎麼有 memory?
- mem0:你完蛋了,沒 HTTP 就沒 memory
- memhall:
from memory_hall import build_runtime跑在 process 裡,直接讀同一個 SQLite 檔
from memory_hall import Settings, build_runtime
from memory_hall.models import WriteMemoryRequest
runtime = build_runtime(settings=Settings(...))
await runtime.start()
await runtime.write_entry(WriteMemoryRequest(
agent_id="codex",
namespace="agent:codex",
type="note",
content="...",
))
不走網路、不用 token、不用起 server。Codex 在 sandbox 擋 TCP 的環境下一樣有完整 memory。
這條 path 平常用不上——大部分 agent 都能打 HTTP。但 sandbox / 離線 / 嚴格 network isolation 的場景,「有 or 沒有」這條路就是差別。
memhall 輸的地方:兩條 code path 要維護。HTTP 和 in-process 行為必須對齊,不然會有「同一段邏輯兩個模式下結果不一樣」的詭異 bug。mem0 沒這困擾因為它只有一條路。
所以 memhall 比 mem0 強嗎
不是。
這 4 點我一直很小心地寫「memhall 輸的地方」——不是客套,是真的。
如果你是一人用的 chatbot——要記住使用者偏好、過去對話、自動提取 fact、個人化 recommendation——mem0 完勝,沒得比。fact extraction 成熟、SDK 豐富、SaaS managed 不用自己維運、LongMemEval 有公開分數、社群大。你用 mem0 能很快把 memory 接進 chatbot 做好。我自己如果要做個 chatbot side project,第一個選仍然是 mem0。
但如果你是 N 個 agent 協作的紀錄層——要 audit 誰寫過什麼、skill 要能 idempotent retry、embedder 不穩時 write path 要高可用、部分 agent 可能沒網路——mem0 的 shape 就是不對。不是做不到,是它 schema / contract / API 從一開始就沒把這些當 primary concern。
社群 2026 Q2 比 recall 分數比得很熱烈。Hindsight 91.4%。Gigabrain typed memory + provenance。這些很重要。但別忘了另一個維度:shape。
選 memory layer 前先問自己 4 題:
- 我的系統是「一個 user」還是「多個 agent」?
- 同一個 event 被寫兩次,是 memory layer 的乾淨責任,還是 caller 要自己處理?
- Embedder 掛掉我想要 write 仍通,還是 write 跟著掛?
- 有沒有 agent 需要無網路 fallback?
答案決定你該選什麼 shape。不是選什麼 recall 分數。
最後
我 5 天前把 agent stack 從 mem0 切到自建 memhall,踩了 Ollama 餓死 bge-m3 跟 Docker volume 被 compose 換掉兩個坑,救援過程寫在另一篇 postmortem。
但那些坑其實可以寫 mem0 也會遇到——基礎設施坑跟 memory layer 選型無關。
真正跟選型有關、讓我 5 天後覺得「對,這條路是對的」的,是上面 4 個結構差別。每一個都在 5 天裡實打實救過我一次。
你在做 multi-agent 或在考慮 multi-agent,這 4 題拿去用。比看 LongMemEval 分數更能幫你選到對的工具。
Links
- memory-hall GitHub
- ADR-0001 為什麼 drop mem0
- ADR-0002 multi-tenant from day one
- ADR-0005 v0.2 minimum viable contract
踩過類似坑歡迎 GitHub issue 聊,或 DM 我。