Skip to main content

Memory System

The conversation engine uses a 3-tier memory system managed by conversation-memory.service.ts. This allows the AI to maintain context across conversations, remember customer preferences, and personalize responses.


Architecture


Three Memory Tiers

Short-Term Memory

PropertyValue
ScopeCurrent conversation thread
ContentsRecent messages, current intent, active context, topic
LifetimeDuration of the conversation thread
StorageConversationMemory with tier = SHORT_TERM
ExpiresWhen thread is resolved or after 24h of inactivity

Purpose: Keeps track of what's happening right now in the conversation. Prevents the AI from repeating questions or losing context mid-thread.

Medium-Term Memory

PropertyValue
ScopePer audience profile (across threads)
ContentsRecent interaction history, topic preferences, resolved issues, sentiment trends
Lifetime30 days, refreshed on each new interaction
StorageConversationMemory with tier = MEDIUM_TERM
Expires30 days after last interaction

Purpose: Remembers recent context about a customer across multiple conversations. Allows the AI to reference "last time you contacted us about X" or know about recent issues.

Long-Term Memory

PropertyValue
ScopePer audience profile (persistent)
ContentsCustomer preferences, purchase history, key facts, communication style, VIP status
LifetimePermanent (until manually deleted)
StorageConversationMemory with tier = LONG_TERM
ExpiresNever (unless workspace is deleted)

Purpose: Stores persistent knowledge about a customer. Enables deep personalization -- the AI knows their preferred language, past purchases, communication preferences, etc.


Memory Retrieval

When generating a reply, the context builder pulls from all three tiers:

// conversation-memory.service.ts
async function getContext(audienceNodeId: string, threadId: string) {
const [shortTerm, mediumTerm, longTerm] = await Promise.all([
prisma.conversationMemory.findMany({
where: { audienceNodeId, tier: 'SHORT_TERM', threadId },
orderBy: { createdAt: 'desc' },
take: 10,
}),
prisma.conversationMemory.findMany({
where: { audienceNodeId, tier: 'MEDIUM_TERM' },
orderBy: { createdAt: 'desc' },
take: 5,
}),
prisma.conversationMemory.findMany({
where: { audienceNodeId, tier: 'LONG_TERM' },
}),
]);

return { shortTerm, mediumTerm, longTerm };
}

Memory Updates

Memory is updated at key points in the conversation lifecycle:

EventMemory Updates
New message receivedShort-term: add message to thread context
Reply sent (auto or manual)Short-term: update conversation state
Thread resolvedMedium-term: store resolution summary; short-term: clear
Purchase detectedLong-term: update purchase history
Preference expressedLong-term: store preference ("prefers English", "likes discounts")
Intent classifiedShort-term: store current intent; medium-term: update interaction pattern
// After generating a reply
await conversationMemory.store(audienceNodeId, 'SHORT_TERM', {
threadId,
lastIntent: classification.intent,
lastReply: generatedReply.text,
timestamp: new Date(),
});

// After resolving a thread
await conversationMemory.store(audienceNodeId, 'MEDIUM_TERM', {
topic: thread.topic,
resolution: thread.resolution,
sentiment: thread.overallSentiment,
resolvedAt: new Date(),
});

How Memory Improves Responses

ScenarioWithout MemoryWith Memory
Customer asks about order status"Can you provide your order number?""Your order #12345 from last Tuesday is being shipped today."
Returning customer with complaintGeneric apology"I'm sorry you're experiencing another issue. Last time we resolved your shipping concern -- is this related?"
Customer mentions preferenceIgnored"Since you prefer text updates, I'll make sure we text you when your order ships."
Repeat question in same threadAnswers from scratch"As I mentioned earlier, the return window is 30 days."

Database Model

model ConversationMemory {
id String @id @default(cuid())
audienceNodeId String
audienceNode AudienceNode @relation(fields: [audienceNodeId], references: [id], onDelete: Cascade)
tier MemoryTier // SHORT_TERM, MEDIUM_TERM, LONG_TERM
threadId String? // Only for SHORT_TERM
data Json // Flexible JSON storage
expiresAt DateTime? // Null for LONG_TERM
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@index([audienceNodeId, tier])
@@index([expiresAt])
}

enum MemoryTier {
SHORT_TERM
MEDIUM_TERM
LONG_TERM
}

Memory Cleanup

Expired memories are cleaned up periodically:

TierCleanup Strategy
Short-termDeleted when thread is resolved or after 24h inactivity
Medium-termDeleted when expiresAt passes (30 days after last interaction)
Long-termNever automatically deleted

Cross-Reference