Skip to main content
Sonzai Docs
Tutorial·~20 min

Memory-Aware Chat

Build a conversational agent that remembers everything — user preferences, past events, commitments, and emotional context — across sessions. By the end you'll know how to seed context programmatically, search memories, and inspect what the agent has learned about a user.

What you'll build

  • → A streaming chat loop where the agent automatically extracts and stores facts
  • → A pre-seeding flow to inject existing user data before the first conversation
  • → A memory search API call to find what the agent knows about a specific topic
  • → A fact timeline query to audit the agent's memory growth over time

How Memory Works

Memory is fully automatic during chat. After each message exchange the platform:

  1. Runs an extraction pipeline to identify facts, preferences, commitments, and events in the conversation
  2. Deduplicates against existing memories using a supersession chain (old facts are retired, not deleted)
  3. Indexes everything for full-text search and temporal queries
  4. On the next conversation, fetches the most relevant memories automatically within a token budget

You never need to manage a vector store, write extraction prompts, or implement retrieval logic. The platform handles all of it.

1. Chat and Let Memory Build

Start chatting. Memory extraction happens automatically after the response streams. Nothing special needed on your end.

import { Sonzai } from "@sonzai-labs/agents";

const client = new Sonzai({ apiKey: process.env.SONZAI_API_KEY! });
const AGENT_ID = "agent_abc";
const USER_ID  = "user_123";

// First conversation — agent has no memory yet
for await (const event of client.agents.chatStream(AGENT_ID, {
  userId: USER_ID,
  messages: [
    { role: "user", content: "My name is Mia. I'm allergic to peanuts and I love hiking." },
  ],
})) {
  process.stdout.write(event.choices?.[0]?.delta?.content ?? "");
}
// Platform extracts: name="Mia", allergy="peanuts", interest="hiking"

// Second conversation — agent recalls all of the above
for await (const event of client.agents.chatStream(AGENT_ID, {
  userId: USER_ID,
  messages: [
    { role: "user", content: "What snacks should I bring on my next hike?" },
  ],
})) {
  process.stdout.write(event.choices?.[0]?.delta?.content ?? "");
}
// Agent knows Mia loves hiking and is allergic to peanuts — no re-intro needed.

Memory is per-user

Facts extracted from user A's conversation are never surfaced to user B. Always pass userId (or user_id / UserID) in every chat call so the platform scopes memory correctly.

2. Pre-Seed Memory from Existing Data

If a user has history in your system — a CRM profile, onboarding answers, past orders — inject it before the first conversation so the agent feels like it already knows them.

// Call once during onboarding or after CRM import
await client.agents.memory.seed(AGENT_ID, {
  userId: USER_ID,
  memories: [
    {
      content: "Mia is a 32-year-old UX designer based in Berlin.",
      type: "user_fact",
    },
    {
      content: "Mia subscribed to the Pro plan on 2024-11-03.",
      type: "shared_experience",
      occurred_at: "2024-11-03T00:00:00Z",
    },
    {
      content: "Mia prefers email over SMS for notifications.",
      type: "user_preference",
    },
    {
      content: "Mia mentioned she wants to get into trail running.",
      type: "user_goal",
    },
  ],
});

Supported memory types: user_fact, user_preference, shared_experience, user_goal, commitment, time_sensitive.

3. Search What the Agent Knows

Query the memory store directly to find what the agent has extracted about a topic. Useful for building user-facing "what does my agent remember?" features or for debugging.

const results = await client.agents.memory.search(AGENT_ID, {
  query: "diet restrictions food allergies",
  userId: USER_ID,
  limit: 10,
});

for (const fact of results.facts) {
  console.log(`[${fact.type}] ${fact.content} (confidence: ${fact.confidence})`);
}
// [user_fact] Mia is allergic to peanuts (confidence: 0.97)
// [user_preference] Mia prefers nut-free snacks on hikes (confidence: 0.85)

4. Browse the Memory Tree

The memory tree is a 7-level hierarchy that organises facts by category (/identity/traits, /preferences/interests, /episodes/sessions, etc.). You can walk it node by node.

// Get top-level nodes
const tree = await client.agents.memory.list(AGENT_ID, {
  userId: USER_ID,
  includeContents: false,  // just node metadata, no fact text
});

for (const node of tree.nodes) {
  console.log(`${node.path} — ${node.fact_count} facts`);
}
// /identity/traits — 3 facts
// /preferences/interests — 5 facts
// /episodes/sessions — 12 facts
// /temporal — 2 facts

// Drill into a node
const identityNode = await client.agents.memory.list(AGENT_ID, {
  userId: USER_ID,
  parentId: "node_identity_traits_id",
  includeContents: true,  // include fact text
});

You can explore the memory tree interactively in the dashboard under Agents → your agent → Users → select user → Memory → Tree Explorer.

5. Inspect the Fact Timeline

The timeline shows every fact in chronological order — when it was created, updated, or superseded. Use it to audit memory growth or build a "conversation history" view.

const timeline = await client.agents.memory.timeline(AGENT_ID, {
  userId: USER_ID,
  // Optional: narrow to a date range
  start: "2025-01-01T00:00:00Z",
  end:   "2025-12-31T23:59:59Z",
});

for (const entry of timeline.entries) {
  console.log(
    `${new Date(entry.created_at).toLocaleDateString()} — ${entry.type}: ${entry.content}`
  );
}

6. List Extracted Facts Directly

For admin UIs or compliance exports, list all raw facts for a user without going through the tree hierarchy. Supports filtering by category.

// All facts for this user (paginated)
const facts = await client.agents.memory.listFacts(AGENT_ID, {
  userId: USER_ID,
  limit: 50,
  offset: 0,
  category: "user_preference",  // optional filter
});

console.log(`Total facts: ${facts.total}`);
for (const f of facts.facts) {
  console.log(`  ${f.content}`);
}

GDPR / right to erasure

To delete all memory for a user, call client.agents.memory.reset(agentId, { userId }). This creates tombstone records that prevent deleted facts from being re-surfaced; the data is removed from retrieval immediately.

7. Look Back in Time (Time Machine)

The time machine lets you see what the agent knew about a user at any specific point in the past — useful for debugging why the agent said something, or for auditing how its understanding evolved.

const snapshot = await client.agents.getTimeMachine(AGENT_ID, {
  userId: USER_ID,
  at: "2025-03-01T00:00:00Z",  // what did the agent know at this moment?
});

console.log("Known facts at 2025-03-01:");
for (const fact of snapshot.facts) {
  console.log(`  ${fact.content}`);
}

How supersession works: When a fact is updated, the old record is retired (not deleted) and a new one is created with a SupersedesID pointer. The time machine replays this chain to reconstruct the state at any timestamp.

Next Steps

  • → Read the Memory & Context reference for the full 7-level hierarchy
  • → Set up Conversations to handle multi-turn chat sessions with automatic session management
  • → Explore Emotions & Mood to understand how the agent's emotional state evolves with memory
  • → Add Custom States to store structured application data alongside memory