Files
creator/AUFTRAG-inventar-fix.md
2026-07-05 23:03:00 +02:00

99 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Auftrag: Inventar-Pipeline reparieren — zu viele, fehlende und schlecht benannte Bausteine
## Kontext
Das Thema `aak` (Uni-Quelle `uni/aak`: Skript + Serien + Klausuren + Hausaufgaben) erzeugte **108 Bausteine**. Soll-Abgleich mit dem Skript ergibt: sinnvoll wären **~6070** (davon ~4045 Skript-Kern). Der letzte QA-Lauf (`storage/qa/aak/20260705-201016.json`) zeigt 36 % Dubletten-Verdacht bei Note 10.0 und Lücken-Quote 0.0 — obwohl Kernkonzepte fehlen.
Zahlenfluss des Laufs (aus `storage/topics/aak/arbeit/inventar-*.json`):
~1150 Roh-Nennungen (24 Chunks × 2 Research-Pässe) → 203 Kandidaten → Filter degradiert 69, Recheck holt 17 zurück → 134 → Dedup prüft 183 Paare, merged nur 17 (9 %) → 117 → Gruppierung: 5 Umbrellas, 17 Umbrella-Merges per Type-Gate verworfen → Supplement +4 → **108**.
Die Pipeline-Stages stehen in `inventory_stages` (`backend/board_inventory.py:1614`). Alle Zeilennummern unten beziehen sich auf den Branch `refactor` inkl. der offenen (uncommitteten) Diffs — siehe „Leitplanken".
## Befund: sechs Root Causes
### R1 — Extraktion erlaubt Beweisschritt-Granularität (Hauptursache „zu viele")
- `templates/Prompt/Blocks-Research.md` erklärt „named theorems with their own statement" und jede „standalone REDUCTION" zum eigenen Baustein. `templates/Prompt/Blocks-Source-Uni.md` nennt „formulas and theorems" als Zielkategorie, ohne Granularitätsgrenze.
- Folge im aak-Lauf: 14 Beweisschritt-/Beweis-Objekt-Bausteine (z. B. `Type-I`, `Schedule D*`, `Gewichtsfunktion d2`, `Zick-Zack-Tour`, `Optimaler Rundweg OPT((Kn,d2)) = n`, `3-SAT ≤ K-COLOR: G=(V,E) Konstruktion`) und 8 Bausteine für EIN Konzept („untere Schranken via ETH": `Lower Bound via k/n/|E|/|T|` + 4 weitere).
- Verstärker: `_is_named_statement` (`backend/blocks.py:1355`) schützt alles mit Reduktions-Syntax vor Demotion — auch Konstruktionsdetails wie `…: G=(V,E) Konstruktion`.
### R2 — Quellen-Nummern als Titel, Reparaturstufe greift nicht (Hauptursache „Benennung")
- Research-Pässe übernehmen Skript-Nummerierungen als Titel („Satz 7.13", „Bemerkung 7.22", „Definition 6.19"). 6 finale Bausteine tragen reine Satz-/Bemerkungs-Nummern.
- `_REFERENCE_RE` (`backend/blocks.py:1022`) matcht nur nackte Referenzen („Bemerkung 7.22"), NICHT „Satz 7.13 (Christofides)" oder „Satz 7.6: Kriterium für Eulerschen Kreis" — die laufen als „gute" Titel durch.
- Rename ist Best-Effort: In `clarify` wird nur umbenannt, wenn ein Judge freiwillig `rename` liefert (`backend/board_inventory.py:663`). Beispiel Cluster `08c52a84`: Judge j1 schlug für „Bemerkung 7.22" den korrekten Konzeptnamen vor („Untere Schranke für FPTAS …"), die Aggregation verwarf ihn.
- Singletons überspringen Naming komplett: `_choose_title` läuft nur bei `len(rows) > 1` (`backend/board_inventory.py:725` und `:802`).
### R3 — Titel-Split zerschneidet Namen (abgeschnittene Titel)
- `_title = entry.split(" — ")[0]` (`backend/textkit.py:35`) + aggressives `_norm_dash` (`backend/textkit.py:77`): ein en-Dash MIT einseitigem Space **innerhalb** eines Titels wird zum Separator → Split mitten im Titel.
- Es gibt keine Klammer-Balance-Prüfung und keinen Repair. Ergebnis: `Aε-Algorithmus (Güte 1+ε, Laufzeit O(n3` und `N P via nicht-deterministische Turingmaschine (Definition 6.` liefen wortwörtlich bis `done`.
- (Die 60-Zeichen-Kürzung in `backend/board_artefacts.py:81` betrifft nur Ordnernamen, nicht Titel — kein Fix nötig.)
### R4 — Dedup-Dämme brechen (36 % Verdacht überleben)
- Merge braucht Einstimmigkeit zweier Judges (`if ja1 and ja2`, `backend/board_inventory.py:1200`); „uneinig" → kein Merge, kein Tiebreak.
- Parameter-Familien werden als Paare je einzeln mit „nein" beschieden (`Lower Bound via k``n``|E|``|T|`, alle QA-Jaccard 1.0).
- Kandidaten-Generierung verpasst Komposita: `_canonical_key` (`backend/blocks.py:1118`) trennt „Cliquenproblem" nicht in „Clique"+Stopwort → „Clique" vs. „Cliquenproblem (k-Clique)" wird ggf. nie Judge-Kandidat.
- Type-Gate der Gruppierung verwarf 17 Umbrella-Merges (`grund: "type-gate"` in `inventar-gruppierung.json`) — exakt die späteren QA-Dubletten (Approximations-Grundbegriffe, Type-I/II+Schedule-Objekte, NP-Definitionen).
- Filter-Recheck holte 17 korrekte Degradierungen zurück (`ueberstimmt`/`floor_veto` in `inventar-filter-535eb84c.json`) — darunter die komplette Lower-Bound-Familie.
### R5 — Echte Lücken trotz Quote 0.0 (Ursache „fehlende")
- Fehlend im aak-Inventar: **SAT/Satz von Cook-Levin** (zentralster Satz), **MGA (Güte 2)**, **Sahni-PTAS Ak**, **Satz von Lawler**, **Satz 6.16 (P=NP-Kriterium)**, Lastfunktion (Def 7.26, per Supplement teilweise nachgeholt).
- QA-Lücken sind token-basiert (`backend/qa.py:163`): 108 übergranulare Bausteine decken jede Sektion irgendwie ab → Konzeptlücken unsichtbar, Supplement (`_proc_gap_check`) ergänzt zu wenig.
- Verlust-Vektor upstream: `clarify` verlangt für Einzel-Reader-Funde Einstimmigkeit aller Judges (`accept = v >= len(outs)`, `backend/board_inventory.py:657`) — ein einziges „nein" verwirft einen real existierenden Fund als `failed-quorum`.
### R6 — QA-Note ist blind für das Problem
- `NOTE_GEWICHTE` (`backend/qa.py:40`) enthält keine Dubletten. 36 % Verdacht + Verschlechterung zum Vorlauf (+0.06) ergeben trotzdem Note 10.0.
## Maßnahmen (priorisiert; M1M3 = Ursachen, M4M5 = Dämme, M6 = Messung)
### M1 — Titel-Hygiene, deterministisch
1. `_norm_dash`/Titel-Split klammer-bewusst machen: Dash innerhalb offener Klammer ist KEIN Separator. Nach dem Split: unbalancierte Klammern → Titel reparieren (bevorzugt: Split rückgängig / nächsten Separator nehmen).
2. `_REFERENCE_RE` erweitern: auch `Satz 7.13 (Christofides)`, `Satz 7.6: Kriterium …`, `… (Definition 6.19)` erkennen. Deterministischer Strip: Referenz-Präfix/Suffix entfernen, Konzeptrest behalten („Satz 7.6: Kriterium für Eulerschen Kreis" → „Kriterium für Eulerschen Kreis"). Reine Nummern ohne Konzeptrest („Bemerkung 7.22") → Rename-PFLICHT (Judge muss Konzeptnamen liefern, sonst bleibt die Karte vor `done` hängen bzw. wird saniert), statt Best-Effort.
3. Naming/Sanierung auch für Singletons erzwingen, wenn Titel `_is_reference`, klammer-unbalanciert oder >80 Zeichen ist (`board_inventory.py:725`, `:802`, `_sanierung_noetig` `:492`).
### M2 — Granularitätsgrenze in den Prompts
1. `Blocks-Research.md` + `Blocks-Source-Uni.md` (und Geschwister `Blocks-Source-*.md`): Negativ-Liste ergänzen — KEINE eigenen Bausteine sind: einzelne Beweisschritte, Hilfskonstruktionen/Beweis-Objekte (Hilfs-Schedules, Gewichtsfunktionen, konstruierte Touren), berechnete Einzel-Instanzen, Parameter-Instanzen desselben Arguments (dieselbe Technik „via k / via n / via |E|" = EIN Baustein). Solche Inhalte gehören als Aspekt in den Eltern-Baustein.
2. Titel-Regel in beide Naming-Prompts (`Blocks-Naming.md`, `Blocks-Klaerung.md`, `Blocks-Sanierung.md`): Titel ist der KONZEPTNAME; Quellen-Nummerierungen („Satz 7.13") sind als Titel verboten, dürfen aber in der Beschreibung stehen.
3. Sätze bleiben erlaubt als Baustein, wenn sie ein eigenständig prüfbares Ergebnis sind (Cook-Levin, Christofides) — benannt nach dem Ergebnis, nicht der Nummer.
### M3 — Fragment-Filter nachschärfen
1. `_is_named_statement` (`blocks.py:1355`) verengen: Reduktions-Syntax mit Konstruktions-Suffix (Doppelpunkt/Zusatz NACH der Relation, z. B. `…≤ K-COLOR: G=(V,E) Konstruktion`) ist NICHT geschützt, sondern fragment-verdächtig.
2. Recheck-Overturn erschweren: `ueberstimmt` nur bei einstimmigem Recheck-Panel statt Mehrheit (`board_inventory.py:1031`). Beleg: 15 von 17 zurückgeholten Karten waren spätere QA-Dubletten.
3. Parameter-Varianten als Verdacht markieren: Titel-Paare, die sich nur durch ein Parameter-Token unterscheiden (`via k`/`via n`/`via |E|`), in `_filter_suspect` bzw. als eigene Familien-Heuristik aufnehmen → Filter-/Dedup-Kandidat.
### M4 — Dedup-Recall erhöhen (ohne Fehlmerge-Risiko aufzugeben)
1. Die drei offenen Diffs (Richtungs-Guard `_direction_conflict`, Judge-Kanal-Guard, Dedup-Prompt-Zeile) BEHALTEN — sie sind Teil der Lösung.
2. Tiebreak statt Veto: bei 1-1-Judge-Split (`uneinig`, `board_inventory.py:1200`) einen dritten Judge entscheiden lassen.
3. Familien-Kollaps: Cluster von ≥3 Titeln, die sich nur im Parameter-Token unterscheiden, als EIN Merge-Fall (ein Judge-Call für die Familie) statt n² Einzelpaare.
4. `_canonical_key` (`blocks.py:1118`): Kompositum-Suffix „…problem" auch verklebt abtrennen („Cliquenproblem" → Stamm + Stopwort), damit solche Paare Kandidaten werden. Generisch halten (Suffix-Regel, keine Wortliste pro Domäne).
5. Type-Gate der Gruppierung (`_GROUP_STANDALONE`-Pfad) lockern: Umbrella-Merge nicht allein wegen gemischter Content-Typen (Definition+Satz+Algorithmus) verwerfen, wenn das Judge-Panel die Gruppe bestätigt hat. Die 17 verworfenen Fälle aus `inventar-gruppierung.json` sind der Prüfmaßstab.
6. NICHT anfassen: die Varianten-Regel „k-X ≠ X / Max-X ≠ X" im Dedup-Prompt und das 2-Judge-Panel für klare Fälle. Bisher 0 Fehlmerges — das muss so bleiben.
### M5 — Lücken schließen
1. `clarify`-Quorum für Einzel-Reader-Funde von Einstimmigkeit auf Mehrheit senken (`board_inventory.py:657`) ODER abgelehnte Funde als Fragment aufbewahren statt `rejected`, damit `gap_check`/Supplement sie wiederfinden kann.
2. Gap-Check konzeptbasiert ergänzen: benannte Ergebnisse des Korpus (Sätze/Definitionen mit eigener Aussage, Abschnittsüberschriften) gegen das Inventar prüfen, nicht nur Token-Abdeckung. Ein benanntes Kernresultat ohne zugeordneten Baustein = Lücke → Supplement-Kandidat.
3. Prüffälle für aak (müssen nach einem Neulauf existieren): SAT/Cook-Levin, MGA, Sahni-PTAS Ak, Satz von Lawler, P=NP-Kriterium (Satz 6.16).
### M6 — QA ehrlich machen
1. `dubletten_verdacht` in `NOTE_GEWICHTE` aufnehmen (`qa.py:40`), damit die Note das Nutzerproblem abbildet.
2. Optional: konzeptbasierte Lücken (aus M5.2) als eigene QA-Quote ausweisen.
## Leitplanken
- **Generisch bleiben**: keine aak-/Komplexitätstheorie-Sonderregeln in Pipeline oder Prompts. aak ist nur der Prüffall. Alle Heuristiken müssen für beliebige Themen tragen.
- **YAGNI**: kleine, gezielte Diffs; bestehende Muster (Judge-Panels, Schema-Parser, Kanban-Stages) wiederverwenden; nichts Neues bauen, was eine Prompt-Zeile löst.
- **Offene Diffs behalten**: `backend/blocks.py`, `backend/board_inventory.py`, `templates/Prompt/Blocks-Dedup.md` enthalten uncommittete Änderungen (Richtungs-Guard). Nicht verwerfen, darauf aufbauen.
- **Niemals `git commit` oder `git push`** — der Nutzer committet selbst.
- **Fehlmerge-Bilanz**: Recall-Erhöhungen (M4) dürfen keine falschen Merges einführen. Jede Lockerung braucht einen Judge-Beleg-Pfad; deterministische Auto-Merges bleiben streng.
- **Keine Echtläufe ohne Freigabe**: `make qa TOPIC=aak` mit LLM-Judges und komplette Neuläufe kosten Tokens — nur nach Rückfrage beim Nutzer starten.
## Validierung (in dieser Reihenfolge)
1. `pytest` in `backend/` — bestehende Tests dürfen nicht brechen; für M1 (Titel-Split, Referenz-Regex) und M4.4 (`_canonical_key`) gezielte Unit-Tests ergänzen (Fixtures: die realen Schadensfälle aus diesem Auftrag).
2. `make test-e2e` — Fake-E2E (`backend/fake_agents.py`) muss grün bleiben; Störfall-Matrix ggf. um Referenz-Titel und unbalancierte Klammern erweitern.
3. QA-Fehler-Injektion (Muster siehe QA-Modul-Tests): injizierte Satz-Nummern-Titel, Parameter-Familien und Konzeptlücken müssen von den geänderten Checks erkannt werden.
4. Erst nach Nutzer-Freigabe: Neulauf aak + `make qa TOPIC=aak`. Erfolgskriterien:
- Baustein-Anzahl ~6070 (statt 108),
- `dubletten_verdacht` < 0.10 (statt 0.361),
- die 5 Prüffall-Konzepte aus M5.3 existieren,
- kein Titel matcht die erweiterte Referenz-Erkennung, keine unbalancierten Klammern,
- Lücken-/Fremd-Quote nicht schlechter als der Referenzlauf `20260705-201016.json`.