Memory Systems
The platform uses a 3-tier memory architecture to give agents access to user context at different time scales. This enables personalization that improves over time while maintaining responsive, context-aware interactions within a single session.
Architecture Overview
| Tier | Scope | Lifetime | Purpose |
|---|---|---|---|
| Profile Memory | User | Permanent | Long-term preferences and attributes |
| Pattern Memory | User | Long-lived | Learned behavioral patterns over time |
| Session Memory | Session | Ephemeral | Short-term context for current interaction |
Each tier serves a distinct role. Together, they give agents a complete picture of who the user is, how they have behaved historically, and what they are doing right now.
┌─────────────────────────────────────────────────┐
│ Agent Execution │
│ │
│ input_data = { │
│ "request": { ... }, ← User input │
│ "profile_memory": { ... }, ← Tier 1 │
│ "pattern_memory": { ... }, ← Tier 2 │
│ "session_memory": { ... }, ← Tier 3 │
│ } │
└─────────────────────────────────────────────────┘Profile Memory
Profile Memory stores long-term, relatively stable user preferences and attributes. It represents what the system knows about the user as a person, typically populated through onboarding flows, explicit preference settings, and inferred attributes that have been confirmed over time.
Contents
- Account attributes — user preferences, saved configurations, notification settings
- Domain preferences — preferred models, output formats, quality thresholds
- Budget range — spending comfort zone
- Provider affinities — preferred and disliked providers
- Constraints — rate limits, compliance requirements, cost caps
Access Pattern
from src.services.user_memory.service import UserMemoryService
memory_service = UserMemoryService()
# Load profile memory for a user
profile = await memory_service.get_profile_memory(user_id="user_456")
# Profile data structure
# {
# "default_model": "gpt-4o",
# "preferred_formats": ["json", "markdown"],
# "budget_range": {"min": 50, "max": 200},
# "provider_preferences": {"liked": ["openai", "anthropic"], "disliked": ["deprecated-v1"]},
# "quality_thresholds": {"accuracy": "high", "latency": "medium", "cost": "low"}
# }Updates
Profile memory is updated through explicit user actions (updating preferences in settings) or through confirmed inferences from pattern analysis:
await memory_service.update_profile_memory(
user_id="user_456",
updates={"preferred_formats": ["json", "markdown", "csv"]}
)Pattern Memory
Pattern Memory captures behavioral patterns learned from the user’s interactions over time. Unlike Profile Memory (which stores explicit preferences), Pattern Memory represents inferred tendencies that emerge from repeated behavior.
Contents
- Interaction patterns — time of day preferences, session frequency, engagement depth
- Decision patterns — what the user tends to accept or reject from results
- Preference evolution — how preferences shift over time
- Category affinity scores — weighted preferences across task categories
Access Pattern
# Load pattern memory for a user
patterns = await memory_service.get_pattern_memory(user_id="user_456")
# Pattern data structure
# {
# "avg_session_duration_min": 12,
# "peak_activity_hours": [10, 14, 20],
# "acceptance_rate_by_category": {
# "extraction": 0.72,
# "analysis": 0.58,
# "reporting": 0.41
# },
# "preference_drift": {
# "from": "verbose",
# "toward": "concise",
# "confidence": 0.68
# },
# "cost_sensitivity": 0.65
# }Learning
Pattern memory is updated automatically after each significant interaction. The system analyzes user behavior and updates pattern scores:
await memory_service.record_interaction(
user_id="user_456",
interaction_type="result_accepted",
context={"category": "extraction", "cost": 0.0045, "format": "json"}
)
# Pattern memory is recalculated in the backgroundSession Memory
Session Memory holds short-term context for the current interaction. It is created when a session starts and discarded when the session ends. This gives agents awareness of what has already happened in the current conversation without permanent storage overhead.
Contents
- Conversation history — messages exchanged in the current session
- Items viewed — results or content the user has seen in this session
- Current intent — what the user appears to be looking for right now
- Temporary preferences — session-specific overrides (e.g., “use only gpt-4o for this session”)
- Agent outputs — results from agents already executed in this session
Access Pattern
# Load session memory
session = await memory_service.get_session_memory(session_id="sess_789")
# Session data structure
# {
# "started_at": "2026-02-08T14:00:00Z",
# "messages": [
# {"role": "user", "content": "Analyze this dataset"},
# {"role": "assistant", "content": "Here are the results..."}
# ],
# "items_viewed": ["run_001", "run_002"],
# "current_intent": "data_analysis",
# "session_overrides": {"model_filter": "gpt-4o"},
# "agent_results": {
# "extractor": {"entities": [...]},
# "analyzer": {"results": [...]}
# }
# }Lifecycle
# Create a new session
session_id = await memory_service.create_session(user_id="user_456")
# Update session context during interaction
await memory_service.update_session_memory(
session_id=session_id,
updates={"current_intent": "data_analysis", "items_viewed": ["run_001"]}
)
# Session is automatically cleaned up after expiration (default: 30 minutes of inactivity)Integrating Memory with Agents
Memory is injected into agents through the input_data dictionary. The orchestrator loads all three memory tiers before pipeline execution and includes them alongside the user’s request:
class PersonalizedAgent(BaseAgent):
async def execute(self, input_data: Dict) -> AsyncIterator[AgentEvent]:
yield AgentEvent(type="agent_start", agent=self.name)
# Access all three memory tiers
profile = input_data.get("profile_memory", {})
patterns = input_data.get("pattern_memory", {})
session = input_data.get("session_memory", {})
# Use memory to personalize the prompt
user_context = f"""
User preferences: {profile.get('preferred_formats', [])}
Budget: {profile.get('budget_range', {})}
Recent behavior: acceptance rate for extraction is {patterns.get('acceptance_rate_by_category', {}).get('extraction', 'unknown')}
Current session intent: {session.get('current_intent', 'general analysis')}
"""
result = await self.llm.generate(
prompt=f"{user_context}\n\nAnalyze: {input_data['request']}",
model=self.model
)
yield AgentEvent(type="agent_complete", agent=self.name, result=result)Memory Persistence
| Tier | Storage | Retention |
|---|---|---|
| Profile Memory | MongoDB (user document) | Permanent, user-deletable |
| Pattern Memory | MongoDB (analytics collection) | Long-lived, decays over time |
| Session Memory | Redis | 30-minute inactivity timeout |
Profile and pattern memory are stored in MongoDB for durability and queryability. Session memory uses Redis for low-latency access and automatic expiration.