This guide maps Mem0 concepts to their SurrealDB Agent Memory equivalents, then describes an incremental migration that can run both systems in parallel.
Concept mapping
| Mem0 concept | SurrealDB Agent Memory equivalent | Notes |
|---|---|---|
| User ID | Scope dimension user | SurrealDB Agent Memory scopes are multi-dimensional; user is one axis |
| Agent ID | Scope dimension agent | Combine with user for agent-per-user isolation |
| Run ID | Session | SurrealDB Agent Memory sessions are first-class records with richer metadata |
| Memory (flat string) | Entities + attributes + relations | SurrealDB 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 |
| History | Session turns + temporal attributes | SurrealDB Agent Memory tracks valid_from/valid_until on attributes |
SDK migration
Python
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)JavaScript
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);Key differences
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.
Incremental migration strategy
Step 1: run both systems in parallel
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)Step 2: validate recall quality
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.
Step 3: migrate existing memories
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.
Step 4: cut over
Once recall quality is satisfactory, remove the dual-write and switch reads to SurrealDB Agent Memory only.
Session management difference
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.