Files
ely-eye/bench/reports/EXTERNAL_APPENDIX.md
ZacharyZhang-NY 29163632af Score all five arms in one resumable pass
The per-arm runner recomputed BM25 and the dense matmul for every arm, which put the
full ablation at roughly 16 hours and left no way to survive a kill. JointRunner
computes each signal once per query, attributes latency to the components each arm
uses, and checkpoints every query to runs/baseline/rankings.jsonl.

- bench/systems.py: JointRunner + ARM_COMPONENTS signal attribution.
- bench/run.py: joint pass, rankings cache with a run fingerprint, per-query resume,
  latency summary, resident-memory progress lines.
- bench/tests/test_joint.py: joint rankings equal the per-arm rankings for all five
  arms; the sparse corpus is scored once even with three arms that need it; cache
  fingerprint mismatch and malformed rows fail fast.
- bench/reports/EXTERNAL_APPENDIX.md: what a real Mem0/Zep run would cost, and which
  published numbers are comparable at all.

Verification: pytest bench/tests -q -> 57 passed; ruff check bench/ -> all checks
passed; mypy (fresh cache) -> no issues in 25 source files.
2026-10-07 01:45:55 -04:00

66 lines
4.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Appendix: what a real Mem0 run would cost, and why its numbers are not ours
`bench/counterpart.py` rebuilds the store-and-retrieve half of Mem0 and Zep inside this repo so the
comparison runs on one corpus, one machine, one scorer. The products themselves are not run here.
This appendix records what running them would take, and which of their published numbers can be
placed next to ours at all.
## what the counterpart reproduces, and what it does not
| behaviour | Mem0 / Zep paper | `bench/counterpart.py` |
|---|---|---|
| store granularity | LLM-extracted facts, deduplicated against existing facts | deterministic 400-character sentence chunks, deduplicated by `(holder, sha256(text))` |
| scope | `user_id` / `agent_id` / `run_id` | `holder` from the frozen corpus |
| invalidation | Zep closes an edge when a newer fact contradicts an older one | `supersede` closes the older item when a newer one in the same `(holder, subject)` group overlaps it by less than 0.5 |
| retrieval | dense + sparse + graph signals, `top_200` budget in the 2026 hosted stack | dense cosine over the same 384-dimensional embedder the ely-eye dense arm uses, `top_k=10` |
| scoring | LLM-as-a-judge on a generated answer | source-level recall@10 against frozen gold |
The extraction step is the part that cannot be made comparable: Mem0's `add()` calls an LLM per
ingest, so its store contents depend on the model, the prompt, and the sampling temperature. Running
it against this corpus would add an LLM to the ingest path and make the memory store itself
non-reproducible, while leaving the retrieval question untouched. That is why the reference point is
in-repo.
## published numbers worth keeping
From the Mem0 paper (arXiv 2504.19413v1, LOCOMO, overall LLM-as-a-judge score, mean ± std over 10 runs):
| system | overall judge score | tokens retrieved per query | search p50 / p95 (s) | total p50 / p95 (s) |
|---|---|---|---|---|
| full-context | 72.90 ± 0.19 | 26,031 | — | 9.870 / 17.117 |
| Mem0 (graph variant) | 68.44 ± 0.17 | 3,616 | 0.476 / 0.657 | 1.091 / 2.590 |
| Mem0 | 66.88 ± 0.15 | 1,764 | 0.148 / 0.200 | 0.708 / 1.440 |
| Zep | 65.99 ± 0.16 | 3,911 | 0.513 / 0.778 | 1.292 / 2.926 |
| best RAG configuration | 60.97 ± 0.20 | k × chunk size | 0.267 / 0.624 | 0.766 / 1.829 |
| LangMem | 58.10 ± 0.21 | 127 | 17.99 / 59.82 | 18.53 / 60.40 |
| A-Mem | 48.38 ± 0.15 | 2,520 | 0.668 / 1.485 | 1.410 / 4.374 |
Two things in that table matter more than the scores:
- **full-context wins the answer-quality column and loses the cost column.** 72.90 at 26,031 tokens
per query, p95 17.1 s. Every memory system trades quality for cost. Any claim that a memory layer
"beats the context window" has to say which column it means.
- **the store-construction cost is not in the table at all.** The paper tabulates search and answer
latency, not `add()`. It reports separately that Zep's construction runs multiple asynchronous LLM
calls, so retrieval immediately after an add often fails and improves hours later. A benchmark that
scores retrieval only, on a store built in advance, hides that entirely.
Sources: arXiv 2504.19413v1 (table 2 and sections 4.1-4.5), the mem0 repository README's 2026
algorithm table (LoCoMo 92.5, LongMemEval 94.4, ~7.0k tokens, p50 0.88-1.09 s, single-pass retrieval
at a `top_200` budget, managed platform), and `docs.mem0.ai/core-concepts/memory-evaluation`.
## why these numbers cannot sit in our ablation table
1. Different metric. Their column is judge-scored answer correctness on 300 LOCOMO questions. Ours is
recall@10 of the gold **source** over 2,928 answerable queries drawn from six third-party sets.
A system can rank the right passage last and still answer correctly from a paraphrase.
2. Different budget. They retrieve `top_200` items; we score `top_k=10`.
3. Different hardware and model. Their run used a hosted production stack; ours is CPU-only with a
384-dimensional multilingual MiniLM, no CUDA in this WSL image.
4. Different corpus age. LOCOMO has 10 dialogues; our conversation set is LongMemEval-cleaned with
7,188 session files and 854 holders, and it also carries five non-conversational modalities that
no memory product is evaluated on here.
The honest use of this appendix is the ordering it implies — full-context > memory systems > naive
RAG on answer quality, and the reverse on cost — plus the two cost columns the paper leaves out.