Mem0 is a memory layer for AI applications. It extracts facts from interactions, reconciles those facts with existing memories, and retrieves selected memories for later prompts.
For example, an assistant may receive these statements over time:
I drink dark-roast coffee without sugar.
I have switched to decaf.A transcript store preserves both statements as events. A memory system may instead retain a current fact such as “The user drinks decaffeinated dark-roast coffee without sugar.” Mem0 packages the model calls, storage, and retrieval needed for that process.
Mem0’s Place in an AI Application #
An application using Mem0 has two main flows:
interaction
-> extract candidate facts
-> compare with existing memories
-> add, update, retain, or delete
-> persist
new request
-> search memories
-> filter or rerank results
-> add relevant results to the model promptMem0 therefore differs from chat history. Chat history records what happened, while memory represents selected information that may remain useful in future interactions.
Memories can be scoped by identifiers such as:
user_idfor facts about one user;agent_idfor facts available to one agent;run_idfor facts associated with one execution or conversation; andapp_idfor application-level isolation.
The platform also supports metadata, generated categories, memory history, feedback, webhooks, and exports. Such features help operate a memory service, but they do not change the fundamental data model: a memory remains a record that can be created, retrieved, updated, and deleted.
Is Mem0 More Than Elasticsearch CRUD? #
The storage-facing API resembles CRUD plus search. Elasticsearch can already store documents, filter metadata, run BM25 full-text search, perform vector search, and combine retrieval signals. Mem0 is not a replacement for those database capabilities.
Mem0 adds a model-driven lifecycle above storage:
- Extraction converts a conversation into durable facts instead of storing every message as a memory.
- Reconciliation decides whether a candidate fact should add a memory, update an existing memory, or make no change.
- Normalization rewrites conversational input into a standalone memory.
- Scoping associates memories with users, agents, applications, and runs.
- Prompt retrieval exposes relevant memories for inclusion in subsequent model requests.
Consider a user who first says “I drink caffeinated coffee” and later says “I have switched to decaf.” Elasticsearch can find and update the original document, but application code must decide that the second statement contradicts the first and formulate the replacement. Mem0 supplies that decision pipeline.
The distinction is architectural rather than magical. A team that already implements fact extraction, contradiction handling, deduplication, retention, retrieval evaluation, and prompt assembly gains less from Mem0. A team that wants those policies behind an SDK or hosted API gains an integrated implementation.
Python Example #
Install the hosted-platform SDK:
uv add mem0aiKeep the API key outside source control. The following example reads it from a
local key.txt file, adds a preference, waits for asynchronous processing, and
searches for the resulting memory:
from pathlib import Path
from time import sleep
from typing import Any, Final
from mem0 import MemoryClient
USER_ID: Final[str] = "mem0-python-demo"
KEY_FILE: Final[Path] = Path(__file__).with_name("key.txt")
def load_api_key() -> str:
api_key = KEY_FILE.read_text(encoding="utf-8").strip()
if not api_key:
raise ValueError(f"Mem0 API key file is empty: {KEY_FILE}")
return api_key
def main() -> None:
client: MemoryClient = MemoryClient(api_key=load_api_key())
messages: list[dict[str, str]] = [
{
"role": "user",
"content": "I prefer dark-roast coffee and drink it without sugar.",
},
{
"role": "assistant",
"content": "I will remember your coffee preference.",
},
]
added: dict[str, Any] = client.add(messages, user_id=USER_ID)
print(added)
# Hosted Mem0 processes additions asynchronously.
sleep(3)
found: dict[str, Any] = client.search(
"How does the user like coffee?",
filters={"user_id": USER_ID},
top_k=5,
threshold=0.3,
rerank=True,
)
print(found)
if __name__ == "__main__":
main()Add key.txt to .gitignore:
key.txtAn environment variable or secret manager is preferable in a deployed application. The local file keeps the example small but must not be committed.
client.add may return a PENDING status because memory extraction and
reconciliation run asynchronously. Production code should poll an appropriate
operation endpoint or use an event-driven integration when available rather
than depend on a fixed sleep.
Why Unrelated Queries Can Return the Same Memory #
A retrieval API ranks candidates; it does not necessarily classify each candidate as relevant or irrelevant. If a user owns only one memory, that memory is the nearest candidate for both a related query and an unrelated query:
memory: User drinks dark-roast coffee without sugar.
query A: How does the user like coffee?
query B: Which country won the World Cup?The coffee memory ranks first for both queries because no other candidate can rank above it. A first-place result can still have a low score.
Mem0 search can combine three signals:
- semantic measures embedding similarity;
- BM25 measures lexical overlap; and
- entity measures entity-related relevance.
The returned score combines retrieval signals for ranking. It is not a
probability that the memory answers the query. Scores also depend on the
embedding model, query distribution, corpus, and retrieval configuration, so
a threshold copied from another system has no guaranteed meaning.
Use an explicit threshold and evaluate it against representative queries:
results = client.search(
query,
filters={"user_id": USER_ID},
top_k=5,
threshold=0.3,
rerank=True,
)Increasing threshold favors precision and may return no memory. Decreasing it
favors recall and admits weaker matches. rerank=True adds a relevance pass
but does not eliminate the need for evaluation.
Mem0 documentation describes a default threshold, but clients and hosted API versions can change. Passing the value explicitly makes application behavior less dependent on a server default. An application may also reject results after retrieval:
relevant = [
memory
for memory in results["results"]
if memory["score"] >= 0.3
]Choose the cutoff from labeled examples containing both questions that should recall a memory and questions that should return nothing. Measure retrieval precision and recall instead of judging a threshold from one query.
Search Response Fields #
A result can look like this:
{
"id": "8e8426d1-b944-4fe2-8ae6-af89d84066f9",
"memory": "User prefers dark-roast coffee without sugar.",
"user_id": "mem0-python-demo",
"agent_id": null,
"app_id": null,
"run_id": null,
"score": 0.0903,
"score_breakdown": {
"semantic": 0.1901,
"bm25": 0.0356,
"entity": 0.0
},
"metadata": {},
"categories": ["food", "user_preferences"],
"created_at": "2026-08-03T08:52:11+00:00",
"updated_at": "2026-08-03T08:52:48.825031+00:00",
"expiration_date": null
}| Field | Meaning |
|---|---|
id | Stable identifier for get, update, delete, history, and feedback operations |
memory | Extracted or normalized fact stored by Mem0 |
user_id | User scope attached to the memory |
agent_id | Agent scope, or null when none was supplied |
app_id | Application scope, or null when none was supplied |
run_id | Run or session scope, or null when none was supplied |
score | Combined ranking score; larger values rank ahead of smaller values within the same search configuration |
score_breakdown.semantic | Contribution from embedding-based similarity |
score_breakdown.bm25 | Contribution from lexical matching |
score_breakdown.entity | Contribution from entity matching |
metadata | Application-provided attributes |
categories | Categories assigned during memory processing |
created_at | Memory creation timestamp |
updated_at | Most recent update timestamp |
expiration_date | Scheduled expiration timestamp, or null |
A result with score=0.0903, weak semantic and BM25 values, and no entity
contribution should be treated as a weak candidate even when it occupies the
first position.
Operational Considerations #
Adopting Mem0 still requires application-level decisions:
- define which interactions are eligible for memory extraction;
- isolate tenants and users with enforced authorization, not only filters;
- decide whether sensitive data may be sent to or retained by the service;
- establish retention and deletion behavior;
- evaluate extraction errors and contradictory memories;
- evaluate retrieval with positive and negative queries; and
- cap the number and size of memories inserted into prompts.
Memory extraction can convert an incorrect model inference into persistent state. Retrieval can then repeat that error across future interactions. Observability, user correction, history, and deletion are therefore part of correctness rather than optional administration.
Mem0 is useful when an application needs a managed fact lifecycle across conversations. Elasticsearch or another database remains sufficient when the application only needs document storage and retrieval, or when the team already owns the memory-management pipeline.
Comments
Read or comment on GitHub (sign-in required to comment)