# Migrate from Mem0

Mapping concepts and incremental migration.

This guide maps Mem0 concepts to their SurrealDB Agent Memory equivalents and walks through an incremental migration strategy that lets you run both systems in parallel during transition.

> [!NOTE]
> `Spectron` was the project name for SurrealDB Agent Memory. These type names
> will be renamed in a future release.

## 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:**
```python
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:**
```python
from surrealdb import AsyncSpectron

client = AsyncSpectron(
    context="dev",
    endpoint="http://localhost:9090",
    api_key="sk-...",
)

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:**
```javascript
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:**
```javascript
import { Spectron } from "@surrealdb/spectron";

const client = new Spectron({
  endpoint: process.env.SPECTRON_ENDPOINT!,
  context: "dev",
  apiKey: process.env.SPECTRON_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"]`). A query at `org/acme` can retrieve org-wide memory across users.

**Authoritative versus experiential**: Mem0 treats all memory uniformly. SurrealDB Agent Memory models **eight pillars** of agent memory; among them, **Authoritative** curated content (ingested documents, knowledge nodes) is distinct from **Experiential** conversational memory (turns and the **six memory categories**). Reconciliation gives **Authoritative** precedence when they conflict. See [Eight pillars and six categories](/docs/agent-memory/architecture/eight-pillars-and-categories.md).

## 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:

```python
class MemoryAdapter:
    def __init__(self, mem0_client, spectron_client):
        self.mem0 = mem0_client
        self.spectron = spectron_client
    
    async def add(self, content: str, user_id: str):
        # Write to both
        self.mem0.add(content, user_id=user_id)
        async with self.spectron.sessions.create(scopes=[f"user/{user_id}"]) as s:
            await s.remember(content)
    
    async def search(self, query: str, user_id: str, use_spectron: bool = False):
        if use_spectron:
            async with self.spectron.sessions.create(scopes=[f"user/{user_id}"]) as s:
                return await s.recall(query, k=5)
        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_spectron=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:

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

async with client.sessions.create(scopes=["user/alice"]) as session:
    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. SurrealDB Agent Memory requires a session as context for each turn. The nearest equivalent to Mem0's `add()` is:

```python
# One-shot add without a long-lived session
async with client.sessions.create(scopes=[f"user/{user_id}"]) as session:
    await client.remember(content, session_id=session.id, role="user")
# Session closes automatically; memory is extracted and persisted
```

If your application does not have natural session boundaries (e.g. it stores individual facts rather than conversations), create a short-lived session for each batch of writes and close it immediately.
