Zum Inhalt springen

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.

ctx categoryaus Klassifikation
spam: kein EntwurfAntwort würde Adresse bestätigen
newsletter: kein Entwurfkeine individuelle Erwartung
Marke Entwurf-vorhanden = falschEntwurfs-Schritt ohne Sprachmodell-Aufruf beendet
UI sieht beide Kategorien als bearbeitet

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.

needsReply: 9 Kategorienbeschwerde, anfrage, bestellung, ...
Optional: information, intern, unknownModell entscheidet
Optional + SKIP/leer: kein Entwurfhas_draft = false
Pflicht + SKIP: Standardtext"Danke für Deine Nachricht..."
Antwortpflicht hat Vorranglieber Standardantwort als Schweigen

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.

Modell-Antwort prüfenleer ODER SKIP ODER beginnt mit SKIP
Optional + SKIP: akzeptierenkein Entwurf, return
Pflicht + SKIP: überschreibenStandardtext einsetzen
"Danke für Deine Nachricht. Ich melde mich zeitnah bei Dir."Standardtext für Pflicht-Mails
Modell darf SKIP zurückgeben, Pflicht hat Vorrang

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.

Ende Entwurfs-Schrittin jedem Fall has_draft setzen
has_draft = trueEntwurf in drafts-Tabelle
has_draft = falsespam, newsletter oder optional+SKIP
queue.completed Audit-Loghas_draft im Eintrag
Auswertungen ohne drafts-Tabelle möglich