Bot WA AI dengan Sentiment Analysis

Cara membuat bot AI WhatsApp dengan sentiment analysis. Deteksi emosi customer, prioritas komplain. Tutorial lengkap!

Bot WA AI dengan Sentiment Analysis
Bot WA AI dengan Sentiment Analysis

Sentiment analysis = Pahami emosi customer!

Bot yang bisa deteksi emosi customer dapat respond lebih tepat, prioritaskan yang marah, dan escalate otomatis ke human.


Kenapa Sentiment Analysis?

📊 MANFAAT:

1. DETECT ANGRY CUSTOMERS
   → Prioritas tinggi
   → Escalate ke senior

2. ADAPT RESPONSE TONE
   → Marah: Lebih empati
   → Happy: Lebih casual

3. EARLY WARNING
   → Detect sebelum escalate
   → Proactive intervention

4. ANALYTICS
   → Track customer satisfaction
   → Identify pain points

Sentiment Categories

😊 SENTIMENT LEVELS:

POSITIVE:
- Senang, puas, terima kasih
- "Bagus banget!", "Makasih ya!"
- Score: 0.6 to 1.0

NEUTRAL:
- Informatif, bertanya
- "Mau tanya harga", "Ada warna lain?"
- Score: 0.4 to 0.6

NEGATIVE:
- Kecewa, marah, frustrasi
- "Kok lama!", "Mengecewakan!"
- Score: 0.0 to 0.4

URGENT/CRITICAL:
- Sangat marah, ancaman
- "Mau report!", "Penipuan!"
- Score: < 0.2 + keywords

Implementation

Using OpenAI for Sentiment:

javascript

async function analyzeSentiment(message) {
    const response = await openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: [{
            role: 'user',
            content: `Analyze the sentiment of this Indonesian message.
            
Return JSON with:
- sentiment: "positive", "neutral", "negative", or "critical"
- score: 0.0 to 1.0 (0 = very negative, 1 = very positive)
- emotion: primary emotion detected
- urgency: "low", "medium", "high"
- keywords: negative keywords found (if any)

Message: "${message}"

Return only valid JSON, no explanation.`
        }],
        response_format: { type: 'json_object' }
    });
    
    return JSON.parse(response.choices[0].message.content);
}

// Example results:
// "Terima kasih, pelayanannya bagus!"
// → { sentiment: "positive", score: 0.9, emotion: "grateful", urgency: "low" }

// "Kok pesanan saya belum sampai? Sudah seminggu!"
// → { sentiment: "negative", score: 0.3, emotion: "frustrated", urgency: "high" }

// "PENIPUAN! Mau saya laporkan ke polisi!"
// → { sentiment: "critical", score: 0.1, emotion: "angry", urgency: "high", keywords: ["penipuan", "laporkan"] }

Keyword-Based (Faster/Cheaper):

javascript

const sentimentKeywords = {
    positive: [
        'terima kasih', 'makasih', 'thanks', 'bagus', 'mantap',
        'puas', 'senang', 'suka', 'keren', 'recommended',
        'cepat', 'ramah', 'helpful', 'love', '👍', '❤️', '😊'
    ],
    negative: [
        'kecewa', 'marah', 'kesal', 'lambat', 'lama',
        'jelek', 'buruk', 'parah', 'zonk', 'nyesel',
        'bohong', 'tipu', 'rugi', 'kapok', '😡', '😤', '💢'
    ],
    critical: [
        'penipuan', 'penipu', 'scam', 'report', 'laporkan',
        'polisi', 'hukum', 'viral', 'sebarkan'
    ]
};

function quickSentimentAnalysis(message) {
    const lowerMessage = message.toLowerCase();
    
    let positiveScore = 0;
    let negativeScore = 0;
    let criticalFound = false;
    const foundKeywords = [];
    
    // Check critical first
    for (const keyword of sentimentKeywords.critical) {
        if (lowerMessage.includes(keyword)) {
            criticalFound = true;
            foundKeywords.push(keyword);
        }
    }
    
    if (criticalFound) {
        return {
            sentiment: 'critical',
            score: 0.1,
            urgency: 'high',
            keywords: foundKeywords
        };
    }
    
    // Check positive/negative
    for (const keyword of sentimentKeywords.positive) {
        if (lowerMessage.includes(keyword)) positiveScore++;
    }
    
    for (const keyword of sentimentKeywords.negative) {
        if (lowerMessage.includes(keyword)) {
            negativeScore++;
            foundKeywords.push(keyword);
        }
    }
    
    // Calculate final sentiment
    const total = positiveScore + negativeScore;
    if (total === 0) {
        return { sentiment: 'neutral', score: 0.5, urgency: 'low' };
    }
    
    const score = positiveScore / total;
    
    if (score > 0.6) {
        return { sentiment: 'positive', score, urgency: 'low' };
    } else if (score < 0.4) {
        return { 
            sentiment: 'negative', 
            score, 
            urgency: negativeScore > 2 ? 'high' : 'medium',
            keywords: foundKeywords
        };
    }
    
    return { sentiment: 'neutral', score, urgency: 'low' };
}

Integrated Chat Handler

javascript

async function handleMessageWithSentiment(userId, userMessage) {
    // 1. Analyze sentiment
    const sentiment = await analyzeSentiment(userMessage);
    
    // 2. Store sentiment data
    await storeSentimentData(userId, sentiment);
    
    // 3. Handle based on sentiment
    if (sentiment.sentiment === 'critical' || sentiment.urgency === 'high') {
        return await handleCriticalMessage(userId, userMessage, sentiment);
    }
    
    // 4. Adapt system prompt based on sentiment
    const systemPrompt = buildPromptWithSentiment(sentiment);
    
    // 5. Generate response
    const response = await generateResponse(userId, userMessage, systemPrompt);
    
    return response;
}

function buildPromptWithSentiment(sentiment) {
    let basePrompt = `Kamu adalah CS AI untuk [BRAND].`;
    
    switch (sentiment.sentiment) {
        case 'negative':
            basePrompt += `

PENTING: Customer ini sedang kecewa/marah.
- Tunjukkan empati terlebih dahulu
- Minta maaf atas ketidaknyamanan
- Tawarkan solusi konkret
- Jangan defensive
- Tone: sangat sopan dan pengertian`;
            break;
            
        case 'positive':
            basePrompt += `

Customer ini senang/puas.
- Apresiasi feedback positif
- Tone: ramah dan cheerful
- Boleh lebih casual`;
            break;
            
        default:
            basePrompt += `

Tone: ramah dan helpful`;
    }
    
    return basePrompt;
}

async function handleCriticalMessage(userId, userMessage, sentiment) {
    // 1. Send calming response
    const calmResponse = `Kak, kami sangat menyesal mendengar pengalaman ini 😔

Masalah kakak adalah prioritas kami. Tim senior akan langsung menghubungi dalam 10 menit.

Mohon tunggu sebentar ya kak. Kami akan selesaikan ini. 🙏`;
    
    // 2. Alert human agents immediately
    await alertSeniorAgent({
        userId,
        message: userMessage,
        sentiment,
        priority: 'CRITICAL',
        timestamp: new Date()
    });
    
    // 3. Create high-priority ticket
    await createTicket({
        userId,
        message: userMessage,
        priority: 1, // Highest
        sentiment: sentiment.sentiment,
        keywords: sentiment.keywords
    });
    
    return calmResponse;
}

Sentiment Dashboard

javascript

// API untuk dashboard
router.get('/analytics/sentiment', async (req, res) => {
    const { startDate, endDate } = req.query;
    
    const stats = await db.sentimentLogs.aggregate([
        {
            $match: {
                timestamp: {
                    $gte: new Date(startDate),
                    $lte: new Date(endDate)
                }
            }
        },
        {
            $group: {
                _id: '$sentiment',
                count: { $sum: 1 },
                avgScore: { $avg: '$score' }
            }
        }
    ]).toArray();
    
    // Calculate overall satisfaction
    const total = stats.reduce((acc, s) => acc + s.count, 0);
    const positive = stats.find(s => s._id === 'positive')?.count || 0;
    const satisfactionRate = ((positive / total) * 100).toFixed(1);
    
    res.json({
        stats,
        total,
        satisfactionRate: `${satisfactionRate}%`
    });
});

// Sentiment trends over time
router.get('/analytics/sentiment/trend', async (req, res) => {
    const trend = await db.sentimentLogs.aggregate([
        {
            $group: {
                _id: {
                    date: { $dateToString: { format: '%Y-%m-%d', date: '$timestamp' } },
                    sentiment: '$sentiment'
                },
                count: { $sum: 1 }
            }
        },
        { $sort: { '_id.date': 1 } }
    ]).toArray();
    
    res.json(trend);
});

// Top negative keywords
router.get('/analytics/sentiment/keywords', async (req, res) => {
    const keywords = await db.sentimentLogs.aggregate([
        { $match: { sentiment: { $in: ['negative', 'critical'] } } },
        { $unwind: '$keywords' },
        {
            $group: {
                _id: '$keywords',
                count: { $sum: 1 }
            }
        },
        { $sort: { count: -1 } },
        { $limit: 20 }
    ]).toArray();
    
    res.json(keywords);
});

Proactive Intervention

javascript

// Monitor conversation sentiment trend
async function monitorConversationSentiment(userId) {
    const recentMessages = await db.sentimentLogs
        .find({ oderId
userId })
        .sort({ timestamp: -1 })
        .limit(5)
        .toArray();
    
    // Check if sentiment is declining
    const scores = recentMessages.map(m => m.score);
    const avgRecent = scores.slice(0, 2).reduce((a, b) => a + b, 0) / 2;
    const avgOlder = scores.slice(2).reduce((a, b) => a + b, 0) / 3;
    
    if (avgRecent < avgOlder - 0.2) {
        // Sentiment declining significantly
        await triggerProactiveIntervention(userId);
    }
}

async function triggerProactiveIntervention(userId) {
    // Alert agent to take over
    await alertAgent({
        type: 'SENTIMENT_DECLINING',
        userId,
        message: 'Customer sentiment menurun, perlu intervensi'
    });
    
    // Or send proactive message
    await sendWhatsApp(userId, 
        `Hai kak, sepertinya ada yang kurang memuaskan ya? 

Boleh ceritakan lebih detail biar kami bisa bantu dengan lebih baik? 

Atau mau langsung chat dengan tim kami? 🙏`
    );
}

Best Practices

DO ✅

- Respond empati untuk negative
- Escalate critical immediately
- Track sentiment trends
- Use keywords + AI combination
- Proactive intervention
- Learn from patterns

DON'T ❌

- Ignore negative sentiment
- Same response for all
- No escalation process
- Rely only on keywords
- No tracking
- Defensive responses

FAQ

Seberapa akurat sentiment analysis?

AI-based: 85-95% akurat. Keyword-based: 70-80%. Kombinasi keduanya terbaik.

Perlu analyze semua message?

Ya, tapi bisa tiered. Quick keyword check dulu, AI untuk yang ambiguous.


Kesimpulan

Sentiment Analysis = Better CX!

No SentimentWith Sentiment
Same responseAdaptive
Miss angry customersPrioritized
No early warningProactive

Setup Sentiment Analysis →


Artikel Terkait