fix(#170): PM-Prompt — Paragraphen-Trennung mit \\n\\n erzwingen

User-Beobachtung im Draft #6: qwen-max nutzte einsame Anfuehrungs-
zeichen (") als Paragraph-Trenner statt \\n\\n. Optisch wirkte das
wie inkorrekte JSON-Escapes mitten im Text.

Zwei Mechanismen:

**1. Prompt-Erweiterung:**
Neuer Abschnitt "Paragraphen-Formatierung" mit explizitem Beispiel:
`"body": "Lead.\\n\\nWirkung 1.\\n\\nWirkung 2.\\n\\n..."`. Klar:
keine Anfuehrungszeichen oder Sonderzeichen als Trenner.

**2. Post-Process-Heuristik:**
Regex `([.!?])"([A-ZÄÖÜ])` → `\\1\\n\\n\\2`. Wenn ein " genau zwischen
Punkt+Whitespace und Großbuchstabe steht, ist es wahrscheinlich ein
Trenn-Klumpen, kein semantischer Anfuehrer. Wird durch echten
Paragraph-Break ersetzt.

Konservativ: nur dieses spezifische Pattern wird touched. Echte
Quotes (z.B. "Es ist Zeit, …", sagt X) bleiben unangetastet, weil sie
nicht direkt nach Satzschluss-Punkt stehen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dotty Dotter 2026-05-06 02:30:58 +02:00
parent aef8f83a08
commit 0377cf4bd9

View File

@ -111,6 +111,19 @@ Drücke das aus über:
5. **Was fehlt / was wäre besser** (2-3 Sätze): konkreter Vorschlag
6. **Schluss-Satz**: was wir fordern, ohne Floskel
## Paragraphen-Formatierung
WICHTIG: trenne die 6 Abschnitte mit **doppeltem Newline** (`\\n\\n`)
im JSON-String. NIEMALS Anführungszeichen oder andere Sonderzeichen
als Paragraph-Trenner verwenden. Beispiel:
```json
{"body": "Lead-Satz.\\n\\nWirkung 1.\\n\\nWirkung 2.\\n\\nWirkung 3.\\n\\nWas fehlt.\\n\\nForderung."}
```
Im JSON: `\\n` als Escape-Sequenz (zwei Zeichen: Backslash + n).
NICHT: rohe Newline-Bytes im String, NICHT: `"`-Zeichen als Trenner.
## BEISPIELE für den Stil
**SCHLECHT** (verboten):
@ -314,10 +327,15 @@ async def generate_draft(
titel = (result.get("titel") or "").strip()[:200]
body = (result.get("body") or "").strip()
# Post-Process: qwen-max liefert manchmal literal-escapte Sequenzen
# ('\\n' als 2 chars statt echtem Newline). UI braucht echte Newlines
# fuer korrekte Paragraphen-Trennung. Konservativ: nur \\n / \\r / \\t.
# Post-Process Step 1: literal-escapte Sequenzen → echte Whitespaces.
# qwen-max liefert manchmal '\\n' als 2 chars statt echtem Newline.
body = body.replace("\\n", "\n").replace("\\r", "\r").replace("\\t", "\t")
# Post-Process Step 2: einsame Anführungszeichen mitten im Text als
# Paragraph-Trenner — qwen tut das gelegentlich trotz Prompt-Anweisung.
# Heuristik: ein " zwischen "Punkt-Whitespace" und "Großbuchstabe" ist
# wahrscheinlich ein Trenn-Klumpen, kein semantischer Anfuehrer.
import re as _re
body = _re.sub(r'([.!?])"([A-ZÄÖÜ])', r'\1\n\n\2', body)
if not titel or not body:
raise ValueError("LLM-Response unvollständig (titel oder body leer)")