Building blocks of machine recall · 2nd edition
Large language models reset the moment a fact slides past the context window. Mem0 is the persistence engine that refuses to forget: extracting salient facts from conversation, consolidating them, and serving them back on demand across sessions, agents, and months of interaction.
Mem0, pronounced “mem-zero”, is an open-source memory layer that sits between an application and its LLM. Rather than stuffing an entire transcript into every request, it distills conversations into compact, addressable facts, stores them in a vector index (and optionally a knowledge graph), and retrieves only what the current query needs.[1][8]
The project ships as a Python and TypeScript SDK, a self-hostable server, a hosted platform, a CLI, and an MCP server. It is governed by a research paper, public documentation, and a 41k-star source repository: the three pillars this report is built on and continuously cross-checks against one another.[1][4][2]
Sources are tagged PRIMARY (paper · docs · source) and SECONDARY (technical write-ups used only to corroborate). Where the two diverge, e.g. the default model, the report says so explicitly.
The substrate
Mem0 is deliberately a thin, swappable layer: it requires an LLM (for fact extraction and update decisions) and an embedding model (for semantic search), then plugs into whatever vector store, graph store, and history database you choose.[4][12]
The core engine is written in Python, with a first-class TypeScript/JavaScript SDK mirroring the same API. Access is offered through four surfaces: the in-process Memory / AsyncMemory classes, a remote MemoryClient hitting the REST API, a self-hosted server, and a CLI. An MCP server exposes the primitives as tools so agents can call them directly.[4][9]
| Layer | Default | Pluggable options (representative) |
|---|---|---|
| LLM | gpt-5-mini ◇ | OpenAI · Anthropic · Google · AWS Bedrock · Azure · Groq · Together · Ollama · 20+ providers |
| Embedder | text-embedding-3-small | OpenAI · HuggingFace · Vertex · Together · Bedrock · 14+ providers |
| Vector store | Qdrant | Chroma · FAISS · Pinecone · Weaviate · Mongo Atlas · Azure AI Search · Elasticsearch · Redis · 10+ |
| Graph store | removed in v3 ◆ | Previously swappable (Neo4j · Memgraph · Kuzu · AGE · Neptune). The v3 OSS algorithm removed external graph stores (~4,000 lines), replacing them with built-in entity linking. See §05. |
| History DB | SQLite | local audit trail of every ADD / UPDATE / DELETE; swap to Postgres in production |
◇ Cross-verification note. The default extraction model has migrated over time: community guides cite gpt-4o-mini and later gpt-4.1-nano, while the current README states gpt-5-mini. All agree the model is configurable, and the default is whatever ships in your installed version.[4][14]
Two deployment philosophies share one API: Mem0 Open Source (you run the LLM endpoint and stores) and Mem0 Platform (hosted). Switching is a matter of swapping Memory() for MemoryClient(api_key=…).[5]
Architecture
A single MemoryConfig object is the spine. It wires the AI-processing layer (LLM + embeddings) to the storage layer (vector + history + optional graph), overridden in layers: built-in defaults → environment variables → config files → programmatic objects.[13]
MemoryConfig (green dashed = configuration flow). Processing fans out to LLM + embeddings; storage spans the vector store, the SQLite history log, and the optional graph store (shown here as the legacy architecture, removed in the v3 OSS algorithm).[13][33]All memory levels live in the same physical stores but are kept apart logically by scoping identifiers, user_id, agent_id, app_id, and run_id, carried in each record’s payload. A search for one user implicitly restricts to records where the other identifiers are null, unless you widen the filter with wildcards.[6][13]
The pipeline
The research paper describes an incremental, two-phase pipeline. Memory is never a passive dump: every new exchange is read for meaning, then weighed against what is already known.[1]
When a message pair arrives, Mem0 assembles a prompt from two context sources: a rolling conversation summary (refreshed asynchronously so extraction never blocks) and a recent-message window. An LLM extraction function distills this into a set of candidate facts, self-contained statements worth remembering.[1]
Each candidate fact is not blindly stored. Mem0 embeds it, runs a semantic search for the top-k most similar existing memories, and hands both to an LLM through a tool-call interface. The model chooses one operation per fact.[1][18]
This is the mechanism behind Mem0’s most-quoted property: controlled forgetting. By delegating the keep/merge/discard decision to a model with the relevant neighbours in view, rather than to brittle if/else rules, the store stays dense and non-contradictory as it grows.[16][18]
“Mem0 implements a novel paradigm that extracts, evaluates, and manages salient information from conversations through dedicated modules for memory extraction and updation.”Chhikara et al., Mem0 research paper, arXiv:2504.19413
The hosted platform and current SDK ship a successor to the paper’s pipeline: a single-pass hierarchical extraction with multi-signal retrieval (semantic + keyword + entity boosting, plus rerankers). It keeps the average retrieval call under ~7,000 tokens while raising benchmark accuracy.
Under the hood
Both the in-process and remote APIs expose the same surface. Here is what each call actually does once it leaves your code.[13][3]
add() always pays for an LLM call (extraction + A.U.D.N.); search() pays only for one embedding plus a vector lookup, optionally enriched by graph traversal and a reranker. This asymmetry, expensive writes, cheap reads, is why Mem0 fits read-heavy chat workloads.[17]reset() wipes the entire store; from_config() builds a configured instance. AsyncMemory mirrors every method with async/await.[13]
Graph memory · Mem0g
The graph variant, Mem0g, stores memories as a directed, labelled graph: entities are nodes, relationships are edges. It exists for queries that must navigate relational paths, the multi-hop questions a flat vector store struggles with.[1]
Extraction here is a two-stage LLM process. An entity extractor identifies people, places, objects, concepts, and events; a relationship generator emits triplets of the form (source, relation, destination). On write, embeddings are computed for source and destination, existing nodes are reused if similarity clears a threshold, and a conflict detector flags relationships the new information contradicts, with outdated edges marked invalid (soft-deleted) rather than erased, preserving history for temporal reasoning.[1][14]
The cost is real: entity extraction is heavier than fact extraction, so add() slows and a graph database must be operated. The guidance, echoed by practitioners, is that graph mode earns its keep only when retrieval actually traverses relationships; otherwise the default flat store is the right call.[17]
In April 2026 Mem0 shipped a new memory algorithm. In the open-source default, the external graph store was replaced by built-in entity linking: on add(), entities are extracted into a parallel {collection}_entities collection; at search time, query entities are matched and used to boost ranking. The trade-off is blunt: this is no longer a queryable graph, and the relations field is gone.[23]
Can you still change the underlying graph DB? On the current default, no. The OSS v2→v3 migration guide is explicit: graph_store, enable_graph, the Neo4j default config, and ~4,000 lines of external graph code have been removed entirely. The swappable provider set (Neo4j, Memgraph, Kuzu, Apache AGE, Neptune) applies to the previous algorithm only.[33]
Memory types
Mem0 maps the classic cognitive categories onto layered storage: a sticky note for the current task, a journal for the session, an archive for the user. What fades quickly and what lasts for months is a choice you make per write.[2]
add() creates short-term plus long-term (semantic + episodic) memories; procedural memory is explicit.[2]The layers are stored separately and combined at query time in three steps: capture (messages enter the conversation layer while the turn is live) → promote (relevant details persist to session or user memory) → retrieve (the search pipeline pulls from all layers, ranking user memories first, then session notes, then raw history).[2]
| Layer | Lifetime | Horizon | Best for |
|---|---|---|---|
| Conversation | single response | short | tool calls, chain-of-thought within a turn |
| Session | minutes–hours | short | multi-step flows (onboarding, a debugging run) |
| User | weeks–forever | long | personalization, preferences, account state |
| Org | configured globally | long | shared FAQs, policies, catalogs across agents |
Use run_id when short-term context should expire automatically; rely on user_id for lasting personalization.[2]
The data model
A memory is a discrete, addressable unit, not a chunk of transcript. Each one carries its content, a semantic embedding, scoping identifiers, a content hash, timestamps, and free-form metadata.[9][14]
old → new with an event type and timestamp, the basis for history() and governance. Graph mode adds typed nodes and labelled edges.[9][13]{
"id": "13efe83b-a8df-4ec0-814e-428d6e8451eb",
"memory": "Likes to play cricket on weekends",
"hash": "87bcddeb-fe45-4353-bc22-15a841c50308",
"metadata": { "category": "hobbies" },
"user_id": "alice",
"created_at": "2024-07-26T08:44:41-07:00",
"updated_at": null
}The evidence
Mem0’s central claim is a three-way win, accuracy, latency, and cost, measured on LOCOMO, a long-conversation QA benchmark, with an LLM-as-a-judge (J) score.[1][7]
The honest caveats. Mem0’s own evaluation notes that small benchmarks like LoCoMo can be inflated by aggressive retrieval or frontier models, and that on short conversations (≤30 turns) full-context prompting can still beat a memory layer on raw accuracy; the win appears as histories grow long enough that replaying them becomes slow and expensive. Independent benchmarks from competing systems report different rankings, a reminder that memory evaluation is contested and configuration-sensitive.[7][20]
“Optimizing one is easy. Balancing all three, accuracy, cost, and latency, at scale is the actual problem.”Mem0 docs, Memory Evaluation
Context engineering
Context engineering is the discipline of deciding what goes into the prompt on every turn. Mem0’s role is to keep that window small and high-signal: instead of replaying history, you retrieve a handful of distilled facts and inject only those.[12][17]
Scope deliberately. Use run_id for ephemeral, self-cleaning session context and user_id for durable personalization; combine with agent_id / app_id to separate user-stated facts from agent-generated inferences.[6]
Mind the write/read economics. Every add() costs an LLM call; every search() costs only an embedding plus a lookup. For read-heavy chat this is a clear win; for write-heavy, low-read workloads the extraction overhead may not pay off.[17]
Structure your metadata early. Wrap memories with categories, a schema_version, and consistent identifiers so you can filter, audit, and run retention workflows later. Mem0 supplies the primitives, but the schema and write policy are yours to design.[22]
Keep the graph off until traversal pays. Default to the flat vector store; reach for Mem0g only when queries genuinely need multi-hop relational reasoning.[17]
m = MemoryClient(api_key=...)
m.add(messages, user_id="alice") # write: extract + reconcile
hits = m.search("dietary restrictions?", # read: cheap retrieval
user_id="alice")
prompt = system + format(hits) + user_turn # inject only what's relevantLimits & trade-offs
Mem0 provides building blocks, not guarantees. A short, honest inventory of what it does not solve for you.[22]
Model interpretation is not free. Extraction and update decisions are LLM calls, so they inherit LLM failure modes: a fact can be misread, an update misjudged, or a retrieved memory ignored at generation time. Memory quality is bounded by the model you wire in.[22]
Governance is yours. Memories are retrievable by design, so secrets and unredacted PII should never be written raw; the docs advise hashing or encrypting sensitive values and building retention workflows on top of the delete API and metadata filters.[2]
Schema drift is real. As an application evolves, metadata conventions change; versioned keys and disciplined write policies are the only defense, and they remain an engineering responsibility.[22]
Benchmarks are contested. Accuracy numbers are configuration-sensitive and the field lacks a settled evaluation; treat any single headline figure, including Mem0’s, as directional, not absolute.[7][20]
Mem0 is a model-driven, framework-agnostic memory layer that separates reasoning from persistence: it distills conversation into addressable facts, reconciles them as they change, and serves them back cheaply, turning a fixed context window into something that behaves like long-term memory. Used well, it is less a database than a discipline for context engineering.
Part II · the operating layer
Beyond the pipeline and the store lies the machinery that makes memory usable in production: how it ages, how it is found, how it is shaped, and how it is governed. Six chapters on Mem0 as a full operating layer, not just an extract-store-retrieve loop.
The missing dimension
An agent that remembers everything forever degrades: stale facts pollute the top-k, inflate tokens, and contradict current truth. Mem0 addresses this with three distinct mechanisms that are easy to conflate but do very different things.[25][27]
Shipped May 2026 as a per-project toggle, Memory Decay biases search ranking toward recently-used memories. Each memory keeps a record of its last accesses (up to 20 timestamps); a memory touched today can earn a ~1.5× boost while one idle for weeks is dampened toward ~0.3×, a roughly 5× spread that reorders candidates without overwhelming the underlying relevance score (clamped to [0,1]). Nothing is deleted; reinforcement runs fire-and-forget, so search latency is unchanged. Disable any time with decay: false.[25]
By default Mem0 memories persist forever. Pass an expiration_date and a memory self-destructs after its window, no cron jobs. The documented pattern is tiered retention: ~7-day session context, ~30-day chat history, permanent preferences and core facts.[26]
Beyond decay and expiry sit classic eviction policies: hard TTL, LRU (drop entries unused for N days; each access resets the clock), and periodic pruning. The critical caveat: Mem0 OSS has no built-in TTL, decay, or expiration. expiration_date is Platform-only, and self-hosted lifecycle management is your responsibility.[30][31]
Retrieval, in depth
Section 04 sketched search as “embed the query, match by similarity.” The real retrieval path is richer: a multi-signal fusion with optional reranking and several specialized modes.[23]
Multi-signal / hybrid retrieval. The V3 algorithm fuses three signals in parallel, semantic similarity, BM25 keyword matching, and entity matching, each normalized and combined into one score. (Hybrid search benefits from an NLP install: pip install mem0ai[nlp] plus a spaCy model.)[23]
Reranking. Vector search returns the right candidates in the wrong order; an optional second-pass reranker re-scores them against the query using Cohere, Hugging Face, Sentence Transformers, or an LLM before anything reaches the context window.[23]
Criteria-Based Retrieval. Beyond generic relevance, you can rank by custom criteria that matter to your app, emotional tone, intent, behavioral signals, so a support agent surfaces frustrated-tone memories first.[28]
Temporal Reasoning & filters. Time-aware retrieval ranks the right dated instance for “last week,” “right now,” or “upcoming,” and V2 filters compose AND/OR over metadata, entity, and time.[29]
Write-side control
Extraction isn’t a black box you have to accept. Mem0 exposes project-level and per-request controls over what it remembers, plus capabilities that rarely make the headline tour.[29]
Architecture, updated
The two-phase pipeline in Section 03 is the paper’s design. The production system has since moved to a single-pass hierarchical extraction with multi-signal retrieval, and the benchmarks I led with are now superseded.[23][24]
Two changes matter most. First, extraction is now single-pass and ADD-oriented, and it treats agent-generated facts as first-class, storing the assistant’s confirmations and recommendations with the same weight as user statements, closing a real coverage gap. Second, as covered in the §05 update, OSS swapped the queryable graph for entity linking.[23]
Unit note: the 2025 paper reports ~26K tokens per conversation for full-context; the 2026 algorithm reports ~6,956 tokens per retrieval call. Different units, same efficiency story. Headline accuracy figures also vary slightly by snapshot (91.6–92.5 on LoCoMo).[23]
| Benchmark | V3 algorithm (2026) | vs. previous |
|---|---|---|
| LoCoMo | 91.6 | +20 points |
| LongMemEval | 94.8 | +27 points (+53.6 on assistant-memory recall) |
| BEAM (1M tokens) | 64.1 | production-scale, 1M-token evaluation |
| biggest category gains | temporal +29.6 · multi-hop +23.1 | the two that matter most for real histories |
Beyond the SDK
It is tempting to treat Mem0 as one library. It is closer to a small ecosystem.[29]
OpenMemory, a local-first, self-hosted memory product (its API ships as a FastAPI service under openmemory/api) that auto-captures coding memories and serves project-scoped context to AI agents, all under your own infrastructure.[29][32]
MCP everywhere. A hosted MCP server (mcp.mem0.ai) and a self-hosted one expose memory as tools; an editor plugin provides nine MCP tools (add_memory, search_memories, …) for Claude Code, Cursor, Codex, OpenCode, and Antigravity. Agents can even self-mint an API key via the CLI in under five seconds.[29]
Framework breadth. Official integrations span 20+ frameworks, LangChain, LangGraph, LlamaIndex, CrewAI, AutoGen, Agno, the OpenAI Agents SDK, Google ADK, Mastra, and the Vercel AI SDK, plus voice/real-time stacks (ElevenLabs, LiveKit, Pipecat) where memory writes run async to avoid adding latency.[29]
Production posture
Memory at scale is infrastructure, and the Platform ships the controls that implies, most of which hide behind a single cover stat.[5]
Tenancy. A full Organizations → Projects hierarchy with members and roles provides multi-tenant isolation, layered on top of the per-memory user_id / agent_id / app_id / run_id scoping covered earlier.[29]
Compliance & data control. SOC 2 (Type 1) and HIPAA compliance, BYOK, and zero-trust options; deployable to Kubernetes, private cloud, or air-gapped with the same API. Because memories are retrievable by design, the docs are explicit that PII should be hashed/encrypted and governed by retention workflows.[7][5]
Observability. Every read and write is logged (the history table underpins history()); the Events API surfaces async operations; webhooks notify external systems; and a managed dashboard exposes usage. Platform advertises sub-50ms retrieval.[29]
Mem0 is not just an extract-store-retrieve pipeline. It is a memory operating layer: it decides what to remember (custom instructions, categories, multimodal input), how to keep it fresh (decay, expiration, eviction), how to find it (multi-signal fusion, reranking, criteria, temporal), and how to govern it (tenancy, compliance, audit), exposed identically across SDKs, a REST API, a CLI, MCP, and OpenMemory.