Squashed 'bahn/wissensdatenbank/' content from commit 07a8196e
git-subtree-dir: bahn/wissensdatenbank git-subtree-split: 07a8196e5f9e55d027f90485beb95f4006387669
This commit is contained in:
+123
@@ -0,0 +1,123 @@
|
||||
# SETUP – Was du (manuell) tun musst
|
||||
|
||||
Code/Config sind vorbereitet. Diese Schritte brauchen Zugaenge/Rechte, die nur du hast.
|
||||
|
||||
## Warum kein Deps-Image? (PyPI ueber Artifactory-Mirror)
|
||||
|
||||
Die GitLab-Runner-Pods erreichen **pypi.org nicht**, **aber** den Artifactory-PyPI-Mirror
|
||||
auf bahnhub. Deshalb setzt die CI `PIP_INDEX_URL` auf den Mirror – dann funktioniert
|
||||
`pip install` direkt im Job, **ohne eigenes Image**:
|
||||
|
||||
```
|
||||
PIP_INDEX_URL = https://bahnhub.tech.rz.db.de/artifactory/api/pypi/pypi-remote/simple
|
||||
```
|
||||
|
||||
Ist bereits in `.gitlab-ci.yml` gesetzt. Falls der Repo-Name im Konzern abweicht,
|
||||
dort anpassen (beim DXP-/pipeship-Team erfragen). `lint`/`pages` brauchen kein pip.
|
||||
|
||||
---
|
||||
|
||||
## Checkliste
|
||||
|
||||
### 1. PyPI-Mirror pruefen
|
||||
- [ ] Ersten Pipeline-Lauf ansehen: `test`-Job muss `pip install` ueber den Mirror
|
||||
schaffen. Falls 403/404: `PIP_INDEX_URL` (Repo-Name) in `.gitlab-ci.yml` anpassen.
|
||||
|
||||
### 2. CI/CD-Variablen anlegen (Settings → CI/CD → Variables, Masked & Protected)
|
||||
- [ x] `CONFLUENCE_URL` = `https://arija-confluence.jaas.service.deutschebahn.com`
|
||||
- [x ] `CONFLUENCE_TOKEN` = dein Confluence-PAT (**neuen** erzeugen, alten rotieren!)
|
||||
- [x ] `GIT_PUSH_TOKEN` = Project Access Token mit Scope `write_repository`
|
||||
- [ ] (optional) `GITLAB_TOKEN` = Read-Token, falls `gitlab_md`-Quellen aus PRIVATEN
|
||||
GitLab-Repos geladen werden (oeffentliche brauchen keinen Token)
|
||||
- (DEPS_IMAGE entfaellt – pip laeuft ueber PIP_INDEX_URL, siehe oben)
|
||||
|
||||
### 3. Schedule (CI/CD → Schedules)
|
||||
- [x] Schedule angelegt: **Cron `0 8-17 * * 1-5`** (stuendlich Mo–Fr 8–17 Uhr), Target
|
||||
Branch `main`. Der `knowledge-etl`-Job laeuft nur bei `schedule`/`web`.
|
||||
|
||||
### 3b. Netzzugang des Runners (WICHTIG fuer den ETL-Lauf)
|
||||
Der Runner muss die Quellen erreichen, sonst kommen 0 Dokumente (Timeouts):
|
||||
- intern: `arija-confluence...deutschebahn.com` (Confluence)
|
||||
- oeffentlich: `www.dbinfrago.com` (Kundeninfos, INB, Regelwerk)
|
||||
|
||||
Der `knowledge-etl`-Job ist bereits auf den **DB-Web-Proxy** konfiguriert
|
||||
(`http://webproxy.comp.db.de:8080`) mit `NO_PROXY` fuer interne Hosts
|
||||
(`.tech.rz.db.de`, `.deutschebahn.com`).
|
||||
- [ ] Pruefen, ob Proxy-Host/Port stimmen (ggf. in `.gitlab-ci.yml` anpassen) und ob
|
||||
der Proxy **keine Authentifizierung** verlangt. Beim naechsten Lauf im Log sehen:
|
||||
laufen Confluence + dbinfrago jetzt durch?
|
||||
|
||||
### 4. Protected Branch + Freigabe (Settings → Repository → Protected Branches)
|
||||
- [x ] `main` schuetzen: „Allowed to push" = niemand (nur via MR); „Allowed to merge" = Maintainer.
|
||||
- [ ] Settings → Merge requests: „Require approval from Code Owners" aktivieren.
|
||||
das ignroerien wir erst mal
|
||||
- [ ] In `CODEOWNERS` die Platzhalter durch echte GitLab-Gruppen/Handles ersetzen
|
||||
(`@einfachbahn-lab/wissensdatenbank-maintainer` -> reale Gruppe).
|
||||
> das auch
|
||||
- [ ] Hinweis: Der automatische Bot-Push auf `main` braucht dann eine Ausnahme
|
||||
(Token-User als „Allowed to push" zulassen) ODER der Bot pusht auf einen Branch
|
||||
`knowledge-data` (in `.gitlab-ci.yml` `TARGET_BRANCH` umstellen) + Auto-MR.
|
||||
-> braucht es das dann noch?
|
||||
ANTWORT: Ja. Da `main` „push = niemand" ist, wuerde der automatische ETL-Push scheitern.
|
||||
Einfachste Loesung: den `GIT_PUSH_TOKEN`-User unter „Allowed to push" fuer `main`
|
||||
zulassen. (CODEOWNERS-Approval ignorieren wir ja erst mal, also kein MR-Zwang.)
|
||||
|
||||
### 5. GitLab Pages aktivieren (Deploy → Pages)
|
||||
- [ x] Nach erstem erfolgreichen `pages`-Job ist die URL unter Deploy → Pages sichtbar.
|
||||
(Job laeuft auf `main`; nutzt nur stdlib, kein Deps-Image noetig.)
|
||||
|
||||
|
||||
das hier verstehe ich noch nicht:
|
||||
### 6. Issue → MR Automatisierung (optional, fuer Self-Service)
|
||||
|
||||
Ziel: Ein Fachbereich meldet neues Wissen per **GitLab-Issue** (Vorlage „Neues Wissen"),
|
||||
ohne yaml/Git zu koennen. Daraus wird automatisch ein Eintrag in `config/tools.yaml`
|
||||
+ ein Merge Request mit Vorschau. So laeuft es:
|
||||
|
||||
1. Person legt ein **Issue** mit der Vorlage an (Tool, Link(s), Scope, Ansprechpartner)
|
||||
und Label `neues-wissen`.
|
||||
2. Aus dem Issue wird ein **tools.yaml-Eintrag** erzeugt (`scripts/issue_to_source.py`)
|
||||
und ein **MR** geoeffnet (`scripts/issue_to_mr.sh <ISSUE_ID>`), inkl. Markdown-Vorschau.
|
||||
3. Reviewer schaut die Vorschau an und merged = Freigabe.
|
||||
|
||||
Du musst nur entscheiden, WIE Schritt 2 ausgeloest wird:
|
||||
- **Manuell (einfachste Variante):** Reviewer fuehrt `scripts/issue_to_mr.sh <ISSUE_ID>`
|
||||
lokal aus (braucht `glab` eingeloggt + aktive venv + `CONFLUENCE_*`). Fuer den Anfang reicht das.
|
||||
- **Automatisch (spaeter):** GitLab-Webhook auf „Issues events" an einen kleinen Dienst,
|
||||
der das Skript ausfuehrt – ODER ein scheduled CI-Job, der offene `neues-wissen`-Issues abarbeitet.
|
||||
|
||||
- [ ] Label `neues-wissen` anlegen (fuer beide Varianten).
|
||||
- [ ] Fuer den Start: Variante „manuell" nutzen. Automatik ist optional/spaeter.
|
||||
|
||||
### 8. Reviews
|
||||
- [ passt erst mal] GitLab Pages (Uebersicht) pruefen.
|
||||
- [passt erst mal ] `pending` freigeben: URL oder `hash:<content_hash>` in
|
||||
`config/approvals.yaml` unter `approved:` eintragen.
|
||||
|
||||
---
|
||||
|
||||
## ETL-Bot - was genau noch zu tun ist
|
||||
|
||||
Der `knowledge-etl`-Job laeuft per Schedule (stuendlich Mo–Fr 8–17 Uhr), baut das Wissen und **pusht das Ergebnis
|
||||
zurueck**. Dafuer:
|
||||
|
||||
1. **`GIT_PUSH_TOKEN`** anlegen: Settings → Access Tokens → *Project Access Token*,
|
||||
Rolle `Maintainer`, Scope **`write_repository`**. Wert als CI/CD-Variable
|
||||
`GIT_PUSH_TOKEN` (Masked & Protected) speichern.
|
||||
2. **Push auf `main` erlauben** (weil `main` „push = niemand" ist): Settings →
|
||||
Repository → Protected Branches → `main` → unter **„Allowed to push"** den
|
||||
Token-User hinzufuegen (heisst typ. `project_<id>_bot`).
|
||||
- **Alternative (main bleibt strikt):** in `.gitlab-ci.yml` `TARGET_BRANCH: knowledge-data`
|
||||
setzen; der Bot pusht dann auf einen Datenbranch (kein Push auf main noetig).
|
||||
3. **Schedule** angelegt (CI/CD → Schedules, Cron `0 8-17 * * 1-5`, Target `main`). ✔
|
||||
|
||||
Mehr ist fuer den Bot nicht noetig – `pip` laeuft ueber den PyPI-Mirror.
|
||||
|
||||
## Quellen-Typen: GitLab / Datei im Repo / public
|
||||
|
||||
- **GitLab** (`gitlab_md`): ✅ Markdown-Dateien aus einem Repo, mit Ordner (`path`) und
|
||||
Tiefe (`max_depth`). Privat-Repos brauchen `GITLAB_TOKEN`.
|
||||
- **Datei im Repo** (`file`): ✅ Handbuch/Doku einfach nach `files/<tool>/` pushen und
|
||||
per `strategy: file` einbinden (PDF wird zu Text, .md direkt). Alles versioniert.
|
||||
- **PDF-Handbuecher via Link** (`pdf`): ✅ direkter PDF-Link oder Seite mit PDF-Links.
|
||||
- **Public Links** (`crawler`/`pdf`/`sitemap`): ✅.
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
# TODO / Roadmap
|
||||
|
||||
## Leitprinzipien (immer beachten)
|
||||
|
||||
- **Qualitaet vor Menge.** Lieber weniger, dafuer sauberes/freigegebenes Wissen.
|
||||
- **Einfach konfigurierbar.** Neues Wissen ueber `config/tools.yaml` /
|
||||
`config/general.yaml` ergaenzbar (Domaene/Tool + Link + Strategie + Scope),
|
||||
ohne Code zu aendern.
|
||||
- **Transparenz.** Jederzeit sichtbar, welches Wissen wo liegt und mit welchen Tags
|
||||
(GitLab Pages: Uebersicht + Wissensquellen, Frontmatter).
|
||||
|
||||
|
||||
|
||||
## Offen (Code/Inhalt)
|
||||
|
||||
### Bugs / beobachten
|
||||
- [x] **Confluence-Abruf schlug im CI fehl** (gefunden 2026-06-28 im ETL-Log): alle
|
||||
`confluence_*`-Quellen brachen mit `No scheme supplied` ab (CI-Variable `CONFLUENCE_URL`
|
||||
ohne `https://`) -> pathos/nur/mateo/infraportal/rechnungsbahnhof wurden nicht
|
||||
aktualisiert. **URL inzwischen korrigiert** - der naechste ETL-Lauf muss bestaetigen,
|
||||
dass es wieder laedt (jetzt in `output/run_log.jsonl` als `sources_failed: 0` sichtbar).
|
||||
- [ ] **ETL-Sichtung insb. wegen Dateigroesse** (naechster Check: 2026-06-30): nach dem
|
||||
Merge von `chore/data-to-output` (v2.0.0) pruefen, ob der naechste ETL-Lauf sauber
|
||||
durchlaeuft (output/staging-Trennung in CI korrekt? `run_log.jsonl` Fehler-frei?).
|
||||
Ausserdem Repo-Groesse im Auge behalten – gerade die INB-Chunks erzeugen viele
|
||||
Dateien. Ggf. alte Chunk-Generationen (`output/chunks/`) per Shallow-Clone oder
|
||||
LFS-Strategie entschaerfen, falls das Repo >500 MB waechst.
|
||||
|
||||
### Monitoring / Logging
|
||||
- [ ] **ETL-Laeufe regelmaessig pruefen** - jetzt einfach ueber `output/run_log.jsonl`
|
||||
(letzte Laeufe inkl. Fehler je Quelle) bzw. die Health-Zeile in der Pages-Fusszeile.
|
||||
- [x] **Lauf-Log umgesetzt** (`output/run_log.jsonl`, append-only, gekappt auf 500):
|
||||
Zeit, verarbeitete Dokumente, Status-Counts, Fehler je Quelle; Health-Zeile auf der
|
||||
Uebersicht; Manifest-Eintrag auf der Chatbot-Seite. (v1.3.0)
|
||||
|
||||
### Inhalt / Datenqualitaet
|
||||
- [x] **Dedup ueberlappender Quellen** (v1.4.0): gleiche Seite je scope/domaene nur ein
|
||||
Dokument (Gate-Dedup per `page_identity`, spezifischere Aufbereitung gewinnt) +
|
||||
`prune_duplicate_files`. Einmalig 42 Alt-Duplikate entfernt (v.a. `nur`).
|
||||
- [x] **pathOS „FAQ PathOS Extern" wieder erfasst** (v1.3.1): Seite ist h2/Absatz-basiert,
|
||||
nicht als Q/A-Tabelle -> Strategie von `confluence_faq` auf `confluence_page` umgestellt.
|
||||
Vorher landete das komplette externe FAQ leer in pending.
|
||||
- [x] **Confluence-Anhaenge (eingebundene PDFs) werden erfasst** (v1.5.0): bei
|
||||
`confluence_page`/`confluence_tree` werden `view-file`/`viewpdf`-Anhaenge ueber den
|
||||
PDF-Parser zu Markdown und als eigene Dokumente (`kind: attachment`, `parent_url` ->
|
||||
Seite) abgelegt. Inkrementell. Default an, per `options: { attachments: false }`
|
||||
abschaltbar. Bilder: alt-Text bleibt als `[Bild: ...]` erhalten (Variante A).
|
||||
Offen/optional: drawio-Diagramme (Text aus XML extrahieren) - aktuell nicht erfasst.
|
||||
- [x] **INB-Sectioning / Chunking** umgesetzt (v1.1.0/1.2.0): heading-basiert,
|
||||
deterministisch, inkrementell, `output/chunks/`, Katalog `output/_index.json`. Default aus,
|
||||
aktiv fuer INB. Spaeter ggf. weitere grosse Dokumente (Regelwerk).
|
||||
|
||||
### Ideen (Konzept steht, Entscheidung offen)
|
||||
- [ ] **#1 Nachtraegliches Taggen.** Regelbasiert: `config/tag_rules.yaml`
|
||||
(match: domain/url/titel/keyword -> add_tags) + Befehl `python -m src.retag`, der
|
||||
NUR die `tags:`-Zeile im Frontmatter neu schreibt (Body unveraendert -> content_hash
|
||||
stabil, kein Netz, idempotent), committet. Optional spaeter LLM/Keyword-Vorschlaege,
|
||||
die in die Regeln einfliessen. Auf Pages ggf. Tag-Filter/Tag-Wolke. Gut v.a. fuer
|
||||
allgemeines Wissen (Kundeninfos). Aufwand: mittel.
|
||||
- [ ] **#5 Intern/extern aus EINER Confluence-Seite trennen.** Regel: extern ⊆ intern
|
||||
(nur EINE Richtung). Marker fuer interne Abschnitte (Konvention zu entscheiden:
|
||||
Panel/Info-Makro vs. Textmarker `[[intern]]..[[/intern]]` vs. Ueberschrift „Nur intern").
|
||||
Quelle `scope: "intern,extern"`: intern = ganze Seite; extern = interne Bloecke
|
||||
entfernt (zu kurz -> nur intern). Sicherheitsnetz: content_filter prueft extern weiter.
|
||||
Erkennung im `md_converter` + Tests + Redakteurs-Doku. Aufwand: mittel-hoch, Leak-Risiko
|
||||
bei vergessenem Marker -> „im Zweifel raus".
|
||||
|
||||
### Bedienbarkeit
|
||||
- [ ] Helfer `scripts/add_tool.py` (interaktiv: Domaene + Link + Strategie + Scope,
|
||||
inkl. yaml-Validierung) als Zwischenschritt zur Web-App.
|
||||
- [ ] **Web-App (Option C, mittelfristig):** Flask auf DBCS, Formular ->
|
||||
GitLab-API legt MR an, mit Live-Vorschau. Pages verlinkt darauf. (Skill
|
||||
`dbcs-python-webapp`.)
|
||||
- [ ] **Issue->MR-Bot verdrahten:** Webhook-Service ODER scheduled `glab`-Job, der
|
||||
`scripts/issue_to_source.py` ausfuehrt und automatisch einen MR samt Vorschau anlegt.
|
||||
(Bausteine `scripts/issue_to_source.py` + `scripts/issue_to_mr.sh` sind da.)
|
||||
|
||||
### Quality Gates
|
||||
- [ ] **`min_quality`-Schwelle** in `filter_rules.json`: `content_filter` setzt Docs
|
||||
unter der Schwelle auf `pending` („niedrige Qualitaet"). Erst Verteilung sichten,
|
||||
dann aktiv schalten. (Score + Aufschluesselung sind jetzt auf den Pages sichtbar.)
|
||||
- [ ] **gitleaks-Baseline** (`.gitleaks.toml`) fuer False Positives in `data/`, dann
|
||||
`secret-scan` scharf schalten (aktuell `allow_failure: true`).
|
||||
|
||||
### Vereinfachungen
|
||||
- [x] **Strategie `kundeninfo` entfernt** (war redundant zu `crawler`/`sitemap`); inkl.
|
||||
`ComponentType.KUNDENINFO` + `ComponentType.DEEPLINK` (beide ungenutzt).
|
||||
- [x] **`docs/catalog.md` (src/catalog.py) abgeschafft** – Seite „Wissensquellen"
|
||||
(config.html) ersetzt es.
|
||||
- [x] **`docs/review_queue.md` (src/review_queue.py) abgeschafft** – Pages-Uebersicht
|
||||
zeigt pending/rejected inkl. Grund + Aktionslinks.
|
||||
- `--only` bleibt (vom MR-`preview`-Job genutzt).
|
||||
|
||||
### Betrieb / Skalierung
|
||||
- [ ] **CI-Timeout** fuer `knowledge-etl` im Auge behalten (inkrementell unkritisch,
|
||||
ein Timeout wird vom naechsten Lauf nachgeholt).
|
||||
- Hinweis: Korpus bleibt bewusst in Git (kein externer Speicher) – Repo = Single
|
||||
Source of Truth.
|
||||
|
||||
---
|
||||
|
||||
## Out of scope (vorerst, bewusst zurueckgestellt)
|
||||
|
||||
- Mehr externes Wissen pro Tool (eigene kund:innenfaehige Confluence-Seite).
|
||||
- Owners-Backfill fuer alte kundeninfo-/INB-Dokumente (bleiben ohne Owner).
|
||||
|
||||
---
|
||||
|
||||
## Erledigt (Kern)
|
||||
|
||||
- [x] ETL-Grundgeruest: Extract (Confluence/Web/Sitemap/PDF/GitLab/File) -> Transform
|
||||
(md_converter, tagger, content_filter) -> Review-Gate (approved/pending).
|
||||
- [x] Strategien: confluence_page/tree/faq, crawler, sitemap, pdf, **gitlab_md**, **file**.
|
||||
- [x] Strategie-Erkennung pro URL (`src/strategy_detect.py`), unbekannte URLs flaggen.
|
||||
- [x] Scope-Modell: tool-weit `intern|extern|allgemein|mixed`, source `intern,extern`
|
||||
(ganze Seite fuer beide). Pro-Seite-Trennlogik bewusst entfernt.
|
||||
- [x] Output `output/processed/<scope>/<domaene>[/<tool>]` IST der Vektor-DB-Feed
|
||||
(kein separater ingest-Export).
|
||||
- [x] Gate 2: Allowlist `config/approvals.yaml` (URL/Hash) im content_filter;
|
||||
`review_notes` erklaeren WARUM pending.
|
||||
- [x] Quality-Score (0-100) pro Dokument (`src/quality.py`), auf Pages + Hilfe erklaert.
|
||||
- [x] **INB-Feintuning**: `pymupdf4llm`-Parser (`parser: pymupdf`) -> echte
|
||||
Markdown-Tabellen; INB 2026/2027 + Regelwerk; PDF-Connector inkrementell (`max_pdfs`,
|
||||
`keep_raw`, `redact`-Option).
|
||||
- [x] **Confluence inkrementell** (`options: { incremental: true }`): Versions-Check
|
||||
pro Seite, unveraenderte Seiten werden uebersprungen (`source_version` im Frontmatter).
|
||||
- [x] GitLab Pages (DB-UX): Logo, Footer (Sebastian Reinig · V.IWF 91 · #Einfachbahn),
|
||||
Filter (extern/intern/allgemein/pending), Suche, Quality-Score sichtbar,
|
||||
Modal mit Inhalt/Quality-Aufschluesselung/Aktionslinks.
|
||||
- [x] **Smoke-Test** fuer `src.site.build()` (erzeugt index/hilfe/chatbot/config/changelog + json).
|
||||
- [x] **Hilfe-Seite** (`hilfe.html`): Strategien-Tabelle, Ablauf, pending/rejected-Gruende.
|
||||
- [x] **Chatbot-Anschluss-Seite** (`chatbot.html`): welche Pfade pro Bot-Typ, wie
|
||||
Frontmatter zu interpretieren ist, Pruefhinweis, RAG-Ablauf.
|
||||
- [x] CI: **PyPI-Mirror** (`PIP_INDEX_URL`) statt Deps-Image; test/etl/pages ohne
|
||||
Custom-Image. Test-Fix `python -m pytest`. renovate.
|
||||
- [x] CI: stuendlicher Daten-Commit (Mo-Fr 8-17) loest `pages`-Refresh aus (kein `ci.skip` mehr),
|
||||
damit die Live-Seite nach dem Schedule aktuell ist.
|
||||
- [x] Owners (1-2 Ansprechpartner) pro Tool/Quelle -> Frontmatter/Katalog/Pages.
|
||||
- [x] Issue-/MR-Templates, CODEOWNERS, `docs/SETUP.md`, scm-info.yaml,
|
||||
Architektur-Steering mit Diagrammen.
|
||||
- [x] Pages: einheitliches Sticky-Menu auf allen Seiten (Logo + Uebersicht/
|
||||
Wissensquellen/Chatbot/Hilfe + CTA), Zaehler (Dokumente, Quellen); Ziel prominent;
|
||||
Strategien gruppiert/vereinfacht; Konfig-Transparenzseite (config.html).
|
||||
- [x] Pages: **Domaenen-Filter** auf der Uebersicht (Dropdown mit Anzahl je Domaene).
|
||||
- [x] CI: MR-Vorschau ueber GitLab-Pages parallel deployments (`pages.path_prefix`),
|
||||
Produktion auf Root.
|
||||
@@ -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 ~**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/<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/`.
|
||||
Reference in New Issue
Block a user