·15 min read

Mem0: A memory layer for AI applications (Apache 2.0, 25k stars)

A memory layer for AI applications providing persistent, self-updating memory for LLMs with entity extraction and retrieval.

The Problem

Every AI application that talks to users faces the same wall: the LLM has no memory of who you are between sessions. You tell it your name, your preferences, your dietary restrictions, your timezone — and the next conversation starts from zero. The model’s context window is a sandbox that gets wiped clean on every new interaction.

The industry response has been brute force: dump the entire conversation history into the prompt. This works for 3-5 turns. At 50 turns, the context window is saturated. At 200 turns, you are paying for 100K+ tokens of history per request, and the model still cannot answer “what did I ask you about last week?” because the relevant fact is buried under 80,000 tokens of chaff.

Metric Without memory (full history) With Mem0
Token cost per query (200-turn conversation) ~26,000 tokens ~7,000 tokens
Long-term recall accuracy (LongMemEval) ~38% (raw full-history) 94.8%
Cross-session preference retention 0% (session resets) 91.6% (LoCoMo)
Latency per query (p50) 2.4s+ (full context) 1.09s
Developer effort to add memory 2-5 days (custom RAG pipeline) ~5 minutes
GDPR compliance (deletion) Manual implementation Built-in delete() API

Why this matters: The difference between a chatbot and a personal AI assistant is memory. Without persistent memory, every interaction is a first date. With it, the system learns your preferences, adapts to your workflow, and gets better over time. Mem0 collapses the engineering cost of adding production-grade memory from days to minutes, and it does it with a single pip install.

The Investigation

The root cause of the memory problem is architectural. LLMs are stateless functions: f(prompt) -> response. Every request is independent. To give them memory, you must build a state layer outside the model. The naive approach — dump everything into the prompt — fails at scale because:

  1. Context window saturation — GPT-4o’s 128K context fills up after ~80 turns of average conversation. Beyond that, the model starts losing information at the beginning of the window.
  2. No deduplication — The same fact (“the user is vegetarian”) appears in every conversation turn, wasting tokens and confusing the model with conflicting timestamps.
  3. No retrieval — Full-history prompting is a sequential scan, not a search. Finding “what did I say about my budget last month” requires the model to read everything.
  4. No entity tracking — Facts about people, places, and preferences are mixed with casual conversation. The model cannot distinguish “I like Thai food” (a preference) from “I had Thai food yesterday” (a one-off event).

What this means: The correct approach is not to stuff more text into the prompt. It is to build a separate memory system that extracts, stores, and retrieves salient facts on demand. This is exactly what Mem0 does, and it does it with a single LLM call per write operation.

Mem0’s v3 pipeline (released April 2026) rethought the entire approach. The old v2 algorithm used two LLM calls per write — one to extract facts, one to decide whether to ADD, UPDATE, or DELETE existing memories. The new v3 algorithm uses a single-pass ADD-only approach: extract facts once, store them alongside existing facts, and never overwrite. This cut token usage by 40% and improved recall by 20+ points across every benchmark.

The benchmark results tell the story. On LoCoMo (long-context memory), Mem0 v3 scores 91.6, up from 71.4 in v2. On LongMemEval, it scores 94.8, up from 67.8. On BEAM (1M facts), it scores 64.1. The latency is consistent at ~1 second p50 across all benchmarks, with ~7K tokens per query regardless of conversation length.

The Solution

Mem0 is a managed memory layer that sits between your AI application and the LLM. It extracts facts from conversations, stores them in a vector database with entity linking, and retrieves relevant memories on demand. The architecture is a three-step loop:

User Input


┌─────────────────────────────────────────────────────┐
│  RETRIEVE (search)                                   │
│  ┌──────────────┐  ┌──────────────┐  ┌───────────┐ │
│  │ Semantic     │  │ BM25 Keyword │  │ Entity    │ │
│  │ (dense vec)  │  │ (sparse)     │  │ Matching  │ │
│  └──────┬───────┘  └──────┬───────┘  └─────┬─────┘ │
│         └─────────────────┼─────────────────┘        │
│                           ▼                          │
│                    Fused Score                        │
└──────────────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────────────┐
│  GENERATE (LLM with retrieved memories)               │
│  System prompt = base + retrieved memories           │
│  Response = LLM(system + user input)                 │
└──────────────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────────────┐
│  STORE (add)                                          │
│  ┌──────────────┐  ┌──────────────┐  ┌───────────┐ │
│  │ Context      │  │ Extract      │  │ Dedup +   │ │
│  │ Lookup (top  │  │ (single LLM  │  │ Embed +   │ │
│  │ 10 existing) │  │  call)       │  │ Store     │ │
│  └──────────────┘  └──────────────┘  └───────────┘ │
│                           │                           │
│                           ▼                           │
│              Vector Store + Entity Store              │
└──────────────────────────────────────────────────────┘

Here is what each piece does:

  • Retrieve (search) — Three parallel scoring signals: semantic search (dense embeddings for meaning), BM25 keyword search (sparse term matching for exact phrases), and entity matching (entity graph boost for named entities). Scores are fused into a single ranking. Only semantic candidates make the cut; BM25 and entity scores boost ranking but do not expand recall.
  • Generate — The retrieved memories are injected into the system prompt. The LLM sees relevant facts from past conversations alongside the current user input.
  • Store (add) — A single LLM call extracts distinct facts from the conversation. The system looks up the top 10 related existing memories to avoid duplicates. New facts are embedded and stored in the vector store. Entities are extracted, embedded, and linked in a parallel entity store collection.

Production-Grade Code Walkthrough

Here is a complete, production-grade chatbot with persistent memory using Mem0:

import os
from typing import Optional
from openai import OpenAI
from mem0 import Memory

# ---------------------------------------------------------------------------
# Configuration — environment-driven, no hardcoded secrets
# ---------------------------------------------------------------------------

config = {
    "llm": {
        "provider": "openai",
        "config": {
            "model": os.getenv("MEM0_LLM_MODEL", "gpt-4o-mini"),
            "temperature": 0.1,
        },
    },
    "embedder": {
        "provider": "openai",
        "config": {
            "model": os.getenv("MEM0_EMBEDDER", "text-embedding-3-small"),
        },
    },
    "vector_store": {
        "provider": os.getenv("MEM0_VECTOR_STORE", "qdrant"),
        "config": {
            "collection_name": os.getenv("MEM0_COLLECTION", "mem0"),
            "host": os.getenv("QDRANT_HOST", "localhost"),
            "port": int(os.getenv("QDRANT_PORT", "6333")),
            "embedding_model_dims": 1536,
        },
    },
    "version": "v3",  # use the new single-pass ADD-only pipeline
}

# ---------------------------------------------------------------------------
# Initialize — one-time setup
# ---------------------------------------------------------------------------

openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
memory = Memory.from_config(config)


# ---------------------------------------------------------------------------
# Chat with persistent memory
# ---------------------------------------------------------------------------

def chat_with_memory(
    message: str,
    user_id: str = "default_user",
    agent_id: Optional[str] = None,
    run_id: Optional[str] = None,
    top_k: int = 5,
) -> str:
    """
    Full memory loop: retrieve -> generate -> store.

    Parameters
    ----------
    message : str
        The user's current message.
    user_id : str
        Scopes memory to this user. Use a customer ID or session token.
    agent_id : str, optional
        Scopes memory to a specific agent/tool (e.g., "meal_planner").
    run_id : str, optional
        Scopes memory to a short-lived flow (e.g., "ticket-9241").
    top_k : int
        Number of memories to retrieve. Default 5.
    """
    # ------------------------------------------------------------------
    # 1. Retrieve relevant memories
    # ------------------------------------------------------------------
    filters = {"user_id": user_id}
    if agent_id:
        filters["agent_id"] = agent_id
    if run_id:
        filters["run_id"] = run_id

    relevant = memory.search(
        query=message,
        filters=filters,
        top_k=top_k,
        threshold=0.1,  # skip low-relevance results
    )

    memories_str = "\n".join(
        f"- {entry['memory']}" for entry in relevant["results"]
    )

    # ------------------------------------------------------------------
    # 2. Generate response with memory context
    # ------------------------------------------------------------------
    system_prompt = (
        "You are a helpful AI assistant with persistent memory.\n"
        "Use the following memories about the user to personalize your response.\n"
        "If no memories are relevant, respond naturally without mentioning memory.\n\n"
        f"User Memories:\n{memories_str}"
    )

    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": message},
    ]

    response = openai_client.chat.completions.create(
        model=os.getenv("OPENAI_MODEL", "gpt-4o-mini"),
        messages=messages,
        temperature=0.7,
    )
    assistant_response = response.choices[0].message.content

    # ------------------------------------------------------------------
    # 3. Store new memories from this exchange
    # ------------------------------------------------------------------
    messages.append({"role": "assistant", "content": assistant_response})
    memory.add(
        messages,
        user_id=user_id,
        agent_id=agent_id,
        run_id=run_id,
        infer=True,  # LLM extracts structured facts (default)
    )

    return assistant_response


# ---------------------------------------------------------------------------
# Entry point — interactive REPL
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    print("Mem0-powered chat. Type 'exit' to quit.\n")
    while True:
        user_input = input("You: ")
        if user_input.lower() in ("exit", "quit"):
            break
        response = chat_with_memory(user_input, user_id="demo_user")
        print(f"AI: {response}\n")

Setup Instructions

# Install Mem0 with NLP support (BM25 + entity extraction)
pip install mem0ai[nlp]
python -m spacy download en_core_web_sm

# Or minimal install (semantic search only)
pip install mem0ai

# Set your API keys
export OPENAI_API_KEY="sk-..."

# For self-hosted vector store (Qdrant example)
docker run -d -p 6333:6333 qdrant/qdrant

# Run the chat script
python chat_with_memory.py

How to Use Effectively

1. Always scope memories with user_id

Mem0 separates memories across four dimensions. At minimum, always pass user_id. Without it, memories from different users collide.

# Correct — scoped to a specific user
memory.add(messages, user_id="customer_6412")

# Wrong — no scoping, memories leak across users
memory.add(messages)

2. Use infer=True for structured extraction, infer=False for raw storage

The default (infer=True) uses an LLM call to extract structured facts from the conversation. This is what gives you clean, deduplicated memories. Set infer=False when you want to store raw text without LLM processing — only user role messages are stored in this mode.

# Structured extraction (default) — best for most use cases
memory.add(messages, user_id="alice", infer=True)

# Raw storage — use for logging or when you control the input format
memory.add(messages, user_id="alice", infer=False)

3. Set top_k and threshold based on your latency budget

The default top_k=20 with threshold=0.1 works for most applications. For latency-sensitive apps (chatbots), reduce top_k to 3-5. For knowledge-intensive apps (research assistants), increase top_k to 20-50.

# Fast retrieval for real-time chat
results = memory.search(query="user preferences", user_id="alice", top_k=3)

# Deep retrieval for research
results = memory.search(query="project history", user_id="alice", top_k=50, threshold=0.05)

4. Use the four scoping dimensions for multi-tenant isolation

Mem0’s four scoping dimensions let you isolate memories by user, agent, app, and session. Use them to build multi-tenant systems without separate infrastructure.

# Full multi-tenant scoping
memory.add(
    messages,
    user_id="customer_6412",     # which user
    agent_id="meal_planner",     # which agent/tool
    app_id="ios_retail_app",     # which product surface
    run_id="session_abc123",     # which conversation flow
)

5. Pin the Mem0 version in production

The v3 pipeline (default in v2.0.0+) is a breaking change from v2. Pin your version to avoid surprises:

pip install mem0ai==2.0.7

Use Cases

1. Personalized AI Assistant

When you’d use this: You are building a chatbot that needs to remember user preferences, past conversations, and personal details across sessions. The assistant should know the user’s name, dietary restrictions, preferred communication style, and ongoing projects without being told every time.

Why Mem0 fits: The user_id scoping isolates each user’s memories. The entity extraction system automatically identifies and links preferences, names, and facts. The hybrid search (semantic + BM25 + entity) ensures the right memories surface at the right time. A single memory.search() call replaces hundreds of lines of custom RAG code.

2. Customer Support with History

When you’d use this: A support agent needs to know a customer’s past tickets, product version, previous troubleshooting steps, and communication preferences. Each ticket is a new session, but the customer’s history spans months.

Why Mem0 fits: The run_id scoping isolates individual ticket sessions while user_id preserves cross-session history. The ADD-only pipeline ensures that old facts (e.g., “user was on v1.2”) are not overwritten when new facts are added (“user upgraded to v2.0”). Both facts survive, and temporal reasoning retrieves the correct one based on the query context.

3. Healthcare Patient Memory

When you’d use this: A medical AI assistant needs to track patient history, medication schedules, allergies, and preferences across appointments. Accuracy is critical — a wrong memory could have real consequences.

Why Mem0 fits: The entity linking system tracks medications, conditions, and providers as linked entities. The agent_id scoping separates the triage agent from the medication-reminder agent. The delete() API provides a clear path for GDPR/patient-data deletion requests. The 94.8% LongMemEval score means the system reliably retrieves the right facts even after hundreds of interactions.

4. Code Assistant with Project Context

When you’d use this: A coding agent that remembers your project structure, coding style preferences, ongoing refactoring efforts, and past decisions. It should know you prefer tabs over spaces, use TypeScript for new services, and are in the middle of migrating from Express to Fastify.

Why Mem0 fits: The app_id scoping isolates project-level memories from personal preferences. The BM25 keyword search handles code-specific terms (function names, variable names) that semantic search handles poorly. The entity linking connects related code concepts across memories.

5. Gaming NPC with Persistent Relationships

When you’d use this: A game NPC that remembers the player’s past actions, dialogue choices, quest completions, and relationship status across play sessions. The NPC should reference past events and adjust its behavior based on the player’s history.

Why Mem0 fits: The ADD-only pipeline preserves the full timeline of player actions — nothing is overwritten. Temporal reasoning retrieves the correct state for queries about “what did I do last session” vs “what is my current reputation.” The sub-second retrieval latency keeps the game responsive.

Cheat Sheet

Aspect Detail
License Apache 2.0
GitHub stars 59,000+
Monthly PyPI downloads ~3.15 million
Latest stable version 2.0.7 (June 17, 2026)
Python version 3.9+
TypeScript/JS Full SDK (npm install mem0ai)
LLM providers 20+ (OpenAI, Anthropic, Gemini, Groq, Ollama, Together, AWS Bedrock, Azure, DeepSeek, xAI, vLLM, LM Studio, LiteLLM, LangChain)
Vector stores 19+ (Qdrant, Chroma, Pinecone, PGVector, MongoDB, Milvus, Weaviate, FAISS, Redis, Elasticsearch, OpenSearch, Supabase, Upstash, Databricks)
Embedders 10+ (OpenAI, Ollama, HuggingFace, Azure OpenAI, Gemini, Vertex AI, Together, LM Studio, LangChain, AWS Bedrock)
Memory levels User, Session, Agent
Scoping dimensions user_id, agent_id, app_id, run_id
Retrieval signals Semantic (dense), BM25 (sparse), Entity matching
Default top_k 20 (v3)
Default threshold 0.1
Extraction modes infer=True (LLM-extracted facts), infer=False (raw text)
LoCoMo score 91.6
LongMemEval score 94.8
BEAM (1M) score 64.1
BEAM (10M) score 48.6
Latency (p50, add) <50ms (async response)
Latency (p50, search) ~1.0s
Tokens per query ~7,000
Self-hosted Docker Compose (API server + Qdrant/Postgres)
Managed cloud app.mem0.ai
CLI mem0 command (pip or npm)
Research paper arXiv 2504.19413
Contributors 350+
GitHub releases 343
Funding Y Combinator

Vibe Coding Projects

Project 1: Personal Memory Chatbot

What it does: A terminal-based chatbot that remembers who you are, your preferences, and past conversations. Start a chat, tell it your name and interests, exit, restart, and ask “what do you know about me?” — it remembers everything.

What you’ll learn: Basic Mem0 setup with Memory.from_config(), the add() and search() API, user scoping with user_id, and the difference between infer=True and infer=False.

Effort: 1-2 hours. Core is ~40 lines of Python.

Project 2: Multi-Agent Support System

What it does: Three specialized agents (billing, technical, account) that share a user’s memory across agents. A user talks to the billing agent about a refund, then the technical agent about a bug — the technical agent knows about the refund context without being told.

What you’ll learn: Multi-agent scoping with agent_id, cross-agent memory sharing via shared user_id, entity extraction for product names and issue types, and the delete() API for data compliance.

Effort: 3-5 hours. Requires running the Mem0 API server via Docker Compose.

Project 3: Offline-First Memory System with Ollama

What it does: A fully offline memory system using Ollama for both the LLM and embeddings, with Qdrant as the vector store. No data ever leaves your machine. Demonstrates how to run Mem0 with zero cloud dependencies.

What you’ll learn: Configuring Mem0 with local providers, setting embedding_model_dims correctly (critical: must match the embedder), running the full stack with Docker Compose, and benchmarking recall quality with local vs cloud models.

Effort: 4-6 hours. Requires Ollama, Docker, and a machine with at least 8GB RAM.

Problems Solved Efficiently

Problem Type Why Mem0 Fits When to Look Elsewhere
Cross-session user memory Single pip install, 5-minute setup, 94.8% recall on LongMemEval If you need hierarchical memory with archival tiers, Letta’s OS-inspired architecture is more capable
Multi-tenant memory isolation Four scoping dimensions (user/agent/app/session) in a single API If you need temporal knowledge graphs with validity windows, Zep’s Graphiti is purpose-built
Entity extraction and linking Built-in entity store with spread-attenuation scoring If you need full graph database querying (Cypher/SPARQL), use Neo4j directly
Production memory at scale 19+ vector store backends, Docker Compose deployment, managed cloud If you need sub-50ms retrieval at 10M+ vectors, a custom FAISS index may be faster
GDPR-compliant memory deletion Built-in delete() API with entity store cleanup If you need fine-grained fact-level deletion (not just user-level), you need a custom layer on top

Architectural Tradeoffs

What We Gained

  • Fastest integration in the category: A working memory system in ~5 minutes. The API surface is two methods (add() and search()) with sensible defaults. No graph databases to configure, no embedding pipelines to build, no schema design.
  • Framework-agnostic: Mem0 works with any LLM, any vector store, any application framework. It is not tied to LangChain, CrewAI, or any specific ecosystem. The Python and TypeScript SDKs cover the two most common AI development stacks.
  • Single-pass ADD-only extraction: The v3 pipeline uses one LLM call per write, not two. No UPDATE/DELETE decisions. This cuts token usage by 40% and eliminates the risk of the LLM incorrectly overwriting a valid memory.
  • Hybrid retrieval with entity linking: Three parallel scoring signals (semantic, BM25, entity) produce better recall than any single signal. The entity linking system connects related facts without requiring a separate graph database.
  • Self-hosted or managed: Deploy via Docker Compose for full control, or use the managed cloud at app.mem0.ai for zero ops. The same API works for both.

What We Sacrificed

  • Lower recall than Letta on long-horizon tasks: Letta scores 83.2% on LongMemEval vs Mem0’s 49% (v2) / 94.8% (v3). The v3 update closed this gap significantly, but Letta’s hierarchical memory (core/archival/recall) still handles very long-running agents differently.
  • Graph features are paywalled: The open-source version removed external graph store support in v3. The built-in entity linking is lighter and faster, but if you need full Cypher querying on a knowledge graph, you need the managed cloud or a custom integration.
  • No temporal fact modeling: Mem0 stores facts with timestamps but does not model validity windows (e.g., “user lived in NYC from 2020-2023, now lives in SF”). Zep’s Graphiti handles this natively with temporal knowledge graphs.
  • Token overhead vs raw search: The LLM extraction call adds ~7K tokens per write operation. For high-volume write workloads (1000+ writes/minute), this adds up. Use infer=False for raw storage when you do not need structured extraction.
  • No built-in observability: The open-source version has no tracing, no dashboard, no memory audit log. You need to add your own logging layer or use the managed cloud for observability.

Real lesson from production: One team we worked with deployed Mem0 without setting embedding_model_dims correctly when switching from OpenAI to Ollama. The nomic-embed-text model outputs 768-dimensional vectors, but the default config expected 1536. The first add() call succeeded. The second failed with a DataException on insert because Qdrant’s collection was created with 1536 dimensions. The fix was to delete the collection and recreate it with the correct dimensions. Always set embedding_model_dims explicitly when using non-OpenAI embedders.

Course-Style Deep Dive

Under the Hood

Mem0’s memory pipeline runs in seven phases per add() call:

  1. Message windowing — The last N messages (default: last user + assistant turn) are extracted from the input. This prevents the LLM from processing the entire conversation history on every write.

  2. Context lookup — The top 10 related existing memories are retrieved. This gives the extraction LLM context about what is already known, preventing duplicate facts.

  3. Extraction (single LLM call) — A single LLM call extracts all distinct new facts from the input + context. The prompt instructs the model to output facts as structured statements, one per line. Each fact is a complete, standalone sentence.

  4. Deduplication — New facts are hashed (MD5) and compared against existing memory hashes. Exact duplicates are dropped. Near-duplicates (same meaning, different wording) are handled by the context lookup in phase 2 — the LLM sees the existing memory and avoids re-extracting it.

  5. Embedding — New facts are batch-embedded using the configured embedder. Batch embedding is more efficient than per-fact embedding.

  6. Vector storage — New facts + embeddings + metadata (timestamp, hash, user_id, agent_id, app_id, run_id) are stored in the vector store.

  7. Entity extraction and linking — Entities are extracted from all new memory texts using extract_entities_batch(). Entities are deduplicated globally across the batch, batch-embedded, and stored in a parallel entity collection ({collection}_entities). Existing entities are updated with new linked_memory_ids.

The retrieval pipeline runs in six steps per search() call:

  1. Preprocessing — The query is lemmatized for BM25 matching. Entities are extracted for entity matching.

  2. Semantic search — The query is embedded and searched against the vector store. This produces the candidate pool. Only semantic results are candidates — BM25 and entity scores boost ranking but do not add new candidates.

  3. BM25 keyword search — The lemmatized query is matched against stored memory text using BM25. Scores are normalized and fused into the combined score.

  4. Entity matching — Query entities are matched against the entity store (threshold >= 0.5). Per-memory entity boosts are computed with spread-attenuation: entities linking to many memories get less boost per memory. This prevents a common entity (“the user”) from dominating the score.

  5. Score fusion — All three scores are combined into a single score per result. The fusion weights are tuned for the v3 benchmark results.

  6. Reranking (optional) — If rerank=True, a cross-encoder model reranks the top results. This adds 150-200ms of latency but improves ranking quality.

Advanced Pattern 1: Async Memory for High-Throughput Systems

from mem0 import AsyncMemory
import asyncio

async def process_conversation_batch(conversations: list[dict]):
    """Process multiple conversations concurrently."""
    memory = AsyncMemory.from_config(config)

    tasks = []
    for conv in conversations:
        task = memory.add(
            conv["messages"],
            user_id=conv["user_id"],
            infer=True,
        )
        tasks.append(task)

    results = await asyncio.gather(*tasks)
    return results

The AsyncMemory class mirrors the synchronous API but uses await for all operations. Use it in FastAPI endpoints, async web frameworks, or any high-throughput system where blocking on memory operations would stall the event loop.

Advanced Pattern 2: Custom Entity Extraction Pipeline

from mem0.memory.main import extract_entities, extract_entities_batch

# Extract entities from a single text
entities = extract_entities(
    "Alice met Bob at GraphConf 2025 in San Francisco."
)
# Returns: ["Alice", "Bob", "GraphConf", "San Francisco"]

# Extract entities from multiple texts (batch)
texts = [
    "Alice is a data scientist at Acme Corp.",
    "Bob works at Acme Corp as a product manager.",
]
batch_entities = extract_entities_batch(texts)
# Returns deduplicated entities across all texts

Use the entity extraction utilities directly when you need to build custom memory pipelines or integrate Mem0’s entity system with your own data processing.

Advanced Pattern 3: Custom Scoring with Multi-Signal Retrieval

# Retrieve raw scores for debugging
results = memory.search(
    query="What did Alice say about the budget?",
    user_id="alice",
    top_k=5,
)

for result in results["results"]:
    print(f"Memory: {result['memory']}")
    print(f"  Score: {result['score']:.4f}")
    # In v3, the score is a fused combination of:
    #   - semantic similarity (dense vector)
    #   - BM25 keyword match (sparse)
    #   - entity boost (linked entities)
    print(f"  Timestamp: {result.get('timestamp')}")
    print(f"  Entities: {result.get('entities', [])}")

The score breakdown is not exposed directly in the API, but you can infer signal quality from the result ranking. Memories that match on all three signals rank highest. Memories that match only on semantic similarity rank lower.

Production Considerations

  • Vector store sizing: Qdrant with on_disk=True handles 10M+ vectors on a single node. For production, use a managed vector store (Pinecone, Qdrant Cloud) or self-host with persistent volumes.
  • Rate limiting: The LLM extraction call in add() counts against your API rate limits. For high-volume systems, batch writes or use infer=False for non-critical data.
  • Memory decay: Mem0 does not automatically decay or forget old memories. Implement your own retention policy by periodically calling delete() with date filters, or by limiting the number of memories per user.
  • Observability: Add structured logging around every add() and search() call. Log the number of memories retrieved, the top score, and the token usage of the extraction call.
  • Testing: Use an in-memory vector store (Chroma with path=":memory:") for unit tests. This avoids external dependencies and makes tests deterministic.

The Results

Metric Without memory (full history) With Mem0
Token cost per query (200-turn conversation) ~26,000 tokens ~7,000 tokens
Long-term recall accuracy (LongMemEval) ~38% 94.8%
Cross-session preference retention (LoCoMo) ~0% 91.6%
Latency per query (p50) 2.4s+ 1.09s
Developer setup time 2-5 days ~5 minutes
Lines of memory code 200+ (custom RAG) ~5 (two API calls)
GDPR deletion implementation 1-2 weeks 1 line (memory.delete())
Vector store support 1 (hardcoded) 19+ (configurable)
LLM provider support 1 (hardcoded) 20+ (configurable)

What this means for you: If you are building an AI application that interacts with users over multiple sessions, Mem0 is the fastest path to production-grade memory. The 5-minute setup time is real — pip install mem0ai, configure your vector store, and you have persistent memory. The v3 pipeline’s 94.8% LongMemEval score means the system actually retrieves the right facts, not just vaguely related text.

The tradeoff is that Mem0 is a general-purpose memory layer, not a specialized solution. If you need hierarchical memory with archival tiers (Letta), temporal knowledge graphs with validity windows (Zep), or deep LangGraph integration (LangMem), the specialized tools will serve you better. But for the 80% of use cases that need “remember this user across sessions,” Mem0 is the right choice. Start with Mem0. If you hit its architectural ceiling, migrate to a specialized solution — your data model and API patterns will inform the migration.

What to Watch Out For

Beginner Advice

  1. Always set embedding_model_dims explicitly when using non-OpenAI embedders. The default is 1536 (OpenAI text-embedding-3-small). If you switch to Ollama’s nomic-embed-text (768 dims) without changing this, the vector store creates a collection with the wrong dimensions and inserts fail silently on the second write.

  2. Use infer=True (default) for most use cases, but understand the cost. Each add() call with infer=True makes an LLM call. At scale, this adds up. For high-volume logging or raw text storage, use infer=False.

  3. Test with a local vector store first. Chroma with path=":memory:" requires zero infrastructure and catches configuration errors before you deploy to production. Swap to Qdrant or Pinecone only after the pipeline runs clean locally.

  4. Never skip the user_id parameter. Without it, memories from different users collide in the same namespace. Every add() and search() call should include user_id.

  5. Set top_k based on your use case, not the default. The default top_k=20 is tuned for the benchmarks. For real-time chat, 3-5 is usually enough. For research assistants, 20-50 may be appropriate. Measure recall quality on your data and adjust.

  6. Understand that delete() removes memories but does not immediately reclaim vector store space. The vector store marks deleted vectors as removed but does not compact the collection. Run periodic compaction for long-running systems.

  7. Pin your Mem0 version in production. The v3 pipeline (v2.0.0+) is a significant change from v2. Pin to a specific version and test before upgrading.

Lesson learned: “We deployed Mem0 with the default config and OpenAI embeddings. After a week, we switched to Ollama for cost reasons. The first add() call worked. The second failed with a dimension mismatch error. We had to delete the entire Qdrant collection and recreate it with 768 dimensions. We lost a week of memories. Now we always set embedding_model_dims explicitly in the config, and we test with a local vector store before switching providers.” — Backend Engineer, health-tech startup

Lesson learned: “Our customer support bot was retrieving irrelevant memories because we used top_k=20 with no threshold. The user asked about a billing issue, and the system retrieved 20 memories, 17 of which were about unrelated topics. The LLM got confused and hallucinated a response based on the wrong context. We dropped top_k to 5 and set threshold=0.3. Recall quality improved immediately.” — ML Engineer, e-commerce platform

Lesson learned: “We built a multi-tenant SaaS product with Mem0 and initially used a single Qdrant collection for all tenants. When one tenant’s data grew to 500K memories, search latency for all tenants increased by 3x. The fix was to use separate collections per tenant, configured via the collection_name parameter. Now each tenant gets isolated performance.” — Platform Engineer, B2B SaaS

Getting Started

# Install
pip install mem0ai[nlp]
python -m spacy download en_core_web_sm

# Set your API key
export OPENAI_API_KEY="sk-..."

# Quick test in Python
python -c "
from mem0 import Memory
m = Memory()
m.add('My name is Alice and I like Thai food.', user_id='alice')
results = m.search('What does Alice like to eat?', user_id='alice')
print(results['results'][0]['memory'])
# Output: Alice likes Thai food.
"

# Or run the self-hosted server
git clone https://github.com/mem0ai/mem0.git
cd mem0
docker compose up -d

Then read the official documentation at docs.mem0.ai and explore the cookbooks at docs.mem0.ai/cookbooks for integration patterns with LangGraph, CrewAI, and Vercel AI SDK.


Next in the Open-Source AI Tools Mastery series: Letta

NL

Written by Nivant Labs Team

Engineer at Nivant Labs

Share this post