> Full SurrealDB documentation index: https://surrealdb.com/docs/llms.txt

# Personal AI assistant

End-user scoped memory with profiles and preferences.

This guide covers building a personal AI assistant that learns from every conversation and retains that knowledge across sessions. The assistant accumulates user preferences, biographical facts, current projects, and behavioural instructions. It injects relevant context automatically at the start of each new session so the experience feels continuous.

## What you are building

- A single-user Context scoped by `user_id`.
- Memory that spans multiple sessions: identity facts, knowledge, context-specific state, and instructions.
- A profile endpoint that produces a ready-to-inject system prompt fragment.
- The ability to forget outdated information when the user's situation changes.

## Step 1 - Create a session per conversation

Each conversation is a new session. The scope ties the session to the user so all extracted facts are associated with them.

```python
from surrealdb.memory import AsyncMemory

memory = AsyncMemory(context="assistant", api_key="sp-...")

async def start_conversation(user_id: str):
    session = await memory.sessions.create(
        scopes=[f"user/{user_id}"],
    )
    return session
```

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

const memory = new AgentMemory({ context: "assistant", apiKey: "sp-..." });

async function startConversation(userId: string) {
    const session = await memory.sessions.create({
        scopes: [`user/${userId}`],
    });
    return session;
}
```

## Step 2 - Inject memory at the start of each session

The profile endpoint returns a structured snapshot of what SurrealDB Agent Memory knows about the user, in sections: `selfFacts`, `static` and `dynamic` facts, `preferences`, and `instructions`. Format it into a system prompt fragment.

```python
async def build_system_prompt(user_id: str) -> str:
    profile = await memory.profile()

    base = "You are a personal AI assistant. Be concise, direct, and helpful."

    profile_block = format_profile(profile)   # see Profiles: injecting into prompts
    if profile_block:
        return f"{base}\n\n## What you know about this user\n\n{profile_block}"

    return base
```

```typescript
async function buildSystemPrompt(userId: string): Promise<string> {
    const profile = await memory.profile();

    const base = "You are a personal AI assistant. Be concise, direct, and helpful.";

    const profileBlock = formatProfile(profile);   // see Profiles: injecting into prompts
    if (profileBlock) {
        return `${base}\n\n## What you know about this user\n\n${profileBlock}`;
    }

    return base;
}
```

A formatted profile looks something like this after a few conversations:

```text
The user is Alice Chen, Head of Platform at Acme Corp. They prefer \
  TypeScript over JavaScript. They live in London. They prefer \
  bullet-point responses and dislike filler phrases. They are \
  currently leading a migration from CommonJS to ESM.
```

This summary draws on the three memory categories, Identity (name, role, location), Knowledge (technical stack) and Context (current project), plus the instructions extraction recorded (response style preferences).

## Step 3 - Run the conversation loop

The simplest integration records each exchange as a pair of turns. SurrealDB Agent Memory extracts facts asynchronously and they are available for the next session.

```python
async def chat(session, user_id: str, user_message: str) -> str:
    # Build context-aware system prompt
    system = await build_system_prompt(user_id)

    # Retrieve session-specific relevant context, returned as one string
    ctx = await memory.sessions.context(session.id, user_message)
    if ctx.context:
        system += f"\n\n## Relevant context\n\n{ctx.context}"

    # Call your LLM
    response = your_llm(system=system, user=user_message)

    # Record both turns
    await memory.remember(user_message, session_id=session.id, role="user")
    await memory.remember(response, session_id=session.id, role="assistant")

    return response
```

```typescript
async function chat(session: Session, userId: string,
    userMessage: string): Promise<string> {
    // Build context-aware system prompt
    let system = await buildSystemPrompt(userId);

    // Retrieve session-specific relevant context, returned as one string
    const ctx = await session.context({ query: userMessage });
    if (ctx.context) {
        system += `\n\n## Relevant context\n\n${ctx.context}`;
    }

    // Call your LLM
    const response = await yourLlm({ system, user: userMessage });

    // Record both turns
    await memory.remember(userMessage, { sessionId: session.id, role: "user" });
    await memory.remember(response, { sessionId: session.id, role: "assistant" });

    return response;
}
```

## Step 4 - How memory builds up

After the user says "I prefer bullet-point responses", the write's `extraction.instructions` holds a new instruction:

```json
{
  "id": "...",
  "label": "response_format",
  "description": "Use bullet points. Avoid filler phrases."
}
```

After the user says "I just moved from Berlin to London", the extraction pipeline updates the `location` attribute on the user's `person` entity and creates a supersession chain. The write's `extraction.corrections` reports the change (its `entityId` is left out here):

```json
{
  "key": "location",
  "oldValue": "Berlin",
  "newValue": "London"
}
```

The old value is retained in the supersession chain for auditability but no longer appears in the active profile.

## Step 5 - Using `chat()` instead of `remember()`

If you want SurrealDB Agent Memory to manage the agent call rather than just recording turns, use the `chat()` endpoint. It will retrieve context, call the Context's configured synthesis model, persist both turns, and run extraction with no callback to supply. Override the model for a single call with `model` if you need to.

```python
session = await memory.sessions.create(scopes=[f"user/{user_id}"])

result = await memory.chat("What are my current projects?", session_id=session.id)
print(result.reply)
print(result.memory_updates)     # extraction diff from the user turn
```

```typescript
const session = await memory.sessions.create({ scopes: [`user/${userId}`] });

const result = await memory.chat("What are my current projects?", { sessionId: session.id });
console.log(result.reply);
console.log(result.citations);
console.log(result.memoryUpdates);
```

The `chat()` call returns `reply`, `citations`, `memoryUpdates`, `sessionId`, and `traceId`. Because the model is configured on the Context rather than passed in, use the `remember()` + your-own-LLM shape when you need custom prompting, tool use, or streaming to your UI.

## Step 6 - Forgetting outdated information

When a user's situation changes significantly - they change jobs, finish a project, move city - you can explicitly forget stale facts rather than waiting for the supersession chain to handle it via new turns.

`forget` is **query-driven**, not field-driven: you describe what to forget in natural language and SurrealDB Agent Memory matches the facts within the caller's `memory:forget` region.

```python
result = await memory.forget("Alice's employer")
print(result.deleted)
```

```typescript
const result = await memory.forget("Alice's employer");
console.log(result.deleted);
```

To preview first, send `{"query": "Alice's employer", "dryRun": true}` to `POST /api/v1/{context_id}/forget`, which returns the would-be count in `deleted` without writing anything. The SDK `forget` methods do not take a dry-run option.

To erase an entire branch rather than matched facts, use the scope-level route **`POST /scopes/forget`** with the subtree path (`user/alice/`), or delete a single entity with **`DELETE /entities/{type}/{name}`**.

`forget` soft-deletes the matched facts (sets `valid_until` to now) and removes them from future retrievals, keeping prior rows for audit. Pass `purge=True` to also remove the supersession history - that is the right-to-be-forgotten path and is irreversible.

## Memory categories in a personal assistant

| Category | Examples |
|---|---|
| Identity | Name, location, occupation, family |
| Knowledge | Domain expertise, tools, languages, opinions |
| Context | Current projects, recent events, open tasks |

Alongside the three categories, extraction records instructions (response style, formatting preferences, topics to avoid) and uncertainties (contradictory or uncertain statements flagged for review). The profile endpoint returns the facts and the instructions in a single call.
