Sub-D Live-Run gegen Prod-DB nach dem db3ada9-Deploy hat einen neuen
Halluzinations-Case gezeigt, den A+C nicht gefangen hat:
BB 8/673 BSW: text aus bsw-bb-2024 S.27 (verifiziert via Volltext-Suche
im PDF), aber LLM hat im quelle-Feld "S. 4" angegeben — die Seite des
Top-2-Chunks im selben Retrieval-Window. Klassischer Cross-Mix zwischen
Q-IDs.
Strukturelle Diagnose: Das [Qn]-Tag aus A ist nur ein weicher Anker im
Prompt. Das LLM darf Text aus Chunk Qn kopieren und trotzdem die quelle
aus Chunk Qm zusammenbauen. Die ZITATEREGEL kann das nicht verhindern,
solange wir der LLM-Selbstauskunft vertrauen.
Fix (Option B aus dem ursprünglichen Plan):
`embeddings.reconstruct_zitate(data, semantic_quotes)` läuft im
analyzer **nach** json.loads aber **vor** Pydantic-Validation:
1. Flachen die retrievten Chunks aller Parteien zu einer einzigen Liste.
2. Pro Zitat: text via Substring oder 5-Wort-Anker gegen alle Chunks
matchen (Helpers `find_chunk_for_text` + `_normalize_for_match`,
identische Logik wie Sub-D Test).
3. Match → quelle/url server-seitig durch _chunk_source_label und
_chunk_pdf_url des matchenden Chunks ÜBERSCHREIBEN.
4. Kein Match → Zitat verworfen (statt mit erfundener quelle persistiert).
Damit kann der LLM nur noch sauber zitieren oder gar nicht — es gibt
keinen Pfad mehr zu "echter Text, falsche quelle".
Tests:
- TestReconstructZitate (5 cases): BB 8/673 Re-Mapping, Drop bei
hallucinated, no-op bei leeren chunks, anchor-match-Fallback,
short-needle und soft-hyphen Edge-Cases
- 185/185 grün (179 + 6 neu)
Refs: #60, #54 (Sub-D)