LLM-Halluzinationen: drei Citation-Cases mit erfundenen Seiten/Snippets #60
Labels
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: tobias/gwoe-antragspruefer#60
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Befund (2026-04-09, autonomer Roadmap-Run #59)
Erster Live-Lauf von Sub-Issue D (Citation Property-Verification, #54) gegen die Prod-DB im Container hat nach den Test-Härtungen aus Commit
b76c08ddrei Cases identifiziert, in denen das LLM eine Quelle so zitiert, dass die angegebene PDF-Seite den Snippet nicht enthält — auch nicht über Anker-Match (5-Wort-Sequenz wortwörtlich im PDF). Das sind die übrig gebliebenen 3 von ursprünglich 15 Failures, die anderen 12 waren Test-False-Positives.Cases
1. NRW GRÜNE Drucksache 18/9605, S. 58 von gruene-nrw-2022
Komplett verschiedene Themen — entweder hat das LLM die Seitenzahl erfunden (das Wahlalter steht möglicherweise auf einer anderen Seite des GRÜNE-NRW-Programms) oder den Snippet konfabuliert. "Wir sollen" als Formulierung deutet auch nicht auf einen LLM-eigenen Programm-Text hin, sondern auf eine Paraphrasierung.
2. NRW BÜNDNIS 90/DIE GRÜNEN Drucksache 18/18100, S. 36 von Grundsatzprogramm 2020
Das Bundes-Grundsatzprogramm 2020 hat Inhalt zu Plattform-Regulierung, aber die Seitenzahl trifft offenbar nicht. Möglicherweise rephrased oder cross-referenced aus mehreren Stellen.
3. LSA SPD Drucksache 8/6645, S. 37 von spd-lsa-2021
Komplett anderes Thema. Der Snippet klingt sehr nach SPD-Bundestagsprogramm-Boilerplate, möglicherweise hat das LLM aus seinem Trainings-Wissen geschöpft statt aus dem retrieved chunk.
Hypothesen
Acceptance Criteria
gwoe-antraege.dbextrahieren und prüfen, welche Chunks dem LLM tatsächlich übergeben wurden (format_quotes_for_prompt-Output reproduzieren)Umfang
3 von 36 substring-getesteten Citations = ~8% Halluzinations-Rate auf der getesteten Stichprobe. Die Stichprobe war die letzten 5 Assessments pro aktivem BL aus der Prod-DB. Bei einer realistischen jährlichen Run-Rate von ~200 Anträgen wären das ~16 erfundene Citations pro Jahr, was die Glaubwürdigkeit der Reports schwächt.
Refs: #54, #59 (Sub-D Live-Verifikation), #50 (Umbrella E2E)
Resolved (2026-04-09 Fortsetzung)
Fix in
db3ada9(vorbereitende Regression-Fix ined64399).Diagnose
In-Process-Reproduktion mit
get_relevant_quotes_for_antrag()zeigte: die Halluzinationen waren kein Embedding-Bug, sondern reines LLM-Compliance-Problem.Fix (Variante A + C)
A — ENUM-Anker in
format_quotes_for_prompt:[Q1]/[Q2]/...-ID im Prompt.[Qn]verweisen, dertext-String MUSS eine wörtliche, zusammenhängende Passage von ≥5 Wörtern aus genau diesem Chunk sein,quelleMUSS exakt das Source-Label des Chunks sein. Wenn kein Chunk passt → leereszitate-Array.analyzer.get_system_promptzieht die Regel im System-Prompt nach.C — Recall-Boost
top_k_per_partei2 → 5. Bringt in Case 2 die zuvor fehlende S.36 ins Window.Verifikation
Die 3 Drucksachen wurden aus der Prod-DB gelöscht und über die normale
/api/analyze-drucksache-Pipeline neu analysiert. Anker-Match-Verifikation (Substring oder 5-Wort-Sequenz vs. tatsächlich retrievte Chunks):Alle vorher halluzinierten Snippets sind durch verifizierbare Originalzitate ersetzt:
Side-Befund — separater Critical Bug
ed64399Während der Untersuchung gefunden:
embeddings.py:528referenzierte nach dem #55-Refactor noch eine undefiniertepartei_upper-Variable. NameError wurde im breitenexcept Exceptioninanalyzer.py:249verschluckt → seiteb045d0(heute 11:22) wären alle Analysen still über den Keyword-Fallback statt Embeddings gelaufen. Im Prod waren noch keine neuen Assessments betroffen (0 neue seit 11:22), aber der Bug hätte alle künftigen Analysen entwertet. Fix:partei_upper→partei_lookupanalyzer.py:249fängt nicht mehrNameError/AttributeError/TypeError/KeyError— Programmierfehler im Embedding-Pfad sollen hart fehlschlagen statt still degradierentests/test_embeddings.pyTests
179/179 grün (177 + 2 neue: ENUM-ID-Format, Regression für
partei_upper).Closing.
Reopened — A+C war insuffizient (Sub-D Live-Run)
Direkt nach dem db3ada9-Deploy habe ich Sub-D () gegen die frische Prod-DB-Kopie laufen lassen: 45 passed, 1 failed. Der Fail war kein Pre-Fix-Stale, sondern ein Post-Deploy-Case des A+C-Pfades:
BB 8/673 BSW zitiert "Wertschätzung für Lehrerinnen und Lehrer, Abbau von Arbeitsüberlastung..." als . PDF S.4 ist die Cover-/Adressseite. Volltext-Suche im PDF: der Snippet steht real auf S.27. Reproduktion der Embedding-Retrieval (top_k=5):
→ Das LLM hat Text aus dem S.27-Chunk kopiert, in aber die Seitenzahl des Top-2-Chunks (S.4) geschrieben. Klassischer Cross-Mix zwischen Q-IDs. A+C kann das nicht verhindern, weil das -Tag nur ein weicher Anker im Prompt ist — der LLM darf den Snippet aus Qn nehmen und in was anderes schreiben.
Fix Option B — server-seitige Quellen-Rekonstruktion ()
Neue Funktion läuft im Analyzer nach aber vor Pydantic-Validation:
Damit kann der LLM nur noch sauber zitieren oder gar nicht — es gibt keinen Pfad mehr zu "echter Text, falsche quelle".
Verifikation
Nach Deploy: BB 8/673 + BB 8/310 (gleiches Window, gleicher Bug) + BUND 21/4275/4940/3660 (drei zwischenzeitlich vom User erzeugte Bundestags-Assessments aus dem A+C-only-Window, davon zwei mit identischem Cross-Mix) gelöscht und über die normale -Pipeline neu erzeugt. Sub-D-Re-Run gegen die frische Prod-DB-Kopie:
→ 0 Halluzinationen über alle aktiven Bundesländer (NRW, LSA, BE, MV, BB, BUND, …).
Tests
185/185 grün (179 + 6 neu in ):
Closing again.