◈Home ▸Blog ◇Contact
Home/Blog/How to Give an AI Agent Long-Term Memory With…
How toAIMem0AI AgentsMemory

How to Give an AI Agent Long-Term Memory With Mem0

8 min readBy Miloš Mitrović

An agent that forgets everything between calls forces the user to repeat context on every turn, and it pays to resend that history as tokens each time. Mem0 fixes both problems by storing durable facts about a user and returning only the relevant ones on demand. The short version: pip install mem0ai, call m.add() to save a conversation as extracted facts, call m.search() to pull back the few that matter, and paste those into your system prompt before you hit the model.

Key Takeaways

  • The short answer: install mem0ai, write memories with m.add(messages, user_id=...), read them with m.search(query, user_id=...), and inject the results into your prompt.
  • Mem0 does not store raw transcripts. Its LLM extracts discrete facts ("prefers window seats"), deduplicates them, and updates ones that changed.
  • Every memory is scoped by a user_id, and optionally an agent_id or run_id, so one store can serve many users without leaking context between them.
  • The open-source package runs fully local by default: a local Qdrant vector store on disk plus a SQLite history file. Only the extraction and embedding calls go to your LLM provider.
  • Recalling three or four relevant facts instead of resending a full transcript cuts prompt tokens on long-running conversations.

What Do You Need Before You Start?

Keep the prerequisites short so you can get to running code fast.

  • Python 3.9 or newer and a virtual environment.
  • An OpenAI API key exported as OPENAI_API_KEY. Mem0 uses it to extract facts and embed them; you can swap the provider later.
  • Optional: a running Qdrant instance if you want a shared vector store instead of the default on-disk one. You do not need it for the first run.

How Do You Add Memory to an Agent With Mem0?

Follow these steps in order. By the end you will store a memory, recall it in a later session, and feed it to a model. This section is self-contained; the code runs as written.

  1. Install the package.
    pip install mem0ai
  2. Set your API key. Mem0 reads it from the environment.
    export OPENAI_API_KEY="sk-..."
  3. Initialize the memory store. With no arguments, Memory() writes to a local Qdrant store on disk and a SQLite history file, so nothing external is required yet.
    from mem0 import Memory
    
    m = Memory()
  4. Write a memory from a conversation. Pass the messages and a user_id. Mem0's LLM pulls out the durable facts and stores them; it does not keep the raw text.
    messages = [
        {"role": "user", "content": "Hi, I'm Alex. I always fly window and I'm vegetarian."},
        {"role": "assistant", "content": "Noted, Alex. Window seat and vegetarian meals."},
    ]
    m.add(messages, user_id="alex")
  5. Recall relevant memories later. In a brand-new session, search by user_id. You get back only the facts that match the query, ranked by relevance.
    results = m.search("What are this traveler's preferences?", user_id="alex")
    for r in results["results"]:
        print(r["memory"], "-", round(r["score"], 3))
  6. Inject the memories into your prompt. This is the step that makes the agent behave as if it remembers. Fetch, format, and prepend before you call the model.
    from openai import OpenAI
    
    client = OpenAI()
    
    def answer(user_id, question):
        hits = m.search(question, user_id=user_id, limit=4)["results"]
        facts = "\n".join(f"- {h['memory']}" for h in hits)
        system = f"You are a travel assistant. Known facts about the user:\n{facts}"
        resp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": system},
                {"role": "user", "content": question},
            ],
        )
        reply = resp.choices[0].message.content
        # persist this exchange so the agent keeps learning
        m.add(
            [{"role": "user", "content": question},
             {"role": "assistant", "content": reply}],
            user_id=user_id,
        )
        return reply
    
    print(answer("alex", "Book me a flight to Lisbon and suggest a meal."))
The pattern is always the same three moves: search before the model call, prepend the results, then add the new exchange after. Everything else is configuration.

Why Extracted Facts Beat Stuffing the Whole Transcript

The naive approach to memory is to keep the entire chat history and resend it every turn. That works until the history outgrows the context window or the token bill outgrows the budget. Vector search over raw turns helps, but it retrieves whole messages full of filler, and it never reconciles a fact that changed.

Mem0 sits between those options. It runs an extraction pass that turns messy dialogue into short, deduplicated statements, and it updates a fact in place when the user contradicts an earlier one.

ApproachWhat gets storedToken cost at turn 50Handles changed facts
Full transcript in contextEvery message, verbatimGrows every turn, unboundedNo, old and new both linger
Vector RAG over raw turnsEmbedded message chunksModerate, retrieves full messagesNo, returns both versions
Mem0 extracted factsDeduplicated fact statementsLow, a handful of short linesYes, updates the stored fact

That reconciliation step is the part people underestimate. If a user says "actually I moved to Berlin," you want the agent to overwrite "lives in Munich," not carry both and pick one at random.

How Do You Scope Memory Per User and Per Agent?

Every call accepts identifiers that partition the store. Use user_id for the person, agent_id when several distinct agents share one backend, and run_id for a single session's working memory.

m.add(messages, user_id="alex", agent_id="travel-bot")
m.search("dietary needs", user_id="alex", agent_id="travel-bot")
m.get_all(user_id="alex")            # everything you hold for this user
m.delete_all(user_id="alex")         # honor a delete request

Scoping is not optional in a real deployment. Without a user_id, one person's preferences bleed into another's answers.

How Do You Point Mem0 at a Shared Vector Store?

The on-disk default is fine for a prototype. For anything multi-process or production-bound, pass a config dict to Memory.from_config() so every worker reads and writes the same store. Out of the box Mem0 embeds with text-embedding-3-small at 1536 dimensions, so keep that in mind when you size a collection.

from mem0 import Memory

config = {
    "llm": {
        "provider": "openai",
        "config": {"model": "gpt-4o-mini", "temperature": 0.1},
    },
    "vector_store": {
        "provider": "qdrant",
        "config": {"host": "localhost", "port": 6333},
    },
}

m = Memory.from_config(config)

The llm block controls the model that does fact extraction, which is a separate choice from the model that writes your user-facing replies. A cheaper model for extraction keeps per-turn cost down without touching answer quality.

Troubleshooting Common Mem0 Errors

  • Search returns nothing right after add(). Extraction is an LLM call and takes a moment; more often the query and stored facts share no meaning. Broaden the query, or call m.get_all(user_id=...) to confirm the fact actually landed.
  • AuthenticationError on the first call. The default setup needs a valid OPENAI_API_KEY in the same shell that runs Python. Export it before launching, or load it with python-dotenv.
  • Facts pile up as near-duplicates. Feed both sides of the exchange, not just the user line. The assistant turn gives the extractor the context it needs to merge related statements instead of storing each phrasing separately.
  • Qdrant connection refused. The host and port in your config must match a running instance. With the default (no config), Mem0 uses a local on-disk store and needs no server at all.
  • Old preferences still surface. Extraction updates facts on contradiction, but only if the new statement is explicit. Prefer "I no longer eat meat" over an oblique aside, or delete the stale memory by id.

What Should You Do Next?

Once the loop works, the useful extensions are cheap to add.

Sources

M
Miloš Mitrović
Email Marketing for Ecommerce

Have a question or a project?

Whether it is about this post or a system you want built, I'm happy to talk.

Get in touch

404

Post not found. It may have been moved or the link is incorrect.

← Back to the blog
Summarize with AI
ChatGPT, Perplexity, and Grok open with the prompt ready to run. Claude, Gemini, and Copilot open a chat with the prompt copied; press Ctrl+V (Cmd+V on Mac) to paste. The full text is included, so it works even without web access.