Priming
Bootstrap agents with user context — display names, demographic facts, chat transcripts from legacy systems, or bulk imports from CSV/CRM.
Priming is how you tell a new agent what it already knows about a user. Instead of waiting for the agent to learn through conversation, you deliver the relevant facts up front: who the user is, where they came from, and what they've said before — all before the first message is exchanged.
What you can build with it
- Migrations from other LLM frameworks — import chat history from Zep, Mem0, Letta, OpenAI Assistants, LangChain, Character.AI, or any custom transcript store
- CRM / CSV bulk imports — prime thousands of users in one call with structured contact data
- Chat-transcript seeding — let the agent "remember" previous conversations from another system
- Display-name + timezone bootstrap — ensure the agent addresses users correctly from turn 1
- Onboarding enrichment — load journal entries, support tickets, or prior interactions so the agent sounds familiar on the user's very first chat
Quickstart
Prime a single user with their display name, timezone, and a short narrative block:
import { Sonzai } from "@sonzai-labs/agents";
const client = new Sonzai({ apiKey: process.env.SONZAI_API_KEY! });
const job = await client.agents.priming.primeUser("agent_abc", "user_123", {
display_name: "Mia Tanaka",
metadata: {
timezone: "Asia/Tokyo",
company: "Acme Corp",
title: "Platform Lead",
email: "[email protected]",
},
content: [
{
type: "text",
body: "Mia joined Acme in 2023 and leads the platform team. She prefers async communication and is an avid coffee enthusiast.",
},
],
source: "crm_onboarding",
});
console.log(job.job_id, job.status, job.facts_created);The call returns immediately with a job_id. LLM fact-extraction runs asynchronously in the background — the primed facts appear in memory within seconds.
Core concepts
Metadata vs content
These are two distinct channels for different kinds of information:
- Metadata is structured and first-class:
display_name,company,title,email,phone,timezone, and acustommap for anything else. Sonzai generates facts from metadata fields synchronously — no LLM extraction required — sofacts_createdis non-zero even with no content blocks. - Content is narrative. Content blocks go through the full LLM extraction pipeline and end up as facts in the agent's memory constellation, exactly as if the user had said those things in a conversation.
Content block types
Each PrimeContentBlock has a type and a body:
| Type | When to use |
|---|---|
"text" | Narrative facts, bullet-point summaries, freeform notes about the user |
"chat_transcript" | A prior conversation from another system. Format as User: …\nAgent: … lines, one session per block |
The extraction pipeline deduplicates across all blocks — you can safely send both raw transcripts and pre-extracted facts from the same source without producing duplicate memories.
Batch vs single
| Method | Use when |
|---|---|
primeUser | Onboarding a single user, enriching an existing user with new content |
batchImport | Migrating many users at once from a CSV, CRM, or legacy system |
Batch imports return a job ID immediately. Use getImportStatus (or GetImportStatus in Go) to poll until the job's status reaches "complete".
Import jobs
Both primeUser and batchImport are async. The ImportJob response carries:
| Field | Meaning |
|---|---|
job_id | Opaque ID — use it to poll status |
status | "pending", "processing", "complete", "error" |
total_users | Number of users submitted (batch only) |
processed_users | Users fully extracted so far (batch only) |
facts_created | Total facts written to memory |
error_message | Set if the job errored |
Batch import
Import multiple users in one call. Useful for migrating from a CRM or seeding a fresh deployment:
const job = await client.agents.priming.batchImport("agent_abc", {
source: "crm_export",
users: [
{
user_id: "user_001",
display_name: "Mia Tanaka",
metadata: { email: "[email protected]", timezone: "Asia/Tokyo" },
content: [
{ type: "text", body: "Mia leads the platform team at Acme Corp." },
],
},
{
user_id: "user_002",
display_name: "Ren Park",
metadata: { email: "[email protected]", company: "Beta Labs", title: "CTO" },
content: [
{ type: "chat_transcript", body: "User: Hey, I need help with our API.\nAgent: Sure, what are you trying to do?" },
],
},
],
});
console.log(`job ${job.job_id}: ${job.total_users} users queued`);
// Poll until done
let status = await client.agents.priming.getImportStatus("agent_abc", job.job_id);
while (status.status !== "complete" && status.status !== "error") {
await new Promise(r => setTimeout(r, 2000));
status = await client.agents.priming.getImportStatus("agent_abc", job.job_id);
}
console.log(`done: ${status.facts_created} facts created`);Full API
| Method | Returns | Description |
|---|---|---|
primeUser(agentId, userId, opts) | PrimeUserResponse | Prime or re-prime a single user. Async — returns a job ID. |
getPrimeStatus(agentId, userId, jobId) | ImportJob | Check status of a single-user priming job. |
addContent(agentId, userId, opts) | AddContentResponse | Append more content blocks to an already-primed user without overwriting metadata. |
getMetadata(agentId, userId) | UserPrimingMetadata | Fetch the stored structured metadata for a user. |
updateMetadata(agentId, userId, opts) | UpdateMetadataResponse | Partially update metadata fields. Provided keys overwrite; omitted keys are preserved. |
batchImport(agentId, opts) | BatchImportResponse | Import many users in one async job. |
getImportStatus(agentId, jobId) | ImportJob | Poll a batch import job by ID. |
listImportJobs(agentId, limit?) | ImportJobListResponse | List recent import jobs for an agent. |
Idempotent by design
Calling primeUser more than once for the same user is safe. Content blocks are processed through the same deduplication pipeline as live chat — repeated or overlapping facts are merged, not doubled.
Combines with other features
With Memory — primed facts become durable memory
Content blocks flow through the exact same extraction pipeline as conversational messages. After priming, you can search for primed facts via memory.search:
// After primeUser completes, primed content is searchable
const results = await client.agents.memory.search("agent_abc", {
query: "platform team",
userId: "user_001",
limit: 5,
});
for (const mem of results.results) {
console.log(mem.content, mem.factType, mem.score);
}Primed facts carry a source_type matching the source string you passed to primeUser or batchImport, so you can distinguish migrated history from organically-learned facts when querying.
With Inventory — structured data import via priming
Use structured_import inside primeUser to seed per-user inventory items alongside narrative facts. This is how you import ownership tables, subscription rosters, or product holdings from a CRM export:
{
"source": "crm_inventory",
"structured_import": {
"entity_type": "product",
"content_csv": "product_name,quantity,purchase_date\nHiking Backpack,1,2025-09-01\nWater Bottle,2,2025-10-12",
"column_mapping": {
"product_name": { "property": "name", "is_label": true },
"quantity": { "property": "quantity", "type": "number" },
"purchase_date": { "property": "purchased_on" }
}
}
}Each row becomes a fact shaped as "User owns <label>" with the row's columns as metadata. See the CRM / CSV migration guide for a full walkthrough.
With migration guides — concrete from-X paths
The Migrations overview lists per-source recipes with full export + import code for every common origin system. Priming is the underlying mechanism each guide uses — the migration guides show you exactly how to shape your existing data into content blocks.
Tutorials
- Migrating from Zep — session-based chat transcript import
- Migrating from Mem0 — extracted-fact migration
- Migrating from OpenAI Assistants — thread-based import
- CRM / CSV bulk import — contact rosters and inventory tables
- Migrations overview — full index of source-system recipes
Next steps
- Memory — where primed content lands and how it's recalled during conversation
- Migrations overview — framework-specific recipes for Zep, Mem0, Letta, LangChain, and more
- Inventory — per-user structured items, often seeded via priming
- Priming API reference — full endpoint documentation
Inventory
Per-user structured items the agent can read and reason about — medications, holdings, pets, goals — stored against a shared KB schema.
Custom State
Per-user counters, flags, and strings the agent reads and writes — energy, currency, progress flags, game state — with a stable schema you control.