Bot WA AI dengan Memory/Context

Cara membuat bot AI WhatsApp yang ingat percakapan sebelumnya. Context management, long-term memory. Tutorial lengkap!

Bot WA AI dengan Memory/Context
Bot WA AI dengan Memory/Context

Bot yang ingat = Customer experience lebih baik!

Bot tanpa memory terasa kaku dan frustasi. Dengan memory management yang baik, bot bisa ingat preferensi, history pembelian, dan konteks percakapan.


Jenis Memory

🧠 TIPE MEMORY:

1. SHORT-TERM (Conversation)
   • Dalam satu sesi chat
   • Context window AI
   • Hilang setelah sesi

2. LONG-TERM (Persistent)
   • Antar sesi/hari
   • Disimpan di database
   • Preferensi, history

3. EPISODIC (Events)
   • Kejadian spesifik
   • "Kemarin order dress merah"
   • Timestamp-based

4. SEMANTIC (Facts)
   • Fakta tentang user
   • "Customer VIP", "Suka warna pink"
   • Tidak time-bound

Short-Term Memory

Basic Context Window:

javascript

// Simpan conversation history per user
const conversations = new Map();

async function chat(userId, userMessage) {
    // Get or create conversation
    if (!conversations.has(userId)) {
        conversations.set(userId, []);
    }
    
    const history = conversations.get(userId);
    
    // Add user message
    history.push({ role: 'user', content: userMessage });
    
    // Keep only last N messages (context window)
    const recentHistory = history.slice(-20);
    
    // Call AI with context
    const response = await openai.chat.completions.create({
        model: 'gpt-4o',
        messages: [
            { role: 'system', content: SYSTEM_PROMPT },
            ...recentHistory
        ]
    });
    
    const aiResponse = response.choices[0].message.content;
    
    // Add AI response to history
    history.push({ role: 'assistant', content: aiResponse });
    
    // Update stored history
    conversations.set(userId, history.slice(-20));
    
    return aiResponse;
}

Dengan Timeout Reset:

javascript

const conversationTimeout = 30 * 60 * 1000; // 30 menit

const conversations = new Map();

function getConversation(userId) {
    const conv = conversations.get(userId);
    
    if (!conv) {
        return createNewConversation(userId);
    }
    
    // Check if expired
    if (Date.now() - conv.lastActivity > conversationTimeout) {
        // Reset conversation tapi keep user data
        return createNewConversation(userId, conv.userData);
    }
    
    // Update last activity
    conv.lastActivity = Date.now();
    return conv;
}

function createNewConversation(userId, userData = null) {
    const conv = {
        userId,
        messages: [],
        lastActivity: Date.now(),
        userData: userData || {}
    };
    conversations.set(userId, conv);
    return conv;
}

Long-Term Memory (Database)

Schema:

javascript

// MongoDB Schema
const userMemorySchema = {
    oderId
userId: String,          // WhatsApp ID
    
    // Profile info
    profile: {
        name: String,
        location: String,
        language: String,
        joinedAt: Date
    },
    
    // Preferences
    preferences: {
        favoriteCategories: [String],
        preferredColors: [String],
        sizeInfo: Object,
        communicationStyle: String
    },
    
    // Purchase history
    purchaseHistory: [{
        orderId: String,
        products: [String],
        total: Number,
        date: Date
    }],
    
    // Important notes
    notes: [{
        content: String,
        addedAt: Date,
        source: String  // 'ai' or 'human'
    }],
    
    // Conversation summaries
    conversationSummaries: [{
        date: Date,
        summary: String,
        topics: [String]
    }],
    
    // Stats
    stats: {
        totalOrders: Number,
        totalSpent: Number,
        lastContact: Date,
        lastPurchase: Date
    }
};

Save & Retrieve:

javascript

async function getUserMemory(userId) {
    let memory = await db.userMemory.findOne({ oderId
userId });
    
    if (!memory) {
        memory = await db.userMemory.create({
            oderId
userId,
            profile: {},
            preferences: {},
            purchaseHistory: [],
            notes: [],
            conversationSummaries: [],
            stats: {
                totalOrders: 0,
                totalSpent: 0,
                lastContact: new Date()
            }
        });
    }
    
    return memory;
}

async function updateUserMemory(userId, updates) {
    await db.userMemory.updateOne(
        { oderId
userId },
        { 
            $set: {
                ...updates,
                'stats.lastContact': new Date()
            }
        }
    );
}

// Add note about user
async function addUserNote(userId, note) {
    await db.userMemory.updateOne(
        { oderId
userId },
        { 
            $push: { 
                notes: {
                    content: note,
                    addedAt: new Date(),
                    source: 'ai'
                }
            }
        }
    );
}

AI Memory Integration

System Prompt dengan Memory:

javascript

async function buildPromptWithMemory(userId) {
    const memory = await getUserMemory(userId);
    
    let memoryContext = '';
    
    if (memory.profile.name) {
        memoryContext += `Nama customer: ${memory.profile.name}\n`;
    }
    
    if (memory.preferences.favoriteCategories?.length) {
        memoryContext += `Kategori favorit: ${memory.preferences.favoriteCategories.join(', ')}\n`;
    }
    
    if (memory.purchaseHistory?.length) {
        const recentPurchases = memory.purchaseHistory.slice(-3);
        memoryContext += `Pembelian terakhir:\n`;
        recentPurchases.forEach(p => {
            memoryContext += `- ${p.products.join(', ')} (${p.date})\n`;
        });
    }
    
    if (memory.notes?.length) {
        const recentNotes = memory.notes.slice(-5);
        memoryContext += `Notes:\n`;
        recentNotes.forEach(n => {
            memoryContext += `- ${n.content}\n`;
        });
    }
    
    const systemPrompt = `${BASE_SYSTEM_PROMPT}

INFORMASI CUSTOMER INI:
${memoryContext || 'Customer baru, belum ada history.'}

Gunakan informasi di atas untuk personalisasi response.
Jika ada info baru tentang customer (nama, preferensi, dll),
sebutkan untuk disimpan dengan format: [SAVE: kategori: info]`;

    return systemPrompt;
}

Extract & Save New Info:

javascript

async function processAndSaveMemory(userId, aiResponse, userMessage) {
    // Check for save commands from AI
    const saveMatches = aiResponse.matchAll(/\[SAVE: (\w+): (.+?)\]/g);
    
    for (const match of saveMatches) {
        const [, category, info] = match;
        
        switch (category) {
            case 'name':
                await updateUserMemory(userId, { 'profile.name': info });
                break;
            case 'preference':
                await db.userMemory.updateOne(
                    { oderId
userId },
                    { $addToSet: { 'preferences.favoriteCategories': info } }
                );
                break;
            case 'note':
                await addUserNote(userId, info);
                break;
        }
    }
    
    // Auto-detect info from conversation
    await autoExtractInfo(userId, userMessage, aiResponse);
}

async function autoExtractInfo(userId, userMessage, aiResponse) {
    // Simple pattern matching
    const namePattern = /nama saya (\w+)/i;
    const nameMatch = userMessage.match(namePattern);
    if (nameMatch) {
        await updateUserMemory(userId, { 'profile.name': nameMatch[1] });
    }
    
    // Or use AI for extraction
    const extraction = await openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: [{
            role: 'user',
            content: `Extract any personal info from this message. 
            Return JSON with fields: name, location, preferences, notes.
            If no info found, return empty object.
            
            Message: "${userMessage}"`
        }],
        response_format: { type: 'json_object' }
    });
    
    const extracted = JSON.parse(extraction.choices[0].message.content);
    
    if (Object.keys(extracted).length > 0) {
        // Save extracted info
        if (extracted.name) {
            await updateUserMemory(userId, { 'profile.name': extracted.name });
        }
        // ... other fields
    }
}

Conversation Summarization

javascript

async function summarizeConversation(userId) {
    const conv = conversations.get(userId);
    if (!conv || conv.messages.length < 10) return;
    
    // Summarize dengan AI
    const summary = await openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: [{
            role: 'user',
            content: `Summarize this customer conversation in 2-3 sentences.
            Include: main topics, outcomes, any important info about customer.
            
            Conversation:
            ${conv.messages.map(m => `${m.role}: ${m.content}`).join('\n')}`
        }]
    });
    
    // Save summary
    await db.userMemory.updateOne(
        { oderId
userId },
        {
            $push: {
                conversationSummaries: {
                    date: new Date(),
                    summary: summary.choices[0].message.content,
                    topics: []
                }
            }
        }
    );
    
    // Clear short-term memory but keep summary reference
    conv.messages = [];
}

// Run summarization periodically or on conversation end
setInterval(async () => {
    for (const [userId, conv] of conversations.entries()) {
        const timeSinceLastActivity = Date.now() - conv.lastActivity;
        
        // If inactive for 1 hour, summarize
        if (timeSinceLastActivity > 60 * 60 * 1000) {
            await summarizeConversation(userId);
        }
    }
}, 15 * 60 * 1000); // Check every 15 minutes

Contoh Percakapan dengan Memory

=== PERCAKAPAN PERTAMA ===

👤: Halo, mau tanya soal dress

🤖: Hai! Selamat datang di [BRAND]! 😊
    Mau cari dress untuk acara apa kak?
    [Customer baru]

👤: Untuk kondangan, nama saya Dina btw

🤖: Hai Kak Dina! Salam kenal ya 😊
    Untuk kondangan, kami punya beberapa pilihan:
    [SAVE: name: Dina]
    ...

=== PERCAKAPAN KEDUA (Seminggu kemudian) ===

👤: Hai lagi

🤖: Hai Kak Dina! Welcome back! 😊
    Gimana dress kondangannya kemarin, cocok?
    Ada yang bisa dibantu hari ini?
    [Memory: Dina, pernah cari dress kondangan]

👤: Bagus! Sekarang mau cari dress casual

🤖: Wah senang dengarnya cocok kak! 💕
    Untuk casual dress, warna favoritnya apa kak?
    Kemarin kan ambil yang [warna X] ya.
    [Memory: Previous purchase reference]

Best Practices

DO ✅

- Simpan info yang relevan saja
- Update memory secara berkala
- Gunakan memory untuk personalisasi
- Summarize long conversations
- Provide option to "forget"

DON'T ❌

- Simpan semua tanpa filter
- Unlimited context window (costly)
- Expose memory to user directly
- Store sensitive info (password, etc)
- Forget to handle privacy

FAQ

Berapa lama simpan memory?

Active customers: unlimited. Inactive > 1 tahun: bisa archive/delete.

Apakah bikin AI lebih mahal?

Sedikit. Tapi memory context biasanya < 500 tokens, worth it untuk better experience.


Kesimpulan

Memory = Personalized experience!

No MemoryWith Memory
GenericPersonalized
Ask againRemember
TransactionalRelationship

Setup AI Memory →


Artikel Terkait