# Plan: Heading-basiertes Chunking (umgesetzt fuer INB) Status: **umgesetzt (Phase 1)**. Die Entscheidungen unten sind getroffen; das Chunking ist als deterministischer, embedding-freier Schritt implementiert und fuer die INB aktiv. ## Getroffene Entscheidungen (Phase 1) - **Ablage:** separater Baum `output/chunks////` (Option A). Das Voll-Dokument in `output/processed/` bleibt der primaere Feed. - **Chunks getrennt vom approved-Feed** (Parent-Document-Muster ueber `parent_url`/`parent_hash`). - **Per-Quelle aktivierbar** ueber `options.chunk` (Default `off`); gemeinsame Defaults in `config/chunking.yaml`, pro Quelle ueberschreibbar (`effective_opts`). - **Nur deterministische, embedding-freie Strategien** (`headings | faq | recursive`). - **Contextual Retrieval (deterministisch):** Kontext-Vorspann je Chunk (`> Kontext: > `), kein LLM/Embedding. - **Inkrementell:** jeder Chunk traegt `parent_hash` (Inhalt) + `chunk_fingerprint` (wirksame Optionen/Strategie). Es wird nur neu gechunkt, wenn sich einer der beiden aendert; deaktivierte Orte/verwaiste Chunks werden entfernt. Laeuft am Ende von `src.main` automatisch mit. - **Lauf:** `python -m src.chunk --data output` (offline auf `output/processed`). Erste Aktivierung: INB 2026 + INB 2027 (`chunk: headings`). Code: `src/transformers/chunker.py` (Logik), `src/chunk.py` (Runner), `config/chunking.yaml` (Defaults), `src/model.py` (Chunk-Felder im Frontmatter). --- # Plan: Heading-basiertes Chunking (urspruengliches Konzept) ## Ziel & Grundidee Grosse Dokumente (v.a. INB, Regelwerke, lange Confluence-Baeume) sind als **eine** Markdown-Datei fuer Retrieval/Zitate unhandlich. Idee: zusaetzlich zur Voll-Datei **Chunks entlang der Ueberschriften** erzeugen. Wichtig (Vorgabe): - **Die Voll-Datei bleibt immer erhalten** (kein Informationsverlust, Single Source of Truth). - Chunks sind **additiv** und abgeleitet -> jederzeit neu erzeugbar. - **Wir stellen die Vektor-DB NICHT.** Wer anschliesst, soll die Wahl haben: Voll-Dokument einlesen **oder** die fertigen Chunks nutzen **oder** selbst chunken. ## Warum an Ueberschriften? - Strategie-uebergreifend nutzbar: das Chunking arbeitet auf dem **erzeugten Markdown**, nicht auf der Quelle. Damit ist es ein **Transform-Schritt nach dem Extrahieren** und funktioniert (potenziell) fuer ALLE Strategien. - Ueberschriften sind die natuerliche, semantische Schnittkante. - Befund INB-Hauptdokument 2026 (Review): ~751 KB, **746 `##`-Ueberschriften**, davon **91 mit „Ziffer X.Y…"** (z.B. `7.3.1.1.1.1 Kapazität`). Ideale Schnittpunkte + praezise Zitierbarkeit. ## Eignung je Strategie | Strategie | Ueberschriften vorhanden? | Chunking sinnvoll? | |-----------|---------------------------|--------------------| | `pdf` (pymupdf, INB/Regelwerk) | ja (`##`), teils „Ziffer" | **sehr** (grosse Dokumente) | | `confluence_tree`/`page` | ja (Hn aus storage->md) | ja bei langen Seiten | | `confluence_faq` | je Frage `###` | ja (1 Chunk je Frage/Antwort) | | `sitemap`/`crawler` (Web) | meist (html2text `#`) | mittel (Seiten oft kurz) | | `file`/`gitlab_md` | ja (Markdown) | ja bei langen Dateien | -> Generischer Helfer `chunk_by_headings(markdown, level, min_chars)` + Opt-in pro Quelle. ## Chunk-Format (Vorschlag) Jeder Chunk = eigene Markdown-Datei mit Frontmatter, das die Herkunft referenziert: ``` --- kind: "chunk" # Voll-Dokument: kind: "document" (Default) parent_url: "" parent_hash: "" section: "7.3.1.1.1.1 Kapazität" ziffer: "7.3.1.1.1.1" # nur wenn erkannt chunk_index: 12 domain/tool/scope/tags/owners/contact/source: geerbt content_hash: "" --- ## 7.3.1.1.1.1 Kapazität ... Abschnittstext ... ``` - Heading bleibt im Chunk-Text (Chunk ist selbst-erklaerend). - Stabiler Slug aus `ziffer`/Heading; Kollision -> Index anhaengen. ## Ablage (Entscheidung offen, s.u.) - **Option A:** separater Baum `output/chunks////NNN-...md`. Vorteil: `output/processed/` bleibt „eine Datei pro Quelle/Seite", Chunks klar opt-in. - **Option B:** Unterordner neben der Voll-Datei: `output/processed/.../.chunks/`. Vorteil: Chunks liegen direkt beim Dokument. Empfehlung: **Option A** (sauberer Feed; `processed/` = Dokumente, `chunks/` = abgeleitet). ## Konfiguration (pro Quelle, additiv, Default AUS) ``` options: chunk: headings # off (Default) | headings chunk_level: 2 # welche Heading-Ebene schneidet (Default: kleinste vorhandene) chunk_min_chars: 200 # kleinere Chunks mit Vorgaenger zusammenfassen ``` INB-Quellen bekaemen `chunk: headings`. Alles andere bleibt zunaechst aus. ## Umsetzungs-Skizze (spaeter) 1. `src/transformers/chunker.py`: `chunk_by_headings(md, level, min_chars) -> [(section, ziffer, text)]` + `make_chunks(doc) -> list[Document]` (erbt Metadaten, setzt kind/parent/section/ziffer). 2. `src/model.py`: `Document` um `kind`, `parent_url`, `parent_hash`, `section`, `ziffer`, `chunk_index` erweitern (Frontmatter). 3. `src/main.py`: nach Filter/Freigabe, wenn `source.options.chunk == "headings"` und Doc `approved` -> Chunks erzeugen und ueber das Gate schreiben (nach `output/chunks/`). 4. `src/store.py` + `src/site.py`: `kind` lesen; auf der Uebersicht Chunks **standardmaessig ausblenden** (Toggle „Chunks anzeigen"), damit die Bestandszahlen (Dokumente) sauber bleiben; eigener Zaehler fuer Chunks. 5. Robustheit: Mini-Chunks zusammenfassen, sehr grosse optional spaeter weiter teilen (Absatz/Token-Budget, evtl. Overlap). 6. Tests: Splitten je Level, Mini-Merge, Ziffer-Extraktion, Parent-Verknuepfung, content_hash stabil. ## Phasen - **Phase 1:** generischer Chunker + nur fuer INB aktiv; zweites Artefakt; UI-Toggle. - **Phase 2:** weitere grosse Dokumente (Regelwerk, lange Confluence-Baeume). - **Phase 3 (optional):** token-bewusstes Sub-Splitting + Overlap. ## Offene Entscheidungen 1. Ablage: `output/chunks/` (Option A, empfohlen) vs. Unterordner beim Dokument (Option B). 2. Chunks in den „approved Feed" (`output/processed/`) aufnehmen oder bewusst getrennt halten (Empfehlung: getrennt, damit Voll-Dokument der primaere Feed bleibt). 3. Heading-Ebene: automatisch kleinste vorhandene vs. pro Quelle konfiguriert. 4. Mini-/Max-Groessen (`chunk_min_chars`, spaeter Token-Budget). 5. Reihenfolge: zuerst INB (Phase 1) – ja/nein. --- # Recherche (Stand 2025/2026) & begruendete Empfehlungen Quellen (Inhalte fuer Compliance umformuliert, keine Originalzitate): [Weaviate-/Firecrawl-Ueberblick](https://www.firecrawl.dev/blog/best-chunking-strategies-rag), [Digital Applied – Retrieval Playbook 2026](https://www.digitalapplied.com/blog/rag-chunking-strategies-2026-retrieval-quality-playbook), [CustomGPT – Chunking Strategies](https://customgpt.ai/rag-chunking-strategies/), [Langcopilot – Practical Guide](https://langcopilot.com/posts/2025-10-11-document-chunking-for-rag-practical-guide), [Airbyte – Chunking Strategies](https://airbyte.com/agentic-data/chunking-strategies), [Anthropic – Contextual Retrieval](https://www.anthropic.com/news/contextual-retrieval). ## Was die Forschung sagt (verdichtet) - **Chunking entscheidet stark ueber die Qualitaet** – mehr als die Modellwahl. Ein Weaviate-Benchmark (Sept 2025) misst bis zu ~9 % Unterschied bei Recall zwischen bester und schlechtester Methode (gleicher Korpus/Retriever). - **Recursive Splitting** (erst an Ueberschriften, dann Absaetze, Saetze, Woerter) gilt breit als **bester Allrounder/Default** (gute Balance Qualitaet/Kosten). - **Struktur-/Heading-basiertes** Splitting ist ideal fuer **technische Dokumente mit klarer Gliederung** – genau unser INB/Regelwerk-Fall. - **Semantic Chunking** bringt nochmal Genauigkeit, ist aber deutlich teurer (mehrfache Rechenkosten, teils ~14x langsamer) – eher spaeter/optional. - **Overlap** bringt laut neueren Analysen (Jan 2026) **kaum messbaren Nutzen** -> als Default **0**, nur optional aktivierbar. - **Chunk-Groesse:** verbreiteter Richtwert ~**400–800 Tokens**; sehr grosse Chunks (Richtung ~2,5k Tokens „context cliff") verschlechtern Treffer. - **Contextual Retrieval** (Anthropic): jedem Chunk einen kurzen Kontext voranstellen (Dokumenttitel + Abschnittspfad) reduziert Retrieval-Fehler erheblich. **Deterministisch (ohne LLM)** machbar, indem wir die **Heading-Breadcrumb** in jeden Chunk schreiben. - **Parent-Document-Retrieval:** kleine Chunks zum Finden, grosses Dokument zum Antworten. Das ist **genau das gewuenschte Muster** „ein Dokument + zugehoerige Chunks". ## Empfehlungen zu den offenen Entscheidungen ### 1) Ablage: separater Baum `output/chunks/` (EMPFOHLEN) - **Pro:** `output/processed/` bleibt „eine Datei pro Quelle/Seite" = stabiler, einfacher Feed; Chunks sind klar als abgeleitet/optional erkennbar; getrennt loeschbar/regenerierbar; Bestandszahlen auf den Pages bleiben sauber. - **Contra:** zwei Wurzelverzeichnisse; Konsument muss wissen, dass es beides gibt (loesen wir per Frontmatter `parent_*` + Doku auf der Chatbot-Seite). - Alternative B (Unterordner beim Dokument) vermischt Voll-Doc und Chunks im selben Baum -> Feed wird unuebersichtlich. **Daher A.** ### 2) Chunks NICHT in den primaeren `output/processed`-Feed (EMPFOHLEN) - Voll-Dokument bleibt der primaere, eindeutige Feed (1 Seite = 1 Datei). Chunks liegen in `output/chunks/` mit `parent_url`/`parent_hash` -> klassisches Parent-Document-Muster. - Konsument hat die Wahl: nur `processed/` (grob), nur `chunks/` (fein) oder beides (Parent-Document-Retrieval). Wir geben die Vektor-DB nicht vor -> maximale Flexibilitaet. ### 3) Heading-Ebene: automatisch kleinste sinnvolle, pro Quelle ueberschreibbar - Default: feinste durchgaengig vorhandene Ebene erkennen (INB: `##`). - `chunk_level: N` erlaubt manuelles Setzen, falls die Auto-Erkennung schlecht trifft. ### 4) Groessen/Overlap (Defaults, grounded) - `chunk_min_chars: 200` (Mini-Abschnitte mit Vorgaenger zusammenfassen). - `chunk_max_chars` optional (z.B. ~4–6k Zeichen ≈ ~1–1.5k Tokens); zu grosse Abschnitte fallen per Recursive-Sub-Split kleiner. - `chunk_overlap: 0` als Default (Overlap nur optional). - **Breadcrumb-Prefix** je Chunk (Dokumenttitel + Abschnittspfad) standardmaessig AN (billiger Contextual-Retrieval-Effekt). ### 5) Reihenfolge: zuerst INB (Phase 1) – JA. ## Chunk-Strategien als Menue (was die Tool-Eintragenden waehlen koennen) Pro Quelle ueber `options.chunk` waehlbar (Default `off`): | `chunk:` Wert | Was passiert | Wofuer geeignet | |---------------|--------------|-----------------| | `off` (Default) | kein Chunking, nur Voll-Datei | kurze Seiten, Kundeninfos | | `headings` | Split an Markdown-Ueberschriften (`chunk_level`) | INB, Regelwerk, lange Confluence-Baeume | | `recursive` | Ueberschriften -> Absaetze -> Saetze, Zielgroesse `chunk_size` | Doks ohne saubere Gliederung (manche PDFs/Webseiten) | | `faq` | je Frage/Antwort ein Chunk (Spezialfall `headings` auf `###`) | `confluence_faq` | | `pages` | ein Chunk je PDF-Seite | PDFs mit unbrauchbaren Ueberschriften | Zusatz-Optionen (alle optional): `chunk_level`, `chunk_size`, `chunk_min_chars`, `chunk_max_chars`, `chunk_overlap`, `chunk_breadcrumb: true|false`. **Nicht im ersten Wurf (spaeter):** `semantic` (Embedding-basiert, teuer) und `contextual` (LLM-generierter Kontext je Chunk) – beide hohe Kosten, erst wenn Bedarf. ## Empfohlener Startpunkt - Generischen `headings`- + `recursive`-Splitter bauen, Breadcrumb-Prefix an, Overlap 0, Ablage `output/chunks/`, Chunks getrennt vom Feed, `parent_*` im Frontmatter. - Phase 1 nur fuer INB aktiv (`chunk: headings`), dann Treffer evaluieren, dann ausweiten. --- # Recherche (Stand 2026) & fundierte Empfehlungen Quellen (Auswahl, paraphrasiert; Inhalte zur Lizenzkonformitaet umformuliert): - [Firecrawl – Best Chunking Strategies for RAG (2026)](https://www.firecrawl.dev/blog/best-chunking-strategies-rag) - [Digital Applied – RAG Chunking Playbook (Mai 2026)](https://www.digitalapplied.com/blog/rag-chunking-strategies-2026-retrieval-quality-playbook) - [customgpt.ai – RAG Chunking Strategies](https://customgpt.ai/rag-chunking-strategies/) - [langcopilot – Document Chunking for RAG (2026)](https://langcopilot.com/posts/2025-10-11-document-chunking-for-rag-practical-guide) - [denser.ai – 8 Methods Compared](https://denser.ai/blog/rag-chunking-strategies/) ## Was die Recherche zeigt (Kernbefunde) - **Die Chunk-Strategie beeinflusst die Retrieval-Qualitaet oft staerker als das Modell.** Ein Weaviate-Benchmark (Sept 2025) nennt bis zu ~9 % Unterschied in der Trefferquote zwischen bester und schlechtester Methode auf demselben Korpus. - **Struktur-/dokumentbewusstes Chunking** (Ueberschriften, Tabellen, Codebloecke erhalten) gilt als entscheidend fuer strukturierte Inhalte; customgpt.ai berichtet hier deutliche Genauigkeitsgewinne. Genau das sind die INB/Regelwerke. - **Recursive Splitting** (~400-800 Token, 10-20 % Overlap) ist der gaengige Allzweck-Default. - **Semantisches Chunking** kann die Trefferquote spuerbar heben, ist aber teuer (jeder Satz wird eingebettet, teils ~14x langsamer) -> braucht ein Embedding-Modell. - **Overlap-Mythos:** eine Analyse (Jan 2026) findet kaum messbaren Nutzen von Overlap; bei semantischen Schnittkanten (Ueberschriften) ist Overlap ohnehin weniger noetig. - **„Context Cliff" ~2.5k Token:** sehr grosse Chunks „verwaessern" das Embedding -> Obergrenze sinnvoll. - **Contextual Retrieval (Anthropic):** ein kurzer Kontext-Vorspann pro Chunk (z.B. Dokumenttitel + Abschnittspfad) reduziert Retrieval-Fehler stark -> billig & deterministisch machbar. ## Was das fuer UNS bedeutet (wichtige Einordnung) Wir **stellen die Vektor-DB nicht** und wollen **deterministische, reproduzierbare** Artefakte in Git. Daraus folgt eine klare Linie: - **Wir liefern, was man ohne Embedding-Modell sauber erzeugen kann:** strukturerhaltende, ueberschriften-basierte Chunks + saubere Voll-MD. Das ist laut Recherche **der mit Abstand wichtigste Hebel** („clean, document-aware input"). - **Semantisches / late / contextual-embedding-Chunking gehoert zum Anschliesser** (er hat Embedding-Modell + Vektor-DB). Wir wuerden es NICHT fest verdrahten – wir liefern die ideale Vorlage dafuer. - Deshalb ist „ein Voll-Dokument + zugehoerige Chunks" genau richtig: maximale Wahl, kein Lock-in. ## Empfehlung je offener Entscheidung | Entscheidung | Optionen | Empfehlung | Begruendung | |--------------|----------|------------|-------------| | **Ablage** | (A) separater Baum `output/chunks//...` · (B) Unterordner beim Dok · (C) in `output/processed/` | **A** | Voll-Dok bleibt der primaere, eindeutige Feed; Chunks klar opt-in/abgeleitet; Konsument waehlt „processed ODER chunks" ohne Doppel-Embedding. | | **Chunks in approved-Feed?** | rein / getrennt | **getrennt** | Verhindert, dass derselbe Inhalt doppelt im Index landet; `output/processed` bleibt „eine Datei pro Quelle". | | **Heading-Ebene** | fix / auto / konfigurierbar | **konfigurierbar mit smartem Default** (kleinste sinnvolle Ebene; klein -> mergen, gross -> bis Zielband teilen) | Recursive-Prinzip: Struktur respektieren, aber Zielgroesse halten (gegen Mini-Fragmente & Context Cliff). | | **Zielgroesse** | – | **~300-1200 Token Band**, hartes Max < ~2000 Token | gaengiger Default-Korridor; unter Context Cliff. | | **Overlap** | 0 / 10-20 % | **Default 0** (optional aktivierbar) | „Overlap-Mythos"; Ueberschriften sind semantische Grenzen. | | **Kontext-Vorspann** | aus / an | **an (empfohlen)**: 1 Zeile „aus > " je Chunk | Contextual-Retrieval-Effekt, deterministisch, billig. | ## Chunk-Strategien als Menue fuer Tool-Eintragende Pro Quelle waehlbar ueber `options.chunk` (Default `off`). Bewusst nur **deterministische, embedding-freie** Verfahren – Semantik-Chunking bleibt dem Anschliesser: | `chunk:` | Was es tut | Wann nutzen | |----------|------------|-------------| | `off` (Default) | nur Voll-Dokument | kurze Seiten, Standard | | `headings` | schneidet an Markdown-Ueberschriften; klein -> mergen, gross -> bis Zielband; `ziffer`/Abschnitt als Metadatum | strukturierte, lange Dokumente (INB, Regelwerk, lange Confluence-Baeume) | | `faq` | ein Chunk je Frage/Antwort | `confluence_faq`-Quellen | | `recursive` | groessenbasiert: Absatz -> Satz bis Zielband (ohne Embeddings) | lange Dokumente **ohne** brauchbare Ueberschriften | | `pages` *(spaeter)* | ein Chunk je PDF-Seite | paginierte PDFs (gewann NVIDIA-2024-Benchmark fuer paginierte Docs) | Gemeinsame Optionen: `chunk_target_tokens` (Default ~500), `chunk_max_tokens` (~1800), `chunk_min_chars` (~200), `chunk_overlap` (Default 0), `chunk_context_header` (Default an). **Bewusst NICHT angeboten** (Begruendung dokumentieren): `semantic`/`late`/`contextual-embedding` -> brauchen ein Embedding-Modell und gehoeren in die Vektor-DB des Anschliessers. Wir liefern dafuer die saubere, strukturierte Vorlage. ## Empfohlener Startpunkt (Phase 1) INB-Quellen: `chunk: headings`, `chunk_level: auto`, `chunk_target_tokens: 500`, `chunk_max_tokens: 1800`, `chunk_overlap: 0`, `chunk_context_header: on`. Ablage unter `output/chunks/allgemein/regulierung/inb-2026/.md`. Voll-MD bleibt unveraendert in `output/processed/`.