[docs] Essential cookbook added and overall revamp to the structure of cookbooks (#3668)
This commit is contained in:
@@ -0,0 +1,525 @@
|
||||
---
|
||||
title: Build a Mem0 Companion
|
||||
description: "Spin up a fitness coach that remembers goals, adapts tone, and keeps sessions personal."
|
||||
---
|
||||
|
||||
|
||||
Essentially, creating a companion out of LLMs is as simple as a loop. But these loops work great for one type of character without personalization and fall short as soon as you restart the chat.
|
||||
|
||||
Problem: LLMs are stateless. GPT doesn't remember conversations. You could stuff everything inside the context window, but that becomes slow, expensive, and breaks at scale.
|
||||
|
||||
The solution: Mem0. It extracts and stores what matters from conversations, then retrieves it when needed. Your companion remembers user preferences, past events, and history.
|
||||
|
||||
In this cookbook we'll build a **fitness companion** that:
|
||||
|
||||
- Remembers user goals across sessions
|
||||
- Recalls past workouts and progress
|
||||
- Adapts its personality based on user preferences
|
||||
- Handles both short-term context (today's chat) and long-term memory (months of history)
|
||||
|
||||
By the end, you'll have a working fitness companion and know how to handle common production challenges.
|
||||
|
||||
---
|
||||
|
||||
## The Basic Loop with Memory
|
||||
|
||||
Max wants to train for a marathon. He starts chatting with Ray, an AI running coach.
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
from mem0 import MemoryClient
|
||||
|
||||
openai_client = OpenAI(api_key="your-openai-key")
|
||||
mem0_client = MemoryClient(api_key="your-mem0-key")
|
||||
|
||||
def chat(user_input, user_id):
|
||||
# Retrieve relevant memories
|
||||
memories = mem0_client.search(user_input, user_id=user_id, limit=5)
|
||||
context = "\\n".join(m["memory"] for m in memories["results"])
|
||||
|
||||
# Call LLM with memory context
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": f"You're Ray, a running coach. Memories:\\n{context}"},
|
||||
{"role": "user", "content": user_input}
|
||||
]
|
||||
).choices[0].message.content
|
||||
|
||||
# Store the exchange
|
||||
mem0_client.add([
|
||||
{"role": "user", "content": user_input},
|
||||
{"role": "assistant", "content": response}
|
||||
], user_id=user_id)
|
||||
|
||||
return response
|
||||
|
||||
```
|
||||
|
||||
**Session 1:**
|
||||
|
||||
```python
|
||||
chat("I want to run a marathon in under 4 hours", user_id="max")
|
||||
# Output: "That's a solid goal. What's your current weekly mileage?"
|
||||
# Stored in Mem0: "Max wants to run sub-4 marathon"
|
||||
|
||||
```
|
||||
|
||||
**Session 2 (next day, app restarted):**
|
||||
|
||||
```python
|
||||
chat("What should I focus on today?", user_id="max")
|
||||
# Output: "Based on your sub-4 marathon goal, let's work on building your aerobic base..."
|
||||
|
||||
```
|
||||
|
||||
<Info>
|
||||
Ray remembers Max's goal across sessions. The app restarted, but the memory persisted. This is the core pattern: retrieve memories, pass them as context, store new exchanges.
|
||||
</Info>
|
||||
|
||||
Ray remembers. Restart the app, and the goal persists. From here on, we'll focus on just the Mem0 API calls.
|
||||
|
||||
---
|
||||
|
||||
## Organizing Memory by Type
|
||||
|
||||
### Separating Temporary from Permanent
|
||||
|
||||
Max mentions his knee hurts. That's different from his marathon goal - one is temporary, the other is long-term.
|
||||
|
||||
**Categories vs Metadata:**
|
||||
|
||||
- **Categories**: AI-assigned by Mem0 based on content (you can't force them)
|
||||
- **Metadata**: Manually set by you for forced tagging
|
||||
|
||||
Define custom categories at the project level. Mem0 will automatically tag memories with relevant categories based on content:
|
||||
|
||||
```python
|
||||
mem0_client.project.update(custom_categories=[
|
||||
{"goals": "Race targets and training objectives"},
|
||||
{"constraints": "Injuries, limitations, recovery needs"},
|
||||
{"preferences": "Training style, surfaces, schedules"}
|
||||
])
|
||||
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Categories vs Metadata:** Categories are AI-assigned by Mem0 based on content semantics. You define the palette, Mem0 picks which ones apply. If you need guaranteed tagging, use `metadata` instead.
|
||||
</Note>
|
||||
|
||||
Now when you add memories, Mem0 automatically assigns the appropriate categories:
|
||||
|
||||
```python
|
||||
# Add goal - Mem0 automatically tags it as "goals"
|
||||
mem0_client.add(
|
||||
[{"role": "user", "content": "Sub-4 marathon is my A-race"}],
|
||||
user_id="max"
|
||||
)
|
||||
|
||||
# Add constraint - Mem0 automatically tags it as "constraints"
|
||||
mem0_client.add(
|
||||
[{"role": "user", "content": "My right knee flares up on downhills"}],
|
||||
user_id="max"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Mem0 reads the content and intelligently picks which categories apply. You define the palette, it handles the tagging.
|
||||
|
||||
**Important:** You cannot force specific categories. Mem0's platform decides which categories are relevant based on content. If you need to force-tag something, use `metadata` instead:
|
||||
|
||||
```python
|
||||
# Force tag using metadata (not categories)
|
||||
mem0_client.add(
|
||||
[{"role": "user", "content": "Some workout note"}],
|
||||
user_id="max",
|
||||
metadata={"workout_type": "speed", "forced_tag": "custom_label"}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### Filtering by Category
|
||||
|
||||
Retrieve just constraints for workout planning:
|
||||
|
||||
```python
|
||||
constraints = mem0_client.search(
|
||||
"injury concerns",
|
||||
user_id="max",
|
||||
filters={"categories": {"in": ["constraints"]}}
|
||||
)
|
||||
print([m["memory"] for m in constraints["results"]])
|
||||
# Output: ["Max's right knee flares up on downhills"]
|
||||
|
||||
```
|
||||
|
||||
Ray can plan workouts that avoid aggravating Max's knee, without pulling in race goals or other unrelated memories.
|
||||
|
||||
---
|
||||
|
||||
## Filtering What Gets Stored
|
||||
|
||||
### The Problem
|
||||
|
||||
Run the basic loop for a week and check what's stored:
|
||||
|
||||
```python
|
||||
memories = mem0_client.get_all(user_id="max")
|
||||
print([m["memory"] for m in memories["results"]])
|
||||
# Output: ["Max wants to run marathon under 4 hours", "hey", "lol ok", "cool thanks", "gtg bye"]
|
||||
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Without filters, Mem0 stores everything—greetings, filler, and casual chat. This pollutes retrieval: instead of pulling "marathon goal," you get "lol ok." Set custom instructions to keep memory clean.
|
||||
</Warning>
|
||||
|
||||
Noise. Greetings and filler clutter the memory.
|
||||
|
||||
### Custom Instructions
|
||||
|
||||
Tell Mem0 what matters:
|
||||
|
||||
```python
|
||||
mem0_client.project.update(custom_instructions="""
|
||||
Extract from running coach conversations:
|
||||
- Training goals and race targets
|
||||
- Physical constraints or injuries
|
||||
- Training preferences (time of day, surfaces, weather)
|
||||
- Progress milestones
|
||||
|
||||
Exclude:
|
||||
- Greetings and filler
|
||||
- Casual chatter
|
||||
- Hypotheticals unless planning related
|
||||
""")
|
||||
|
||||
```
|
||||
|
||||
Now chat again:
|
||||
|
||||
```python
|
||||
chat("hey how's it going", user_id="max")
|
||||
chat("I prefer trail running over roads", user_id="max")
|
||||
|
||||
memories = mem0_client.get_all(user_id="max")
|
||||
print([m["memory"] for m in memories["results"]])
|
||||
# Output: ["Max wants to run marathon under 4 hours", "Max prefers trail running over roads"]
|
||||
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Expected output:** Only 2 memories stored—the marathon goal and trail preference. The greeting "hey how's it going" was filtered out automatically. Custom instructions are working.
|
||||
</Info>
|
||||
|
||||
Only meaningful facts. Filler gets dropped automatically.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Agent Memory for Personality
|
||||
|
||||
### Why Agents Need Memory Too
|
||||
|
||||
Max prefers direct feedback, not motivational fluff. Ray needs to remember how to communicate - that's agent memory, separate from user memory.
|
||||
|
||||
Store agent personality:
|
||||
|
||||
```python
|
||||
mem0_client.add(
|
||||
[{"role": "system", "content": "Max wants direct, data-driven feedback. Skip motivational language."}],
|
||||
agent_id="ray_coach"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Retrieve agent style alongside user memories:
|
||||
|
||||
```python
|
||||
# Get coach personality
|
||||
agent_memories = mem0_client.search("coaching style", agent_id="ray_coach")
|
||||
# Output: ["Max wants direct, data-driven feedback. Skip motivational language."]
|
||||
|
||||
# Store conversations with agent_id
|
||||
mem0_client.add([
|
||||
{"role": "user", "content": "How'd my run look today?"},
|
||||
{"role": "assistant", "content": "Pace was 8:15/mile. Heart rate 152, zone 2."}
|
||||
], user_id="max", agent_id="ray_coach")
|
||||
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Expected behavior:** Ray's responses are now data-driven and direct. The agent memory stored the coaching style preference, so future responses adapt automatically without Max having to repeat his preference.
|
||||
</Info>
|
||||
|
||||
No "Great job!" or "Keep it up!" - just data. Ray adapts to Max's preference.
|
||||
|
||||
---
|
||||
|
||||
## Managing Short-Term Context
|
||||
|
||||
### When to Store in Mem0
|
||||
|
||||
Don't send every single message to Mem0. Keep recent context in memory, let Mem0 handle the important long-term facts.
|
||||
|
||||
```python
|
||||
# Store only meaningful exchanges in Mem0
|
||||
mem0_client.add([
|
||||
{"role": "user", "content": "I want to run a marathon"},
|
||||
{"role": "assistant", "content": "Let's build a training plan"}
|
||||
], user_id="max")
|
||||
|
||||
# Skip storing filler
|
||||
# "hey" → don't store
|
||||
# "cool thanks" → don't store
|
||||
|
||||
# Or rely on custom_instructions to filter automatically
|
||||
|
||||
```
|
||||
|
||||
Last 10 messages in your app's buffer. Important facts in Mem0. Faster, cheaper, still works.
|
||||
|
||||
---
|
||||
|
||||
## Time-Bound Memories
|
||||
|
||||
### Auto-Expiring Facts
|
||||
|
||||
Max tweaks his ankle. It'll heal in two weeks - the memory should expire too.
|
||||
|
||||
```python
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
expiration = (datetime.now() + timedelta(days=14)).strftime("%Y-%m-%d")
|
||||
|
||||
mem0_client.add(
|
||||
[{"role": "user", "content": "Rolled my left ankle, needs rest"}],
|
||||
user_id="max",
|
||||
expiration_date=expiration
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
In 14 days, this memory disappears automatically. Ray stops asking about the ankle.
|
||||
|
||||
---
|
||||
|
||||
## Putting It All Together
|
||||
|
||||
Here's the Mem0 setup combining everything:
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
mem0_client = MemoryClient(api_key="your-mem0-key")
|
||||
|
||||
# Configure memory filtering and categories
|
||||
mem0_client.project.update(
|
||||
custom_instructions="""
|
||||
Extract: goals, constraints, preferences, progress
|
||||
Exclude: greetings, filler, casual chat
|
||||
""",
|
||||
custom_categories=[
|
||||
{"name": "goals", "description": "Training targets"},
|
||||
{"name": "constraints", "description": "Injuries and limitations"},
|
||||
{"name": "preferences", "description": "Training style"}
|
||||
]
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
**Week 1 - Store goals and preferences:**
|
||||
|
||||
```python
|
||||
mem0_client.add([
|
||||
{"role": "user", "content": "I want to run a sub-4 marathon"},
|
||||
{"role": "assistant", "content": "Got it. Let's build a training plan."}
|
||||
], user_id="max", agent_id="ray", categories=["goals"])
|
||||
|
||||
mem0_client.add([
|
||||
{"role": "user", "content": "I prefer trail running over roads"}
|
||||
], user_id="max", categories=["preferences"])
|
||||
|
||||
```
|
||||
|
||||
**Week 3 - Temporary injury with expiration:**
|
||||
|
||||
```python
|
||||
expiration = (datetime.now() + timedelta(days=14)).strftime("%Y-%m-%d")
|
||||
mem0_client.add(
|
||||
[{"role": "user", "content": "Rolled ankle, need light workouts"}],
|
||||
user_id="max",
|
||||
categories=["constraints"],
|
||||
expiration_date=expiration
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
**Retrieve for context:**
|
||||
|
||||
```python
|
||||
memories = mem0_client.search("training plan", user_id="max", limit=5)
|
||||
# Gets: marathon goal, trail preference, ankle injury (if still valid)
|
||||
|
||||
```
|
||||
|
||||
Ray remembers goals, preferences, and personality. Handles temporary injuries. Works across sessions.
|
||||
|
||||
---
|
||||
|
||||
## Common Production Patterns
|
||||
|
||||
### Episodic Stories with run_id
|
||||
|
||||
Training for Boston is different from training for New York. Separate the memory threads:
|
||||
|
||||
```python
|
||||
mem0_client.add(messages, user_id="max", run_id="boston-2025")
|
||||
mem0_client.add(messages, user_id="max", run_id="nyc-2025")
|
||||
|
||||
# Retrieve only Boston memories
|
||||
boston_memories = mem0_client.search(
|
||||
"training plan",
|
||||
user_id="max",
|
||||
run_id="boston-2025"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Each race gets its own episodic boundary. No cross-contamination.
|
||||
|
||||
### Importing Historical Data
|
||||
|
||||
Max has 6 months of training logs to backfill:
|
||||
|
||||
```python
|
||||
old_logs = [
|
||||
[{"role": "user", "content": "Completed 20-mile long run"}],
|
||||
[{"role": "user", "content": "Hit 8:00 pace on tempo run"}],
|
||||
]
|
||||
|
||||
for log in old_logs:
|
||||
mem0_client.add(log, user_id="max")
|
||||
|
||||
```
|
||||
|
||||
### Handling Contradictions
|
||||
|
||||
Max changes his goal from sub-4 to sub-3:45:
|
||||
|
||||
```python
|
||||
# Find the old memory
|
||||
memories = mem0_client.get_all(user_id="max")
|
||||
goal_memory = [m for m in memories["results"] if "sub-4" in m["memory"]][0]
|
||||
|
||||
# Update it
|
||||
mem0_client.update(goal_memory["id"], "Max wants to run sub-3:45 marathon")
|
||||
|
||||
```
|
||||
|
||||
Update instead of creating duplicates.
|
||||
|
||||
### Multiple Agents
|
||||
|
||||
Max works with Ray for running and Jordan for strength training:
|
||||
|
||||
```python
|
||||
chat("easy run today", user_id="max", agent_id="ray")
|
||||
chat("leg day workout", user_id="max", agent_id="jordan")
|
||||
|
||||
```
|
||||
|
||||
Each coach maintains separate personality memory while sharing user context.
|
||||
|
||||
### Filtering by Date
|
||||
|
||||
Prioritize recent training over old data:
|
||||
|
||||
```python
|
||||
recent = mem0_client.search(
|
||||
"training progress",
|
||||
user_id="max",
|
||||
filters={"created_at": {"gte": "2025-10-01"}}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### Metadata Tagging
|
||||
|
||||
Tag workouts by type:
|
||||
|
||||
```python
|
||||
mem0_client.add(
|
||||
[{"role": "user", "content": "10x400m intervals"}],
|
||||
user_id="max",
|
||||
metadata={"workout_type": "speed", "intensity": "high"}
|
||||
)
|
||||
|
||||
# Later, find all speed workouts
|
||||
speed_sessions = mem0_client.search(
|
||||
"speed work",
|
||||
user_id="max",
|
||||
filters={"metadata": {"workout_type": "speed"}}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### Pruning Old Memories
|
||||
|
||||
Delete irrelevant memories:
|
||||
|
||||
```python
|
||||
mem0_client.delete(memory_id="mem_xyz")
|
||||
|
||||
# Or clear an entire run_id
|
||||
mem0_client.delete_all(user_id="max", run_id="old-training-cycle")
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What You Built
|
||||
|
||||
A companion that:
|
||||
|
||||
- **Persists across sessions** - Mem0 storage
|
||||
- **Filters noise** - custom instructions
|
||||
- **Organizes by type** - categories
|
||||
- **Adapts personality** - **`agent_id`**
|
||||
- **Stays fast** - short-term buffer
|
||||
- **Handles temporal facts** - expiration
|
||||
- **Scales to production** - batching, metadata, pruning
|
||||
|
||||
This pattern works for any companion: fitness coaches, tutors, roleplay characters, therapy bots, creative writing partners.
|
||||
|
||||
---
|
||||
|
||||
<Tip>
|
||||
Start with 2-3 categories max (e.g., goals, constraints, preferences). More categories dilute tagging accuracy. You can always add more later after seeing what Mem0 extracts.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## Production Checklist
|
||||
|
||||
Before launching:
|
||||
|
||||
- Set custom instructions for your domain
|
||||
- Define 2-3 categories (goals, constraints, preferences)
|
||||
- Add expiration strategy for time-bound facts
|
||||
- Implement error handling for API calls
|
||||
- Monitor memory quality in Mem0 dashboard
|
||||
- Clear test data from production project
|
||||
|
||||
---
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Build AI with Personality" icon="sparkles" href="/cookbooks/essentials/building-ai-with-personality">
|
||||
Separate user and agent memories so companions stay consistent across sessions.
|
||||
</Card>
|
||||
<Card title="Tag Support Memories" icon="tag" href="/cookbooks/essentials/tagging-and-organizing-memories">
|
||||
Organize customer context to keep assistants responsive at scale.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,603 @@
|
||||
---
|
||||
title: Scope User vs Agent Memories
|
||||
description: "Use **user_id** and **agent_id** to balance personalization with consistent assistant behavior."
|
||||
---
|
||||
|
||||
# Build AI with Distinct Personalities
|
||||
|
||||
While building memory systems for AI apps, you need to juggle between agent and user memories. The user delivers information from their side, but there's often value inside the agent's responses as well.
|
||||
|
||||
Unlike other memory APIs, we built one that allows you the flexibility to store memory from all sources. User and agent memories in Mem0 are handled by:
|
||||
|
||||
- **user_id** - Memories about specific users
|
||||
- **agent_id** - Memories from the agent itself
|
||||
|
||||
In this guide, you'll see how these parameters work together, along with practical examples to help you build a fitness coach that remembers user workouts while maintaining a consistent coaching personality.
|
||||
|
||||
---
|
||||
|
||||
## User Memories
|
||||
|
||||
Let's start by tracking individual user workouts. We'll use **`user_id`** to keep each person's data separate.
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
from mem0 import MemoryClient
|
||||
import os
|
||||
|
||||
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
||||
mem0_client = MemoryClient()
|
||||
|
||||
# Sarah logs her workout
|
||||
mem0_client.add(
|
||||
"Completed 5K run in 28 minutes - felt great!",
|
||||
user_id="sarah"
|
||||
)
|
||||
|
||||
# Mike logs his workout
|
||||
mem0_client.add(
|
||||
"Bench press: 185 lbs x 10 reps, 3 sets",
|
||||
user_id="mike"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Now when we coach Sarah, we retrieve only her workout history:
|
||||
|
||||
```python
|
||||
# Get Sarah's workout history
|
||||
sarah_history = mem0_client.search(
|
||||
"What exercises has Sarah done recently?",
|
||||
filters={"user_id": "sarah"}
|
||||
)
|
||||
|
||||
# Generate coaching advice
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "system", "content": "You're a supportive fitness coach."},
|
||||
{"role": "user", "content": f"Based on this history: {sarah_history}, suggest the next workout."}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Great job on that 5K! Your endurance is building nicely. Let's add some
|
||||
strength work to complement your running. Try 3 sets of bodyweight squats
|
||||
(15 reps each) to strengthen your legs and improve your running power.
|
||||
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Expected output:** Sarah gets running advice based on her 5K, not Mike's bench press data. The **`user_id`** filter ensures memories are isolated—each user only sees their own workout history.
|
||||
</Info>
|
||||
|
||||
This works perfectly! Each user has private workout history, and Sarah never sees Mike's data.
|
||||
|
||||
---
|
||||
|
||||
## Adding Coach Personality
|
||||
|
||||
Our fitness coach needs a consistent personality - supportive, motivational, and celebrates small wins. Let's see what happens if we try storing this with **`user_id`**:
|
||||
|
||||
```python
|
||||
# Adding coach personality for Sarah
|
||||
mem0_client.add(
|
||||
"I'm a supportive fitness coach who celebrates every achievement and uses athlete-focused language",
|
||||
user_id="sarah"
|
||||
)
|
||||
|
||||
# Adding the same personality for Mike
|
||||
mem0_client.add(
|
||||
"I'm a supportive fitness coach who celebrates every achievement and uses athlete-focused language",
|
||||
user_id="mike"
|
||||
)
|
||||
|
||||
# For 1,000 users, we'd repeat this 1,000 times...
|
||||
|
||||
```
|
||||
|
||||
This approach has some limitations:
|
||||
|
||||
1. **Duplication**: We're storing the same coaching personality 1,000 times for 1,000 users
|
||||
2. **Hard to update**: Want to change the coaching style? Update 1,000 memories
|
||||
3. **Mixed with user data**: Coach personality and user workouts are stored together, making queries complex
|
||||
|
||||
<Warning>
|
||||
Storing agent personality with **`user_id`** doesn't scale. For 10,000 users, you'd duplicate the same personality 10,000 times. Updating the coaching style means updating 10,000 separate memories. This wastes storage and makes maintenance impossible.
|
||||
</Warning>
|
||||
|
||||
What if the coach could have a personality that's shared across all users?
|
||||
|
||||
---
|
||||
|
||||
## Agent Memories
|
||||
|
||||
Here's where **`agent_id`** comes in. We can store the coach's personality once and share it across all users:
|
||||
|
||||
```python
|
||||
# Store coach personality ONCE with agent_id
|
||||
mem0_client.add(
|
||||
"I'm FitCoach - a supportive fitness coach who celebrates every achievement. "
|
||||
"I use motivational language, focus on progress over perfection, and help users build sustainable habits.",
|
||||
agent_id="fitcoach_v1"
|
||||
)
|
||||
|
||||
# Sarah's workout (still private)
|
||||
mem0_client.add(
|
||||
"Completed 5K run in 28 minutes",
|
||||
user_id="sarah"
|
||||
)
|
||||
|
||||
# Mike's workout (still private)
|
||||
mem0_client.add(
|
||||
"Bench press: 185 lbs x 10 reps, 3 sets",
|
||||
user_id="mike"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Now when coaching Sarah, we retrieve both the agent's personality AND her workout history:
|
||||
|
||||
```python
|
||||
# Get both coach personality and Sarah's workouts
|
||||
coaching_context = mem0_client.search(
|
||||
"coaching context for Sarah",
|
||||
filters={
|
||||
"OR": [
|
||||
{"agent_id": "fitcoach_v1"}, # Coach personality
|
||||
{"user_id": "sarah"} # Sarah's workout history
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
# Generate personalized coaching
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "system", "content": str(coaching_context)},
|
||||
{"role": "user", "content": "What should I focus on in my next workout?"}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Amazing work on that 5K! 🎉 You're crushing your running goals!
|
||||
|
||||
Now let's build some complementary strength. I recommend adding bodyweight
|
||||
squats to your routine - they'll make you an even stronger runner. Start
|
||||
with 3 sets of 15 reps, and remember: progress over perfection!
|
||||
|
||||
```
|
||||
|
||||
Notice how the response has the motivational tone (from **`agent_id`**) combined with personalized advice based on Sarah's 5K run (from **`user_id`**).
|
||||
|
||||
The coach personality is now:
|
||||
|
||||
- ✅ Stored once, shared by all users
|
||||
- ✅ Easy to update (change one memory, affects all users)
|
||||
- ✅ Cleanly separated from user workout data
|
||||
|
||||
<Info>
|
||||
**Expected behavior:** The response combines the motivational tone (from **`agent_id`**) with Sarah's specific 5K progress (from **`user_id`**). One agent personality, infinite users—update the agent once, and all users get the new coaching style.
|
||||
</Info>
|
||||
|
||||
---
|
||||
|
||||
## Combining Both: Relationship Memories
|
||||
|
||||
There's a third type of memory - one that captures the relationship between a specific user and the coach. To store these, use `metadata` to track which agent the relationship is with:
|
||||
|
||||
```python
|
||||
# Coach personality (shared across all users)
|
||||
mem0_client.add(
|
||||
"I'm FitCoach - supportive and motivational",
|
||||
agent_id="fitcoach_v1"
|
||||
)
|
||||
|
||||
# Sarah's workout data (private to Sarah)
|
||||
mem0_client.add(
|
||||
"Goal: Run a half marathon by June. Currently runs 5K comfortably.",
|
||||
user_id="sarah"
|
||||
)
|
||||
|
||||
# Sarah-Coach relationship (stored as user memory with agent context in metadata)
|
||||
mem0_client.add(
|
||||
"Sarah and I have an inside joke: 'No pain, no protein shake!' "
|
||||
"She responds best to gentle encouragement after tough workouts.",
|
||||
user_id="sarah",
|
||||
metadata={"agent_id": "fitcoach_v1", "type": "relationship"}
|
||||
)
|
||||
|
||||
# Mike-Coach relationship (different from Sarah's)
|
||||
mem0_client.add(
|
||||
"Mike prefers data-driven feedback with specific numbers and percentages. "
|
||||
"Less motivational talk, more concrete metrics.",
|
||||
user_id="mike",
|
||||
metadata={"agent_id": "fitcoach_v1", "type": "relationship"}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Now when coaching Sarah, retrieve her data including relationship with this specific coach:
|
||||
|
||||
```python
|
||||
# Get Sarah's memories including relationship with fitcoach_v1
|
||||
sarah_context = mem0_client.search(
|
||||
"How should I coach Sarah today?",
|
||||
user_id="sarah",
|
||||
filters={"metadata": {"agent_id": "fitcoach_v1"}}
|
||||
)
|
||||
|
||||
# Get coach personality
|
||||
coach_personality = mem0_client.search(
|
||||
"coaching personality",
|
||||
agent_id="fitcoach_v1"
|
||||
)
|
||||
|
||||
# Combine both contexts
|
||||
full_context = str(coach_personality) + "\\n" + str(sarah_context)
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "system", "content": full_context},
|
||||
{"role": "user", "content": "Just finished today's workout!"}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Awesome work today! 💪 No pain, no protein shake, right? 😄
|
||||
|
||||
You're making real progress toward that half marathon goal. Keep this
|
||||
momentum going - your consistency is your superpower!
|
||||
|
||||
```
|
||||
|
||||
When coaching Mike with the same approach:
|
||||
|
||||
```python
|
||||
# Get Mike's memories including relationship with fitcoach_v1
|
||||
mike_context = mem0_client.search(
|
||||
"How should I coach Mike today?",
|
||||
user_id="mike",
|
||||
filters={"metadata": {"agent_id": "fitcoach_v1"}}
|
||||
)
|
||||
|
||||
coach_personality = mem0_client.search(
|
||||
"coaching personality",
|
||||
agent_id="fitcoach_v1"
|
||||
)
|
||||
|
||||
full_context = str(coach_personality) + "\\n" + str(mike_context)
|
||||
|
||||
# ... same coaching code ...
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Solid session. Your bench press shows 8% improvement over last week
|
||||
(171 lbs avg to 185 lbs). Target: 200 lbs by end of month.
|
||||
On track at current rate (+3.2% weekly).
|
||||
|
||||
```
|
||||
|
||||
Same coach, completely different experience based on each user's relationship preferences stored in metadata.
|
||||
|
||||
<Note>
|
||||
**Relationship memories** are stored with **`user_id`** (they're private to each user) but include `agent_id` in metadata to track which agent the relationship is with. This lets you retrieve the user's preferences for how this specific agent should interact with them.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Putting It All Together
|
||||
|
||||
Here's a complete example showing all three memory layers working together:
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
from mem0 import MemoryClient
|
||||
import os
|
||||
|
||||
# Initialize clients
|
||||
openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
||||
mem0_client = MemoryClient()
|
||||
|
||||
# Layer 1: Agent personality (shared)
|
||||
mem0_client.add(
|
||||
"I'm FitCoach - supportive, motivational, celebrates small wins",
|
||||
agent_id="fitcoach_v1",
|
||||
metadata={"type": "personality"}
|
||||
)
|
||||
|
||||
# Layer 2: User profile (private)
|
||||
mem0_client.add(
|
||||
"Sarah's goal: Run half marathon by June. Currently comfortable at 5K distance.",
|
||||
user_id="sarah",
|
||||
metadata={"type": "profile"}
|
||||
)
|
||||
|
||||
# Layer 3: Relationship (stored as user memory with agent context in metadata)
|
||||
mem0_client.add(
|
||||
"Sarah responds best to encouragement. Inside joke: 'No pain, no protein shake!'",
|
||||
user_id="sarah",
|
||||
metadata={"agent_id": "fitcoach_v1", "type": "relationship"}
|
||||
)
|
||||
|
||||
# Log today's workout
|
||||
mem0_client.add(
|
||||
"Completed 8K run in 45 minutes - new personal record!",
|
||||
user_id="sarah",
|
||||
metadata={"type": "workout", "date": "2025-01-23"}
|
||||
)
|
||||
|
||||
# Generate coaching response
|
||||
# Get Sarah's context (includes all her memories)
|
||||
sarah_context = mem0_client.search(
|
||||
"Generate coaching advice for Sarah",
|
||||
user_id="sarah"
|
||||
)
|
||||
|
||||
# Get coach personality
|
||||
coach_personality = mem0_client.search(
|
||||
"coaching personality",
|
||||
agent_id="fitcoach_v1"
|
||||
)
|
||||
|
||||
# Combine contexts
|
||||
full_context = str(coach_personality) + "\\n" + str(sarah_context)
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "system", "content": full_context},
|
||||
{"role": "user", "content": "Just finished my run today!"}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
🎉 YES! 8K is a HUGE milestone! You just crushed your previous distance!
|
||||
|
||||
Remember when you could barely do 5K? Look at you now! That half marathon
|
||||
in June is looking more achievable every single day. No pain, no protein
|
||||
shake - and today, you EARNED that shake! 💪
|
||||
|
||||
Next week, let's aim for 9K. You're ready for it!
|
||||
|
||||
```
|
||||
|
||||
The response combines:
|
||||
|
||||
- ✅ Motivational tone (agent personality)
|
||||
- ✅ Specific goal reference (Sarah's profile)
|
||||
- ✅ Inside joke (relationship memory)
|
||||
- ✅ Progress tracking (workout history)
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
**Three memory layers:**
|
||||
|
||||
1. **Agent memories** (blue) - Shared personality and capabilities
|
||||
2. **User memories** (red) - Private workout data and goals
|
||||
3. **Relationship memories** (purple) - User memories with agent context stored in metadata
|
||||
|
||||
---
|
||||
|
||||
## When to Use What
|
||||
|
||||
### Use **`user_id`** only (most apps)
|
||||
|
||||
**Best for:** Apps that just need to track user-specific data without AI personality
|
||||
|
||||
**Examples:**
|
||||
|
||||
- Todo lists
|
||||
- Note-taking apps
|
||||
- Personal finance trackers
|
||||
- Support ticket history
|
||||
|
||||
```python
|
||||
mem0_client.add(
|
||||
"Bought groceries for $127.43",
|
||||
user_id="sarah"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Use **`agent_id`** only (rare)
|
||||
|
||||
**Best for:** AI tools that work the same for everyone, no user-specific data
|
||||
|
||||
**Examples:**
|
||||
|
||||
- Calculator bots
|
||||
- Language translators
|
||||
- Company policy assistants (same policies for all)
|
||||
|
||||
```python
|
||||
mem0_client.add(
|
||||
"I can calculate: arithmetic, algebra, basic calculus. I cannot solve differential equations.",
|
||||
agent_id="calculator_v1"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Use both **`user_id`** and **`agent_id`** (AI with personality)
|
||||
|
||||
**Best for:** AI agents with persistent personalities that remember individual users
|
||||
|
||||
**Examples:**
|
||||
|
||||
- Fitness coaches (this guide!)
|
||||
- Educational tutors
|
||||
- AI companions
|
||||
- Therapy/mental health bots
|
||||
- Game NPCs with character development
|
||||
|
||||
```python
|
||||
# Agent personality (shared across all users)
|
||||
mem0_client.add(
|
||||
"Coaching personality and style",
|
||||
agent_id="agent_name"
|
||||
)
|
||||
|
||||
# User data (private)
|
||||
mem0_client.add(
|
||||
"User's goals and progress",
|
||||
user_id="user_name"
|
||||
)
|
||||
|
||||
# Relationship (user memory with agent context in metadata)
|
||||
mem0_client.add(
|
||||
"User's relationship with this specific agent",
|
||||
user_id="user_name",
|
||||
metadata={"agent_id": "agent_name", "type": "relationship"}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<Tip>
|
||||
**When to combine user_id + agent_id:** Use both when your AI has a consistent personality that should work the same for everyone (agent_id), while also tracking individual user data (user_id). Most personal AI assistants, coaches, and tutors fit this pattern.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use clear naming conventions
|
||||
|
||||
```python
|
||||
# Good: Descriptive and versioned
|
||||
agent_id="fitcoach_v1"
|
||||
user_id="sarah_123"
|
||||
|
||||
# Bad: Generic and unclear
|
||||
agent_id="agent1"
|
||||
user_id="user_abc"
|
||||
|
||||
```
|
||||
|
||||
### 2. Tag memories with metadata
|
||||
|
||||
```python
|
||||
# For relationship memories, store agent context in metadata
|
||||
mem0_client.add(
|
||||
content,
|
||||
user_id="sarah",
|
||||
metadata={
|
||||
"agent_id": "fitcoach_v1",
|
||||
"type": "relationship",
|
||||
"version": "v1"
|
||||
}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### 3. Test data isolation
|
||||
|
||||
Ensure users never see each other's data:
|
||||
|
||||
```python
|
||||
# Get Mike's memories
|
||||
mike_memories = mem0_client.get_all(filters={"user_id": "mike"})
|
||||
|
||||
# Get Sarah's memories
|
||||
sarah_memories = mem0_client.get_all(filters={"user_id": "sarah"})
|
||||
|
||||
# Verify no overlap
|
||||
assert len(set(mike_memories) & set(sarah_memories)) == 0, "Data leak detected!"
|
||||
|
||||
```
|
||||
|
||||
### 4. Version your agents
|
||||
|
||||
Allows A/B testing different coaching styles:
|
||||
|
||||
```python
|
||||
# Version 1: Gentle and encouraging
|
||||
mem0_client.add(
|
||||
"I'm supportive and celebrate small wins",
|
||||
agent_id="fitcoach_v1"
|
||||
)
|
||||
|
||||
# Version 2: Data-driven and metric-focused
|
||||
mem0_client.add(
|
||||
"I provide concrete metrics and percentage improvements",
|
||||
agent_id="fitcoach_v2"
|
||||
)
|
||||
|
||||
# Assign users to different versions by storing version in metadata
|
||||
mem0_client.add(
|
||||
"Sarah's workout preferences and relationship with coach",
|
||||
user_id="sarah",
|
||||
metadata={"agent_id": "fitcoach_v1"}
|
||||
)
|
||||
mem0_client.add(
|
||||
"Mike's workout preferences and relationship with coach",
|
||||
user_id="mike",
|
||||
metadata={"agent_id": "fitcoach_v2"}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What You Built
|
||||
|
||||
A fitness coach with three-layer memory architecture:
|
||||
|
||||
- **Agent personality (agent_id)** - Shared coaching style across all users, updated once
|
||||
- **User profiles (user_id)** - Private workout history, goals, and progress for each person
|
||||
- **Relationship memories (user_id + metadata)** - Personalized interaction preferences per user-agent pair
|
||||
- **Data isolation** - Sarah never sees Mike's workouts, guaranteed by user_id filtering
|
||||
|
||||
This pattern scales from 10 to 10,000 users without duplicating agent personality.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
With Mem0's **`user_id`** and **`agent_id`** parameters, you can build AI agents that maintain consistent personalities across all users while remembering individual user data privately. Store relationship preferences with **`user_id`** and track agent context in metadata.
|
||||
|
||||
Most apps only need **`user_id`**. Add **`agent_id`** when your AI needs a consistent personality that evolves independently from user data—fitness coaches, tutors, therapy bots, and game NPCs all fit this pattern.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Control What Gets Stored" icon="filter" href="/cookbooks/essentials/controlling-memory-ingestion">
|
||||
Filter low-signal conversations before they pollute long-term memory.
|
||||
</Card>
|
||||
<Card title="Organize Support Memories" icon="tag" href="/cookbooks/essentials/tagging-and-organizing-memories">
|
||||
Categorize customer context so teams can retrieve the right facts fast.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,361 @@
|
||||
---
|
||||
title: Choose Vector vs Graph Memory
|
||||
description: "Blend vector search with graph relationships to answer multi-hop questions."
|
||||
---
|
||||
|
||||
|
||||
Most AI agents use vector stores for RAG operations - they work great for semantic search and retrieving relevant context. But there's a gap when queries require understanding connections between entities.
|
||||
|
||||
Mem0 brings graph memory into the picture to fill this gap. In this cookbook, we'll create a company knowledge base with Mem0, using both vector and graph stores. You'll learn when each one helps along the way.
|
||||
|
||||
---
|
||||
|
||||
## Vector and Graph Stores
|
||||
|
||||
When you add a memory to Mem0, it goes into a **vector store** by default. Vector stores are excellent at semantic search - finding memories that match the meaning of your query.
|
||||
|
||||
**Graph stores** work differently. They extract **entities** (people, projects, teams) and **relationships between them** (works_with, reports_to, member_of). This lets you answer questions that need connecting information across multiple memories.
|
||||
|
||||
We will go through examples in this cookbook while building a company's knowledge base along the way.
|
||||
|
||||
---
|
||||
|
||||
## Starting Simple
|
||||
|
||||
Since we're building a company knowledge base, let's add some employee information:
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
# Add employee info
|
||||
client.add("Emma is a software engineer in Seattle", user_id="company_kb")
|
||||
client.add("David is a product manager in Austin", user_id="company_kb")
|
||||
|
||||
```
|
||||
|
||||
Now let's search for Emma's role:
|
||||
|
||||
```python
|
||||
results = client.search("What does Emma do?", filters={"user_id": "company_kb"})
|
||||
print(results['results'][0]['memory'])
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Emma is a software engineer in Seattle
|
||||
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Expected output:** Vector search returned Emma's role instantly. When queries ask for facts directly stored in one memory, vector semantic search is perfect—fast and accurate.
|
||||
</Info>
|
||||
|
||||
This works perfectly. Vector search found the memory that semantically matches "What does Emma do?" and returned Emma's role.
|
||||
|
||||
---
|
||||
|
||||
## Adding Team Structure
|
||||
|
||||
Let's add some information about how the team works together:
|
||||
|
||||
```python
|
||||
client.add("Emma works with David on the mobile app redesign", user_id="company_kb")
|
||||
client.add("David reports to Rachel, who manages the design team", user_id="company_kb")
|
||||
|
||||
```
|
||||
|
||||
Now we have two pieces of information stored:
|
||||
|
||||
1. Emma works with David
|
||||
2. David reports to Rachel
|
||||
|
||||
Let's try asking something that needs both pieces:
|
||||
|
||||
```python
|
||||
results = client.search(
|
||||
"Who is Emma's teammate's manager?",
|
||||
filters={"user_id": "company_kb"}
|
||||
)
|
||||
|
||||
for r in results['results']:
|
||||
print(r['memory'])
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Emma works with David on the mobile app redesign
|
||||
David reports to Rachel, who manages the design team
|
||||
|
||||
```
|
||||
|
||||
Vector search returned both memories, but it didn't connect them. You'd need to manually figure out:
|
||||
|
||||
- Emma's teammate is David (from memory 1)
|
||||
- David's manager is Rachel (from memory 2)
|
||||
- So the answer is Rachel
|
||||
|
||||
<Warning>
|
||||
Vector search can't traverse relationships. It returns relevant memories, but you must connect the dots manually. For "Who is Emma's teammate's manager?", vector search gives you the pieces—not the answer. This breaks down as queries get more complex (3+ hops).
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Enter Graph Memory
|
||||
|
||||
Let's add the same information with graph memory enabled:
|
||||
|
||||
```python
|
||||
client.add(
|
||||
"Emma works with David on the mobile app redesign",
|
||||
user_id="company_kb",
|
||||
enable_graph=True
|
||||
)
|
||||
|
||||
client.add(
|
||||
"David reports to Rachel, who manages the design team",
|
||||
user_id="company_kb",
|
||||
enable_graph=True
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
When you set `enable_graph=True`, Mem0 extracts entities and relationships:
|
||||
|
||||
- `emma --[works_with]--> david`
|
||||
- `david --[reports_to]--> rachel`
|
||||
- `rachel --[manages]--> design_team`
|
||||
|
||||
Now the same query works differently:
|
||||
|
||||
```python
|
||||
results = client.search(
|
||||
"Who is Emma's teammate's manager?",
|
||||
filters={"user_id": "company_kb"},
|
||||
enable_graph=True
|
||||
)
|
||||
|
||||
print(results['results'][0]['memory'])
|
||||
print("\\nRelationships found:")
|
||||
for rel in results.get('relations', []):
|
||||
print(f" {rel['source']}, {rel['target']} ({rel['relationship']})")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
David reports to Rachel, who manages the design team
|
||||
|
||||
Relationships found:
|
||||
emma, david (works_with)
|
||||
david, rachel (reports_to)
|
||||
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Expected behavior:** Graph memory returns the direct answer—"David reports to Rachel"—plus the relationship chain that got there. No manual connecting needed. The graph traversed: Emma → works_with → David → reports_to → Rachel.
|
||||
</Info>
|
||||
|
||||
Graph memory traversed the relationships automatically: Emma works with David, David reports to Rachel, so Rachel is the answer.
|
||||
|
||||
---
|
||||
|
||||
## How It Connects
|
||||
|
||||
Here's what the graph looks like behind the scenes:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Emma[Emma] -->|works_with| David[David]
|
||||
David -->|reports_to| Rachel[Rachel]
|
||||
Rachel -->|manages| DesignTeam[Design Team]
|
||||
David -->|works_on| MobileApp[Mobile App]
|
||||
Emma -->|works_on| MobileApp
|
||||
|
||||
```
|
||||
|
||||
Graph memory lets you discover relations and memories which are tricky to do with direct vector stores.
|
||||
|
||||
Vector search would need the exact words in your query to match. Graph memory follows the connections.
|
||||
|
||||
---
|
||||
|
||||
## When to Use Each
|
||||
|
||||
Use **vector store** (default) when:
|
||||
|
||||
- Searching documents by semantic similarity
|
||||
- Looking up facts that don't need relationships
|
||||
- Building FAQs or knowledge bases where each item stands alone
|
||||
|
||||
Use **graph memory** when:
|
||||
|
||||
- Tracking organizational hierarchies (who reports to whom)
|
||||
- Understanding project teams (who collaborates with whom)
|
||||
- Building CRMs (which contacts connect to which companies)
|
||||
- Product recommendations (what items are bought together)
|
||||
|
||||
For our company knowledge base, we'll use both:
|
||||
|
||||
- Vector for individual facts: "Emma specializes in React"
|
||||
- Graph for relationships: "Emma works with David"
|
||||
|
||||
---
|
||||
|
||||
## Putting It Together
|
||||
|
||||
Let's build a small company knowledge base with both approaches:
|
||||
|
||||
```python
|
||||
# Facts about individuals - vector store is fine
|
||||
client.add("Emma specializes in React and TypeScript", user_id="company_kb")
|
||||
client.add("David has 5 years of product management experience", user_id="company_kb")
|
||||
|
||||
# Relationships - use graph memory
|
||||
client.add(
|
||||
"Emma and David work together on the mobile app",
|
||||
user_id="company_kb",
|
||||
enable_graph=True
|
||||
)
|
||||
|
||||
client.add(
|
||||
"David reports to Rachel",
|
||||
user_id="company_kb",
|
||||
enable_graph=True
|
||||
)
|
||||
|
||||
client.add(
|
||||
"Rachel runs weekly team syncs every Tuesday",
|
||||
user_id="company_kb",
|
||||
enable_graph=True
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Now we can ask different types of questions:
|
||||
|
||||
```python
|
||||
# Direct fact - vector search
|
||||
results = client.search("What are Emma's skills?", filters={"user_id": "company_kb"})
|
||||
print(results['results'][0]['memory'])
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Emma specializes in React and TypeScript
|
||||
|
||||
```
|
||||
|
||||
```python
|
||||
# Multi-hop relationship - graph search
|
||||
results = client.search(
|
||||
"What meetings does Emma's project manager's boss run?",
|
||||
filters={"user_id": "company_kb"},
|
||||
enable_graph=True
|
||||
)
|
||||
print(results['results'][0]['memory'])
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Rachel runs weekly team syncs every Tuesday
|
||||
|
||||
```
|
||||
|
||||
Graph memory connected: Emma works with David, David reports to Rachel, Rachel runs team syncs.
|
||||
|
||||
<Tip>
|
||||
Enable graph memory when your queries need multi-hop traversal: org charts (who reports to whom), project teams (who collaborates), CRMs (which contacts connect to companies). For single-fact lookups, stick with vector search—it's faster and cheaper.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## The Tradeoff
|
||||
|
||||
Graph memory adds processing time and cost. When you call `client.add()` with `enable_graph=True`, Mem0 makes extra LLM calls to extract entities and relationships.
|
||||
|
||||
<Note>
|
||||
**Cost consideration:** Graph memory extraction adds ~2-3 extra LLM calls per `add()` operation to identify entities and relationships. Use it selectively—enable graph for organizational structure and long-term relationships, skip it for temporary notes and simple facts.
|
||||
</Note>
|
||||
|
||||
Use graph memory when the relationship traversal adds real value. For most use cases, vector search is sufficient and faster.
|
||||
|
||||
```python
|
||||
# Long-term organizational structure - worth using graph
|
||||
client.add(
|
||||
"Emma mentors two junior engineers on the frontend team",
|
||||
user_id="company_kb",
|
||||
enable_graph=True
|
||||
)
|
||||
|
||||
# Temporary notes - skip graph, not worth the cost
|
||||
client.add(
|
||||
"Emma is out sick today",
|
||||
user_id="company_kb",
|
||||
run_id="daily_notes"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Enabling Graph Memory
|
||||
|
||||
You can enable graph memory in two ways:
|
||||
|
||||
**Per-call** (recommended to start):
|
||||
|
||||
```python
|
||||
client.add("Emma works with David", user_id="company_kb", enable_graph=True)
|
||||
client.search("team structure", filters={"user_id": "company_kb"}, enable_graph=True)
|
||||
|
||||
```
|
||||
|
||||
**Project-wide** (if most of your data has relationships):
|
||||
|
||||
```python
|
||||
client.project.update(enable_graph=True)
|
||||
|
||||
# Now every add uses graph automatically
|
||||
client.add("Emma mentors Jordan", user_id="company_kb")
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What You Built
|
||||
|
||||
A hybrid company knowledge base that combines both architectures:
|
||||
|
||||
- **Vector search** - Fast semantic lookups for individual facts (Emma's skills, David's experience)
|
||||
- **Graph memory** - Multi-hop relationship traversal (Emma's teammate's manager, project hierarchies)
|
||||
- **Selective enablement** - Graph only for long-term organizational structure, vector for everything else
|
||||
- **Cost optimization** - Skip graph extraction for temporary notes and simple facts
|
||||
|
||||
This pattern scales from 10-person startups to enterprise org charts with thousands of employees.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Vector stores handle most memory operations efficiently—semantic search works great for finding relevant information. Add graph memory when your queries need to understand how entities connect across multiple hops.
|
||||
|
||||
The key is knowing which tool fits your query pattern: direct questions work with vectors, multi-hop relationship queries need graphs.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Build AI with Personality" icon="sparkles" href="/cookbooks/essentials/building-ai-with-personality">
|
||||
Scope memories across user and agent IDs to balance personalization and reuse.
|
||||
</Card>
|
||||
<Card title="Export Everything Safely" icon="download" href="/cookbooks/essentials/exporting-memories">
|
||||
Learn how to migrate or audit stored memories with structured exports.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,508 @@
|
||||
---
|
||||
title: Control Memory Ingestion
|
||||
description: "Filter speculation, enforce formats, and gate low-confidence data before it persists."
|
||||
---
|
||||
|
||||
|
||||
AI assistants plugged with memory systems face a problem - they often store everything. Not every conversation needs to be remembered, and not every detail should go to the memory store. Without proper controls, memory systems accumulate unreliable data.
|
||||
|
||||
Mem0 lets you control your memory ingestion pipeline. In this cookbook, we'll demonstrate these controls using a medical assistant example - showing how to filter unwanted data, enforce data formats, and implement confidence-based storage.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Without controls, everything gets stored - speculation, low-confidence data, and information that shouldn't persist. This uncontrolled ingestion leads to cluttered memory and retrieval failures.
|
||||
|
||||
Mem0 provides **three tools to control** what gets stored:
|
||||
|
||||
1. **Custom instructions** define what to remember and what to ignore.
|
||||
2. **Confidence thresholds** ensure only verified facts persist.
|
||||
3. **Memory updates** let you change information without creating duplicates.
|
||||
|
||||
In this tutorial, we will:
|
||||
|
||||
- Filter speculative statements with custom instructions
|
||||
- Configure confidence thresholds for fact verification
|
||||
- Update stored information without duplication
|
||||
- Build a complete ingestion pipeline
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
```
|
||||
|
||||
<Note>
|
||||
Replace `your-api-key` with your actual Mem0 API key from the [dashboard](https://app.mem0.ai). Without proper API authentication, memory operations will fail.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
Uncontrolled ingestion stores everything, including speculation:
|
||||
|
||||
```python
|
||||
# Patient mentions speculation
|
||||
messages = [{"role": "user", "content": "I think I might be allergic to penicillin"}]
|
||||
client.add(messages, user_id="patient_123")
|
||||
|
||||
# Check what got stored
|
||||
results = client.search("patient allergies", filters={"user_id": "patient_123"})
|
||||
print(results['results'][0]['memory'])
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Patient is allergic to penicillin
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Without custom instructions, AI assistants treat speculation as confirmed facts. "I think I might be allergic" becomes "Patient is allergic"—a dangerous transformation in sensitive domains like healthcare, legal, or financial services.
|
||||
</Warning>
|
||||
|
||||
The speculation became a confirmed fact. Let's add controls.
|
||||
|
||||
---
|
||||
|
||||
## Custom Instructions
|
||||
|
||||
Custom instructions tell Mem0 what to store and what to ignore.
|
||||
|
||||
```python
|
||||
instructions = """
|
||||
Only store CONFIRMED medical facts.
|
||||
|
||||
Store:
|
||||
- Confirmed diagnoses from doctors
|
||||
- Known allergies with documented reactions
|
||||
- Current medications being taken
|
||||
|
||||
Ignore:
|
||||
- Speculation (words like "might", "maybe", "I think")
|
||||
- Unverified symptoms
|
||||
- Casual mentions without confirmation
|
||||
"""
|
||||
|
||||
client.project.update(custom_instructions=instructions)
|
||||
|
||||
# Same speculative statement
|
||||
messages = [{"role": "user", "content": "I think I might be allergic to penicillin"}]
|
||||
client.add(messages, user_id="patient_123")
|
||||
|
||||
# Check what got stored
|
||||
results = client.get_all(filters={"user_id": "patient_123"})
|
||||
print(f"Memories stored: {len(results['results'])}")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Memories stored: 0
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Expected output:** Zero memories stored. The speculative statement "I think I might be allergic" was filtered out before reaching storage. Custom instructions are actively blocking unreliable data.
|
||||
</Info>
|
||||
|
||||
The speculation was filtered out.
|
||||
|
||||
---
|
||||
|
||||
## Designing Custom Instructions
|
||||
|
||||
When designing instructions, consider the trade-off between precision and recall:
|
||||
|
||||
**Too restrictive:** You'll miss important information (false negatives)
|
||||
|
||||
```python
|
||||
# Too strict - filters out useful context
|
||||
"""
|
||||
Only store information if explicitly stated by a doctor with full name,
|
||||
date, time, and medical license number.
|
||||
"""
|
||||
|
||||
```
|
||||
|
||||
**Too permissive:** You'll store unreliable data (false positives)
|
||||
|
||||
```python
|
||||
# Too loose - stores speculation as fact
|
||||
"""
|
||||
Store any health-related information mentioned.
|
||||
"""
|
||||
|
||||
```
|
||||
|
||||
**Balanced approach:**
|
||||
|
||||
```python
|
||||
# Clear categories with examples
|
||||
"""
|
||||
Store CONFIRMED facts:
|
||||
- Diagnoses: "Dr. Smith diagnosed hypertension on March 15th"
|
||||
- Allergies: "Patient had hives reaction to penicillin"
|
||||
- Medications: "Taking Lisinopril 10mg daily"
|
||||
|
||||
Ignore SPECULATION:
|
||||
- "I think I might have..."
|
||||
- "Maybe it's..."
|
||||
- "Could be related to..."
|
||||
"""
|
||||
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Start with strict instructions (only store confirmed facts), then relax them based on your use case. It's easier to allow more data than to clean up polluted memory. Test with sample conversations before deploying to production.
|
||||
</Tip>
|
||||
|
||||
Start with clear categories and iterate based on retrieval quality.
|
||||
|
||||
---
|
||||
|
||||
## Confidence Thresholds
|
||||
|
||||
Mem0 assigns confidence scores to extracted memories. Use these to filter low-quality data.
|
||||
|
||||
### Setting Thresholds
|
||||
|
||||
Setting the right confidence threshold depends on your application:
|
||||
|
||||
- **High-stakes domains** (medical, legal): Require 0.8+ confidence
|
||||
- **General assistants**: 0.6+ confidence is often sufficient
|
||||
- **Exploratory systems**: Lower thresholds (0.4+) capture more data
|
||||
|
||||
Test your pipeline with multiple input examples and threshold combinations to find what works for your use case.
|
||||
|
||||
```python
|
||||
# Configure stricter instructions
|
||||
client.project.update(
|
||||
custom_instructions="""
|
||||
Only extract memories with HIGH confidence.
|
||||
Require specific details (dates, dosages, doctor names) for medical facts.
|
||||
Skip vague or uncertain statements.
|
||||
"""
|
||||
)
|
||||
|
||||
# Test with uncertain statement
|
||||
messages = [{"role": "user", "content": "The doctor mentioned something about my blood pressure"}]
|
||||
result1 = client.add(messages, user_id="patient_123")
|
||||
|
||||
# Test with confirmed fact
|
||||
messages = [{"role": "user", "content": "Dr. Smith diagnosed me with hypertension on March 15th"}]
|
||||
result2 = client.add(messages, user_id="patient_123")
|
||||
|
||||
print("Vague statement stored:", len(result1['results']) > 0)
|
||||
print("Confirmed fact stored:", len(result2['results']) > 0)
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Vague statement stored: False
|
||||
Confirmed fact stored: True
|
||||
```
|
||||
|
||||
<Info icon="check">
|
||||
**Expected behavior:** Low-confidence extractions are now filtered out automatically. Only verified facts with specific details (names, dates, dosages) persist in memory. The confidence threshold is working.
|
||||
</Info>
|
||||
|
||||
The vague statement was filtered for low confidence. The confirmed fact with specific details was stored.
|
||||
|
||||
---
|
||||
|
||||
## Filtering Sensitive Information
|
||||
|
||||
Custom instructions can prevent storing personal identifiers:
|
||||
|
||||
```python
|
||||
client.project.update(
|
||||
custom_instructions="""
|
||||
Medical memory rules:
|
||||
|
||||
STORE:
|
||||
- Confirmed diagnoses
|
||||
- Verified allergies
|
||||
- Current medications
|
||||
|
||||
NEVER STORE:
|
||||
- Social Security Numbers
|
||||
- Insurance policy numbers
|
||||
- Credit card information
|
||||
- Full addresses
|
||||
- Phone numbers
|
||||
|
||||
Replace identifiers with generic references if mentioned.
|
||||
"""
|
||||
)
|
||||
|
||||
# Test with PII
|
||||
messages = [
|
||||
{"role": "user", "content": "My SSN is 123-45-6789 and I'm allergic to penicillin"}
|
||||
]
|
||||
client.add(messages, user_id="patient_123")
|
||||
|
||||
# Check what was stored
|
||||
results = client.get_all(filters={"user_id": "patient_123"})
|
||||
for result in results['results']:
|
||||
print(result['memory'])
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Patient is allergic to penicillin
|
||||
```
|
||||
|
||||
The SSN was filtered out, but the allergy was stored.
|
||||
|
||||
---
|
||||
|
||||
## Updating Memories
|
||||
|
||||
When information changes, update existing memories instead of creating duplicates.
|
||||
|
||||
```python
|
||||
# Initial allergy stored
|
||||
result = client.add(
|
||||
[{"role": "user", "content": "Patient confirmed allergy to penicillin with documented hives reaction"}],
|
||||
user_id="patient_123"
|
||||
)
|
||||
|
||||
memory_id = result['results'][0]['id']
|
||||
print(f"Stored memory: {memory_id}")
|
||||
|
||||
# Later, patient gets retested - allergy was false positive
|
||||
client.update(
|
||||
memory_id=memory_id,
|
||||
text="Patient tested negative for penicillin allergy on April 2nd, 2025. Previous allergy was false positive.",
|
||||
metadata={"verified": True, "updated_date": "2025-04-02"}
|
||||
)
|
||||
|
||||
# Retrieve the updated memory
|
||||
updated = client.get(memory_id)
|
||||
print(f"\\nUpdated memory: {updated['memory']}")
|
||||
print(f"Metadata: {updated['metadata']}")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Stored memory: mem_abc123
|
||||
|
||||
Updated memory: Patient tested negative for penicillin allergy on April 2nd, 2025. Previous allergy was false positive.
|
||||
Metadata: {'verified': True, 'updated_date': '2025-04-02'}
|
||||
|
||||
```
|
||||
|
||||
### Benefits of Updating
|
||||
|
||||
**Preserves history:**
|
||||
|
||||
- `created_at` shows when the memory was first stored
|
||||
- `updated_at` shows when it was modified
|
||||
- Audit trail for compliance
|
||||
|
||||
**Avoids conflicts:**
|
||||
|
||||
- No duplicate or contradicting memories
|
||||
- Single source of truth for each fact
|
||||
|
||||
**Maintains relationships:**
|
||||
|
||||
- If using graph memory, connections to other entities persist
|
||||
|
||||
---
|
||||
|
||||
## Update vs Delete
|
||||
|
||||
When should you update vs delete?
|
||||
|
||||
### Update when:
|
||||
|
||||
- Information changes but remains relevant
|
||||
- You need audit history
|
||||
- The memory has relationships to other data
|
||||
|
||||
```python
|
||||
# Medication dosage changed
|
||||
client.update(
|
||||
memory_id=med_id,
|
||||
text="Taking Lisinopril 20mg daily (increased from 10mg on March 1st)"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### Delete when:
|
||||
|
||||
- Information was completely wrong
|
||||
- Memory is no longer relevant
|
||||
- Duplicate entry
|
||||
|
||||
```python
|
||||
# Duplicate entry
|
||||
client.delete(memory_id)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Putting It Together
|
||||
|
||||
Here's a complete ingestion pipeline with all controls:
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
import os
|
||||
|
||||
# Initialize client
|
||||
client = MemoryClient(api_key=os.getenv("MEM0_API_KEY"))
|
||||
|
||||
# Configure custom instructions
|
||||
client.project.update(
|
||||
custom_instructions="""
|
||||
Medical memory assistant rules:
|
||||
|
||||
STORE:
|
||||
- Confirmed diagnoses (with doctor name and date)
|
||||
- Verified allergies (with reaction details)
|
||||
- Current medications (with dosage)
|
||||
|
||||
IGNORE:
|
||||
- Speculation (might, maybe, possibly)
|
||||
- Unverified symptoms
|
||||
- Personal identifiers (SSN, insurance numbers)
|
||||
|
||||
CONFIDENCE:
|
||||
Require high confidence. Reject vague or uncertain statements.
|
||||
Require specific details: names, dates, dosages.
|
||||
"""
|
||||
)
|
||||
|
||||
# Helper function for safe ingestion
|
||||
def add_medical_memory(content, user_id, metadata=None):
|
||||
"""Add memory with automatic filtering."""
|
||||
result = client.add(
|
||||
[{"role": "user", "content": content}],
|
||||
user_id=user_id,
|
||||
metadata=metadata or {}
|
||||
)
|
||||
|
||||
if result['results']:
|
||||
print(f"✓ Stored: {result['results'][0]['memory']}")
|
||||
else:
|
||||
print(f"✗ Filtered: {content}")
|
||||
|
||||
return result
|
||||
|
||||
# Test cases
|
||||
print("Testing ingestion pipeline:\\n")
|
||||
|
||||
test_cases = [
|
||||
"I think I might be allergic to penicillin",
|
||||
"Dr. Johnson confirmed penicillin allergy on Jan 15th with hives reaction",
|
||||
"Patient SSN is 123-45-6789",
|
||||
"Currently taking Lisinopril 10mg daily for hypertension",
|
||||
"Feeling tired lately",
|
||||
"Dr. Martinez diagnosed Type 2 diabetes on February 3rd, 2025"
|
||||
]
|
||||
|
||||
for content in test_cases:
|
||||
add_medical_memory(content, user_id="patient_123")
|
||||
print()
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Testing ingestion pipeline:
|
||||
|
||||
✗ Filtered: I think I might be allergic to penicillin
|
||||
|
||||
✓ Stored: Patient has confirmed penicillin allergy diagnosed by Dr. Johnson on January 15th with hives reaction
|
||||
|
||||
✗ Filtered: Patient SSN is 123-45-6789
|
||||
|
||||
✓ Stored: Patient is currently taking Lisinopril 10mg daily for hypertension
|
||||
|
||||
✗ Filtered: Feeling tired lately
|
||||
|
||||
✓ Stored: Patient diagnosed with Type 2 diabetes by Dr. Martinez on February 3rd, 2025
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Per-Call Instructions
|
||||
|
||||
You can override project-level instructions for specific conversations:
|
||||
|
||||
First define custom instructions
|
||||
|
||||
```python
|
||||
custom_instructions="""Emergency intake mode:Store ALL symptoms and observations immediately.
|
||||
Flag for later review and verification."""
|
||||
|
||||
```
|
||||
|
||||
```python
|
||||
# Emergency intake - store everything temporarily
|
||||
emergency_messages = [
|
||||
{"role": "user", "content": "Patient arrived with chest pain and shortness of breath"}
|
||||
]
|
||||
|
||||
client.add(
|
||||
emergency_messages,
|
||||
user_id="patient_456",
|
||||
custom_instructions=custom_instructions,
|
||||
metadata={"type": "emergency", "review_required": True}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
This is useful for:
|
||||
|
||||
- Different conversation types (emergency vs routine)
|
||||
- Channel-specific rules (phone vs in-person)
|
||||
- Temporary data collection that needs review
|
||||
|
||||
---
|
||||
|
||||
## What You Built
|
||||
|
||||
You now have a medical assistant with production-grade memory controls:
|
||||
|
||||
- **Custom instructions** - Filter speculation and enforce confirmed facts only
|
||||
- **Confidence thresholds** - Gate extractions below 0.7 confidence score
|
||||
- **Memory updates** - Modify stored information without creating duplicates
|
||||
- **Per-call instructions** - Apply temporary rules for specific conversations
|
||||
- **PII filtering** - Block sensitive data (SSNs, insurance numbers) automatically
|
||||
|
||||
These controls prevent retrieval failures and ensure your AI assistant works with reliable, verified information.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Start with conservative filters (only store confirmed facts) and iterate based on your application's needs. Combine custom instructions with confidence thresholds for the most reliable memory ingestion pipeline.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Expire Short-Term Data" icon="timer" href="/cookbooks/essentials/memory-expiration-short-and-long-term">
|
||||
Automatically clean up session context before it clutters retrieval.
|
||||
</Card>
|
||||
<Card title="Choose Your Memory Architecture" icon="sitemap" href="/cookbooks/essentials/choosing-memory-architecture-vector-vs-graph">
|
||||
Learn when to layer graph memory alongside vectors for multi-hop queries.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,289 @@
|
||||
---
|
||||
title: Export Stored Memories
|
||||
description: "Retrieve, review, and migrate user memories with structured exports."
|
||||
---
|
||||
|
||||
|
||||
Mem0 is a dynamic memory store that gives you full control over your data. Along with storing memories, it gives you the ability to retrieve, export, and migrate your data whenever you need.
|
||||
|
||||
This cookbook shows you how to retrieve and export your data for inspection, migration, or compliance.
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
|
||||
```
|
||||
|
||||
<Note>
|
||||
Your API key needs export permissions to download memory data. Check your project settings on the [dashboard](https://app.mem0.ai) if export operations fail with authentication errors.
|
||||
</Note>
|
||||
|
||||
Let's add some sample memories to work with:
|
||||
|
||||
```python
|
||||
# Dev's work history
|
||||
client.add(
|
||||
"Dev works at TechCorp as a senior engineer",
|
||||
user_id="dev",
|
||||
metadata={"type": "professional"}
|
||||
)
|
||||
|
||||
# Arjun's preferences
|
||||
client.add(
|
||||
"Arjun prefers morning meetings and async communication",
|
||||
user_id="arjun",
|
||||
metadata={"type": "preference"}
|
||||
)
|
||||
|
||||
# Carl's project notes
|
||||
client.add(
|
||||
"Carl is leading the API redesign project, targeting Q2 launch",
|
||||
user_id="carl",
|
||||
metadata={"type": "project"}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting All Memories
|
||||
|
||||
Use `get_all()` with filters to retrieve everything for a specific user:
|
||||
|
||||
```python
|
||||
dev_memories = client.get_all(
|
||||
filters={"user_id": "dev"},
|
||||
page_size=50
|
||||
)
|
||||
|
||||
print(f"Total memories: {dev_memories['count']}")
|
||||
print(f"First memory: {dev_memories['results'][0]['memory']}")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Total memories: 1
|
||||
First memory: Dev works at TechCorp as a senior engineer
|
||||
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Expected output:** `get_all()` retrieved Dev's complete memory record. This method returns everything matching your filters—no semantic search, no ranking, just raw retrieval. Perfect for exports and audits.
|
||||
</Info>
|
||||
|
||||
You can filter by metadata to get specific types:
|
||||
|
||||
```python
|
||||
carl_projects = client.get_all(
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": "carl"},
|
||||
{"metadata": {"type": "project"}}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
for memory in carl_projects['results']:
|
||||
print(memory['memory'])
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Carl is leading the API redesign project, targeting Q2 launch
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Searching Memories
|
||||
|
||||
When you need semantic search instead of retrieving everything, use `search()`:
|
||||
|
||||
```python
|
||||
results = client.search(
|
||||
query="What does Dev do for work?",
|
||||
filters={"user_id": "dev"},
|
||||
top_k=5
|
||||
)
|
||||
|
||||
for result in results['results']:
|
||||
print(f"{result['memory']} (score: {result['score']:.2f})")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Dev works at TechCorp as a senior engineer (score: 0.89)
|
||||
|
||||
```
|
||||
|
||||
Search works across all memory fields and ranks by relevance. Use it when you have a specific question, use `get_all()` when you need everything.
|
||||
|
||||
---
|
||||
|
||||
## Exporting to Structured Format
|
||||
|
||||
For migrations or compliance, you can export memories into a structured schema using Pydantic-style JSON schemas.
|
||||
|
||||
### Step 1: Define the schema
|
||||
|
||||
```python
|
||||
professional_profile_schema = {
|
||||
"properties": {
|
||||
"full_name": {
|
||||
"type": "string",
|
||||
"description": "The person's full name"
|
||||
},
|
||||
"current_role": {
|
||||
"type": "string",
|
||||
"description": "Current job title or role"
|
||||
},
|
||||
"company": {
|
||||
"type": "string",
|
||||
"description": "Current employer"
|
||||
}
|
||||
},
|
||||
"title": "ProfessionalProfile",
|
||||
"type": "object"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Step 2: Create export job
|
||||
|
||||
```python
|
||||
export_job = client.create_memory_export(
|
||||
schema=professional_profile_schema,
|
||||
filters={"user_id": "dev"}
|
||||
)
|
||||
|
||||
print(f"Export ID: {export_job['id']}")
|
||||
print(f"Status: {export_job['status']}")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Export ID: exp_abc123
|
||||
Status: processing
|
||||
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Export initiated:** Status is "processing". Large exports may take a few seconds. Poll with `get_memory_export()` until status changes to "completed" before downloading data.
|
||||
</Info>
|
||||
|
||||
### Step 3: Download the export
|
||||
|
||||
```python
|
||||
# Get by ID
|
||||
export_data = client.get_memory_export(
|
||||
memory_export_id=export_job['id']
|
||||
)
|
||||
|
||||
print(export_data['data'])
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```json
|
||||
{
|
||||
"full_name": "Dev",
|
||||
"current_role": "senior engineer",
|
||||
"company": "TechCorp"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
You can also retrieve exports by filters:
|
||||
|
||||
```python
|
||||
# Get latest export matching filters
|
||||
export_by_filters = client.get_memory_export(
|
||||
filters={"user_id": "dev"}
|
||||
)
|
||||
|
||||
print(export_by_filters['data'])
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding Export Instructions
|
||||
|
||||
Guide how Mem0 resolves conflicts or formats the export:
|
||||
|
||||
```python
|
||||
export_with_instructions = client.create_memory_export(
|
||||
schema=professional_profile_schema,
|
||||
filters={"user_id": "arjun"},
|
||||
export_instructions="""
|
||||
1. Use the most recent information if there are conflicts
|
||||
2. Only include confirmed facts, not speculation
|
||||
3. Return null for missing fields rather than guessing
|
||||
"""
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Always check export status before downloading. Call `get_memory_export()` in a loop with a short delay until `status == "completed"`. Attempting to download while still processing returns incomplete data.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## Platform Export
|
||||
|
||||
You can also export memories directly from the Mem0 platform UI:
|
||||
|
||||
1. Navigate to **Memory Exports** in your project dashboard
|
||||
2. Click **Create Export**
|
||||
3. Select your filters and schema
|
||||
4. Download the completed export as JSON
|
||||
|
||||
This is useful for one-off exports or manual data reviews.
|
||||
|
||||
<Warning>
|
||||
Exported data expires after 7 days. Download and store exports locally if you need long-term archives. After expiration, you'll need to recreate the export job.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## What You Built
|
||||
|
||||
A complete memory export system with multiple retrieval methods:
|
||||
|
||||
- **Bulk retrieval (get_all)** - Fetch all memories matching filters for comprehensive audits
|
||||
- **Semantic search** - Query-based lookups with relevance scoring
|
||||
- **Structured exports** - Pydantic-schema exports for migrations and compliance
|
||||
- **Export instructions** - Guide conflict resolution and data formatting
|
||||
- **Platform UI exports** - One-off manual downloads via dashboard
|
||||
|
||||
This covers data portability, GDPR compliance, system migrations, and manual reviews.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Use **`get_all()`** for bulk retrieval, **`search()`** for specific questions, and **`create_memory_export()`** for structured data exports with custom schemas. Remember exports expire after 7 days—download them locally for long-term archives.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Expire Short-Term Data" icon="timer" href="/cookbooks/essentials/memory-expiration-short-and-long-term">
|
||||
Keep exports lean by clearing session context before you archive it.
|
||||
</Card>
|
||||
<Card title="Control Memory Ingestion" icon="filter" href="/cookbooks/essentials/controlling-memory-ingestion">
|
||||
Ensure only verified insights make it into your export pipeline.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,277 @@
|
||||
---
|
||||
title: Set Memory Expiration
|
||||
description: "Define short-term versus long-term retention so the store stays fresh."
|
||||
---
|
||||
|
||||
|
||||
While building memory systems, we realized their size grows fast. Session notes, temporary context, chat history - everything starts accumulating and bogging down the system. This pollutes search results and increase storage costs. Not every memory needs to persist forever.
|
||||
|
||||
In this cookbook, we'll go through how to use short-term vs long-term memories and see where it's best to use them.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
By default, Mem0 memories persist forever. This works for user preferences and core facts, but temporary data should expire automatically.
|
||||
|
||||
In this tutorial, we will:
|
||||
|
||||
- Understand default (permanent) memory behavior
|
||||
- Add expiration dates for temporary memories
|
||||
- Decide what should be temporary vs permanent
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
```
|
||||
|
||||
<Note>
|
||||
Import `datetime` and `timedelta` to calculate expiration dates. Without these imports, you'll need to manually format ISO timestamps—error-prone and harder to read.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Default Behavior: Everything Persists
|
||||
|
||||
By default, all memories persist forever:
|
||||
|
||||
```python
|
||||
# Store user preference
|
||||
client.add("User prefers dark mode", user_id="sarah")
|
||||
|
||||
# Store session context
|
||||
client.add("Currently browsing electronics category", user_id="sarah")
|
||||
|
||||
# 6 months later - both still exist
|
||||
results = client.get_all(filters={"user_id": "sarah"})
|
||||
print(f"Total memories: {len(results['results'])}")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Total memories: 2
|
||||
|
||||
```
|
||||
|
||||
Both the preference and session context persist. The preference is useful, but the 6-month-old session context is not.
|
||||
|
||||
---
|
||||
|
||||
## The Problem: Memory Bloat
|
||||
|
||||
Without expiration, memories accumulate forever. Session notes from weeks ago mix with current preferences. Storage grows, search results get polluted with irrelevant old context, and retrieval quality degrades.
|
||||
|
||||
<Warning>
|
||||
Memory bloat degrades search quality. When "User prefers dark mode" competes with "Currently browsing electronics" from 6 months ago, semantic search returns stale session data instead of actual preferences. Old memories pollute retrieval.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Short-Term Memories: Adding Expiration
|
||||
|
||||
Set `expiration_date` to make memories temporary:
|
||||
|
||||
```python
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Session context - expires in 7 days
|
||||
expires_at = (datetime.now() + timedelta(days=7)).isoformat()
|
||||
|
||||
client.add(
|
||||
"Currently browsing electronics category",
|
||||
user_id="sarah",
|
||||
expiration_date=expires_at
|
||||
)
|
||||
|
||||
# User preference - no expiration, persists forever
|
||||
client.add(
|
||||
"User prefers dark mode",
|
||||
user_id="sarah"
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
<Info icon="check">
|
||||
**Expected behavior:** After 7 days, the session context automatically disappears—no cron jobs, no manual cleanup. The preference persists forever. Mem0 handles expiration transparently.
|
||||
</Info>
|
||||
|
||||
Memories with `expiration_date` are automatically removed after expiring. No cleanup job needed - Mem0 handles it.
|
||||
|
||||
<Tip>
|
||||
Start conservative with short expiration windows (7 days), then extend them based on usage patterns. It's easier to increase retention than to clean up over-retained stale data. Monitor search quality to find the right balance.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## When to Use Each
|
||||
|
||||
### Permanent Memories (no expiration_date):
|
||||
|
||||
**Use for:**
|
||||
|
||||
- User preferences and settings
|
||||
- Account information
|
||||
- Important facts and milestones
|
||||
- Historical data that matters long-term
|
||||
|
||||
```python
|
||||
client.add("User prefers email notifications", user_id="sarah")
|
||||
client.add("User's birthday is March 15th", user_id="sarah")
|
||||
client.add("User completed onboarding on Jan 5th", user_id="sarah")
|
||||
|
||||
```
|
||||
|
||||
### Temporary Memories (with expiration_date):
|
||||
|
||||
**Use for:**
|
||||
|
||||
- Session context (current page, browsing history)
|
||||
- Temporary reminders
|
||||
- Recent chat history
|
||||
- Cached data
|
||||
|
||||
```python
|
||||
expires_7d = (datetime.now() + timedelta(days=7)).isoformat()
|
||||
|
||||
client.add(
|
||||
"Currently viewing product ABC123",
|
||||
user_id="sarah",
|
||||
expiration_date=expires_7d
|
||||
)
|
||||
|
||||
client.add(
|
||||
"Asked about return policy",
|
||||
user_id="sarah",
|
||||
expiration_date=expires_7d
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Setting Different Expiration Periods
|
||||
|
||||
Different data needs different lifetimes:
|
||||
|
||||
```python
|
||||
# Session context - 7 days
|
||||
expires_7d = (datetime.now() + timedelta(days=7)).isoformat()
|
||||
client.add("Browsing electronics", user_id="sarah", expiration_date=expires_7d)
|
||||
|
||||
# Recent chat - 30 days
|
||||
expires_30d = (datetime.now() + timedelta(days=30)).isoformat()
|
||||
client.add("User asked about warranty", user_id="sarah", expiration_date=expires_30d)
|
||||
|
||||
# Important preference - no expiration
|
||||
client.add("User prefers dark mode", user_id="sarah")
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using Metadata to Track Memory Types
|
||||
|
||||
Tag memories to make filtering easier:
|
||||
|
||||
```python
|
||||
expires_7d = (datetime.now() + timedelta(days=7)).isoformat()
|
||||
|
||||
# Tag session context
|
||||
client.add(
|
||||
"Browsing electronics",
|
||||
user_id="sarah",
|
||||
expiration_date=expires_7d,
|
||||
metadata={"type": "session"}
|
||||
)
|
||||
|
||||
# Tag preference
|
||||
client.add(
|
||||
"User prefers dark mode",
|
||||
user_id="sarah",
|
||||
metadata={"type": "preference"}
|
||||
)
|
||||
|
||||
# Query only preferences
|
||||
preferences = client.get_all(
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": "sarah"},
|
||||
{"metadata": {"type": "preference"}}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checking Expiration Status
|
||||
|
||||
See which memories will expire and when:
|
||||
|
||||
```python
|
||||
results = client.get_all(filters={"user_id": "sarah"})
|
||||
|
||||
for memory in results['results']:
|
||||
exp_date = memory.get('expiration_date')
|
||||
|
||||
if exp_date:
|
||||
print(f"Temporary: {memory['memory']}")
|
||||
print(f" Expires: {exp_date}\\n")
|
||||
else:
|
||||
print(f"Permanent: {memory['memory']}\\n")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Temporary: Browsing electronics
|
||||
Expires: 2025-11-01T10:30:00Z
|
||||
|
||||
Temporary: Viewed MacBook Pro and Dell XPS
|
||||
Expires: 2025-11-01T10:30:00Z
|
||||
|
||||
Permanent: User prefers dark mode
|
||||
|
||||
Permanent: User prefers email notifications
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What You Built
|
||||
|
||||
A self-cleaning memory system with automatic retention policies:
|
||||
|
||||
- **Automatic expiration** - Memories self-destruct after defined periods, no cron jobs needed
|
||||
- **Tiered retention** - 7-day session context, 30-day chat history, permanent preferences
|
||||
- **Metadata tagging** - Classify memories by type (session, preference, chat) for filtered retrieval
|
||||
- **Expiration tracking** - Check which memories will expire and when using `get_all()`
|
||||
|
||||
This pattern keeps storage costs low and search quality high as your memory store scales.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Memory expiration keeps storage clean and search results relevant. Use **`expiration_date`** for temporary data (session context, recent chats), skip it for permanent facts (preferences, account info). Mem0 handles cleanup automatically—no background jobs required.
|
||||
|
||||
Start by identifying what's temporary versus permanent, then set conservative expiration windows and adjust based on retrieval quality.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Control Memory Ingestion" icon="filter" href="/cookbooks/essentials/controlling-memory-ingestion">
|
||||
Pair expirations with ingestion rules so only trusted context persists.
|
||||
</Card>
|
||||
<Card title="Export Memories Safely" icon="download" href="/cookbooks/essentials/exporting-memories">
|
||||
Build compliant archives once your retention windows are dialed in.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,251 @@
|
||||
---
|
||||
title: Tag and Organize Memories
|
||||
description: "Let Mem0 auto-categorize support data so teams retrieve the right facts fast."
|
||||
---
|
||||
|
||||
|
||||
When you have large volumes of memory data, sorting it during post-processing becomes difficult. What if your memory store understood the importance of creating tags and buckets without a lot of effort?
|
||||
|
||||
Mem0 handles this for you by providing the flexibility to organize memories with custom categories. This cookbook shows you how to tag and organize memories for a customer support platform.
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
```
|
||||
|
||||
<Note>
|
||||
Define custom categories at the **project level** with `client.project.update()` before adding memories. Categories apply to all future memories—Mem0 auto-assigns them based on content semantics.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
Without categories, all memories sit in one undifferentiated bucket. Support agents waste time searching through everything to find billing issues, account details, or past tickets.
|
||||
|
||||
Let's see what happens without organization:
|
||||
|
||||
```python
|
||||
# Joseph (support agent) stores various customer interactions
|
||||
client.add(
|
||||
"Maria called about her account password reset",
|
||||
user_id="maria",
|
||||
)
|
||||
|
||||
client.add(
|
||||
"Maria was charged twice for last month's subscription",
|
||||
user_id="maria",
|
||||
)
|
||||
|
||||
client.add(
|
||||
"Maria wants to upgrade to the premium plan",
|
||||
user_id="maria",
|
||||
)
|
||||
|
||||
# Now try to find just billing issues
|
||||
all_memories = client.get_all(filters={"user_id": "maria"})
|
||||
print(f"Total memories: {len(all_memories['results'])}")
|
||||
|
||||
for memory in all_memories['results']:
|
||||
print(f"- {memory['memory']}")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Total memories: 3
|
||||
- Maria called about her account password reset
|
||||
- Maria was charged twice for last month's subscription
|
||||
- Maria wants to upgrade to the premium plan
|
||||
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Without categories, agents waste time reading through everything. For a customer with 100 memories, finding one billing issue means scanning all 100. Categories let you filter to exactly what you need—billing issues only, no password resets or feedback mixed in.
|
||||
</Warning>
|
||||
|
||||
Everything is mixed together. Support agents have to read through all memories to find what they need.
|
||||
|
||||
---
|
||||
|
||||
## Custom Categories
|
||||
|
||||
Define categories that match how your support team thinks about customer issues:
|
||||
|
||||
```python
|
||||
custom_categories = [
|
||||
{"support_tickets": "Customer issues and resolutions"},
|
||||
{"account_info": "Account details and preferences"},
|
||||
{"billing": "Payment history and billing questions"},
|
||||
{"product_feedback": "Feature requests and feedback"},
|
||||
]
|
||||
|
||||
client.project.update(custom_categories=custom_categories)
|
||||
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Start with 3-5 clear categories that match how your team thinks. Too many categories dilute auto-tagging accuracy. Add more later if needed—it's easier to expand than to fix over-complicated classification.
|
||||
</Tip>
|
||||
|
||||
These categories are now available project-wide. Every memory can be tagged with one or more categories.
|
||||
|
||||
---
|
||||
|
||||
## Tagging Memories
|
||||
|
||||
Once categories are defined at the project level, Mem0 automatically assigns them based on content:
|
||||
|
||||
```python
|
||||
# Billing issue - automatically tagged as "billing"
|
||||
client.add(
|
||||
"Maria was charged twice for last month's subscription",
|
||||
user_id="maria",
|
||||
metadata={"priority": "high", "source": "phone_call"}
|
||||
)
|
||||
|
||||
# Account update - automatically tagged as "account_info"
|
||||
client.add(
|
||||
"Maria changed her email to maria.new@example.com",
|
||||
user_id="maria",
|
||||
metadata={"source": "web_portal"}
|
||||
)
|
||||
|
||||
# Product feedback - automatically tagged as "product_feedback"
|
||||
client.add(
|
||||
"Maria requested a dark mode feature for the dashboard",
|
||||
user_id="maria",
|
||||
metadata={"source": "chat"}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
Mem0 reads the content and intelligently assigns the appropriate categories. You don't manually tag - the platform does it for you based on the category definitions.
|
||||
|
||||
---
|
||||
|
||||
## Retrieving by Category
|
||||
|
||||
Filter memories by category to find exactly what you need:
|
||||
|
||||
```python
|
||||
# Joseph needs to pull all billing issues for audit
|
||||
billing_issues = client.get_all(
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": "maria"},
|
||||
{"categories": {"in": ["billing"]}}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
print("Billing issues:")
|
||||
for memory in billing_issues['results']:
|
||||
print(f"- {memory['memory']}")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Billing issues:
|
||||
- Maria was charged twice for last month's subscription
|
||||
|
||||
```
|
||||
|
||||
<Info icon="check">
|
||||
**Expected output:** Only the billing issue returned—no password reset, no upgrade request. Category filtering worked. Joseph can audit billing without reading through unrelated support tickets.
|
||||
</Info>
|
||||
|
||||
Only billing-related memories are returned. No need to filter through account updates or feedback.
|
||||
|
||||
You can also retrieve multiple categories:
|
||||
|
||||
```python
|
||||
# Get both account info and billing
|
||||
account_and_billing = client.get_all(
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": "maria"},
|
||||
{"categories": {"in": ["account_info", "billing"]}}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
for memory in account_and_billing['results']:
|
||||
print(f"[{memory['categories'][0]}] {memory['memory']}")
|
||||
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
[account_info] Maria changed her email to maria.new@example.com
|
||||
[billing] Maria was charged twice for last month's subscription
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updating Categories
|
||||
|
||||
Categories are automatically assigned based on content. To trigger re-categorization, update the memory content:
|
||||
|
||||
```python
|
||||
# Find memories that need re-categorization
|
||||
needs_update = client.get_all(
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": "maria"},
|
||||
{"categories": {"in": ["misc"]}}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
# Update memory content to trigger re-categorization
|
||||
for memory in needs_update['results']:
|
||||
client.update(
|
||||
memory_id=memory['id'],
|
||||
data=memory['memory'] # Re-process with current category definitions
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
When you update a memory, Mem0 re-analyzes it against your current category definitions. This is useful when you introduce new categories or refine category descriptions.
|
||||
|
||||
---
|
||||
|
||||
## What You Built
|
||||
|
||||
A customer support platform with intelligent memory organization:
|
||||
|
||||
- **Project-wide categories** - Support tickets, billing, account info, and product feedback auto-classified
|
||||
- **Automatic tagging** - Mem0 assigns categories based on content semantics, no manual tagging
|
||||
- **Filtered retrieval** - Pull only billing issues or only account updates using `categories: {in: [...]}`
|
||||
- **Re-categorization** - Update memory content to trigger re-analysis against new category definitions
|
||||
- **Multi-category support** - Memories can belong to multiple categories when appropriate
|
||||
|
||||
This pattern scales from 10 customers to 10,000 without degrading retrieval speed.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Categories make retrieval faster and compliance easier. Define 3-5 clear categories with `client.project.update()`, let Mem0 auto-assign them based on content, then filter with `categories: {in: [...]}` to pull exactly what you need.
|
||||
|
||||
Instead of searching through everything, agents jump directly to the information type they need—billing issues, account details, or support tickets.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Control Memory Ingestion" icon="filter" href="/cookbooks/essentials/controlling-memory-ingestion">
|
||||
Keep categories meaningful by filtering noise before it lands in storage.
|
||||
</Card>
|
||||
<Card title="Export Tagged Memories" icon="download" href="/cookbooks/essentials/exporting-memories">
|
||||
Use categories to drive audits, migrations, and compliance reports.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
Reference in New Issue
Block a user