Skip to main content

Benchmark for LLM memory decay โ€” measure how AI memory architectures forget across long conversations

Project description

๐Ÿ”ญ MemoryLens

Measure how your AI's memory forgets

An open-source benchmark for LLM memory decay โ€” 8 memory architectures, 6 metrics, 4 domain scenarios, statistical rigor, zero API keys required.

CI PyPI Python License: MIT PRs Welcome

Install ยท Quick Start ยท Results ยท How It Works ยท Contributing


The Problem

Every LLM application that runs multi-turn conversations has a memory strategy โ€” usually "keep everything in context and hope." Almost nobody measures what that strategy actually remembers after 50, 100, or 200 turns.

MemoryLens answers three questions:

  • How much does an AI still recall after N conversation turns โ€” and at what token cost?
  • Which memory architecture (full history, RAG, tiered compression, entity store, knowledge graphโ€ฆ) retains facts most efficiently?
  • When a user updates a fact ("I moved to Mumbai"), does the memory surface the new value, the stale one โ€” or contradict itself with both?

Every core metric is content-based and deterministic: no API key, fully reproducible. Add any LLM key and a two-stage answer+judge pipeline measures what the model actually answers.


Install

pip install memorylens-bench
Extra Installs For
memorylens-bench[dashboard] streamlit, plotly, pandas Interactive dashboard
memorylens-bench[server] fastapi, uvicorn REST API
memorylens-bench[faiss] faiss-cpu FAISS vector backend
memorylens-bench[groq] groq LLM evaluation via Groq
memorylens-bench[openai] openai LLM evaluation via OpenAI
memorylens-bench[anthropic] anthropic LLM evaluation via Anthropic
memorylens-bench[all] everything above โ€”

Python 3.10โ€“3.13 ยท Linux, macOS, Windows (all tested in CI).


Quick Start

CLI โ€” results in under a minute, no API key

memorylens                        # 100-turn benchmark, 3 backends
memorylens --seeds 5              # multi-seed: mean ยฑ std across 5 personas
memorylens --scenario medical     # domain scenarios: default | edtech | support | medical
memorylens --backends naive rag_chunked cascading graph
memorylens --seeds 5 --fit-curves # fit Ebbinghaus + exponential forgetting curves
memorylens --llm --provider groq  # real answer+judge LLM evaluation

Python API

from memorylens import run_benchmark, results_to_display_dict

raw = run_benchmark(total_turns=100, backends=["naive", "rag", "cascading"])
results = results_to_display_dict(raw)
print(results["cascading"]["recall"])   # recall at each checkpoint

Dashboard and REST API

pip install "memorylens-bench[dashboard]"
streamlit run dashboard.py            # decay curves, run history, cost projection

pip install "memorylens-bench[server]"
uvicorn memorylens.api:app            # POST /v1/benchmarks โ†’ job id โ†’ poll results

Benchmark Results

All numbers below are reproduced from this exact code (v0.4.0) with: memorylens --seeds 5 --backends naive rag rag_chunked cascading summary entity graph faiss

Recall@T โ€” 100 turns (mean ยฑ std, n=5 personas)

Backend T=10 T=50 T=100 Tokens/query @ T=100
naive (1,200-token budget) 100% 100% 35.0 ยฑ 5.6% 1,193
rag 100% 100% 100% 67
rag_chunked 100% 100% 100% 56
cascading 100% 100% 100% 326
summary (extractive) 100% 100% 100% 356
entity 100% 100% 100% 63
graph 100% 100% 100% 62
faiss 100% 100% 100% 67

Read this before quoting numbers. At 100 turns with 8 templated facts, every retrieval- and extraction-based backend saturates โ€” the differentiation is token cost at equal recall (rag_chunked delivers the same recall as naive at ~1/21 the tokens) and naive's collapse once its context budget evicts early turns. Two structural caveats:

  1. entity / graph exploit the fact templates. Facts are injected as "My X is Y", which the regex extractors match by construction. Their 100% shows structured stores never lose what they capture โ€” it says nothing about free-form conversation. Harder paraphrased injections are on the roadmap.
  2. summary is extractive in zero-key mode โ€” it keeps fact-bearing lines verbatim, so substring recall is guaranteed. With an LLM key, compression becomes abstractive and honest decay appears.

Stress test โ€” 200 turns

At 200 turns the bounded systems saturate and real decay separates the field (memorylens --turns 200 --checkpoints 10 50 100 150 200 --seeds 5 --backends ...):

Backend T=100 T=150 T=200 Tokens/query @ T=200
naive 35.0 ยฑ 5.6% 7.5 ยฑ 6.9% 7.5 ยฑ 6.9% 1,189
rag_chunked (bounded index) 100% 17.5 ยฑ 6.9% 5.0 ยฑ 6.9% 70
cascading 100% 100% 100% 326
rag ยท summary ยท entity ยท graph ยท faiss 100% 100% 100% 62โ€“382

The two production-realistic constraints fail in opposite ways: naive's fixed token budget evicts old turns wholesale, while rag_chunked's bounded FIFO vector index (200 chunks) silently drops the earliest facts even though each query costs only 70 tokens. Cascading survives because its cold tier compresses fact-bearing lines instead of discarding them.

Temporal drift and contradiction after fact updates

Two facts update mid-conversation (city at T=40, age at T=60). At T=100:

Backend Drift (stale-only retrieval) Contradiction (old + new surfaced)
naive 0.0 1.0 โ€” full history keeps both values
cascading 0.0 0.0 โ€” cold summaries patched in place
entity / graph 0.0 0.0 โ€” updates overwrite in place

Drift is a retrieval-layer proxy (worst-case bound). For behavioral measurement โ€” does the model answer with the stale value โ€” run memorylens --llm.


How It Works

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  LAYER 1 โ€” SIMULATOR                                                    โ”‚
โ”‚  Injects facts at known turns, fires domain filler queries in between   โ”‚
โ”‚  Facts can be updated mid-conversation to test drift + contradiction    โ”‚
โ”‚                                                                         โ”‚
โ”‚  T=0  "My name is Arjun Sharma."                                        โ”‚
โ”‚  T=1  "My city is Bangalore."                                           โ”‚
โ”‚  T=40 "My city has changed to Mumbai."   โ† update event                โ”‚
โ”‚  Scenarios: default (tech Q&A) ยท edtech ยท support ยท medical             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  LAYER 2 โ€” MEMORY BACKENDS (8 implementations)                          โ”‚
โ”‚                                                                         โ”‚
โ”‚  naive        Full history, evict oldest at a token budget              โ”‚
โ”‚  rag          Embed every message, retrieve top-K (upper bound)         โ”‚
โ”‚  rag_chunked  Chunked + bounded FIFO index (production-realistic)       โ”‚
โ”‚  cascading    Hot/Warm/Cold tiers with Ebbinghaus temporal decay        โ”‚
โ”‚  summary      Rolling compression (LLM or extractive)                   โ”‚
โ”‚  entity       Structured key-value fact store                          โ”‚
โ”‚  graph        NetworkX knowledge graph, in-place fact updates           โ”‚
โ”‚  faiss        FAISS vector index (optional dependency)                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  LAYER 3 โ€” EVALUATOR (6 metrics, dual mode)                             โ”‚
โ”‚                                                                         โ”‚
โ”‚  Content mode (no API key): deterministic substring checks              โ”‚
โ”‚  LLM mode (any provider):   answer+judge pipeline โ€” did the LLM         โ”‚
โ”‚                             actually answer correctly?                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The 6 Metrics

Metric What It Measures
Recall@T Is the correct current fact value in retrieved context at turn T?
Precision@K Of K retrieved chunks, how many contain a real fact?
Temporal Drift After an update, what fraction of retrieval still carries the stale value? (worst-case proxy)
Contradiction Does the context surface both old and new values at once, forcing the LLM to arbitrate?
Memory Noise Ratio What fraction of retrieved context is irrelevant to any known fact?
Cascade Efficiency Recall-per-token vs the naive baseline

The 4 Temporal Decay Functions

The cascading backend's warm-tier scoring uses a pluggable forgetting curve โ€” compare them with --decay:

Name Formula Reference
ebbinghaus (default) e^{-t / (Sยทโˆš(1+t))} Ebbinghaus (1885)
exponential e^{-kยทt/window} Jost (1897)
linear 1 โˆ’ t/window Wickelgren (1972)
default max(0.2, 1 โˆ’ 0.6ยทt/w) Original heuristic

--fit-curves fits both Ebbinghaus and exponential models to your measured Recall@T series and reports half-life, stability, and Rยฒ.


LLM Provider Support

All content metrics work with no API key. Add any one key for the LLM answer+judge pass:

Provider Key Default Model Free Tier
Groq GROQ_API_KEY llama-3.1-8b-instant โœ…
OpenAI OPENAI_API_KEY gpt-4o-mini โŒ
Anthropic ANTHROPIC_API_KEY claude-haiku-4-5 โŒ
OpenRouter OPENROUTER_API_KEY llama-3.1-8b-instruct:free โœ…
Ollama (none โ€” local) llama3.2 โœ…
memorylens --list-providers
memorylens --llm                  # auto-detect
memorylens --llm --provider groq  # force one

How MemoryLens Compares

Most evaluation frameworks measure quality at a single point in time. MemoryLens measures how memory quality changes over conversation turns โ€” a dimension the tools below don't cover (they solve different problems, and compose well with this one):

Framework Focus Decay over turns Multi-architecture No-API mode
MemoryLens Memory decay โœ… โœ… 8 backends โœ…
RAGAS RAG answer quality โŒ โŒ โŒ
TruLens LLM app monitoring โŒ โŒ โŒ
DeepEval LLM answer quality โŒ โŒ Partial
MemGPT / Letta A memory system, not a benchmark โ€” โ€” โ€”

Details: docs/comparison-with-existing-tools.md


Project Structure

memorylens/              installable package
โ”œโ”€โ”€ memory/              8 backends + decay functions (add yours here)
โ”œโ”€โ”€ simulator/           facts, conversation generator, personas, scenario registry
โ”œโ”€โ”€ evaluation/          metrics, benchmark runner, stats, experiment logger
โ”œโ”€โ”€ utils/               local embeddings, LLM providers, SQLite run storage
โ”œโ”€โ”€ api.py               FastAPI REST server
โ””โ”€โ”€ cli.py               `memorylens` command

tests/                   39 integration tests, no API key needed
dashboard.py             Streamlit dashboard (decay curves, run history, export)
docs/                    guides: adding a backend, comparisons, methodology

Contributing

A new memory backend is 3 methods and one registry line โ€” the full guide with a worked example is in docs/adding-a-new-backend.md:

from memorylens.memory.base import BaseMemory

class YourMemory(BaseMemory):
    name = "your_backend"
    def add_message(self, role, content, turn): ...
    def get_context(self, query, current_turn): ...
    def reset(self): ...

New scenarios are a data file plus one registry entry. New metrics are plain functions.

git clone https://github.com/Neal006/memorylens && cd memorylens
pip install -e ".[server,dev]"
pytest tests -q          # all green before you start

Start here: good first issue ยท Guide: CONTRIBUTING.md ยท Plans: ROADMAP.md


Citation

@software{memorylens2026,
  author  = {Daftary, Neal},
  title   = {{MemoryLens}: A Temporal Decay Benchmark for {LLM} Memory Architectures},
  year    = {2026},
  url     = {https://github.com/Neal006/memorylens},
  version = {0.4.0}
}

License

MIT โ€” free to use, modify, and distribute.


If MemoryLens is useful to you, a โญ helps other researchers and developers find it.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

memorylens_bench-0.4.0.tar.gz (62.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

memorylens_bench-0.4.0-py3-none-any.whl (61.1 kB view details)

Uploaded Python 3

File details

Details for the file memorylens_bench-0.4.0.tar.gz.

File metadata

  • Download URL: memorylens_bench-0.4.0.tar.gz
  • Upload date:
  • Size: 62.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for memorylens_bench-0.4.0.tar.gz
Algorithm Hash digest
SHA256 08051ba753a322415f8b3504eca440396267f6669b3c2c8077fd44e0055c6671
MD5 9ffe25b626675baaabe96bbe041a3274
BLAKE2b-256 b670a81cd05a74f6417c6d178df2a4ec5591a2439c960b3708cf6a3323246405

See more details on using hashes here.

Provenance

The following attestation bundles were made for memorylens_bench-0.4.0.tar.gz:

Publisher: release.yml on Neal006/memorylens

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file memorylens_bench-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for memorylens_bench-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d7b26bf36afbe911babe401d43dab28ae50ff0d398080654b4d183a2a9d89c34
MD5 45fd092bc6bbd3144fd3f51b163ad0f9
BLAKE2b-256 85bf8df798b3e024a1a00d9010193aa0f5ba5bf6fde944b3f8a4fccc5f684aaf

See more details on using hashes here.

Provenance

The following attestation bundles were made for memorylens_bench-0.4.0-py3-none-any.whl:

Publisher: release.yml on Neal006/memorylens

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page