Vitto Memory & Intelligence Architecture
How Vitto remembers users across browsers, devices, and sessions
Vitto Memory & Intelligence Architecture
Overview
Vitto's intelligence comes from three layers:
- User Profile — who the user IS (from MongoDB
userscollection) - User Memories — what Vitto has LEARNED about the user (from
user_agent_memories) - System Intelligence — how Vitto operates (from
vitto-minddatabase)
┌──────────────────────────────────────────────────────────────────────┐
│ VITTO CHAT REQUEST │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ Frontend sends: │
│ ├── prompt (enriched) → goes to Anthropic as user message │
│ ├── rawUserMessage → used for memory extraction │
│ ├── systemPrompt → persona + context │
│ ├── userId (from JWT) → identity key for everything │
│ └── history[] → conversation continuity │
│ │
│ Backend enriches with: │
│ ├── User Profile ← MongoDB `users` collection │
│ ├── User Memories ← MongoDB `user_agent_memories` │
│ └── Knowledge Distribution ← Multi-tier knowledge system │
│ │
│ After response, backend: │
│ ├── 1. detectAndSaveExplicitMemory() ← Regex, immediate save │
│ └── 2. extractAndSaveMemories() ← LLM extraction, async │
│ │
└──────────────────────────────────────────────────────────────────────┘
Memory Creation Pipeline
Step 1: Explicit Intent Detection (immediate, no LLM)
When the user says "remember this" or states a fact explicitly, the backend uses regex patterns to detect and save immediately.
Patterns detected:
"remember this/that, I'm ..."→ saves as explicit_identity fact"my name is ..."→ saves as full_name fact"I'm the/a [role]"→ saves as job_title fact"remember that ..."→ saves as user_stated_fact
Location: ChatService.detectAndSaveExplicitMemory()
Confidence: 1.0 (explicit statement)
Latency: ~1ms (no LLM call)
Step 2: LLM Extraction (async, after response)
After every chat response, a cheap LLM call (Claude Haiku, 300 tokens max) analyzes the user's raw message and extracts structured facts.
What it extracts:
| Type | Example | Key |
|---|---|---|
fact |
"I'm the CEO" | job_title |
preference |
"I prefer dark mode" | ui_theme |
skill |
"5 years of Angular" | angular_experience |
personality |
"I like direct answers" | communication_style |
CRITICAL: The extraction receives rawUserMessage (the clean user input), NOT the enriched prompt that contains system instructions. Without this separation, the extraction LLM sees 500+ tokens of noise and returns [].
Location: ChatService.extractAndSaveMemories()
Confidence: 0.7-1.0 (based on explicitness)
Latency: ~500ms (async, doesn't block chat response)
Step 3: Storage
Both steps save to user_agent_memories in gograb-api via UserAgentService.saveMemory().
Upsert behavior: If a memory with the same userId + agentId + key exists, it gets updated (not duplicated) and its reinforcementCount increments.
Memory Recall Pipeline
On every chat request, before the Anthropic API call:
- Load User Profile from
userscollection → name, email, roles, subscription, spaces, account age - Load Agent Memories from
user_agent_memories→ facts, preferences, skills, personality - Inject into system prompt → Anthropic sees the full context
Location: ChatService.loadUserContext()
Storage Architecture
What goes WHERE (and why)
| Data | Database | Collection | Why |
|---|---|---|---|
| User profile | gograb-api |
users |
User-owned data |
| What Vitto knows about user | gograb-api |
user_agent_memories |
User-owned data (GDPR) |
| User privacy preferences | gograb-api |
user_preferences |
User-owned data |
| Vitto's operational state | vitto-mind |
multiple | Vitto system data |
| AI agent decisions/patterns | ai-agents |
multiple | AI system data |
| Vector embeddings | ai-memory |
agent_embeddings |
Performance isolation |
What does NOT go in user_agent_memories
- Conversation history (stored locally via flash-persistence)
- Vitto's strategies/skills (stored in
vitto-mind) - Agent performance metrics (stored in
ai-agents)
Frontend Architecture
Three Chat Entry Points
| Component | Library | Used By |
|---|---|---|
AIPanelComponent |
@libs-ai/vitto-hub |
gograb-space sidebar |
VittoChatShellComponent |
@libs-ai/vitto-chat |
gograb-ai app |
VittoHubComponent |
@libs-ai/gograb-ai-lib |
inline chat widgets |
All three MUST:
- Send
rawUserMessagealongside the enrichedprompt - Include
userId(from JWT/TenantContext/auth_backup) - NOT create memories client-side (memory creation is backend-only)
Local Persistence (flash-persistence)
Chat messages are persisted locally via VittoChatPersistenceService using autoFlashState (IndexedDB). This provides:
- Conversation history survives page refresh
- Session management (create, switch, delete conversations)
- Scoped per userId + spaceId
This is LOCAL persistence only. It does NOT replace the backend memory system.
Debugging
Check what memories exist
# Via API (requires JWT)
curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/ai-api/chat/memories
# Via MongoDB directly
db.useragentmemories.find({ userId: ObjectId("YOUR_USER_ID"), agentId: "vitto" })
Backend logs to watch
[MEMORY] Explicit save: job_title="CEO and founder" for user 679abc...
[MEMORY] Extracting from: "remember this, I'm the founder and CEO" for user 679abc...
[MEMORY] Extraction result: [{"type":"fact","key":"role","value":"founder and CEO",...}]
[MEMORY] Extracted and saved 1/1 memories for user 679abc...: role
If memories are NOT being saved, check:
- Is userId a valid ObjectId? Look for:
Invalid userId format (not a valid ObjectId) - Is UserAgentService injected? Look for:
Memory pipeline skipped: userId=..., userAgentService=false - Is the user blocked? Check
user_preferences.agentPrivacy.blockedAgents - Did extraction return empty? Look for:
No memories extracted from message - Is the Anthropic API key set? Look for:
ANTHROPIC_API_KEY not set
Bugs Fixed (Session 105)
| Bug | Root Cause | Fix |
|---|---|---|
| Extraction got polluted prompt | Frontend embedded system prompt + history into prompt field |
Added rawUserMessage field, backend uses it for extraction |
| Two memory systems, different DBs | Frontend VittoMonitorService.createMemory() saved to vitto-mind; backend recall read user_agent_memories |
Removed frontend memory creation entirely; all memory ops are backend-only |
| Non-ObjectId userId crashed save | getOrCreateLocalUserId() generates dev-{timestamp} |
UserAgentService now validates ObjectId format before operations |
| No explicit "remember" detection | Relied on async LLM extraction for direct statements | Added detectAndSaveExplicitMemory() with regex patterns for immediate save |
| Silent failures | Extraction errors logged at debug level |
Upgraded to warn level with [MEMORY] prefix |
| No debugging endpoint | Could not verify memories existed | Added GET /ai-api/chat/memories |
Last Updated: February 11, 2026 Session: 105