Skip to content

How to Add Long-Term Memory to AI Agents with Mem0: A Practical Guide

Purpose

I was building a small AI assistant for my own projects, and I kept hitting the same wall.

I told the assistant that I prefer Python examples and PostgreSQL. Then I opened a new session a few days later and asked how I should implement a backend service.

It suggested JavaScript and MySQL.

The model was not “stupid.” The problem was simpler:

Most LLM API calls are stateless.

A model only knows the context I send with the current request. If I want it to remember something from yesterday, I need to send that information again or store it somewhere that can be retrieved later.

That is the problem Mem0 is designed to solve.

Mem0 is an open-source memory layer for AI applications. Instead of sending an entire conversation history on every request, it can extract useful memories from past interactions, store them, and retrieve the relevant ones when the user asks something later.

In this post, I will use the current Mem0 v3-style Python API to show:

  • how to install and configure Mem0;
  • how to add a long-term memory;
  • how to retrieve it in a later interaction;
  • how user_id, agent_id, and run_id scope memories;
  • how the newer Mem0 memory algorithm differs from older tutorials;
  • how Mem0 differs from chat history, RAG, and a plain vector database;
  • when Mem0 is useful, and when it is unnecessary.

The hard part of long-term AI memory is not storing facts. It is deciding what deserves to be remembered, what has become stale, and what should be retrieved for the current request.

What Problem Does Mem0 Actually Solve?

There are several straightforward ways to make an AI application “remember.”

Option 1: Send the Full Chat History Every Time

The simplest approach is to keep the entire conversation and send it back with every LLM request.

That works for short conversations, but it becomes inefficient as the history grows:

  • token usage increases on every request;
  • old messages consume context window space;
  • irrelevant details add noise;
  • latency and cost increase;
  • eventually, old messages need to be truncated or summarized.

A short-lived chatbot can often use this approach. A long-running assistant usually needs something more selective.

Option 2: Build Your Own Memory Pipeline

You can store facts in a database and build the rest yourself:

  1. decide which conversation details are worth remembering;
  2. convert memories into embeddings;
  3. store them in a vector database;
  4. prevent duplicates;
  5. retrieve the right memories for each new question;
  6. decide what to do when old information becomes stale.

This gives you full control, but it is a lot of infrastructure for a feature that many AI applications need.

Option 3: Use a Memory Layer

This is where Mem0 fits.

Where Mem0 fits
User
AI Application
├── Recent chat history
├── Mem0 memories
└── External knowledge / RAG
LLM Context
LLM

Mem0 does not make the LLM itself stateful. It gives your application a persistent memory system that can be queried between sessions.

That distinction is important:

Mem0 retrieves memory; your application still decides how to use that memory in the LLM prompt.

Mem0 in Plain English

The easiest way I think about it is:

  • Chat history is a transcript.
  • Mem0 is a curated notebook.

Suppose a conversation contains this:

User: I'm rebuilding my backend.
Assistant: Which database are you using?
User: PostgreSQL. We stopped using MongoDB.

A raw chat history keeps all three messages.

A memory layer might extract a compact fact such as:

User's backend uses PostgreSQL.

Later, when the user asks:

How should I structure the database layer?

the application can retrieve that memory and put it back into the LLM context.

The goal is not to replay every old message. The goal is to retrieve only the information that is useful now.

Install Mem0

Install the Mem0 Python package:

Install Mem0
pip install mem0ai

For this self-hosted example, I will use Qdrant as the vector store.

Install the Qdrant Python client:

Install Qdrant client
pip install qdrant-client

The Python client is not the Qdrant server itself. Start a local Qdrant instance with Docker:

Start Qdrant
docker run -p 6333:6333 qdrant/qdrant

Set your OpenAI API key:

Set OpenAI API key
export OPENAI_API_KEY="sk-..."

Mem0 supports multiple LLM, embedding, and vector-store providers. I am using OpenAI + Qdrant here because it keeps the example easy to understand.

Your First Mem0 Example

Create a file named mem0_example.py:

mem0_example.py
from mem0 import Memory
config = {
"vector_store": {
"provider": "qdrant",
"config": {
"collection_name": "my_memories",
"host": "localhost",
"port": 6333,
},
},
"llm": {
"provider": "openai",
"config": {
"model": "gpt-5-mini",
"temperature": 0.1,
},
},
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-3-small",
},
},
}
memory = Memory.from_config(config)
# First interaction: store a durable preference.
memory.add(
"I prefer Python examples and PostgreSQL.",
user_id="alice",
)
# Later interaction: retrieve relevant memories.
results = memory.search(
"How should I implement my backend?",
filters={"user_id": "alice"},
)
for item in results["results"]:
print(item["memory"], item.get("score"))

Run it:

Terminal window
python mem0_example.py

A simplified result can look like this:

Simplified search result
{
"results": [
{
"id": "mem-uuid",
"memory": "User prefers Python examples and PostgreSQL.",
"score": 0.82,
"metadata": {}
}
]
}

The exact wording and score can vary because memory extraction and retrieval involve models and similarity ranking.

The important part is that the second interaction does not need the original conversation transcript. It searches persistent memory instead.

Turning Retrieved Memories into LLM Context

Retrieving memory is only half the job.

Your application still needs to put the useful memories into the final LLM prompt:

Build LLM context from Mem0 results
user_id = "alice"
user_message = "How should I implement my backend?"
results = memory.search(
user_message,
filters={"user_id": user_id},
)
memory_text = "\n".join(
f"- {item['memory']}"
for item in results["results"]
)
prompt = f"""
Relevant memories about the user:
{memory_text}
User:
{user_message}
"""
print(prompt)

The resulting prompt may look like:

Relevant memories about the user:
- User prefers Python examples and PostgreSQL.
User:
How should I implement my backend?

The LLM now has a much better chance of giving a Python + PostgreSQL answer without receiving the user’s entire conversation history.

The Important Concept: user_id

A memory system becomes dangerous if memories from different users can mix.

Mem0 lets you scope memories using entity IDs.

A basic multi-user setup might look like:

alice
├── prefers Python
└── uses PostgreSQL
bob
├── prefers TypeScript
└── uses MySQL

When adding Alice’s memory:

memory.add(
"I prefer Python.",
user_id="alice",
)

When searching:

memory.search(
"What language should I use?",
filters={"user_id": "alice"},
)

The search must be scoped to the correct user.

With the current v3-style API, entity IDs for search() and get_all() belong inside filters.

This is different from older Mem0 examples on the web that may use:

memory.search("query", user_id="alice")

That old style can fail on newer versions.

User, Agent, App, and Run Memory

Mem0 supports several IDs for scoping memories:

  • user_id — memory about an end user;
  • agent_id — memory associated with a specific agent;
  • app_id — memory scoped to an application;
  • run_id — memory associated with a particular run or workflow context.

For example:

results = memory.search(
"What do we know about this task?",
filters={
"user_id": "alice",
"agent_id": "coding-agent",
"run_id": "backend-migration-2026",
},
)

A useful rule is to use the narrowest scope that matches your data model.

Do not treat every memory as globally available.

How Mem0 Works Internally

The current Mem0 memory pipeline is more interesting than the older “save a vector and search it” model.

At a high level:

Current Mem0 memory pipeline
ADD
Conversation
Retrieve related memories
Single-pass fact extraction
Exact deduplication
Memory + embeddings + entities
SEARCH
Query
├── Semantic similarity
├── BM25 keyword signal
└── Entity signal
Score fusion
Relevant memories

There are two important ideas here.

1. Memory Extraction Is ADD-Only

Older Mem0 architectures used a second LLM pass to classify memory mutations such as:

ADD
UPDATE
DELETE

The newer v3 memory algorithm changed this substantially.

The extraction pipeline is designed around a single-pass ADD-only model: it extracts new memories and accumulates them rather than asking a second LLM call to rewrite or delete existing memories during every add operation.

This reduces the complexity of memory mutation and avoids having a model continuously rewrite the memory store.

2. Search Is Hybrid

Search is not only vector similarity.

The current retrieval system can combine:

  • semantic similarity;
  • BM25 keyword matching;
  • entity signals.

This matters because embeddings are good at semantic similarity, but they are not always ideal for exact names, identifiers, or entity-heavy queries.

You can also inspect ranking details in OSS search with explain=True when debugging retrieval behavior.

For example:

results = memory.search(
"food preferences",
filters={"user_id": "alice"},
explain=True,
)
print(results["results"][0]["score_details"])

That is useful when you want to understand why a specific memory ranked highly.

What Happens When a Memory Becomes Outdated?

This is one of the hardest problems in long-term memory.

Suppose a user says:

I live in San Francisco.

Six months later:

I moved to Seattle.

An older memory system might try to immediately replace the first statement with the second.

But that approach has problems.

The old information may still matter historically:

User lived in San Francisco before moving to Seattle.

The newer ADD-only approach makes a different trade-off: memories can accumulate, and retrieval must decide which information is most relevant.

That means your application still needs to think about:

  • recency;
  • metadata;
  • explicit deletion;
  • source-of-truth data;
  • stale memory handling.

This is why long-term memory is not just a storage problem.

A system can remember perfectly and still answer incorrectly if it retrieves stale information.

For authoritative facts such as an account balance, current address for shipping, payment state, or medical record, query the real system of record instead of trusting memory alone.

Why Not Just Use a Vector Database?

A vector database is part of the memory stack, but it is not the entire memory system.

ConcernPlain vector storeMem0
Store embeddingsYesYes
Semantic retrievalYesYes
Extract useful facts from conversationsManualBuilt in
Exact deduplicationManualBuilt into memory pipeline
User / agent scopingDesign it yourselfEntity IDs
Keyword + entity retrievalBuild it yourselfHybrid retrieval support
Stale fact policyBuild it yourselfStill requires application design

If you already have a mature extraction, deduplication, ranking, and memory lifecycle system, you may not need Mem0.

If you only have a vector database and do not want to build all of that memory logic yourself, Mem0 can save a substantial amount of work.

How Mem0 Uses Entities and Graph Memory

A lot of older Mem0 content describes graph memory using examples such as:

Alice --reports_to--> Bob
Bob --lives_in--> London

That can make it sound like Mem0 is simply building a Neo4j-style typed knowledge graph and traversing explicit relationship edges.

The newer architecture is more nuanced.

Think of it as entity-aware memory linking:

Memory A
"Alice works at Acme"
├── Alice
└── Acme
Memory B
"Alice prefers Python"
└── Alice
Memory C
"Acme is migrating to PostgreSQL"
├── Acme
└── PostgreSQL

Entities give the retrieval system another signal in addition to semantic and keyword matching.

For example, a query mentioning Acme can benefit from memories linked to the same entity even when the exact wording differs.

This is particularly useful for:

  • people;
  • organizations;
  • products;
  • projects;
  • named systems;
  • recurring entities across conversations.

OSS vs Mem0 Platform

Be careful when reading older Mem0 graph tutorials.

Older versions may mention:

  • enable_graph;
  • graph_store;
  • Neo4j-specific setup;
  • separate graph visualization.

Those references may describe an earlier architecture.

Current Mem0 v3 documentation emphasizes entity extraction/linking and hybrid retrieval rather than requiring users to build explicit typed relationship graphs for normal memory search.

The managed Mem0 Platform also integrates graph/entity capabilities differently from older OSS graph-store examples.

The practical takeaway is simple:

Do not copy an old enable_graph=True tutorial without checking the documentation for your installed Mem0 version.

Mem0 v3 vs Older Mem0 Tutorials

This is worth calling out explicitly because Mem0 has changed quickly.

If you search for examples online, you may find code like:

memory.search("query", user_id="alice")

or memory lifecycle explanations based on:

ADD
UPDATE
DELETE
NOOP

or graph configuration such as:

graph_store
enable_graph
Neo4j

Those examples may be correct for an older release, but they do not represent the current v3-style architecture.

The important changes are:

Old tutorials Current v3 direction
search(..., user_id=...) → search(..., filters={...})
two-pass memory mutation → single-pass ADD-only extraction
vector-only style explanation → semantic + BM25 + entity signals
separate graph-store emphasis → entity linking integrated into retrieval

This is why I recommend checking the current Mem0 migration docs before copying snippets from older blog posts.

Mem0 vs Chat History vs RAG

These are not competing solutions. They solve different problems.

LayerMain question it answersExample
Chat historyWhat did we just talk about?Last few turns
Mem0What do I remember about this user or agent?Prefers Python
RAGWhat does an external knowledge source say?Company documentation

A production agent often combines all three:

Three context sources
System Prompt
+
Recent Chat History
+
Relevant Long-Term Memories
+
RAG Results
+
Current User Message

Chat History

Best for short-term conversational continuity.

Mem0

Best for durable information that should survive across sessions.

RAG

Best for retrieving external knowledge such as:

  • product documentation;
  • policies;
  • PDFs;
  • internal knowledge bases;
  • code repositories.

A common mistake is to use memory as a replacement for RAG.

It is not.

A user’s preference for Python is a memory.

The current API specification for your payment service belongs in your documentation or source repository.

Mem0 vs Zep vs Letta vs Cognee

Several open-source projects operate in the broader AI memory space.

At a high level:

ToolMain emphasisGood fit
Mem0Drop-in long-term memory layerAdding persistence to an existing agent
ZepTemporal / agent memoryTime-aware conversational systems
LettaStateful agent architectureBuilding long-lived agents around persistent state
CogneeGraph-oriented knowledge infrastructureKnowledge-heavy and graph-heavy workflows

I would not choose based on GitHub stars or benchmark screenshots.

The more useful questions are:

  • Do I need a standalone memory layer or a complete agent framework?
  • Do I need strong temporal reasoning?
  • Do I already have a vector database?
  • Do I need graph-heavy knowledge representation?
  • Do I need hosted infrastructure or self-hosting?
  • Can I evaluate retrieval quality with my own conversations?

For a developer who already has an agent and simply wants to add persistent user memory, Mem0 remains a straightforward option to evaluate.

Benchmarks: Read the Fine Print

Mem0 publishes benchmark results for its newer memory algorithm, including strong scores on long-context memory evaluations.

Those results are useful, but benchmark numbers should not be treated as universal application guarantees.

Always check:

  • which Mem0 product or version was tested;
  • which model was used;
  • which dataset was used;
  • which baseline was compared;
  • whether the result refers to the managed Platform, OSS SDK, or both.

A memory system that performs well on a benchmark can still fail on your application if:

  • extraction misses important facts;
  • the wrong memory is retrieved;
  • stale information ranks too highly;
  • user scoping is incorrect;
  • your prompt ignores the retrieved memory.

Use benchmark numbers as a signal, not as a substitute for testing.

The Real Problems with AI Memory

Long-term memory solves one obvious problem:

The agent forgets.

But it introduces several new ones.

1. Forgetting

A useful fact is never extracted.

Example:

User: Always give me PostgreSQL examples.

If that preference is not stored, it cannot be retrieved later.

2. False Memory

The system stores an interpretation that is not actually supported by the conversation.

That can happen because extraction itself uses a model.

3. Stale Memory

A fact used to be correct but is no longer current.

User works at Company A.

A year later, the user works somewhere else.

4. Wrong Retrieval

A memory is correct, but irrelevant to the current request.

For example:

User prefers Python.

That does not mean every future answer should involve Python.

A memory layer needs precision, not just recall.

The difficult part is not simply “remember more.”

It is:

remember the right things, retrieve them at the right time, and know when memory should not be trusted.

Privacy and Security

Persistent memory deserves more security attention than a normal prompt.

Do not casually store:

  • passwords;
  • API keys;
  • authentication tokens;
  • payment credentials;
  • recovery codes;
  • unnecessary personal data.

At minimum, think about:

  • per-user isolation;
  • deletion requests;
  • retention periods;
  • encryption at rest;
  • access controls;
  • audit requirements;
  • privacy regulations.

Memory can be more sensitive than raw chat logs because it deliberately converts scattered conversation details into compact, reusable facts.

Treat it as a real user data store.

When Should You Use Mem0?

Mem0 is a good fit when your application becomes noticeably better if it remembers information across sessions.

Examples:

  • a personal assistant that remembers preferences;
  • a coding agent that remembers project conventions;
  • a tutoring agent that remembers what the learner already knows;
  • a support assistant that remembers recurring issues;
  • a workflow agent that needs previous decisions;
  • a SaaS assistant that personalizes answers for each user.

A useful test is:

Do users frequently need to say, “I already told you this”?

If yes, long-term memory may be worth adding.

When You Probably Do Not Need Mem0

Skip it when persistence adds no meaningful value.

Examples:

  • one-shot summarization;
  • document conversion;
  • simple stateless API calls;
  • basic calculator-like tools;
  • short-lived workflows;
  • RAG systems that only need to answer from documents.

Every memory layer adds:

  • storage;
  • model calls;
  • retrieval logic;
  • privacy requirements;
  • debugging complexity.

Do not add memory just because “AI agents should have memory.”

When Mem0 Is Not Enough

Mem0 is not your source of truth.

Do not use it as the authoritative system for:

  • account balances;
  • payment status;
  • order state;
  • permissions;
  • medical facts;
  • inventory;
  • financial records.

For authoritative data:

Agent
├── Memory → preferences / context
└── Database / API → current truth

Memory should help the application understand context.

It should not replace the systems that hold facts your business must get exactly right.

Final Architecture

A practical long-running agent often looks like this:

Long-term agent architecture
┌─────────────────────┐
│ User │
└──────────┬──────────┘
┌─────────────────────┐
│ AI Application │
└──────────┬──────────┘
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
Recent messages Mem0 memories RAG / APIs / DB
(short-term) (long-term) (source of truth)
│ │ │
└────────────────────┼────────────────────┘
┌─────────────────────┐
│ LLM Context │
└──────────┬──────────┘
┌─────────────────────┐
│ LLM │
└─────────────────────┘

Each layer has a different job:

  • chat history keeps the conversation coherent;
  • Mem0 remembers durable user or agent context;
  • RAG retrieves external knowledge;
  • databases and APIs provide authoritative current facts.

That separation makes the system easier to reason about and safer to maintain.

Summary

Mem0 is useful because it gives AI applications a reusable memory layer instead of forcing developers to build extraction, storage, scoping, and retrieval from scratch.

The current Mem0 architecture is also different from many older tutorials:

  • search() scopes entity IDs through filters;
  • the newer extraction flow is single-pass and ADD-only;
  • retrieval can combine semantic, BM25, and entity signals;
  • older enable_graph and mutation-oriented examples may no longer match the current API.

The biggest lesson is not that every AI agent needs memory.

It is that long-term memory changes the engineering problem.

Without memory, the challenge is forgetting.

With memory, the challenge becomes deciding what deserves to be remembered, what has become stale, and what should be retrieved for the current request.

That is the part Mem0 helps with — and the part you still need to design carefully.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments