192 lines
5.4 KiB
Python
192 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Ollama Integration für KI-gestütztes E-Mail-Parsing"""
|
|
|
|
import json
|
|
import requests
|
|
import re
|
|
from datetime import datetime
|
|
|
|
OLLAMA_URL = "http://192.168.0.150:11434/api/generate"
|
|
DEFAULT_MODEL = "gemma4:latest"
|
|
|
|
def query_ollama(prompt, model=DEFAULT_MODEL, temperature=0.1):
|
|
"""Ollama API aufrufen"""
|
|
try:
|
|
response = requests.post(
|
|
OLLAMA_URL,
|
|
json={
|
|
"model": model,
|
|
"prompt": prompt,
|
|
"stream": False,
|
|
"options": {
|
|
"temperature": temperature,
|
|
"num_predict": 500
|
|
}
|
|
},
|
|
timeout=30
|
|
)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
def extract_json_from_response(text):
|
|
"""JSON aus Ollama-Antwort extrahieren"""
|
|
# Versuche direkt als JSON zu parsen
|
|
try:
|
|
return json.loads(text)
|
|
except:
|
|
pass
|
|
|
|
# Suche nach JSON-Block
|
|
json_match = re.search(r'\{[\s\S]*\}', text)
|
|
if json_match:
|
|
try:
|
|
return json.loads(json_match.group())
|
|
except:
|
|
pass
|
|
|
|
return {"error": "Kein gültiges JSON gefunden", "raw": text}
|
|
|
|
def parse_email_with_ollama(email_subject, email_body, sender_name):
|
|
"""E-Mail mit Ollama parsen"""
|
|
|
|
prompt = f"""Analysiere diese Restaurant-Reservierungs-E-Mail.
|
|
|
|
WICHTIG: Suche nach einer Buchungsnummer im Format RES-YYYY-MM-DD-XXX.
|
|
Wenn eine Buchungsnummer erwähnt wird, ist es eine Änderung oder Stornierung.
|
|
Wenn keine Buchungsnummer vorhanden ist, ist es eine neue Reservierung.
|
|
|
|
Absender: {sender_name}
|
|
Betreff: {email_subject}
|
|
|
|
E-Mail-Inhalt:
|
|
---
|
|
{email_body}
|
|
---
|
|
|
|
Extrahiere folgende Informationen und antworte NUR mit JSON:
|
|
{{
|
|
"booking_number": "RES-2025-05-12-001" oder null,
|
|
"intent": "new" | "modification" | "cancellation",
|
|
"name": "Name des Gastes",
|
|
"phone": "Telefonnummer",
|
|
"email": "E-Mail-Adresse",
|
|
"date": "YYYY-MM-DD",
|
|
"time": "HH:MM",
|
|
"guests": 4,
|
|
"occasion": "Geburtstag/Dinner/etc. oder null",
|
|
"notes": "Weitere Informationen",
|
|
"confidence": 0.95,
|
|
"needs_review": false,
|
|
"review_reason": null
|
|
}}
|
|
|
|
Beispiele für Änderungen:
|
|
- "Ich möchte meine Reservierung RES-2025-05-12-001 verschieben"
|
|
- "Bitte stornieren Sie meinen Tisch"
|
|
- "Wir sind jetzt 6 statt 4 Personen"
|
|
|
|
Antworte nur mit dem JSON-Objekt, keine Erklärungen."""
|
|
|
|
result = query_ollama(prompt)
|
|
|
|
if "error" in result:
|
|
return {
|
|
"error": result["error"],
|
|
"needs_review": True,
|
|
"review_reason": "Ollama-Fehler"
|
|
}
|
|
|
|
response_text = result.get("response", "")
|
|
parsed = extract_json_from_response(response_text)
|
|
|
|
# Validiere und ergänze
|
|
if "confidence" not in parsed:
|
|
parsed["confidence"] = 0.5
|
|
|
|
if "needs_review" not in parsed:
|
|
parsed["needs_review"] = parsed.get("confidence", 0) < 0.7
|
|
|
|
return parsed
|
|
|
|
def generate_confirmation_email(reservation, is_new=True):
|
|
"""Bestätigungsmail-Text generieren"""
|
|
|
|
if is_new:
|
|
template = f"""Hallo {reservation.get('name', 'Gast')},
|
|
|
|
vielen Dank für Ihre Reservierung bei uns!
|
|
|
|
━━━━━━━━━━━━━━━━━━━━━━━
|
|
📋 BUCHUNGSNUMMER: {reservation['booking_number']}
|
|
━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
📅 Datum: {reservation.get('date', 'unbekannt')}
|
|
⏰ Uhrzeit: {reservation.get('time_from', 'unbekannt')}
|
|
👥 Personen: {reservation.get('guests', 'unbekannt')}
|
|
🍽️ Tisch: {reservation.get('table_name', 'wird zugewiesen')}
|
|
|
|
Bei Änderungen antworten Sie einfach auf diese E-Mail
|
|
und nennen Sie Ihre Buchungsnummer: {reservation['booking_number']}
|
|
|
|
Wir freuen uns auf Ihren Besuch!
|
|
|
|
Mit freundlichen Grüßen
|
|
Ihr Restaurant-Team"""
|
|
else:
|
|
template = f"""Hallo {reservation.get('name', 'Gast')},
|
|
|
|
Ihre Reservierung wurde aktualisiert.
|
|
|
|
━━━━━━━━━━━━━━━━━━━━━━━
|
|
📋 BUCHUNGSNUMMER: {reservation['booking_number']}
|
|
━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
📅 Neuer Termin: {reservation.get('date', 'unbekannt')} um {reservation.get('time_from', 'unbekannt')}
|
|
👥 Personen: {reservation.get('guests', 'unbekannt')}
|
|
|
|
Alle anderen Details bleiben bestehen.
|
|
|
|
Bei weiteren Änderungen nennen Sie bitte immer Ihre Buchungsnummer: {reservation['booking_number']}
|
|
|
|
Mit freundlichen Grüßen
|
|
Ihr Restaurant-Team"""
|
|
|
|
return template
|
|
|
|
def suggest_table_for_group(available_tables, guests, date, time, ollama_model=DEFAULT_MODEL):
|
|
"""Ollama schlägt optimalen Tisch vor"""
|
|
|
|
tables_info = "\n".join([
|
|
f"- {t['name']}: {t['seats']} Plätze, Bereich: {t.get('area_name', 'unbekannt')}"
|
|
for t in available_tables
|
|
])
|
|
|
|
prompt = f"""Schlage den besten Tisch für diese Reservierung vor:
|
|
|
|
Gruppe: {guests} Personen
|
|
Datum: {date}
|
|
Uhrzeit: {time}
|
|
|
|
Verfügbare Tische:
|
|
{tables_info}
|
|
|
|
Berücksichtige:
|
|
- Passende Größe für {guests} Personen
|
|
- Atmosphäre (Fenster bevorzugt)
|
|
- Nicht zu groß oder zu klein
|
|
|
|
Antworte mit JSON:
|
|
{{
|
|
"suggested_table_id": 5,
|
|
"reasoning": "Tisch 5 hat 6 Plätze, ist am Fenster und ideal für {guests} Personen",
|
|
"alternatives": [3, 7]
|
|
}}"""
|
|
|
|
result = query_ollama(prompt, model=ollama_model)
|
|
|
|
if "error" in result:
|
|
return None
|
|
|
|
return extract_json_from_response(result.get("response", "")) |