Skip to content

Migrate

Migrate from Mem0

This guide maps Mem0 concepts to their SurrealDB Agent Memory equivalents, then describes an incremental migration that can run both systems in parallel.

Mem0 conceptSurrealDB Agent Memory equivalentNotes
User IDScope dimension userSurrealDB Agent Memory scopes are multi-dimensional; user is one axis
Agent IDScope dimension agentCombine with user for agent-per-user isolation
Run IDSessionSurrealDB Agent Memory sessions are first-class records with richer metadata
Memory (flat string)Entities + attributes + relationsSurrealDB Agent Memory extracts structured triples from text
add()remember()SurrealDB Agent Memory also deduplicates and reconciles on write
search()recall()SurrealDB Agent Memory adds graph-density reranking
get_all()profile()Returns structured snapshot, not a flat list
delete()forget()Query-driven; POST /scopes/forget erases a whole subtree
HistorySession turns + temporal attributesSurrealDB Agent Memory tracks valid_from/valid_until on attributes

Mem0:

from mem0 import Memory

m = Memory()
m.add("I prefer vegetarian food.", user_id="alice")
results = m.search("What are Alice's food preferences?", user_id="alice")

SurrealDB Agent Memory:

from surrealdb.memory import AsyncMemory

client = AsyncMemory(
    context="<your-context-id>",
    endpoint="https://<your-context-host>",
    api_key="sp-...",
)

await client.remember("I prefer vegetarian food.", scopes=["user/alice"])
results = await client.recall("What are Alice's food preferences?", k=5, lens=["user/alice"])
for hit in results.hits:
    print(hit.text)

Mem0:

import { Memory } from "mem0ai";

const m = new Memory();
await m.add("I prefer vegetarian food.", { user_id: "alice" });
const results = await m.search("food preferences", { user_id: "alice" });

SurrealDB Agent Memory:

import { AgentMemory } from "@surrealdb/memory";

const client = new AgentMemory({
  endpoint: process.env.AGENT_MEMORY_ENDPOINT!,
  context: "dev",
  apiKey: process.env.AGENT_MEMORY_API_KEY!,
});

await client.remember("I prefer vegetarian food.", { scopes: ["user/alice"] });
const results = await client.recall("What are Alice's food preferences?", { k: 5, lens: ["user/alice"] });
console.log(results.hits);

Structured extraction: Mem0 stores memories as flat text strings. SurrealDB Agent Memory extracts structured entities, attributes, and relations. When you write "Alice prefers vegetarian food", it creates an entity person/alice with attribute food_preference = "vegetarian". Future writes that contradict this (e.g. "Alice now eats fish") update the attribute with a supersession chain, so you can see the history.

Conflict resolution: Mem0 stores new memories alongside old ones. SurrealDB Agent Memory detects when a new memory contradicts a stored attribute and automatically supersedes the old value. The old value is not deleted - it is marked as superseded with a timestamp.

Scoped multi-tenancy: Mem0 uses separate user_id and agent_id parameters. SurrealDB Agent Memory uses slash-path scope (for example ["org/acme/user/alice"]). A reader sees records at the scopes its grants cover and below them, never records tagged at an ancestor scope, so a key granted org/acme/* can read every user's memory under org/acme.

Documents versus conversation: Mem0 treats all memory uniformly. SurrealDB Agent Memory records the kind of source behind every fact, such as an uploaded document or a conversation turn, and sorts facts into three memory categories: identity, knowledge and context. When a conversation contradicts a fact from a document, neither value replaces the other: both are kept and an uncertainty is raised for someone to resolve. See Authority when pillars meet.

Wrap your memory calls in a thin adapter that writes to both Mem0 and SurrealDB Agent Memory:

class MemoryAdapter:
    def __init__(self, mem0_client, agent_memory_client):
        self.mem0 = mem0_client
        self.agent_memory = agent_memory_client
    
    async def add(self, content: str, user_id: str):
        # Write to both
        self.mem0.add(content, user_id=user_id)
        await self.agent_memory.remember(content, scopes=[f"user/{user_id}"])
    
    async def search(self, query: str, user_id: str, use_agent_memory: bool = False):
        if use_agent_memory:
            return await self.agent_memory.recall(query, k=5, lens=[f"user/{user_id}"])
        return self.mem0.search(query, user_id=user_id)

Compare recall results between the two systems for a sample of production queries. Use the use_agent_memory=True flag on a percentage of traffic while monitoring for quality regressions.

Export existing Mem0 memories and replay them into SurrealDB Agent Memory:

existing_memories = m.get_all(user_id="alice")

session = await client.sessions.create(scopes=["user/alice"])
for item in existing_memories:
    await client.remember(item["memory"], session_id=session.id)

SurrealDB Agent Memory's reconciliation pipeline deduplicates on write, so replaying memories that contain the same facts will produce correct structured state rather than duplicates.

Once recall quality is satisfactory, remove the dual-write and switch reads to SurrealDB Agent Memory only.

Mem0's add() takes a user_id directly - there is no concept of a session. Every SurrealDB Agent Memory turn belongs to a session, but you do not have to create one: a write without a session_id creates a session for itself and returns its id. The nearest equivalent to Mem0's add() is:

# One-shot add; the write creates its own session
result = await client.remember(content, scopes=[f"user/{user_id}"], role="user")
print(result.session_id)

Sessions have no close step. If your application has natural conversation boundaries, create a session per conversation and pass its id to each write, so the turns are grouped together.

Was this page helpful?