Explorer
/tmp/restic-stage/lead-engine/email_classifier.py
← Zurück ↓ Download
"""
Email Classification Engine
Classifies incoming emails into categories for intelligent routing
"""

import json
import os
from typing import Dict, List, Optional
from dataclasses import dataclass
import anthropic


@dataclass
class EmailMessage:
    """Represents an incoming email"""
    from_email: str
    from_name: str
    subject: str
    body: str
    timestamp: str
    message_id: str


@dataclass
class ClassificationResult:
    """Result of email classification"""
    category: str
    priority: str
    sentiment: str
    suggested_action: str
    reasoning: str
    confidence: float


class EmailClassifier:
    """Classifies emails using Claude API"""

    CATEGORIES = {
        "kurs_anfrage": {
            "keywords": ["kurs", "course", "training", "lernen", "learn", "klasse"],
            "action": "auto_reply",
            "template": "kurs_info"
        },
        "tech_support": {
            "keywords": ["fehler", "error", "bug", "problem", "nicht funktionieren", "hilfe", "help"],
            "action": "escalate",
            "template": None
        },
        "sales_inquiry": {
            "keywords": ["preis", "price", "kosten", "cost", "angebot", "offer", "deal"],
            "action": "route_to_sales",
            "template": None
        },
        "feedback": {
            "keywords": ["feedback", "meinung", "opinion", "suggestion", "idee", "idea"],
            "action": "log_and_thank",
            "template": "thank_you"
        },
        "general_question": {
            "keywords": ["wie", "what", "why", "warum", "frage", "question"],
            "action": "claude_answer",
            "template": None
        }
    }

    def __init__(self, api_key: Optional[str] = None):
        """Initialize classifier with Claude API key"""
        self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
        if not self.api_key:
            raise ValueError("ANTHROPIC_API_KEY environment variable not set")

        self.client = anthropic.Anthropic(api_key=self.api_key)

    def classify_email(self, email: EmailMessage) -> ClassificationResult:
        """
        Classify an email using Claude API

        Args:
            email: EmailMessage object with email details

        Returns:
            ClassificationResult with category, priority, sentiment, and action
        """

        prompt = f"""Du bist ein Email-Klassifikations-Agent für agentsolutions.tech.

Klassifiziere diese eingehende Email:

VON: {email.from_email}
NAME: {email.from_name}
BETREFF: {email.subject}
TEXT: {email.body}

Aufgaben:
1. KATEGORIE bestimmen aus: kurs_anfrage / tech_support / sales_inquiry / feedback / general_question
2. PRIORITÄT einschätzen: high / medium / low
3. SENTIMENT analysieren: positive / neutral / negative
4. SUGGESTED_ACTION: auto_reply / escalate / route_to_sales / log_only
5. Kurze REASONING (max 50 Wörter)
6. CONFIDENCE (0.0-1.0)

Antworte AUSSCHLIESSLICH als JSON (kein zusätzlicher Text):
{{
    "category": "...",
    "priority": "...",
    "sentiment": "...",
    "suggested_action": "...",
    "reasoning": "...",
    "confidence": 0.95
}}"""

        message = self.client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=300,
            messages=[
                {"role": "user", "content": prompt}
            ]
        )

        response_text = message.content[0].text.strip()

        # Extract JSON from response
        try:
            # Try to find JSON in the response
            start_idx = response_text.find('{')
            end_idx = response_text.rfind('}') + 1
            if start_idx != -1 and end_idx > start_idx:
                json_str = response_text[start_idx:end_idx]
                result_dict = json.loads(json_str)
            else:
                raise ValueError("No JSON found in response")
        except json.JSONDecodeError as e:
            raise ValueError(f"Failed to parse Claude response as JSON: {response_text}") from e

        return ClassificationResult(
            category=result_dict.get("category", "general_question"),
            priority=result_dict.get("priority", "medium"),
            sentiment=result_dict.get("sentiment", "neutral"),
            suggested_action=result_dict.get("suggested_action", "log_only"),
            reasoning=result_dict.get("reasoning", ""),
            confidence=float(result_dict.get("confidence", 0.8))
        )

    def get_action_for_category(self, category: str) -> str:
        """Get the action to take for a given category"""
        return self.CATEGORIES.get(category, {}).get("action", "log_only")

    def get_template_for_category(self, category: str) -> Optional[str]:
        """Get the response template for a given category"""
        return self.CATEGORIES.get(category, {}).get("template")