fix(chunker): Outline-first Kapitel-Split (#345)#348
Merged
Conversation
split_by_chapters() bekommt mit pdf_path einen Outline-first-Pfad: Kapitelgrenze
= fitz-aufgeloeste PDF-Lesezeichen-Zielseite, ueber eine exakte physisch->Marker-
Seiten-Map (zwei Zweige: PageLabels-Label-Map bzw. Zaehl-Map nicht-leerer Seiten)
auf den [S. N]-Marker gemappt und per Titel-Wort-Overlap validiert (>=50 %, sonst
HiPS-Kaskade Substring/Fuzzy-80). Fehlgeschlagene Grenzen werden verworfen und ins
Vorsegment gemergt; >50 % Fehlschlaege oder Degenerations-Guards (Median-Segment
< 400 Woerter, Riesensegment > 70 %) -> Outline verwerfen -> heuristischer
Normalpfad. Front-/Backmatter-Filter, Root-Descend (Single-Root -> Ebene tiefer),
Duplikat-Merge (Seitenabstand <=1), Offset-Voting-Kreuzcheck (Confidence-Flag),
Diagnose-Zeile. Ohne nutzbare Outline exakt bisheriges Verhalten; ohne pdf_path
(Alt-Aufrufer/eval) unveraendert.
- _TOC_TRAIL_RE nur erweitert: gespacte Dot-Leader (`. . . 9`) + roemische
Seitenzahlen. _CHAPTER_RE/_is_real_chapter_match unangetastet, test_chapter_regex
bleibt gruen. (Alte Regex liess reale Klingenberg-TOC-Zeilen als Kapitel durch.)
- Chunk.source ("outline"/"heuristic"/"words").
- extract_overview(text, chapters=chunks): ein Split pro Lauf, outline-basiert.
- orchestrator: split_by_chapters(text, pdf_path) + extract_overview(chapters),
Degenerations-Guard mit hartem by-chapter-Abbruch statt Warnung (der 15,5-h-/
32-Mio-Token-/0-Notes-Lauf).
Empirie: Klingenberg 9, DAMA 17, Kuhlen 6 (39-Seiten-Drift), Gantert, Hobohm 12 —
alle Quelle outline, 100 % Validierung. Heuristik-Pfad auf Klingenberg: 117 Chunks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #345 (PR 1 des Implementierungsplans #345/#346, Revision 4).
Architektur-Kurzfassung
split_by_chapters(text, pdf_path)bekommt einen Outline-first-Pfad: Kapitelgrenze = fitz-aufgelöste PDF-Lesezeichen-Zielseite statt fragilem Titel-Matching im Volltext.get_toc(simple=False), Zielseite aus dem fitz-aufgelösten Tupel-Element; NUL-Padding-Bereinigung)._FRONTMATTER_REals SSoT +verzeichnis-Substring + Plan-Termliste), Root-Descend (genau 1 Wurzel → eine Ebene tiefer, Moser-Muster), Duplikat-Merge (Seitenabstand ≤1, längerer Titel gewinnt), Sanity ≥2/≤60.physical_pages_by_anchorbleibt VERBOTEN ([MITTEL] Seitenanker systematisch um ~4-6 PDF-Seiten verschoben — auch bei perfekten Eval-Werten #342-Erbe).N Kapitel erkannt (Quelle: outline, Validierung M/N, K Grenzen verworfen).pdf_path(Alt-Aufrufer/eval) unverändert.Dazu:
_TOC_TRAIL_REnur erweitert (gespacte Dot-Leader, römische Seitenzahlen —_CHAPTER_RE/_is_real_chapter_matchunangetastet);Chunk.source;extract_overview(text, chapters=chunks)-Injektion (ein Split pro Lauf); orchestrator-Aufrufstellen + harter by-chapter-Abbruch bei degeneriertem Split (der 15,5-h-/32-Mio-Token-/0-Notes-Lauf) statt Warnung.Geänderte Dateien
generative/pipeline/pdf_chunker.py— Outline-Logik,_TOC_TRAIL_RE,Chunk.source,extract_overview(chapters=).generative/config.py—MAX_SANE_OUTLINE_CHAPTERS,MIN_CHAPTER_SEGMENT_WORDS.generative/orchestrator.py—split_by_chapters(pdf_path=…),extract_overview(chapters=…), by-chapter-Abbruch-Guard.generative/tests/test_outline_chapter_split.py,test_by_chapter_degeneration_abort.py(neu).RED-Nachweis (vor dem Fix)
test_klingenberg_chapter_count_and_sourceprüftheuristik > 3× outline._TOC_TRAIL_RE(\.{2,}|\s{3,}) matcht die realen Klingenberg-Zeilen mit gespacten Dot-Leadern (1 Einleitung . . . . 1) NICHT → sie wurden fälschlich zu Kapiteln. Erweiterung fängt sie (5 parametrisierte Negativtests aus echten Zeilen).Akzeptanzkriterien v4 PR 1
test_real_book_outline_split(Klingenberg/DAMA/Kuhlen/Gantert/Hobohm): Quelle outline, Kapitelzahl im Band, 100 % der Grenzen passieren das Validierungs-Gate. Kuhlen (39-Seiten-Drift, kein PageLabels) = eigener Test, 6 Teile A–F. Echte PDFs sind Tills Privatbestand →pytest.skipohneATOMIC_AGENT_TEST_BOOK_DIR(in CI übersprungen); lokal 8/8 grün verifiziert.test_no_outline_falls_back_to_normal_path(synthetisch, läuft überall) +test_no_pdf_path_is_backward_compatible.test_overview_budget_from_chapters≤1700 Wörter (real Klingenberg 1554 vs. 88.725 Volltext; N2-Baseline war ~21k).test_chapter_regex.py).test_labels_branch_maps_to_printed_label(Druckseite = phys+100 → Grenze mappt auf[S. 103], nicht den phys. Index).test_root_descend_single_root(1 Wurzel, 3 L2-Kinder).Suite-Zahlen
uv run pytest generative lib/decision_engine/tests shared/tests -q→ 6268 passed, 11 skipped (davon 8 echte Buch-Tests ohne gesetztesATOMIC_AGENT_TEST_BOOK_DIR), 9 deselected, 0 failed (418 s). Mit gesetztem Buch-Verzeichnis zusätzlich 8/8 echte Buch-Tests grün (197 s).ruff check+ruff format --checksauber.Abweichungen vom Plan (mit Begründung)
literatur/abbildungals Substring droppten fälschlich Ganterts echtes Kapitel „Zweiter Teil. … Literatur, Bücher, Medien". Regressionstesttest_gantert_keeps_literatur_content_chapter. Folge: einzelne Backmatter-Sektionen (z. B. „Weiterführende Literatur") können als validierte Chunks durchrutschen — harmlos (echte Dokumentsektionen), kein Verlust von Inhaltskapiteln.ATOMIC_AGENT_TEST_BOOK_DIR(Defaultconfig.LITERATURE_DIR) und skippen bei Abwesenheit — keine hartcodierten Pfade, CI-tauglich; die synthetischen Fixtures (c/d/e/g/h) sind die dauerhafte Abdeckung._validate_boundary(die echte Gate-Funktion inkl. Fuzzy) statt nacktem Wort-Overlap: Klingenberg-Body hat Encoding-Mojibake („Datenqualit�t"), Hobohm-Teil-Divider zeigt auf die erste Inhaltsseite („Informationsforschung") — beide korrekte Grenzen, die reiner Overlap fälschlich abgelehnt hätte.