mem0
mem0 is an open-source memory layer for AI agents. It stores what a user or an agent has said as memories, and finds the relevant ones again by meaning…
mem0
mem0 is an open-source memory layer for AI
agents. It stores what a user or an agent has said as memories, and finds the
relevant ones again by meaning. mem0 needs a vector store. Its redis vector
store works with a KiviDB database unchanged: the index, the vector search, the
filters and the keyword scoring all run inside KiviDB.
If you would rather not run mem0 yourself, KiviDB Cloud has agent memory built in.
What it needs
- A KiviDB database. Search is part of every build, and the Lua build is not needed.
- mem0's
redisvector store. Do not use itsvalkeyvector store: it does not start on KiviDB (see Known differences). redisvlolder than 0.28. mem0 2.2.1 calls aredisvlmethod that 0.28 removed, so with a newerredisvlmem0 fails on start-up with'SearchIndex' object has no attribute 'set_client'. This happens on every database, not only KiviDB.
pip install mem0ai "redisvl<0.28" sentence-transformers
Connect to KiviDB Cloud
Take the endpoint, port, username and password from your database's overview. With TLS on, also download the CA certificate from Configuration.
import os
from mem0 import Memory
user = os.environ["KIVIDB_USERNAME"]
password = os.environ["KIVIDB_PASSWORD"] # URL-encode it if it contains @ : / or #
host = "your-name.cloud.kividb.io"
config = {
"vector_store": {
"provider": "redis",
"config": {
# TLS on port 6380, trusting the CA certificate from Configuration.
"redis_url": f"rediss://{user}:{password}@{host}:6380/0?ssl_ca_certs=/path/to/kividb-ca.pem",
# Without TLS: f"redis://{user}:{password}@{host}:6379/0"
"collection_name": "mem0",
"embedding_model_dims": 384,
},
},
# A local embedding model: no API key, nothing leaves your machine.
"embedder": {
"provider": "huggingface",
"config": {"model": "sentence-transformers/all-MiniLM-L6-v2", "embedding_dims": 384},
},
}
memory = Memory.from_config(config)
embedding_model_dims must equal the embedder's dimensions. To use another
embedding model, such as OpenAI's text-embedding-3-small (1536), change both.
An existing collection keeps the dimensions it was created with, so use a new
collection_name when you change the model.
Self-hosted KiviDB connects the same way, with your own host, port and
credentials: redis://localhost:6379/0 for a local container started with
docker run -p 6379:6380 kividb/kividb:v1.1.2.
Use it
# Store messages as they are. infer=False makes no language-model call.
memory.add([{"role": "user", "content": "I am vegetarian and allergic to peanuts"}],
user_id="alice", infer=False)
# Find memories by meaning, only among this user's.
hits = memory.search("what should I cook for her?", filters={"user_id": "alice"}, top_k=5)
for h in hits["results"]:
print(round(h["score"], 2), h["memory"])
memory.get_all(filters={"user_id": "alice"})
memory_id = hits["results"][0]["id"]
memory.update(memory_id, "I am vegan and allergic to peanuts")
memory.delete(memory_id)
memory.delete_all(user_id="alice")
With infer=True (the default), mem0 asks a language model to pull facts out
of the conversation before storing them. That needs an llm entry in the config
with your provider's key. The vector store works the same either way.
Where mem0 keeps things
mem0 creates one search index named after collection_name, and keeps one hash
per memory under mem0:<collection_name>:<memory id>. Each hash holds the text,
the 384-dimension vector, the user_id, agent_id and run_id, the
timestamps and your metadata. With infer=True it adds a second index,
<collection_name>_entities.
The history of each memory (memory.history(id)) is not stored in KiviDB.
mem0 keeps it in a SQLite file on the machine running mem0, at
~/.mem0/history.db unless you set history_db_path in the config.
Known differences
| On KiviDB 1.1.2 | What to do | |
|---|---|---|
memory.reset() | Drops and recreates the index, but leaves the memories' hashes in place. The new index picks them up again, so they come back in searches. | Delete them yourself, as below. |
valkey vector store | Fails on start-up with <collection>: no such index. It checks for an index by the wording of Redis's and Valkey's error, and KiviDB's is different. | Use the redis vector store. |
| Keyword part of search | mem0 adds a keyword score to the vector score. KiviDB matches whole words only ("preferences" does not match "prefer"), so the keyword part adds a little less than on Redis. | Nothing. The ranking comes mostly from the vectors. |
To clear a collection on 1.1.2, call reset(), then delete its
keys:
import redis
r = redis.Redis.from_url(config["vector_store"]["config"]["redis_url"])
for key in r.scan_iter(match="mem0:mem0:*", count=1000): # mem0:<collection_name>:*
r.delete(key)
How this is tested
We ran mem0 2.2.1 with redisvl 0.26 and 0.27 against KiviDB 1.1.2,
both with and without TLS and with an ACL user. The same suite ran against
Redis 8 as a baseline. The suite covers:
- adding memories for two users and an agent;
getandget_allbyuser_id,agent_id, anduser_idwithrun_id;- searches whose filters keep users apart, including a user id with
@and.in it; update,history,delete,delete_allandreset.
The search results matched Redis 8's, both in their order and in which
memories were left out by the filters. Everything passed except reset(), as
described above.
With 1,000 memories, on one machine with the database in a local container, the
median call to the database took about 6 ms. A median search took 25 ms, most
of it computing the query's embedding.
We have not tested mem0 on sharded databases.