"""
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")