Antwort-Entwurf
Warum dieser Schritt
Warum ein Entwurf vom Sprachmodell
Die vorangegangenen Pipeline-Schritte haben die Email zerlegt, eingeordnet und mit passendem Vorwissen angereichert. Der Antwort-Entwurfs-Schritt führt diese Vorarbeit zu einem konkreten Antworttext zusammen, den der Operator anschließend prüfen kann.
Drei Begründungen rechtfertigen die Position dieses Schritts in der Pipeline:
- Vorbereitung statt Automation: Der Entwurf ist ein Vorschlag, kein Versand. Der Operator behält die Entscheidung darüber, ob, wie und wann eine Antwort hinausgeht (siehe Mensch im Loop).
- Nach dem Wissen, vor dem Versand: Erst wenn Klassifikation, Wissensextraktion und Retrieval ihre Ergebnisse abgelegt haben, hat das Sprachmodell genug Kontext für einen sinnvollen Entwurf.
- Skip statt Vermutung: Bei eindeutig nicht antwortwürdigen Kategorien (spam, newsletter) erzeugt der Schritt keinen Entwurf, statt einen unpassenden Text zu generieren, den der Operator dann verwerfen müsste.
Wann kein Entwurf erzeugt wird
Die Suche hat passenden Kontext aus vorhandenem Wissen zusammengestellt. Bevor ein Antwortentwurf entsteht, prüft der Entwurfs-Schritt zuerst, ob einer überhaupt sinnvoll ist.
Wann wird bewusst kein Entwurf erzeugt?
Nicht jede Email braucht eine Antwort. Spam soll keine bekommen, ein Newsletter auch nicht. Manche kategorisierten Emails brauchen optional eine Antwort, andere zwingend. Bevor der Entwurfs-Schritt das Sprachmodell aufruft, prüft er, ob ein Entwurf sinnvoll ist. Vier Aspekte zeigen die Skip-Logik im Detail.
Wichtig: SPAM UND NEWSLETTER ERHALTEN KEINEN ANTWORTENTWURF. DER ENTWURFS-SCHRITT BEENDET SICH FÜR DIESE KATEGORIEN OHNE AUFRUF DES SPRACHMODELLS.
Kategorien mit Skip-Verhalten
Direkt am Anfang des Schritts steht eine kurze Prüfung: $cat = $ctx["category"] ?? "unknown"; if (in_array($cat, ["spam", "newsletter"], true)) { $ctx["has_draft"] = false; return; }. Bei diesen beiden Kategorien wird das Sprachmodell gar nicht erst befragt, der Entwurfs-Schritt beendet sich sofort. Welche Kategorien übersprungen werden, ist im Klassifikations-Katalog hinterlegt, nicht im Code. Pro Kategorie ist eine Antwort-Vorgabe gepflegt: Antwort erwartet, kein Entwurf, oder Entscheidung durch das Sprachmodell. Neue Kategorien lassen sich über die Admin-Oberfläche ergänzen, ohne den Code zu ändern.
Die Begründung ist pragmatisch: Spam soll keine Antwort bekommen, weil das den Absender bestätigen würde, dass die Adresse aktiv ist. Newsletter sind massenhaft versandte Inhalte ohne Erwartung einer individuellen Antwort. Beide Kategorien werden in der Bedienoberfläche als bearbeitet markiert, ohne dass jemand sie anfassen müsste.
Andere Kategorien gehen weiter
Alle anderen Kategorien laufen durch die Antwort-Generierung, auch wenn nicht jede zwingend eine Antwort braucht. Eine reine Information (zum Beispiel eine Server-Statusmail, die nicht durch eine Vorfilter-Regel gefangen wurde) wird zwar an das Modell geschickt, aber das Modell kann selbst entscheiden, ob ein Entwurf sinnvoll ist.
Im Code wird zwischen needsReply und nicht-needsReply unterschieden: $needsReply = in_array($cat, ["beschwerde","anfrage","bestellung","bewerbung","kooperation","rechtliches","terminänderung","feedback","persönlich"], true);. Die im Array genannten Kategorien sind genau diejenigen, für die im Klassifikations-Katalog eine Antwort erwartet wird. Der Array sollte zukünftig dynamisch aus dem Katalog generiert werden, damit Drift zwischen Code und Katalog vermieden wird.
Die übrigen Kategorien (information, intern und alles, was nicht in der Liste steht) gelten als optional. Wenn das Modell hier "SKIP" antwortet oder einen leeren Text liefert, wird kein Entwurf gespeichert. Das deckt zum Beispiel reine Bestätigungs-Mails ab, die sinnvoll keine eigene Antwort verdienen.
SKIP als Modell-Signal
Das Sprachmodell kann dem System mitteilen, dass keine Antwort sinnvoll ist, indem es im Antwort-Text das Wort "SKIP" zurückgibt oder die Antwort mit "SKIP" beginnt. Im Code wird das so geprüft: $text === "" || strtoupper($text) === "SKIP" || str_starts_with(strtoupper($text), "SKIP").
Dieses Signal hat unterschiedliche Wirkung je nach Antwortpflicht. Bei optionalen Kategorien wird das Signal akzeptiert: kein Entwurf, has_draft auf false, return. Bei antwortpflichtigen Kategorien überschreibt der Code das SKIP-Signal mit einem Standardtext: "Danke für Deine Nachricht. Ich melde mich zeitnah bei Dir." Damit existiert immer ein Entwurf, auch wenn das Modell keine eigene Antwort liefert.
Marke "Entwurf vorhanden" als Pipeline-Signal
Am Ende des Entwurfs-Schritts steht in jedem Fall ein Wert in $ctx["has_draft"]: true wenn ein Entwurf erzeugt und gespeichert wurde, false wenn der Schritt ohne Entwurf endete. Diese Marke wird vom QueueWorkerService gelesen und ins abschließende queue.completed-Audit-Log geschrieben.
Damit ist im Nachhinein nachvollziehbar, ob für eine Email ein Entwurf existiert, ohne in die drafts-Tabelle schauen zu müssen. Eine Auswertung kann zählen, wie viele eingehende Mails Entwürfe ausgelöst haben und wie viele übersprungen wurden, geordnet nach Kategorie.