Squashed 'bahn/wissensdatenbank/' content from commit 07a8196e

git-subtree-dir: bahn/wissensdatenbank
git-subtree-split: 07a8196e5f9e55d027f90485beb95f4006387669
This commit is contained in:
2026-06-30 21:19:25 +02:00
commit cfaf670100
4724 changed files with 667022 additions and 0 deletions
+306
View File
@@ -0,0 +1,306 @@
# 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/<scope>/<domaene>/<docslug>/` (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: <Dokument> > <Abschnitt>`), 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: "<url der Voll-Quelle>"
parent_hash: "<content_hash der Voll-Datei>"
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: "<hash des Chunk-Texts>"
---
## 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/<scope>/<domaene>/<docslug>/NNN-...md`.
Vorteil: `output/processed/` bleibt „eine Datei pro Quelle/Seite", Chunks klar opt-in.
- **Option B:** Unterordner neben der Voll-Datei: `output/processed/.../<docslug>.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 ~**400800 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. ~46k Zeichen ≈ ~11.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/<scope>/...` · (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 <Dok> > <Abschnittspfad>" 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/<ziffer-oder-slug>.md`. Voll-MD bleibt
unveraendert in `output/processed/`.