mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-13 02:24:34 +00:00
237 lines
9.3 KiB
Text
237 lines
9.3 KiB
Text
---
|
|
title: "User Profiles"
|
|
sidebarTitle: "Profiles"
|
|
description: "Automatically maintained context about your users"
|
|
icon: "circle-user"
|
|
---
|
|
|
|
User profiles are **automatically maintained collections of facts about your users** that Supermemory builds from all their interactions. Think of it as a persistent "about me" document that's always up-to-date.
|
|
|
|
Each `containerTag` gets it's own profile.
|
|
|
|
> Note: It's called "user" profile, but in reality it can be anything - an agent, organization, etc.
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="Instant Context" icon="bolt">
|
|
No search needed — comprehensive user info always ready
|
|
</Card>
|
|
<Card title="Auto-Updated" icon="rotate">
|
|
Profiles update as users interact with your system
|
|
</Card>
|
|
</CardGroup>
|
|
|
|
## Why Profiles?
|
|
|
|
Traditional memory systems rely entirely on search:
|
|
|
|
| Problem | Search Only | With Profiles |
|
|
|---------|------------|---------------|
|
|
| Context retrieval | 3-5 queries | 1 call |
|
|
| Response time | 200-500ms | 50-100ms |
|
|
| Basic user info | Requires specific queries | Always available |
|
|
|
|
**Search is too narrow**: When you search for "project updates", you miss that the user prefers bullet points, works in PST, and uses specific terminology.
|
|
|
|
**Profiles provide the foundation**: Instead of searching for basic context, profiles give your LLM a complete picture of who the user is.
|
|
|
|

|
|
|
|
A pure search architecture means every turn pays a `search(prompt)` round trip before the agent can respond. A profile is attached once and sits alongside every user prompt and agent output — no extra call, no latency, and no risk of the query missing something important.
|
|
|
|
---
|
|
|
|
## Non-literal-matching use cases
|
|
|
|
Semantic search retrieves content that's *similar to the query* — it's built for questions like "what did we discuss about the migration?" It's a poor fit for facts that should be known **regardless of what's being asked**, because there's rarely a query that's semantically close to them.
|
|
|
|
The clearest example is the user's own name. If someone tells your agent "call me Dhravya, not my full name" once during onboarding, that fact has almost nothing in common — vector-wise — with "help me plan a trip to Japan" or "review this PR." A search for either of those queries will not surface the name preference, because search only returns what's relevant to the query, and a name preference isn't relevant to trip planning or code review — it should just always be there.
|
|
|
|
```typescript
|
|
// Weeks earlier, during onboarding
|
|
await client.add({
|
|
content: "Call me Dhravya, not my full first name",
|
|
containerTag: "user_123",
|
|
});
|
|
|
|
// Later — an unrelated query
|
|
const results = await client.search({
|
|
q: "help me plan a trip to Japan",
|
|
containerTag: "user_123",
|
|
});
|
|
// The name preference won't be in `results` — it's not semantically
|
|
// related to trip planning, so search correctly leaves it out.
|
|
|
|
// But it's always in the profile, independent of the query:
|
|
const { profile } = await client.profile({ containerTag: "user_123" });
|
|
console.log(profile.static); // ["User goes by Dhravya, not their full name", ...]
|
|
```
|
|
|
|
This is the general pattern: names, pronouns, timezone, tone/format preferences, role, and other facts that should color *every* response — not just responses to a matching query — belong in the profile, not left to be caught by search. If your agent needs to "just know" something at all times, that's a strong signal it belongs in the profile rather than relying on a lucky semantic match.
|
|
|
|
---
|
|
|
|
## Static vs Dynamic
|
|
|
|
Profiles separate two types of information:
|
|
|
|
### Static Profile
|
|
|
|
Long-term, stable facts:
|
|
|
|
- "Sarah is a senior software engineer at TechCorp"
|
|
- "Sarah specializes in distributed systems"
|
|
- "Sarah prefers technical docs over video tutorials"
|
|
|
|
### Dynamic Profile
|
|
|
|
Recent context and temporary states:
|
|
|
|
- "Sarah is migrating the payment service to microservices"
|
|
- "Sarah is preparing for a conference talk next month"
|
|
- "Sarah is debugging a memory leak in auth service"
|
|
|
|
---
|
|
|
|
## Buckets
|
|
|
|
Static and dynamic split facts by how long-lived they are. **Buckets** split them by *topic* — a third, independent axis you define, like `preferences`, `goals`, or `work`. As content is ingested, a classifier sorts each fact into the buckets it matches.
|
|
|
|
Every org starts with a default `preferences` bucket. Add your own in console settings at the organization level, or per space — space buckets are add-only, so a container tag always keeps every org-level bucket.
|
|
|
|
```typescript
|
|
const { profile } = await client.profile({
|
|
containerTag: "user_123",
|
|
include: ["buckets"],
|
|
buckets: ["preferences", "goals"], // optional — omit for all configured buckets
|
|
});
|
|
|
|
console.log(profile.buckets.preferences);
|
|
console.log(profile.buckets.goals);
|
|
```
|
|
|
|
Bucket descriptions steer the classifier, so a precise description ("explicit first-person preferences only, exclude inferred traits") produces cleaner buckets than a vague one. Buckets are separate from [`filterPrompt`](/concepts/customization), which controls what gets ingested at all — buckets only organize facts that already made it into the profile.
|
|
|
|
<Card title="Profile Buckets reference" icon="tags" href="/user-profiles/buckets">
|
|
Request bucketed profiles, create buckets at the org or space level, get AI-generated suggestions, and see validation limits.
|
|
</Card>
|
|
|
|
---
|
|
|
|
## How It Works
|
|
|
|
Profiles are built automatically through ingestion:
|
|
|
|
1. **Ingest content** — Users [add documents](/ingestion/add-memories), chat, or any content
|
|
2. **Extract facts** — AI analyzes content for facts about the user
|
|
3. **Update profile** — System adds, updates, or removes facts
|
|
4. **Always current** — Profiles reflect the latest information
|
|
|
|
<Note>
|
|
You don't manually manage profiles — they build themselves as users interact. Start by [adding content](/ingestion/add-memories) to see profiles in action.
|
|
</Note>
|
|
|
|
---
|
|
|
|
## Profiles + Search
|
|
|
|
Profiles don't replace search — they complement it:
|
|
|
|
- **Profile** = broad foundation (who the user is, preferences, background)
|
|
- **Search** = specific details (exact memories matching a query)
|
|
|
|
### Example
|
|
|
|
User asks: **"Can you help me debug this?"**
|
|
|
|
**Without profiles**: LLM has no context about expertise, projects, or preferences.
|
|
|
|
**With profiles**: LLM knows:
|
|
- Senior engineer (adjust technical level)
|
|
- Working on payment service (likely context)
|
|
- Prefers CLI tools (tool suggestions)
|
|
- Recent memory leak issues (possible connection)
|
|
|
|
---
|
|
|
|
## Filtering Profiles
|
|
|
|
Not many people realize this, but profiles support the same [metadata filtering](/concepts/filtering) as memory and document search. A profile is synthesized from the underlying memories in a container tag, so any `AND`/`OR` metadata filter you'd pass to `search` also narrows which memories are eligible to contribute to `static`, `dynamic`, and `buckets`.
|
|
|
|
```typescript
|
|
// Only build the profile from memories tagged as onboarding data
|
|
const { profile } = await client.profile({
|
|
containerTag: "user_123",
|
|
filters: {
|
|
AND: [{ key: "source", value: "onboarding" }],
|
|
},
|
|
});
|
|
```
|
|
|
|
This is useful when a container tag mixes memories from several sources or contexts and you only want one of them reflected in the profile — for example, a support agent that should only see profile facts derived from support tickets, not from an internal wiki synced into the same container:
|
|
|
|
```typescript
|
|
const { profile } = await client.profile({
|
|
containerTag: "org_customer_442",
|
|
filters: {
|
|
AND: [{ key: "channel", value: "support_ticket" }],
|
|
},
|
|
include: ["static", "dynamic"],
|
|
});
|
|
```
|
|
|
|
Filters apply on top of the search query too — combine `q` and `filters` to scope both the profile synthesis and the accompanying search results in one call. See [Filtering Profiles](/recall/user-profiles#filtering-profiles) for the full parameter reference.
|
|
|
|
---
|
|
|
|
## Use Cases
|
|
|
|
### Personalized AI Assistants
|
|
|
|
Profiles provide: expertise level, communication preferences, tools used, current projects.
|
|
|
|
```typescript
|
|
const systemPrompt = `You are assisting ${userName}.
|
|
|
|
Background: ${profile.static.join('\n')}
|
|
Current focus: ${profile.dynamic.join('\n')}
|
|
|
|
Adjust responses to their expertise and preferences.`;
|
|
```
|
|
|
|
### Customer Support
|
|
|
|
Profiles provide: product usage, previous issues, tech proficiency.
|
|
|
|
- No more "let me look up your account"
|
|
- Agents immediately understand context
|
|
- AI support references past interactions naturally
|
|
|
|
### Educational Platforms
|
|
|
|
Profiles provide: learning style, completed courses, strengths/weaknesses.
|
|
|
|
### Development Tools
|
|
|
|
Profiles provide: preferred languages, coding style, current project context.
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="User Profiles API" icon="code" href="/recall/user-profiles">
|
|
Fetch and use profiles via the API
|
|
</Card>
|
|
<Card title="Profile Buckets" icon="tags" href="/user-profiles/buckets">
|
|
Create and configure topical buckets
|
|
</Card>
|
|
<Card title="Graph Memory" icon="network" href="/concepts/graph-memory">
|
|
How the underlying knowledge graph works
|
|
</Card>
|
|
<Card title="AI SDK Integration" icon="triangle" href="/integrations/ai-sdk">
|
|
Automatic profile injection with AI SDK
|
|
</Card>
|
|
<Card title="Add Memories" icon="plus" href="/ingestion/add-memories">
|
|
Build profiles by adding content
|
|
</Card>
|
|
</CardGroup>
|