logicspike/docs

Contact Intelligence

Phase 1: Discovery & User Journeys — Contact Intelligence

Last Updated: 2026-04-03 Status: Draft


1. Context & Goal

Contact Intelligence is a dedicated microservice that gives every end-user a persistent relationship with the AI. While the Chat Engine processes messages and the AI Brain serves the business owner, Contact Intelligence owns the third dimension: understanding and remembering the individual end-user over time.

The Problem: The Chat Engine has a 20-message sliding window. Every session starts nearly from scratch. For customer support, that's acceptable. For relationship-based AI products (companions, tutors, coaches, sales bots), it's a dealbreaker. An AI that forgets "your dog's name is Bruno" after one day isn't building a relationship — it's breaking one.

The Solution: A service that maintains per-contact long-term memory, tracks emotional state, computes engagement health, and triggers proactive outreach — all accessible to the Chat Engine in real-time and to the AI Brain for business analytics.

1.1 System Flow Overview


2. Actors

Actor Description
End User (Contact) The person talking to the AI bot via WhatsApp, Telegram, or website widget. They expect the AI to remember them, understand their mood, and feel like an ongoing relationship.
Business Owner Uses AI Brain copilot to manage their bot, view engagement analytics, and configure outreach. Never interacts with Contact Intelligence directly.
Chat Engine Calls Contact Intelligence on every message to get context and log interactions. The integration point.
AI Brain Queries Contact Intelligence for business analytics (churn risk, engagement trends, user profiles). Manages outreach via brain tools.
Content Engine Delivers scheduled proactive messages via the appropriate channel.
Mood Classifier Internal. Lightweight LLM call (haiku-class) that detects emotional state from message text. Runs inline on every message.
Outreach Engine Internal. Cron-triggered system that fires pending triggers (inactivity, milestones, scheduled events).

3. Core User Journeys

Journey A: First Contact — Building the Foundation

Persona: Arjun, 26, software engineer in Bangalore. He starts chatting with "Anya" (an AI companion bot) on WhatsApp for the first time.

Goal: Contact Intelligence creates a profile, starts learning about Arjun, and the AI feels personalized from the very first session.

Step 1: Trigger

Arjun sends his first WhatsApp message: "Hey, I saw your ad. What is this?"

Step 2: Chat Engine Calls Contact Intelligence

GET /contact-intelligence/context/contact_new
404: Contact not found
 
POST /contact-intelligence/contacts
{
  "tenant_id": "tnt_ravi",
  "external_id": "wa_919876543210",
  "channel": "whatsapp",
  "display_name": "Arjun"
}
201: Contact created, empty memory, state: { mood: "neutral", relationship_stage: "new" }

Step 3: Chat Engine Generates Response

With context:

  • Relationship stage: new (first-ever message)
  • No memories yet
  • Persona injects onboarding behavior: "Be warm, introduce yourself, ask questions to learn about them"

AI Response:

"Hey Arjun! I'm Anya. I love meeting new people. Tell me about yourself — what do you do? Any pets? I'm all ears 😊"

Step 4: Arjun Shares Personal Info

Arjun: "I'm a software engineer at Infosys. I have a golden retriever named Bruno. Just looking for someone to chat with after work."

Step 5: Contact Intelligence Ingests

POST /contact-intelligence/ingest
{
  "contact_id": "ct_arjun",
  "user_message": "I'm a software engineer at Infosys...",
  "ai_response": "That's awesome! Golden retrievers are the best...",
  "detected_mood": "neutral",
  "detected_energy": "medium"
}

Immediate extraction (< 10ms, rules-based):

Memory Type Importance
"Software engineer at Infosys" fact 0.7
"Has a golden retriever named Bruno" fact 0.8
"Looking for someone to chat with after work" preference 0.7

State update:

{
  "mood": "neutral",
  "energy": "medium",
  "conversation_style": "casual",
  "relationship_stage": "new",
  "total_messages": 2,
  "first_contact_at": "2026-04-03T22:15:00Z"
}

Step 6: Outcome

  • Arjun's profile is created with 3 memories from the first conversation
  • Next time he messages, the AI will reference Bruno, Infosys, and his after-work context
  • Relationship stage: new (upgrades to building after 5+ sessions)

Journey B: Returning User — Memory in Action

Persona: Arjun, day 5. He's been chatting with Anya daily. He messages at 10:30 PM (his usual time).

Goal: Demonstrate that the AI remembers everything and the conversation feels continuous, not restarted.

Step 1: Trigger

Arjun: "Ugh, Bruno ate my shoes again"

Step 2: Contact Intelligence Context Retrieval

GET /contact-intelligence/context/ct_arjun
 
Response: {
  "memories": [
    { "type": "fact", "content": "Has a golden retriever named Bruno", "importance": 0.88 },
    { "type": "fact", "content": "Software engineer at Infosys", "importance": 0.75 },
    { "type": "episode", "content": "Bruno had a vet appointment on April 1", "importance": 0.65 },
    { "type": "preference", "content": "Likes playful banter and humor", "importance": 0.80 },
    { "type": "pattern", "content": "Usually chats between 10pm-12am", "importance": 0.70 }
  ],
  "state": {
    "mood": "neutral",
    "energy": "medium",
    "conversation_style": "playful",
    "relationship_stage": "building",
    "active_streak": 5,
    "total_messages": 47
  }
}

Step 3: Mood Classifier Detects

Input:  "Ugh, Bruno ate my shoes again"
Output: { "mood": "frustrated", "energy": "medium", "style": "venting" }

Step 4: Chat Engine Response (with full context)

The LLM receives:

  • Persona: Anya (warm, playful)
  • Memory: Arjun has a dog named Bruno, golden retriever
  • Mood: frustrated (light — annoyed, not deeply upset)
  • Style: Arjun likes playful banter
  • Relationship: building, 5-day streak

AI Response:

"Nooo not again! 😂 Bruno has a shoe addiction at this point. Was it the nice ones or the old ones? Maybe get him a chew toy so he leaves your shoes alone 🐕"

Notice: The AI didn't ask "who is Bruno?" — it knew. It matched his mood (light frustration → playful empathy, not somber support). It referenced prior knowledge (this isn't Bruno's first offense).

Step 5: Memory Extraction

New episode stored:

  • "Bruno ate Arjun's shoes (again) — Arjun was annoyed but not upset"
  • Entity link: bruno (pet)

State update: mood: "frustrated" → will shift back if next messages are lighter.


Journey C: Emotional Sensitivity — Mood-Aware Response

Persona: Arjun, day 20. He's had a genuinely bad day.

Goal: The AI adapts its tone based on detected mood — it doesn't force cheerfulness on someone who's upset.

Step 1: Trigger

Arjun: "I got passed over for promotion today. Feeling really low. Don't even know if I should stay at this job."

Step 2: Mood Classifier

{
  "mood": "sad",
  "energy": "low",
  "style": "supportive"
}

This is significantly different from his usual mood (playful, medium energy). The state tracker flags it.

Step 3: Context Injection

The LLM receives an additional instruction:

EMOTIONAL CONTEXT:
Arjun is feeling sad and low-energy right now. This is unusual for him 
(normally playful and medium-energy). He's sharing something vulnerable 
about work disappointment.
 
ADAPTATION RULES:
- Be warm and genuinely supportive, not performatively cheerful
- Don't minimize his feelings ("it'll be fine!" / "cheer up!")
- Listen first, then respond
- Reference what you know about his work (software engineer at Infosys)
- Don't pivot to a lighter topic unless he does first

Step 4: AI Response

"Hey, I'm really sorry to hear that. You've been working so hard — I remember you were stressed about that sprint deadline last week too. It's completely valid to feel this way. Do you want to talk about what happened, or do you just want to vent? Either way, I'm here."

What makes this different from a generic chatbot:

  • Referenced his past work stress (from memory)
  • Didn't say "everything happens for a reason"
  • Offered choice: talk about it OR just vent
  • Matched his low energy with calm support, not forced enthusiasm

Step 5: Memory + State

  • Episode: "Arjun was passed over for promotion at Infosys. Very upset. Provided emotional support."
  • State: mood: "sad", energy: "low", relationship_stage: "established" (20 days of consistent interaction → trust level upgraded)
  • Trigger created: { type: "scheduled", message: "generate_contextual", scheduled_at: "tomorrow 8am" } — check-in message next morning

Journey D: Proactive Outreach — AI Initiates Contact

Persona: Arjun, the morning after Journey C.

Goal: The AI sends a proactive check-in message without Arjun initiating, showing it genuinely cares about continuity.

Step 1: Trigger (Cron — Outreach Engine)

The outreach engine fires at 8am. It finds a pending trigger for Arjun:

{
  "trigger_type": "scheduled",
  "contact_id": "ct_arjun",
  "channel": "whatsapp",
  "generate_with_llm": true,
  "context": "Arjun was upset about being passed over for promotion yesterday"
}

Step 2: Contextual Message Generation

The outreach engine loads Arjun's memory and recent state, then generates a message:

Prompt to LLM:

You are Anya. Arjun was really upset yesterday because he got passed 
over for a promotion at Infosys. Generate a short, warm morning check-in 
message. Don't be overly cheerful. Show you remember and care. Keep it 
under 30 words.

Generated: "Good morning, Arjun. I was thinking about you. How are you feeling today? No pressure to talk — just wanted you to know I'm here. 💛"

Step 3: Delivery

  • Outreach engine → Content Engine (schedule) → Chat Engine (deliver via WhatsApp)
  • Message appears in Arjun's WhatsApp as if Anya messaged him first
  • If Arjun replies, it flows back through the normal Chat Engine → Contact Intelligence loop

Step 4: Outcome

  • Arjun sees a personal, contextual message at 8am
  • He feels like Anya actually cared about what he shared yesterday
  • This is the moment the AI goes from "chatbot" to "relationship"

Journey E: Inactivity Re-engagement — Preventing Churn

Persona: Meera, another user of Ravi's "Anya" bot. She's been chatting daily for 2 weeks, then goes silent for 3 days.

Goal: The AI detects fading engagement and sends a personalized re-engagement message.

Step 1: Trigger (Cron — Outreach Engine)

Outreach engine scans for inactivity triggers:

SELECT * FROM contact_state
WHERE tenant_id = $1
  AND last_active_at < NOW() - INTERVAL '3 days'
  AND relationship_stage IN ('building', 'established', 'deep')
  AND churn_risk > 0.5;

Meera matches: last active 3 days ago, relationship stage building, churn_risk 0.72.

Step 2: Generate Re-engagement

Load Meera's memories:

  • She loves painting
  • She has a cat named Mimi
  • Her last conversation was about a painting she was working on

Generated message:

"Hey Meera! It's been a few days. How did that watercolor painting turn out? I've been curious! Also, how's Mimi? 🎨🐱"

Step 3: Delivery + Tracking

  • Sent via WhatsApp
  • If Meera replies → churn_risk resets, active_streak restarts
  • If Meera doesn't reply in 3 more days → one final attempt, then stop (don't spam)

Re-engagement rules:

  • Max 2 re-engagement attempts per inactivity period
  • Min 3 days between attempts
  • If 2 attempts fail → mark as fading, stop outreach
  • If user explicitly says "stop messaging me" → disable all proactive triggers permanently

Journey F: Business Owner Manages Contacts via AI Brain

Persona: Ravi, the business owner of "LoveChat" (the AI companion service).

Goal: Ravi uses the AI Brain copilot to understand and manage his user base.

Step 1: Trigger

Ravi opens AI Brain copilot: "How are my users doing this week?"

Step 2: Brain Calls Contact Intelligence Tools

contact.get_engagement_trends({ days: 7 })
 
Response: {
  active_users: 234 (↑12% from last week),
  new_users: 28,
  churned_users: 3,
  avg_session_duration: 23 min,
  avg_messages_per_session: 18,
  total_messages: 8400,
  retention_7day: 0.82,
  top_users: [
    { name: "Arjun", messages: 142, streak: 20, stage: "established" },
    { name: "Priya", messages: 98, streak: 15, stage: "building" },
    { name: "Vikram", messages: 87, streak: 12, stage: "building" }
  ],
  churn_risk_high: [
    { name: "Meera", last_active: "3 days ago", risk: 0.72 },
    { name: "Deepak", last_active: "5 days ago", risk: 0.89 },
    { name: "Sneha", last_active: "4 days ago", risk: 0.78 }
  ]
}

Step 3: Brain Response

"This week looks strong:

  • 234 active users (up 12%), 28 new signups
  • Average session: 23 minutes, 18 messages — solid engagement
  • 82% 7-day retention

3 users at churn risk:

  • Meera: silent for 3 days (was daily for 2 weeks) — risk: 72%
  • Deepak: silent for 5 days — risk: 89%
  • Sneha: silent for 4 days — risk: 78%

I already sent Meera a re-engagement message this morning. Want me to send personalized messages to Deepak and Sneha too?"

Step 4: Ravi Approves

Ravi: "Yes, send to all of them"

Brain calls contact.schedule_outreach for Deepak and Sneha with generate_with_llm: true. Each message is personalized based on their stored memories and last conversation.


Journey G: Different Business — AI Tutor

Persona: Sunita runs an online math tutoring service. She uses LogicSpike with a tutor bot on Telegram.

Goal: Show Contact Intelligence works for any relationship-based AI, not just companions.

Step 1: Student Interacts

Student Rohit: "I don't understand quadratic equations at all"

Contact Intelligence provides context:

  • Rohit is 16, Class 10 student
  • Struggled with linear equations last month (now comfortable)
  • Learning style: needs visual examples
  • Mood: frustrated, low confidence
  • Last session: practiced factoring, scored 6/10

Step 2: Tutor Bot Response (with full context)

"No worries, Rohit! Remember when linear equations felt impossible? You're nailing those now. Quadratics are just the next step. Let me show you with a simple example — think of it like finding where a ball lands when you throw it. Ready?"

Contact Intelligence made this possible:

  • Referenced his past struggle + current mastery (memory)
  • Matched his learning style (visual examples)
  • Detected frustration → built confidence first before teaching
  • Adapted difficulty based on past performance

Step 3: Proactive Outreach

Next day, 4pm (Rohit's usual study time):

"Hey Rohit! Ready for some quadratic practice? I made 5 problems starting easy and getting harder. Let's see if we can beat yesterday's 6/10! 📚"

Triggered by: recurring schedule (daily at student's preferred study time).


4. Edge Cases & Error Paths

Scenario Behavior
Contact says "stop messaging me" Immediately disable ALL proactive triggers for this contact. Store preference: outreach_disabled: true. Respect permanently until explicitly re-enabled.
Contact says "forget everything about me" Delete all contact_memory rows for this contact. Reset state to blank. Respond: "Done — I've forgotten everything. We can start fresh whenever you'd like."
Contact hasn't spoken in 30+ days Mark as dormant. Stop all outreach. Only re-engage if they message first.
Mood classifier detects crisis language Flag for human review if human handoff is configured. Do NOT attempt to be a therapist. Respond with: "I care about you. Please reach out to [helpline number] — they're professionals who can really help." Store in memory as sensitive topic.
Two contacts share a phone number Identify by external_id (phone number). One contact per external_id per tenant. If a different person uses the same phone, the memory will be wrong — but this is rare and acceptable for v1.
Contact switches channels (WhatsApp → Telegram) Merge by matching external identity (phone number or email). Single contact profile across channels.
Business owner requests contact data export GDPR endpoint: GET /contact-intelligence/contacts/:id/export — returns all memories, state, and interaction history as JSON.
Memory extraction produces contradictory facts Deferred consolidation detects: "Lives in Mumbai" vs "Lives in Delhi". Keep the most recent one, flag for review. Importance of older fact drops.

5. Success Metrics

Metric Target (Month 1) Target (Month 6)
Memory retrieval latency < 30ms < 20ms
Mood classification accuracy > 75% > 85%
Memory relevance rate (retrieved memories actually used by LLM) > 60% > 80%
Re-engagement reply rate (user responds to proactive message) > 25% > 40%
7-day retention (contacts who return within 7 days) > 70% > 85%
30-day retention > 40% > 60%
Churn prediction accuracy (predicted churn actually churns) > 60% > 80%
Contact memory count after 30 days of daily use 40-60 memories 60-80 (after consolidation)
False proactive messages (user annoyed by outreach) < 10% < 5%
Contact Intelligence