v2.0: 3-Raum-System - Hauptraum, Saal A, Saal B mit 18 Tischen, Raum-Buchungen, API-Doku
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Utils module
|
||||
@@ -0,0 +1,192 @@
|
||||
#!/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", ""))
|
||||
Reference in New Issue
Block a user