KI-Email-RAG-System: Automatisierte Postfach-Verarbeitung
Ein vollständiges System für ein einzelnes Postfach: Emails automatisiert kategorisieren, verarbeiten und halb- oder vollautomatisch beantworten. Mit lokalem LLM, SQL-, Vektor- und Graph-Datenbank, account-spezifischen Prompts, Protokollierung und GUI.
Ziel, Scope und Entscheidungsgrenzen
Vor der Implementierung müssen 14 Definitionsblöcke formal festgelegt werden. Jeder Block beantwortet Fragen, die die Architektur, die Entscheidungslogik und die Qualitätskriterien des Systems bestimmen. Ohne diese Definitionen ist jede technische Entscheidung beliebig.
Die zentrale Frage lautet nicht: Welche Technik verwenden wir? Die zentrale Frage lautet: Welche Entscheidungen darf das System unter welchen Bedingungen mit welchen Daten und mit welcher Nachweisbarkeit treffen?
| Block | Thema | Kernfrage |
|---|---|---|
| 1 | Zielbild des Systems | Was soll das System leisten und was ausdrücklich nicht? |
| 2 | Scope des ersten Releases | Was wird in V1 umgesetzt und was ist bewusst verschoben? |
| 3 | Automationsgrad und Entscheidungsrechte | Wann darf das System eigenständig handeln? |
| 4 | Fachliche Objektdefinitionen | Welche Objekte existieren und welche Felder haben sie? |
| 5 | Prozessgrenzen und Sollprozess | Wie sieht der vollständige Verarbeitungsweg einer Email aus? |
| 6 | Kategorien, Prioritäten und Aktionsmatrix | Welche Kombination aus Kategorie, Priorität und Konfidenz löst welche Aktion aus? |
| 7 | Datenquellen und Wahrheitsquellen | Welche Datenbank ist für welche Daten maßgeblich? |
| 8 | Retrieval und Wissenslogik | Welche Inhalte werden wie gechunkt, eingebettet und durchsucht? |
| 9 | LLM-Aufgaben und Prompt-Architektur | Welche LLM-Aufgaben existieren und wie ist jede spezifiziert? |
| 10 | Sicherheits- und Datenschutzmodell | Welche Daten werden wie geschützt und wer darf was? |
| 11 | Betriebsmodell und Lastannahmen | Wie viele Emails pro Stunde, welche Latenz, welches RAM-Budget? |
| 12 | Fehlerklassen und Fallback-Regeln | Was passiert bei welchem Fehler? |
| 13 | API- und Integrationsgrenzen | Welche Endpunkte, Rollen und Idempotenzregeln gelten? |
| 14 | Qualitätsmodell und Abnahme | Woran erkennen wir messbar, dass das System funktioniert? |
Block 1: Zielbild des Systems
Das Zielbild definiert den exakten Zweck des Systems. Ohne diese Definition ist jede spätere Architekturentscheidung beliebig.
| Festlegung | Definition |
|---|---|
| Primärziel | Ein einzelnes Email-Postfach vollständig verarbeiten: Emails automatisiert kategorisieren, Wissen extrahieren, in drei Datenbanken speichern und Antwort-Entwürfe generieren. Alle Daten bleiben lokal. |
| Sekundärziele | Absender-Profile aufbauen, Beziehungsnetzwerk aus Email-Kommunikation ableiten, Suchbarkeit über alle historischen Emails herstellen, Antwortqualität durch Feedback-Loop verbessern. |
| Nicht-Ziele | Kein Multi-Postfach-System. Kein Ticketsystem. Kein CRM. Kein Newsletter-Versand. Keine Kalenderverwaltung. Kein Spam-Filter (Vorfilterung durch Mailserver). Kein Cloud-LLM. |
| Erfolgskriterien | 90% der Emails werden korrekt kategorisiert. Antwort-Entwürfe werden in weniger als 50% der Fälle substanziell geändert. Substanziell bedeutet: Diff-Anteil über 30% der Zeichenlänge oder inhaltliche Korrektur (nicht nur Formatierung, Anrede oder Signatur). Durchschnittliche Verarbeitungszeit unter 90 Sekunden pro Email (dominiert durch sequentielle LLM-Aufrufe). Keine Email geht verloren. |
| Abbruchkriterien | Kategorisierungsgenauigkeit unter 70% nach 500 verarbeiteten Emails. Mehr als 5% Fehlzustellungen, die nicht innerhalb von 24 Stunden durch Audit-Log-Prüfung oder Absender-Rückmeldung erkannt werden. LLM-Antworten werden in über 80% der Fälle verworfen. |
| Maximal erlaubte Fehlerklassen | Falsch-positive Automatik-Antwort: 0 (darf nicht passieren). Falsche Kategorisierung: tolerierbar mit manueller Korrektur. LLM-Timeout: tolerierbar mit Fallback in manuelle Queue. |
Block 2: Scope des ersten Releases
V1 hat einen harten Scope. Alles außerhalb wird bewusst verschoben, nicht vergessen. ArangoDB und Relationsextraktion sind von Anfang an integraler Bestandteil des Systems. Der Knowledge Graph ist kein optionales Feature, sondern Kern der Architektur. Gleichzeitig ist das System so gebaut, dass ein temporärer ArangoDB-Ausfall den Betrieb nicht blockiert: Emails werden weiterhin kategorisiert, eingebettet und beantwortet — der Graph-Kontext fehlt dann im Retrieval, bis ArangoDB wieder verfügbar ist.
| Dimension | V1 Scope | Bewusst verschoben |
|---|---|---|
| Postfächer | Ein einzelnes IMAP-Postfach | Multi-Postfach, Exchange/Graph-API |
| Mailtypen | Eingehende Emails, Antworten auf eigene Mails | Kalendereinladungen, Lesebestätigungen, Bounces |
| Anhänge | PDF, Bilder (JPEG/PNG) | DOCX, XLSX, ZIP, Audio, Video |
| Sprachen | Deutsch, Englisch | Weitere Sprachen |
| Kategorisierung | Automatisch mit LLM, manuell korrigierbar | Trainierbare Klassifikatoren, Fine-Tuning |
| Antwortgenerierung | Entwürfe zur manuellen Freigabe | Vollautomatischer Versand (erst ab V2 nach Qualitätsnachweis) |
| GUI | Dashboard, Email-Ansicht, Antwort-Editor, Konfiguration | Mobile App, Echtzeit-Benachrichtigungen |
| API | CRUD für Emails, Threads, Kategorien. Hybrid Search. Freigabe-Endpoint | Webhooks, externe Integrationen, Batch-Operationen |
| Betrieb | Systemd-Service, Health-Check, Cron-basiertes Polling | IMAP IDLE (Push), Kubernetes, Horizontale Skalierung |
Block 3: Automationsgrad und Entscheidungsrechte
Für jede Aktion, die das System ausführen kann, muss festgelegt sein, ob sie automatisch, nach Freigabe oder gar nicht erlaubt ist.
| Aktion | V1 Automationsgrad | Bedingung |
|---|---|---|
| Email abrufen und speichern | Vollautomatisch | Immer |
| Email parsen (MIME, Header, Body) | Vollautomatisch | Immer |
| Thread zuordnen | Vollautomatisch | Über Message-ID/References-Header |
| Anhänge extrahieren und speichern | Vollautomatisch | Nur unterstützte Formate (PDF, Bilder) |
| Kategorisieren | Vollautomatisch | LLM-Klassifikation + Regelvorfilter. Ergebnis immer sichtbar und korrigierbar |
| Priorität zuweisen | Vollautomatisch | Aus Kategorie, Absender-Profil und Sentiment abgeleitet |
| Stimmung analysieren | Vollautomatisch | LLM-basiert, gespeichert als Metadatum |
| In Wissensbasis aufnehmen (Ingest) | Vollautomatisch | Chunking, Embedding, NER, Relationsextraktion |
| Antwort-Entwurf generieren | Vollautomatisch | Für Kategorien anfrage, beschwerde, bestellung, intern — wenn Klassifikation erfolgreich. Kein Entwurf für information, newsletter, spam. Nur als Entwurf gespeichert, nie direkt versendet |
| Antwort freigeben und versenden | Nur nach manueller Freigabe | In V1 keine Vollautomatik. Mensch prüft und gibt frei |
| Email verschieben/labeln/archivieren | Nur nach manueller Freigabe | System schlägt vor, Mensch bestätigt |
| Email löschen | Nicht erlaubt | System darf keine Emails löschen |
| Externes System anstoßen | Nicht erlaubt in V1 | Keine Webhooks, keine API-Calls nach außen |
Entscheidungsrecht: Der Mensch hat in V1 das letzte Entscheidungsrecht über jede ausgehende Kommunikation und jede destruktive Aktion.
Block 4: Fachliche Objektdefinitionen
Jedes zentrale Objekt im System muss eindeutig definiert sein, bevor das Datenmodell entworfen wird.
| Objekt | Definition | Pflichtfelder | Erzeugt durch | Lebensdauer |
|---|---|---|---|---|
| Mailbox | Das angebundene IMAP-Postfach mit Zugangsdaten und Konfiguration | host, port, user, password_encrypted, display_name | Admin bei Einrichtung | Permanent |
| Eine einzelne empfangene oder gesendete Nachricht | message_id, from, to, subject, date, body_text, body_html, raw_mime | IMAP-Abruf | Gemäß Aufbewahrungsfrist | |
| Thread | Eine Konversation aus zusammengehörigen Emails | thread_id, subject_normalized, email_ids[], participant_ids[] | Thread-Zuordnungslogik | Solange zugehörige Emails existieren |
| Kontaktprofil | Aggregiertes Profil eines Absenders/Empfängers | email_address, display_name, first_seen, last_seen, email_count | Automatisch bei erster Email | Gemäß DSGVO-Löschfrist |
| Anhang | Eine Datei, die einer Email beigefügt ist | filename, mime_type, size_bytes, email_id, storage_path | MIME-Parsing | An Email gebunden |
| Kategorie | Klassifikation einer Email nach Typ | name, parent_category_id (nullable), description | Admin-Konfiguration | Permanent, versioniert |
| Klassifikation | Zuordnung einer Email zu einer Kategorie | email_id, category_id, confidence, source (rule|llm|manual), created_at | Regelengine oder LLM | An Email gebunden |
| Priorität | Dringlichkeitsstufe einer Email | level (critical|high|normal|low), email_id, reason | Prioritätslogik | An Email gebunden |
| Stimmung | Sentiment-Bewertung einer Email | email_id, sentiment (positive|neutral|negative|angry), confidence | LLM-Analyse | An Email gebunden |
| Entität | Erkannte benannte Entität aus Email-Text | name, normalized_name, type (PER|ORG|LOC|MISC), source_email_id | SpaCy NER + Regex | Permanent im Graph |
| Relation | Beziehung zwischen zwei Entitäten | source_entity_id, target_entity_id, relation_type, source_email_id | LLM-Extraktion | Permanent im Graph |
| Prompt | System-Prompt für eine LLM-Aufgabe | task_type, prompt_text, model, temperature, max_tokens, version | Admin-Konfiguration | Permanent, versioniert |
| Template | Wiederverwendbare Antwortvorlage | name, body_template, placeholders[], category_id (nullable) | Admin-Konfiguration | Permanent, versioniert |
| Antwort-Entwurf | Vom LLM generierter Antwortentwurf | email_id, draft_text, prompt_id, confidence, status (draft|approved|rejected|sent) | Antwortgenerierung | An Email gebunden |
| Freigabe | Menschliche Entscheidung über einen Entwurf | draft_id, action (approve|reject|edit), editor_id, edited_text (nullable), created_at | Mensch über GUI | Permanent (Audit) |
| Audit-Eintrag | Unveränderliches Protokoll einer Systemaktion | timestamp, action_type, entity_type, entity_id, actor (system|user), details_json | Jede Systemkomponente | Permanent (append-only) |
| Regel | Regelbasierte Zuordnungslogik (vor LLM) | name, condition_type (regex|sender|keyword), condition_value, action, priority_order | Admin-Konfiguration | Permanent, versioniert |
| Queue-Eintrag | Email in der Verarbeitungswarteschlange | email_id, status (pending|processing|done|failed|dead), priority, retry_count, next_retry_at | IMAP-Abruf | Bis Verarbeitung abgeschlossen |
| LLM-Log | Request/Response-Paar eines LLM-Aufrufs | task_type, prompt, response, model, tokens_in, tokens_out, latency_ms, email_id | LLM-Aufrufe | Gemäß Aufbewahrungsfrist |
| Verarbeitungslauf | Ein vollständiger Pipeline-Durchlauf für eine Email. Bündelt alle Schritte (Parsing bis Entwurf) als nachvollziehbare Einheit | run_id, email_id, status (pending|running|done|failed|partial), started_at, finished_at, steps_completed[], error_step (nullable) | Queue-Worker bei Verarbeitungsbeginn | Permanent (Audit). Ermöglicht gezieltes Reprocessing einzelner Emails |
| Account-Regelwerk | Gebündelte Konfiguration pro Mailbox: Prompts, Regeln, Templates, Schwellwerte als zusammengehörige Einheit | mailbox_id, name, prompt_ids[], rule_ids[], template_ids[], confidence_thresholds{}, version, active | Admin-Konfiguration | Permanent, versioniert. Wechsel zwischen Regelwerk-Versionen ohne Datenverlust |
| Feedback | Diff zwischen LLM-Entwurf und menschlich editierter Antwort. Datengrundlage für Prompt-Optimierung | draft_id, email_id, original_text, edited_text, diff_ratio, edit_type (formatting|content|tone|rejected), created_at | Automatisch bei Versand eines editierten Entwurfs | Permanent |
| Zusammenfassung | Einzeiler-Zusammenfassung einer Email. Feld in der Email-Tabelle, nicht eigenständiges Objekt | Gespeichert als summary-Feld in Email (nullable VARCHAR(500)) | LLM-Aufruf (gebündelt mit Klassifikation) | An Email gebunden |
Verhältnis der drei Status-Systeme
Das System verwendet drei getrennte Status-Felder, die unterschiedliche Perspektiven abbilden:
- Email-Status (fetched → parsed → classified → indexed → draft_ready → awaiting_review → approved/rejected/archived → sent): Bildet den fachlichen Zustand der Email ab. Relevant für GUI und API
- Queue-Status (pending → processing → done/failed/dead): Bildet den technischen Verarbeitungszustand in der Worker-Queue ab. Ein Queue-Eintrag wird pro Verarbeitungsdurchlauf erstellt. Bei Reprocessing entsteht ein neuer Queue-Eintrag für dieselbe Email
- Verarbeitungslauf-Status (pending → running → done/failed/partial): Bildet einen einzelnen Pipeline-Durchlauf ab. Jeder Queue-Eintrag erzeugt genau einen Verarbeitungslauf. Der Verarbeitungslauf protokolliert, welche Schritte erfolgreich waren (steps_completed[]) und wo ggf. der Fehler auftrat (error_step). Bei Reprocessing entsteht ein neuer Verarbeitungslauf mit eigener run_id
Block 5: Prozessgrenzen und Sollprozess
Der vollständige Verarbeitungsweg einer eingehenden Email, Schritt für Schritt. Jeder Schritt hat definierte Ein- und Ausgaben, Vorbedingungen und Fehlerverhalten.
| Schritt | Input | Output | Vorbedingung | Fehlerverhalten |
|---|---|---|---|---|
| 1. IMAP-Abruf | IMAP-Verbindungsdaten | Rohe Email (RFC 5322) | IMAP erreichbar, Credentials gültig | Retry mit Backoff. Nach 5 Fehlversuchen: Alert |
| 2. Rohdaten-Speicherung | Rohe Email | Email-Datensatz in MariaDB mit raw_mime | MariaDB erreichbar | Transaktion rollback. Email bleibt auf IMAP unmarkiert |
| 3. MIME-Parsing | raw_mime | Strukturierte Felder: body_text, body_html, headers, attachment_list | Email in DB gespeichert | Parsing-Fehler: Email als unparseable markieren, in manuelle Queue |
| 4. Thread-Zuordnung | Message-ID, In-Reply-To, References | thread_id (existierend oder neu) | Email geparst | Kein Match: neuen Thread erstellen. Mehrdeutig: manuell zuordnen |
| 5. Anhang-Extraktion | MIME-Parts mit Content-Disposition: attachment | Gespeicherte Dateien + Anhang-Datensätze | Email geparst | Nicht unterstütztes Format: Anhang als unsupported markieren, Metadaten trotzdem speichern |
| 6. Kontaktprofil-Update | From, To, CC Header | Aktualisierte Kontaktprofile | Email geparst | Unkritisch: bei Fehler nur loggen |
| 7. Regelbasierte Vorfilterung | Email-Felder + Regelkatalog | Kategorie (wenn Regel greift) oder weiter zu LLM | Regeln konfiguriert | Fehler in Regel: überspringen, LLM entscheidet |
| 8. LLM-Klassifikation | Email-Body + Thread-Kontext + Prompt | Kategorie + Unterkategorie + Konfidenz | Ollama erreichbar, Modell geladen | Timeout: Email als unclassified in manuelle Queue. Ungültiges JSON: Retry 1x, dann manuell |
| 9. Priorität + Sentiment | Kategorie + Absender-Profil + Email-Body | Prioritätsstufe + Stimmungsbewertung | Klassifikation abgeschlossen | Unkritisch: Defaults (normal/neutral) bei Fehler |
| 10. NER + Entitätsextraktion | Email-Body (bereinigt) | Entitäten-Liste (PER, ORG, LOC, MISC) | SpaCy geladen | SpaCy nicht verfügbar: überspringen, ohne Entitäten weiter |
| 11. Chunking + Embedding | Email-Body + Metadaten | Chunks mit Vektoren in Qdrant + MariaDB | sentence-transformers geladen, Qdrant erreichbar | Embedding-Fehler: Email als not_embedded markieren, manuell nacharbeiten |
| 12. Relationsextraktion | Email-Body + erkannte Entitäten | Relationen im ArangoDB-Graph | Entitäten vorhanden, ArangoDB erreichbar | LLM-Fehler: überspringen, Graph bleibt unvollständig |
| 13. RAG-Anreicherung | Email + Thread-Historie + Hybrid Search Ergebnis | Kontextdokument für Antwortgenerierung | MariaDB erreichbar (Pflicht). Qdrant und ArangoDB optional (graceful degradation: ohne Qdrant kein Dense Search, ohne ArangoDB kein Graph-Kontext) | Teilfehler: mit verfügbarem Kontext weitermachen. Nur Sparse Search wenn Qdrant fehlt |
| 14. Antwort-Entwurf | Kontextdokument + Account-Prompt + Template | Antwort-Entwurf mit Konfidenz | Klassifikation + RAG abgeschlossen | LLM-Fehler: kein Entwurf, Email wartet auf manuelle Antwort |
| 15. Freigabe | Entwurf in GUI angezeigt | Freigabe/Ablehnung/Bearbeitung durch Mensch | Entwurf generiert | Kein Fehler möglich (menschlicher Schritt) |
| 16. SMTP-Versand | Freigegebener Entwurf + Email-Header | Gesendete Antwort mit korrekten Reply-Headern | Freigabe erteilt, SMTP erreichbar | Versand-Fehler: Retry 3x mit Backoff. Danach: manuelle Eskalation |
| 17. Protokollierung | Alle Ergebnisse der Schritte 1-16 | Audit-Einträge (append-only) | Keine | Logging-Fehler: stderr + Alert, Verarbeitung geht weiter |
| 18. Feedback-Speicherung | Diff zwischen Entwurf und finaler Antwort | Feedback-Datensatz für Prompt-Optimierung | Antwort versendet UND Entwurf wurde editiert | Unkritisch: bei Fehler nur loggen |
Zustandsmaschine einer Email
Jede Email durchläuft definierte Zustände. Übergänge sind atomar — ein Zustand wird erst gesetzt, wenn der zugehörige Schritt erfolgreich abgeschlossen ist.
| Zustand | Bedeutung | Commit-Grenze | IMAP-Flag |
|---|---|---|---|
| fetched | Rohdaten in MariaDB gespeichert | Nach INSERT in emails-Tabelle + raw_mime gespeichert | Noch nicht als gesehen markiert |
| parsed | MIME zerlegt, Header/Body/Anhänge strukturiert | Nach UPDATE der strukturierten Felder in emails | Noch nicht als gesehen markiert |
| classified | Kategorie, Priorität, Sentiment zugewiesen | Nach INSERT in classifications + UPDATE priority/sentiment | Jetzt als gesehen markiert auf IMAP |
| indexed | Chunks in MariaDB + Vektoren in Qdrant + Entitäten/Relationen in ArangoDB | Nach erfolgreicher Synchronisation aller drei Datenbanken | Bereits markiert |
| partially_indexed | MariaDB-Chunks vorhanden, aber Qdrant oder ArangoDB fehlgeschlagen | Sofort bei Teilfehler gesetzt | Bereits markiert |
| draft_ready | Antwort-Entwurf generiert und gespeichert | Nach INSERT in drafts-Tabelle | Bereits markiert |
| awaiting_review | Entwurf wartet auf menschliche Freigabe | Sofort nach draft_ready, wenn kein Fehler | Bereits markiert |
| approved | Mensch hat Entwurf freigegeben | Nach INSERT in approvals-Tabelle | Bereits markiert |
| sent | Antwort per SMTP versendet | Nach SMTP-Bestätigung + UPDATE status | Bereits markiert |
| rejected | Entwurf verworfen, neuer Entwurf möglich | Nach INSERT in approvals mit action=reject | Bereits markiert |
| archived | Email ohne Antwort-Bedarf abgeschlossen (information, newsletter, spam) | Nach manueller Bestätigung oder automatisch bei Kategorie ohne Entwurf | Bereits markiert |
| failed | Kritischer Fehler, manuell zu prüfen | Sofort bei unkompensiertem Fehler | Nicht markiert, wenn vor classified |
Entscheidende Grenze: Die Email wird auf IMAP erst als gesehen markiert, wenn der Zustand classified erreicht ist. Scheitert die Verarbeitung vorher, wird die Email beim nächsten Polling erneut abgerufen. Ab classified ist die Email in MariaDB als Source of Truth gesichert und der IMAP-Zustand ist unkritisch.
Block 6: Kategorien, Prioritäten und Aktionsmatrix
Die Entscheidungslogik des Systems wird als Matrix formalisiert. Jede Kombination aus Kategorie, Priorität und Konfidenz ergibt eine definierte Aktion.
Hauptkategorien (V1)
| Kategorie | Beschreibung | Unterkategorien (Beispiele) |
|---|---|---|
| anfrage | Frage oder Informationsanforderung | produkt, preis, termin, allgemein |
| beschwerde | Unzufriedenheit oder Reklamation | lieferung, qualitaet, service, rechnung |
| bestellung | Kaufabsicht oder Auftragserteilung | neu, aenderung, stornierung |
| information | Mitteilung ohne direkten Handlungsbedarf | statusupdate, bestaetigung, weiterleitung, benachrichtigung, automatische-antwort |
| intern | Interne Kommunikation | abstimmung, freigabe, info |
| newsletter | Automatisierter Massenversand | marketing, branche, service |
| spam | Unerwünschte oder irrelevante Email | werbung, phishing, irrelevant |
Prioritätsstufen
| Stufe | Kriterium | Max. Reaktionszeit |
|---|---|---|
| critical | Beschwerde + negatives Sentiment + VIP-Absender | 1 Stunde |
| high | Beschwerde oder Bestellung oder kritischer Absender | 4 Stunden |
| normal | Standardanfrage, interne Kommunikation | 24 Stunden |
| low | Newsletter, Information ohne Handlungsbedarf | Keine Frist |
Aktionsmatrix (V1)
Die Aktionsmatrix verwendet die korrigierte Konfidenz (nach heuristischer Anpassung gemäß Block 9, Abschnitt Konfidenz-Herkunft).
| Kategorie | Priorität | Konfidenz | Aktion |
|---|---|---|---|
| anfrage | normal/high | ≥ 0.8 | Entwurf generieren, Freigabe anfordern |
| anfrage | beliebig | < 0.8 | Nur kategorisieren, manuelle Bearbeitung |
| beschwerde | critical/high | beliebig | Eskalation: sofort in GUI hervorheben, Entwurf generieren |
| beschwerde | normal | ≥ 0.8 | Entwurf generieren, Freigabe anfordern |
| beschwerde | normal/low | 0.5-0.79 | Kategorisieren, in manuelle Queue, kein Entwurf |
| bestellung | beliebig | ≥ 0.7 | Entwurf generieren (Bestätigung), Freigabe anfordern |
| information | beliebig | ≥ 0.9 | Archivieren (nach Freigabe), kein Entwurf |
| intern | beliebig | ≥ 0.8 | Entwurf generieren, Freigabe anfordern |
| newsletter | low | ≥ 0.95 | Automatisch als gelesen markieren (nach Freigabe) |
| spam | low | ≥ 0.95 | In Spam-Ordner verschieben (nach Freigabe) |
| beliebig | beliebig | < 0.5 | Keine automatische Aktion, nur in manuelle Queue |
Block 7: Datenquellen und Wahrheitsquellen
Bei drei Datenbanken muss eindeutig festgelegt sein, welche Datenbank für welche Daten die Wahrheit enthält.
| Datenbank | Enthält | Ist Source of Truth für | Bei Konflikt |
|---|---|---|---|
| MariaDB | Emails, Threads, Kontaktprofile, Kategorien, Entwürfe, Audit-Log, Queue, Chunks, Entitäten, Relationen, LLM-Logs, Regeln, Prompts, Templates | Alle transaktionalen und Konfigurations-Daten | MariaDB gilt immer |
| Qdrant | Vektoren der Email-Chunks, Metadaten pro Vektor | Semantische Ähnlichkeitssuche | Vektor-Neuberechnung aus MariaDB-Chunks |
| ArangoDB | Entitäten als Knoten, Relationen als Kanten | Graph-Traversal und Beziehungsabfragen | Neuaufbau aus MariaDB-Entitäten und -Relationen |
Synchronitätsregeln:
- Qdrant und ArangoDB werden im Ingest-Prozess synchron mit MariaDB befüllt. Schlägt die Synchronisation fehl, wird die Email als partially_indexed markiert und die fehlenden Indizes werden per Retry nachgeholt
- Bei Löschung einer Email werden MariaDB-Datensatz, Qdrant-Vektoren und ArangoDB-Knoten/Kanten in einem orchestrierten Löschverfahren entfernt (keine echte verteilte Transaktion). Reihenfolge: (1) MariaDB soft-delete (Email-Status auf deleted, Daten bleiben erhalten als Referenz), (2) Qdrant-Vektoren löschen (anhand der chunk_ids aus MariaDB), (3) ArangoDB-Knoten/Kanten löschen (anhand der entity_ids aus MariaDB), (4) MariaDB hard-delete (Datensatz endgültig entfernen). Nur Schritt 4 ist irreversibel und wird erst ausgeführt, wenn Schritt 2 und 3 erfolgreich waren. Schlägt Schritt 2 oder 3 fehl, wird der Rückstand in der Queue mit Typ cleanup erfasst und per Cron nachgeholt. Solange soft-deleted, ist die Email in API und GUI nicht mehr sichtbar, aber die Daten stehen für die Bereinigung noch zur Verfügung
- Re-Indexierung (z.B. nach Modellwechsel) liest ausschließlich aus MariaDB und schreibt nach Qdrant/ArangoDB
- Gesendete Antworten werden als Email-Datensatz in MariaDB gespeichert (Typ: outgoing) und durchlaufen die Schritte NER, Embedding und Graph-Anreicherung. Sie werden nicht klassifiziert und erzeugen keinen Entwurf. Ihr Inhalt steht für Retrieval bei zukünftigen Anfragen zur Verfügung
Block 8: Retrieval und Wissenslogik
Was wird gechunkt, eingebettet und durchsucht — und was nicht.
| Festlegung | Definition |
|---|---|
| Chunk-Quelle | Nur der bereinigte Email-Body (Text-Version). Keine HTML-Tags, keine Signatur, kein Quoted-Text aus vorherigen Antworten |
| Chunk-Strategie | Absatzbasiert. Jeder Absatz ist ein Chunk, es sei denn er überschreitet 512 Zeichen — dann wird am nächsten Satzende geteilt |
| Chunk-Overlap | Kein Overlap. Absätze sind natürliche Sinneinheiten |
| Chunk-Metadaten | email_id, thread_id, sender_email, date, category, priority, chunk_position |
| Embedding-Modell | intfloat/multilingual-e5-large-instruct, 1024 Dimensionen |
| Embedding-Prefixe | "query: " für Suchanfragen, "passage: " für Email-Chunks |
| Was wird eingebettet | Bereinigter Body-Text als Chunks. Betreff als separater Chunk mit Metadatum subject=true. Anhänge (PDF-Text) als eigene Chunks mit Metadatum attachment_id |
| Was wird nicht eingebettet | HTML-Markup, Signaturen, Disclaimer, Quoted-Text, Header-Rohdaten |
| Hybrid Search Gewichtung | Dense (Qdrant): 0.5, Sparse (MariaDB FULLTEXT): 0.3, Graph (ArangoDB): 0.2. Fusion via Reciprocal Rank Fusion |
| Graph-Kontext | Bei Antwortgenerierung: Graph-Traversal Tiefe 1 ab erkannten Entitäten der aktuellen Email. Ergebnis als zusätzlicher Kontext im Prompt |
| Dublettenverhinderung | Vor Ingest: Prüfung auf message_id in MariaDB. Identische message_id wird nicht erneut verarbeitet |
| Re-Embedding | Nur bei Modellwechsel. Alle Chunks werden aus MariaDB gelesen und neu eingebettet. Alte Qdrant-Collection wird nach erfolgreicher Neuindexierung gelöscht |
| Thread-Kontext in Klassifikation | Betreff + aktueller Body + Zusammenfassungen der letzten 3 Emails im Thread. Bei knappen Antworten (Body unter 50 Zeichen) wird der Betreff und die letzte vollständige Email im Thread als Primärkontext verwendet |
| Thread-Kontext in Antwortgenerierung | Vollständiger Body der letzten 5 Emails im Thread (bereinigt, ohne Quoted-Text). Zusätzlich: Hybrid Search Ergebnisse + Graph-Kontext. Maximales Kontextfenster: 4096 Tokens, danach Truncation der ältesten Emails |
Block 9: LLM-Aufgaben und Prompt-Architektur
Jede LLM-Aufgabe ist ein eigenständiger Aufruf mit eigenem Prompt, eigenem Output-Schema und eigener Fehlerbehandlung.
| Aufgabe | Ziel | Output-Format | Modell | Temperatur | Max Tokens | Fallback |
|---|---|---|---|---|---|---|
| Klassifikation+Sentiment+Zusammenfassung (gebündelt) | Kategorie, Unterkategorie, Konfidenz, Stimmung, Einzeiler-Zusammenfassung in einem Aufruf | JSON: {category, subcategory, confidence, sentiment, sentiment_confidence, summary} | qwen3:8b | 0.1 | 512 | Manuelle Queue (bei Totalfehler). Betreff als Zusammenfassung, neutral als Sentiment-Default (bei Teilfehler) |
| Entitätserkennung | Ergänzend zu SpaCy: Vertragsnummern, Rechnungsnummern, Fachbegriffe | JSON: {entities: [{name, type}]} | qwen3:8b | 0.1 | 512 | Nur SpaCy-Ergebnisse |
| Relationsextraktion | Beziehungen zwischen Entitäten | JSON: {relations: [{source, target, type}]} | qwen3:8b | 0.1 | 512 | Keine Relationen für diese Email |
| Antwort-Entwurf | Kontextbasierte Antwort unter Einhaltung des Account-Prompts | Freitext (Email-Body) | qwen3:8b | 0.5 | 1024 | Kein Entwurf, manuelle Bearbeitung |
| Qualitätsprüfung | Prüfung des Entwurfs auf Halluzination und Tonalität | JSON: {quality_ok, issues[]} | qwen3:8b | 0.1 | 256 | Entwurf trotzdem anzeigen, mit Warnung |
Denkmodus: Klassifikation, Sentiment und Entitätserkennung laufen mit /no_think (schnell, deterministisch). Relationsextraktion und Antwort-Entwurf laufen mit /think (Reasoning für komplexe Aufgaben).
Konfidenz-Herkunft
Die Konfidenzwerte werden vom LLM als Teil der JSON-Ausgabe geliefert (self-reported confidence). LLM-Konfidenz ist nicht kalibriert — ein Wert von 0.9 bedeutet nicht, dass 90% der Klassifikationen mit diesem Wert korrekt sind. Deshalb wird die LLM-Konfidenz durch zwei heuristische Korrekturen ergänzt:
- Wenn die Regel-Engine vor dem LLM bereits eine Kategorie zugewiesen hat und das LLM dieselbe Kategorie liefert: Konfidenz wird auf mindestens 0.9 angehoben
- Wenn das LLM eine Kategorie mit hoher Konfidenz liefert, aber der Betreff und die Absender-Historie einer anderen Kategorie entsprechen: Konfidenz wird um 0.2 reduziert, Email wird zur manuellen Prüfung markiert
Prompt-Injection-Schutz
Email-Body wird vor Übergabe an LLM-Prompts in einen markierten Kontext-Block eingeschlossen (z.B. <email_content>...</email_content>). Der System-Prompt enthält eine explizite Anweisung, Anweisungen innerhalb des Kontext-Blocks nicht als System-Anweisungen zu interpretieren. Zusätzlich: Output-Validierung gegen das definierte JSON-Schema. Freitext-Felder (summary, draft) werden auf maximale Länge begrenzt.
Halluzinationsprüfung: Grenzen
Die Qualitätsprüfung des Antwort-Entwurfs verwendet dasselbe Modell (qwen3:8b), das den Entwurf generiert hat. Das ist eine strukturelle Schwäche: Das Modell erkennt eigene Halluzinationen nicht zuverlässig. Die Prüfung kann Formatfehler, fehlende Pflichtbestandteile und offensichtliche Widersprüche zum Kontext erkennen. Sie kann nicht garantieren, dass alle Fakten im Entwurf korrekt sind. Deshalb bleibt in V1 die menschliche Freigabe die primäre Halluzinationsschranke.
Block 10: Sicherheits- und Datenschutzmodell
| Festlegung | Regel |
|---|---|
| Datenverarbeitung | Alle Email-Inhalte, Vektoren, Graph-Daten und LLM-Prompts/Responses bleiben auf dem lokalen Server. Kein externer API-Aufruf für Inhalte |
| Personenbezogene Daten | Email-Adressen, Namen, Absender-Profile, Kommunikationsinhalte sind personenbezogen im Sinne der DSGVO |
| Löschpflicht | Auf Anfrage: Alle Emails eines Absenders, dessen Kontaktprofil, zugehörige Vektoren in Qdrant, zugehörige Knoten/Kanten in ArangoDB, zugehörige Audit-Einträge (anonymisiert, nicht gelöscht) |
| Auskunftspflicht | Auf Anfrage: Welche Emails, Kategorisierungen, Entwürfe und Entitäten zu einer Person gespeichert sind. Export als JSON oder PDF |
| Verschlüsselung Transit | TLS für IMAP und SMTP. HTTPS für GUI und API |
| Verschlüsselung Ruhe | Datenbank-Verbindungen verschlüsselt. Email-Bodies optional AES-256-verschlüsselt in MariaDB (konfigurierbar) |
| Credentials | IMAP/SMTP-Passwörter AES-256-verschlüsselt in Konfigurationsdatei. Nicht in Versionskontrolle. Environment-Variablen als Alternative |
| Rollen | Admin: Konfiguration, Prompts, Regeln, API-Keys. Operator: Freigabe, Email-Ansicht, Suche. Viewer: Nur lesen, kein Versand |
| API-Authentifizierung | Bearer-Token im Authorization-Header. Tokens mit Scope-Einschränkung (read, write, admin). Token-Rotation erzwungen alle 90 Tage |
| Token-Lifecycle | Erstellung nur durch Admin-Rolle. Jeder Token hat: Erstellungsdatum, Ablaufdatum (max. 90 Tage), letzter Zugriff, Scope, Ersteller-ID. Widerruf sofort wirksam (Token-Blacklist in MariaDB). Alle Token-Operationen werden im Audit-Log erfasst |
| Interner vs. externer Zugriff | Lokale Anfragen (127.0.0.1, ::1) dürfen den Health-Endpoint ohne Token nutzen. Alle anderen Endpoints erfordern immer einen gültigen Token, unabhängig von der Herkunft. Kein impliziter Trust für lokale IPs bei schreibenden Operationen |
| IP-Einschränkung | Optional konfigurierbar pro Token: Whitelist erlaubter IP-Adressen oder CIDR-Bereiche. Wenn gesetzt, werden Anfragen von nicht-gelisteten IPs abgelehnt (403), auch mit gültigem Token |
| Audit jeder API-Nutzung | Jeder API-Aufruf wird protokolliert: Zeitstempel, Token-ID (nicht Token-Wert), Endpoint, Methode, Response-Code, IP-Adresse. Bei schreibenden Operationen zusätzlich: Request-Body-Hash für Nachvollziehbarkeit |
Block 11: Betriebsmodell und Lastannahmen
Das Zielprofil ist ein aktives Geschäftspostfach mit etwa 1.000 Emails pro Tag. Die Architektur muss dieses Volumen mit akzeptabler Latenz verarbeiten können, ohne dass die Queue dauerhaft wächst.
| Parameter | Annahme V1 | Begründung |
|---|---|---|
| Mails pro Tag | ~1.000 | Aktives Geschäftspostfach mit Kundenanfragen, internem Verkehr und automatisierten Benachrichtigungen |
| Peak pro Stunde | 200 | Morgenstunden 08:00-10:00 nach Nacht-Akkumulation, Montag nach Wochenende |
| Durchschnitt pro Stunde | ~42 | 1.000 Mails / 24h = ~42/h über 24h. Reale Verteilung: 80% zwischen 07:00-19:00 = ~67 Mails/h in Geschäftszeiten, ~17/h nachts |
| Gleichzeitige Verarbeitungen | 2 Worker (Pipeline) + 1 LLM (sequentiell) | Parsing, Embedding und NER parallelisierbar. LLM-Aufrufe sequentiell wegen RAM. Worker füllen die LLM-Queue vor |
| Max. LLM-Aufrufe pro Email | 3-5 | Klassifikation+Sentiment+Zusammenfassung (gebündelt, 1 Aufruf) + Entitäten + Relationen + Entwurf + optional Qualitätsprüfung |
| LLM-Durchsatz erforderlich | ~800 Aufrufe/Stunde im Peak | 200 Mails × ~4 Aufrufe = 800. Bei 10s/Aufruf und 1 LLM: 360/h Kapazität. Peak-Rückstand wird in Nebenzeiten abgebaut. Queue-Backlog von ~2h im Worst Case akzeptabel (Block 11: Max. Rückstand 2h) |
| Max. Anhangsgröße | 25 MB | Standard-Email-Limit |
| Max. Threadtiefe | 50 Emails pro Thread | Darüber hinaus: nur letzte 50 für Kontext |
| Max. Verarbeitungszeit pro Email | 90 Sekunden (ohne Anhänge) | Parsing (~1s) + 3-5 LLM-Aufrufe à ~10s (~30-50s) + Embedding (~5s) + NER (~2s) + Graph (~2s) + Overhead. LLM dominiert |
| Max. Verarbeitungszeit mit OCR | 180 Sekunden | OCR für Bilder/Scans ist CPU-intensiv, läuft parallel zum LLM-Worker |
| Queue-Kapazität | 50.000 Einträge | Mehrtägiger Puffer bei Ausfall oder Wartung |
| Max. erlaubter Queue-Rückstand | 2 Stunden | Wenn Queue-Alter > 2h: Alert. System muss schneller verarbeiten als Mails eingehen |
| RAM-Budget LLM (qwen3:8b) | 6 GB | Q4_K_M-Quantisierung, 5.2 GB Download + Overhead |
| RAM-Budget Embedding | 2 GB | multilingual-e5-large-instruct |
| RAM-Budget gesamt | 32 GB empfohlen | LLM (6) + Embedding (2) + MariaDB (2) + Qdrant (2) + ArangoDB (2) + SpaCy (1) + FastAPI (1) + Worker (2) + OS (4) + Reserve (8) |
| Disk I/O | SSD/NVMe erforderlich | Bei 1.000 Mails/Tag mit Anhängen: ~2-5 GB/Tag neue Daten. HDD zu langsam für parallele DB-Zugriffe |
| Recovery-Zeit nach Ausfall | < 5 Minuten | Systemd-Restart. Emails auf IMAP bleiben erhalten, Queue wird ab letztem Stand fortgesetzt |
| IMAP-Polling-Intervall | 30 Sekunden | Bei 200 Mails/h im Peak: 30s-Intervall erfasst ~2 Mails pro Poll. Reduziert Queue-Latenz gegenüber 60s |
Block 12: Fehlerklassen und Fallback-Regeln
| Fehlerklasse | Erkennung | Retry | Backoff | Fallback |
|---|---|---|---|---|
| IMAP nicht erreichbar | Connection Timeout | Ja, unbegrenzt | Exponentiell: 1m, 2m, 4m, max 15m | Alert nach 3 Fehlversuchen. Emails bleiben auf Server |
| SMTP nicht erreichbar | Connection Timeout | 3x | Exponentiell: 30s, 60s, 120s | Entwurf bleibt als approved, Retry per Cron |
| MIME-Parsing fehlgeschlagen | Exception im Parser | Nein | - | Email als unparseable markieren, manuelle Queue, raw_mime erhalten |
| Anhang nicht lesbar | OCR/PDF-Fehler | Nein | - | Anhang-Metadaten speichern, Inhalt als nicht indexierbar markieren |
| LLM-Timeout | Response > 60s | 1x | Sofort | Aufgabe überspringen, Email in manuelle Queue |
| LLM-Ungültiges JSON | JSON-Parse-Fehler | 1x mit angepasstem Prompt | Sofort | Aufgabe überspringen, Default-Werte |
| Qdrant nicht erreichbar | Connection Error | 3x | Exponentiell: 5s, 10s, 20s | Email als not_embedded markieren, Retry per Cron |
| ArangoDB nicht erreichbar | Connection Error | 3x | Exponentiell: 5s, 10s, 20s | Email als not_graphed markieren, Retry per Cron |
| MariaDB-Transaktion fehlgeschlagen | SQL Exception | 1x | Sofort | Rollback, Email bleibt auf IMAP unmarkiert, Alert |
| Dublette erkannt | message_id bereits in DB | Nein | - | Email auf IMAP als gesehen markieren, nicht erneut verarbeiten |
| Thread-Zuordnung mehrdeutig | Mehrere mögliche Threads | Nein | - | Neuen Thread erstellen, manuell zusammenführbar |
| Embedding-Fehler | Exception im Modell | 1x | Sofort | Email ohne Embedding speichern, Retry per Cron |
Block 13: API- und Integrationsgrenzen
Ressourcen und Endpunkte (V1)
| Ressource | Endpunkte | Methoden | Sync/Async |
|---|---|---|---|
| Emails | /api/emails, /api/emails/{id} | GET, GET(list) | Synchron |
| Threads | /api/threads, /api/threads/{id} | GET, GET(list) | Synchron |
| Kontaktprofile | /api/contacts, /api/contacts/{id} | GET, GET(list), PATCH | Synchron |
| Kategorien | /api/categories, /api/categories/{id} | GET, POST, PATCH, DELETE | Synchron |
| Klassifikationen | /api/emails/{id}/classification | GET, PATCH (manuelle Korrektur) | Synchron |
| Entwürfe | /api/emails/{id}/draft | GET, PATCH | Synchron |
| Freigabe | /api/emails/{id}/draft/approve | POST | Asynchron (löst SMTP-Versand aus) |
| Verwerfen | /api/emails/{id}/draft/reject | POST | Synchron |
| Suche | /api/search | POST (query, filters) | Synchron |
| Regeln | /api/rules, /api/rules/{id} | GET, POST, PATCH, DELETE | Synchron |
| Prompts | /api/prompts, /api/prompts/{id} | GET, POST, PATCH | Synchron |
| Templates | /api/templates, /api/templates/{id} | GET, POST, PATCH, DELETE | Synchron |
| Queue | /api/queue | GET (list, filter by status) | Synchron |
| Health | /api/health | GET | Synchron |
| Metriken | /api/metrics | GET | Synchron |
| Audit | /api/audit | GET (list, filter by entity) | Synchron |
| Re-Klassifizieren | /api/emails/{id}/reclassify | POST | Asynchron |
| Eskalieren | /api/emails/{id}/escalate | POST | Synchron |
Rollen und Scopes
| Rolle | Scopes | Darf nicht |
|---|---|---|
| admin | read, write, config, approve, delete | - |
| operator | read, write, approve | Regeln/Prompts/Templates ändern, API-Keys verwalten |
| viewer | read | Schreiben, Freigeben, Konfigurieren |
Idempotenzregeln: POST /approve und POST /reject sind idempotent (mehrfacher Aufruf ändert nichts nach erster Ausführung). GET-Endpunkte sind immer idempotent. Rate-Limit: 100 Requests/Minute pro API-Key.
Email-Statusmodell als API-Ressource
Jede Email hat einen maschinenlesbaren Status, der über die API abfragbar und filterbar ist:
| Status | Bedeutung | Erlaubte Übergänge |
|---|---|---|
| fetched | Abgerufen, Rohdaten gespeichert | → parsed, → failed |
| parsed | MIME zerlegt, strukturiert | → classified, → failed |
| classified | Kategorie + Priorität zugewiesen | → indexed, → partially_indexed, → failed |
| indexed | Vollständig in allen 3 DBs | → draft_ready, → archived (wenn kein Entwurf nötig) |
| partially_indexed | MariaDB OK, Qdrant/ArangoDB fehlend | → indexed (nach Retry), → draft_ready |
| draft_ready | Entwurf generiert | → awaiting_review |
| awaiting_review | Wartet auf menschliche Freigabe | → approved, → rejected |
| approved | Freigegeben, Versand ausstehend | → sent, → failed |
| sent | Antwort versendet | Terminal |
| rejected | Entwurf verworfen | → awaiting_review (neuer Entwurf) |
| archived | Abgeschlossen ohne Antwort | Terminal |
| failed | Kritischer Fehler | → fetched (manuelles Reprocessing) |
Reprocessing-Endpunkte
| Endpunkt | Methode | Wirkung | Vorbedingung |
|---|---|---|---|
| /api/emails/{id}/reparse | POST | MIME-Parsing erneut ausführen | Status ≥ fetched |
| /api/emails/{id}/reclassify | POST | Klassifikation + Sentiment + Priorität erneut | Status ≥ parsed |
| /api/emails/{id}/reindex | POST | Chunking + Embedding + NER + Graph erneut | Status ≥ classified |
| /api/emails/{id}/redraft | POST | Neuen Antwort-Entwurf generieren | Status ≥ indexed |
| /api/emails/{id}/reprocess | POST | Gesamte Pipeline ab fetched neu durchlaufen | Beliebiger Status |
Concurrency und Locking
- Jede Email kann nur von einem Worker gleichzeitig verarbeitet werden. Der Queue-Eintrag wird mit processing + worker_id + started_at gelockt
- Lock-Timeout: 300 Sekunden. Danach wird der Lock freigegeben und die Email kann von einem anderen Worker übernommen werden
- GUI-Freigabe: Optimistic Locking über Versionsnummer im Entwurf. Wenn zwei Benutzer gleichzeitig denselben Entwurf bearbeiten, gewinnt der erste Speichervorgang. Der zweite erhält HTTP 409 Conflict
API-Versionierung und Fehlercodes
Versionierung: In V1 laufen alle Endpunkte unter /api/ ohne Versions-Prefix. Bei Breaking Changes in V2 wird ein /api/v2/ Prefix eingeführt, wobei /api/ als Alias für die aktuellste Version dient. Non-breaking Erweiterungen (neue Felder, neue Endpunkte) werden ohne Versionswechsel ergänzt.
| HTTP-Code | Bedeutung | Wann |
|---|---|---|
| 200 | Erfolg | GET, PATCH |
| 201 | Erstellt | POST (neue Ressource) |
| 202 | Akzeptiert, asynchron | POST /approve, /reprocess, /reindex |
| 400 | Ungültige Anfrage | Fehlende Pflichtfelder, ungültiges JSON |
| 401 | Nicht authentifiziert | Fehlender oder ungültiger Token |
| 403 | Nicht autorisiert | Token-Scope reicht nicht, IP nicht erlaubt |
| 404 | Nicht gefunden | Ressource existiert nicht |
| 409 | Konflikt | Optimistic Locking gescheitert, Dublette |
| 422 | Semantischer Fehler | Statusübergang nicht erlaubt, ungültige Referenz |
| 429 | Rate-Limit überschritten | Mehr als 100 Requests/Minute |
| 500 | Serverfehler | Unerwarteter interner Fehler |
| 503 | Service nicht verfügbar | LLM, Qdrant oder ArangoDB nicht erreichbar |
Block 14: Qualitätsmodell und Abnahme
Messbare Qualitätsziele
| Teilbereich | Metrik | Zielwert V1 | Messmethode |
|---|---|---|---|
| Klassifikation | Accuracy (Hauptkategorie) | ≥ 90% | 100 manuell gelabelte Emails als Goldstandard |
| Klassifikation | Accuracy (Unterkategorie) | ≥ 75% | Gleicher Goldstandard |
| Priorisierung | Critical/High korrekt erkannt | ≥ 95% Recall | Goldstandard mit markierten Prioritäten |
| Antwort-Entwurf | Ohne substanzielle Änderung verwendbar (Diff-Anteil ≤ 30% der Zeichenlänge, keine inhaltliche Korrektur) | ≥ 50% | Automatische Diff-Analyse: Levenshtein-Distanz / Entwurfslänge. Manuelle Stichprobe für inhaltliche Korrekturen (20 Emails/Monat) |
| Falsch-positive Automatik | Fälschlich automatisiert | 0 (in V1 keine Automatik) | Audit-Log-Prüfung |
| Falsch-negative Eskalation | Critical nicht eskaliert | 0 | Goldstandard-Abgleich |
| Parsing | Erfolgsquote MIME-Parsing | ≥ 99% | Fehlerprotokoll / Gesamtzahl |
| Thread-Rekonstruktion | Korrekte Zuordnung | ≥ 95% | Stichproben-Prüfung gegen Message-ID-Ketten |
| Retrieval-Relevanz | Precision@5 der Hybrid Search | ≥ 0.7 | Manuelle Bewertung der Top-5-Ergebnisse |
| GUI-Reaktionszeit | Time to First Byte | < 500ms | Browser-Messung |
| API-Stabilität | Uptime | ≥ 99% | Health-Check-Monitoring |
| Wiederanlauf | Recovery nach Ausfall | < 5 Minuten | Simulierter Ausfall + Messung |
| Löschroutine | Vollständige Löschung über alle 3 DBs | 100% | Löschung ausführen + Prüfung auf Rückstände |
| Audit-Vollständigkeit | Jede Aktion protokolliert | 100% | Vergleich Audit-Log vs. erwartete Einträge pro Email |
Testmengen für Abnahme
| Testmenge | Umfang | Zweck |
|---|---|---|
| Goldstandard-Emails | 100 Emails, manuell kategorisiert und priorisiert | Klassifikations- und Priorisierungsgenauigkeit messen |
| Schwierige Randfälle | 20 Emails mit mehrdeutiger Kategorie, gemischtem Sentiment, langen Threads | Robustheit der Klassifikation testen |
| Bösartige Eingaben | 10 Emails mit Prompt-Injection-Versuchen, XSS in HTML-Body, übergroßen Anhängen | Sicherheit testen |
| Lange Threads | 5 Threads mit 20+ Emails, tiefer Verschachtelung | Thread-Rekonstruktion und Kontext-Aufbau testen |
| Mehrsprachige Mails | 20 Emails (Deutsch/Englisch gemischt, inklusive Code-Switching) | Multilingual-Fähigkeit testen |
| Fehlerhafte MIME | 10 Emails mit defektem MIME, fehlendem Content-Type, kaputtem Encoding | Parser-Robustheit testen |
| Anhang-Sonderfälle | 10 Emails mit passwortgeschütztem PDF, Bild ohne OCR-Text, 25MB-Datei | Anhangverarbeitung testen |
Vor der Implementierung zu erstellende Artefakte
Aus diesen 14 Blöcken ergeben sich 12 formale Artefakte, die vor der ersten Codezeile fertiggestellt sein müssen:
- Ziel- und Nicht-Ziel-Dokument (aus Block 1)
- Scope-Dokument V1 (aus Block 2)
- Fachliches Domänenmodell (aus Block 4)
- Sollprozess mit allen Zuständen und Übergängen (aus Block 5)
- Entscheidungs- und Eskalationsmatrix (aus Block 3 + 6)
- Datenmodell für MariaDB, Qdrant und ArangoDB (aus Block 4 + 7)
- Prompt- und LLM-Aufgabenmatrix (aus Block 9)
- Sicherheits- und Datenschutzkonzept (aus Block 10)
- API- und Rollenmodell (aus Block 13)
- Fehler- und Fallback-Katalog (aus Block 12)
- Betriebs- und Lastprofil (aus Block 11)
- Test- und Abnahmekatalog (aus Block 14)