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 redis vector store. Do not use its valkey vector store: it does not start on KiviDB (see Known differences).
  • redisvl older than 0.28. mem0 2.2.1 calls a redisvl method that 0.28 removed, so with a newer redisvl mem0 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.2What 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 storeFails 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 searchmem0 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;
  • get and get_all by user_id, agent_id, and user_id with run_id;
  • searches whose filters keep users apart, including a user id with @ and . in it;
  • update, history, delete, delete_all and reset.

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.