How to Test AI Agent Memory: 5 Simulation Runs with Mem0
How to Test AI Agent Memory: 5 Simulation Runs with Mem0
AI agents become genuinely useful when they remember things across sessions. A personal assistant should know that a user prefers vegetarian restaurants, recognize when that preference changes to vegan, and never surface the older preference as if it were still current. That sounds straightforward, but the path from "memory write" to "correct retrieval weeks later" involves more failure modes than most developers anticipate and almost none of them are visible to conventional testing.
Unit tests verify that individual functions behave correctly. Integration tests verify that memory writes and searches work end to end. Neither test captures how memory behaves across many sessions where preferences evolve, contradict each other, and compete for retrieval. You can write assert memory.search("diet") == "vegan" in a test and have it pass cleanly, and still ship an assistant that recommends a cheese plate to a user who switched from vegetarian to vegan six weeks ago. The function worked. The pipeline worked. The failure lives in the accumulated state that no individual test ever generated.
This is what makes long-term memory testing genuinely hard. The bugs are emergent, but they only appear after enough history has accumulated to create ambiguity at retrieval time. A memory system that looks perfect in a five-turn test conversation can look very different after eighty turns across a dozen sessions, especially when the user's preferences are evolving.
Note: This article documents what we found when we built a memory simulator and ran it against Mem0's hosted platform using Python SDK mem0ai==2.0.1. The results showed that Mem0 is a reliable foundation for long-term agent memory with the right integration pattern; it handles most real-world memory scenarios well.
What We Built
Memsim (Memory-Simulator) is a small open-source framework that generates synthetic user trajectories, replays them through a memory backend, and scores the results against known ground truth. The design is deliberately minimal where trajectories are sequences of turns, each turn has a type, and ground truth is tracked deterministically so that probes can score memory accuracy against what the system should know at any given point.
Here is what our set up looked like:
- 80 turns per trajectory
- 3 seeds per contradiction rate
- 4 contradiction rates: 0%, 5%, 15%, and 30%
- 12 total trajectories per run
- Mem0 as the memory backend
- Semantic scoring in the final run
The simulator tracks ground truth at every turn, which means that at any point in a trajectory, you can ask "what is the canonical current value of the user's diet preference?" and get a precise answer. That ground truth is what the probes score against.
The Four Memory Behaviors We Measured
Before getting to the results, it's worth being precise about what each probe is actually measuring.
- Stale facts: This measures whether an older preference surfaces after a newer update exists. This is the vegetarian/vegan problem: the user updated their preference, but the assistant is still retrieving and acting on the old value.
- Unresolved contradictions: In this we measure whether old and new values appear together in the top-k results without a clear signal about which is current.
- Scope leakage: This measures whether one user's memories appear in another user's namespace. In multi-tenant memory systems, this is a correctness bug with direct privacy implications.
- Retrieval drift: Finally, retrieval drift measures whether a stable fact remains consistently retrievable as the trajectory grows longer.
Experiments
The most important thing about these experiments is that they were designed as a progression rather than a single measurement. Each run changed exactly one thing like the evaluation method, the integration pattern, or the probe design, so that we could isolate what each change contributed to the results.
TL;DR
The experiments were intentionally incremental. Each run changed one part of the setup so we could separate Mem0 behavior from evaluator behavior, timing artifacts, and downstream resolution logic.
| Run | What changed | Stale facts | Unresolved contradictions | Scope leakage | Retrieval drift | Outcome |
|---|---|---|---|---|---|---|
| Default baseline | Mem0 default behavior with exact scoring | 0.948 | 0.377 | 1.000 | 0.421 | Mem0 retrieved many current facts, but exact scoring and unresolved historical memories made contradictions look worse. |
| Custom instructions | Added latest-wins-style extraction guidance | 0.398 | 0.346 | 1.000 | 0.799 | Prompt-level guidance alone was not enough to reliably create a current-state view. |
| Timestamp-aware V1 | Added downstream timestamp-aware resolution | 0.839 | 0.834 | 1.000 | 0.354 | Treating the newest timestamped memory as current substantially improved stale and contradiction behavior. |
| Timestamp-aware V2 | Added semantic scoring and improved drift tracking | 0.917 | 0.917 | 1.000 | 0.750 | Best raw stale/contradiction scores; semantic scoring gave Mem0 credit for correct paraphrases. |
| Timestamp-aware V3 | Removed fallback drift checkpoints and kept only stable durable facts | 0.853 | 0.850 | 1.000 | 0.917 | Cleanest final measurement; Mem0 stayed isolated, retained stable facts, and handled most contradictions with timestamp-aware resolution. |
Run 1: Default baseline with exact scoring
The first run used Mem0's default configuration with exact-match scoring and no special integration logic. It represents the experience of a developer with the standard SDK integration and evaluates the results by checking whether retrieved text contains the expected value verbatim.
Outcomes:
- Scope leakage was perfect at 1.0 across every contradiction rate.
- Stale facts came in at 0.948 averaged across all contradiction rates.
- The unresolved contradictions score of 0.377 looked alarming at first,
- Retrieval drift’s performance turned out to reflect a combination of the ADD-only architecture surfacing historical entries and the exact scorer's inability to credit semantically correct paraphrases.
Run 2: Custom instructions with latest-wins guidance
Outcomes:
- Stale facts dropped to 0.398.
- Unresolved contradictions improved slightly to 0.346 averaged, and retrieval drift dipped initially but improved at later stages.
Run 3: Timestamp-aware resolution
Outcomes:
- Run 3 implemented sorting after calling
mem0.search(). The most recent matching memory was treated as the canonical current value. - This single change transformed the unresolved contradictions score from 0.346 to 0.834 and stale facts from 0.398 to 0.839.
Run 4: Semantic scoring
Outcomes:
- Stale facts improved to 0.917 and unresolved contradictions to 0.917.
- Retrieval drift improved to 0.750.
Run 5: Final Run with Drift Probe Fix
Outcomes:
The final results: stale facts at 0.853, unresolved contradictions at 0.850, scope leakage at 1.0, and retrieval drift at 0.917. All four probes are above the 0.7 healthy threshold on average.
Why Timestamp-Aware Resolution Worked?
Mem0's latest algorithm was redesigned to preserve memory history rather than overwrite it. Instead of two LLM passes that decided whether to ADD, UPDATE, or DELETE existing memories, the new pipeline uses a single-pass ADD-only extraction with hybrid retrieval that combines semantic, keyword, and entity signals.
Recipe for Teams Building Memory-Augmented Agents
The progression from Run 1 to Run 5 maps the decisions a production team faces when building on Mem0. Each one has a concrete practice attached.
- Semantic Scoring: Exact matching undercounts correct retrievals because memory systems return semantically correct paraphrases.
- ADD-Only Architecture: The new algorithm preserves the full sequence of preference changes rather than overwriting them.
- Timestamp-Aware Resolution: When your application needs the latest value for a field, sort retrieved memories by
created_atdescending.
Running the Experiment Yourself
The final run can be reproduced with the following command. You will need mem0ai==2.0.1 version.
pip install -e".[mem0]"
pip install matplotlib tabulate
exportMEM0_API_KEY="your_key_here"
python examples/run_article_sweep.py
--backend mem0
--seeds3
--turns80
--user-id memsim_article_timestamp_aware_v3
--downstream-resolution timestamp-aware
--scorer semantic
--output memsim_output/mem0_article_timestamp_aware_v3
The simulator's final run validates that the timestamp-aware resolution holds across various contradiction rates with above-threshold scores on all four probes.
Final Thoughts
Mem0 provides the durable memory foundation, while the simulator provides the instrument for validating that the application is using that foundation correctly.