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
+160
View File
@@ -0,0 +1,160 @@
# Architektur & Wissensfluss (Steering)
Diese Datei ist die verbindliche Referenz dafuer, **was mit dem Wissen passiert**.
Bei jeder Aenderung am Datenfluss (neue Connectoren, Filter, Gates, Ziele) ist
dieses Dokument inkl. der Diagramme zu aktualisieren.
## Ziel
Klar definieren, **welches Wissen in Chatbots darf und welches nicht**. Klassifikation
nach Scope: **allgemein** (intern UND extern, nicht toolspezifisch), **intern** (nur DB
InfraGO intern), **extern** (public / fuer EVUs/EIUs). Aufgenommenes Wissen wird
verarbeitet und als `output/processed/<scope>/...` fuer Chatbots/Vektor-DB bereitgestellt.
## Grundprinzipien
- **Denken in Tools**: Jede Wissenseinheit gehoert zu einem Tool und einer Domaene.
- **Scope ist Pflicht**: `intern | extern | allgemein` (allgemein = intern UND extern).
- **Nur freigegebenes Wissen verlaesst die Pipeline** (`review_status: approved`).
- **Repo = Single Source of Truth**: Ergebnis liegt versioniert in `output/processed/`.
## Context-Diagramm (C4 Level 1)
```mermaid
graph TD
subgraph Quellen
CF[Confluence BES]
WEB[dbinfrago.com Kundeninfos]
PDF[INB-PDFs Regulierung]
end
ETL[Wissens-ETL-Pipeline]
subgraph Repo [Git-Repo = Single Source of Truth]
PROC[output/processed/<scope>/<domaene>/]
RAW[staging/raw/ Roh-PDFs]
STG[staging/pending/ - versioniert fuer Transparenz]
end
VDB[(Vektor-DB / RAG)]
USER[1st-Level-Support & Kund:innen]
CF --> ETL
WEB --> ETL
PDF --> ETL
ETL --> PROC
ETL --> RAW
ETL --> STG
PROC --> VDB
VDB --> USER
```
## Datenfluss (Freigabe + ETL)
Die **Freigabe** ist der Merge Request (Mensch, 4-Augen via CODEOWNERS): er entscheidet,
welche Quelle mit welchem Scope aufgenommen wird. Der **Auto-Filter** danach ist ein
**Sicherheitsnetz** (kein Freigabe-Knopf) standardmaessig wird approved.
```mermaid
flowchart LR
MR[Merge Request<br/>Mensch: 4-Augen] --> E[Extract<br/>connectors/]
E --> T[Transform<br/>md_converter, tagger]
T --> G1{Auto-Filter<br/>content_filter}
G1 -->|approved = Default| REL[output/processed/ - live]
G1 -->|pending: Blacklist/zu kurz| RV[staging/pending]
RV -->|approvals.yaml Korrektur| REL
REL --> VDB[(Vektor-DB liest output/processed/<scope>/)]
```
## Wo liegt welches Wissen?
**Klare Trennung in zwei Top-Level-Ordner:**
- `output/` = **was raus darf** (Konsumenten-Feed). Eine einzige Regel: „lies aus `output/`, ignoriere alles andere".
- `staging/` = **intern** (Sichtung, Audit, optionale Roh-PDFs). Kein Wissen, das ein Anschliesser einlesen sollte.
| Pfad | Inhalt | Versioniert |
|------|--------|-------------|
| `output/processed/<scope>/<domaene>/[<tool>/]` | freigegebenes Wissen (Markdown) | ja |
| `staging/raw/<domaene>/` | heruntergeladene Roh-PDFs | ja |
| `staging/pending/` | wartet auf manuelle Sichtung | ja (Transparenz) |
| `output/chunks/<scope>/<domaene>/<docslug>/` | abgeleitete Chunks (optional, Default aus) | ja |
| `staging/review_report.json` | Audit/Report des letzten Laufs | ja |
| `output/_meta.json` | globale Metadatei: `last_run`, `last_change`, `documents`, `by_scope`, `content_signature` (fuer nachgelagerte Systeme) | ja |
| `output/_index.json` | dokument-genauer Katalog: pro Voll-Dokument Domaene/Tool/Scope, `last_updated`, `content_hash`, `kind` (`document`/`attachment`) und - falls vorhanden - die zugehoerigen Chunks (Anzahl/Pfad) und Anhang-Dokumente (Name/Pfad); Aggregate `documents_total`, `chunks_total`, `attachments_total`, `by_domain`, `by_scope` | ja |
| `output/run_log.jsonl` | Lauf-Historie (append-only, 1 Zeile je ETL-Lauf): Zeit, verarbeitete Dokumente, Status-Counts, Fehler je Quelle (gekappt auf letzte 500) | ja |
## Klassifikation intern/extern
Es gibt **keine Inhaltstrennung pro Seite**. Klassifiziert wird pro Tool/Quelle:
1. **Tool-`scope`** in `config/tools.yaml`: `intern` | `extern` | `allgemein` | `mixed`
(mixed = Quellen mit unterschiedlichem Scope; im Zweifel zwei Seiten).
2. **Source-`scope`**: `intern` | `extern` | `allgemein` | `"intern,extern"`
(ganze Seite fuer beide, dupliziert). Quellen erben sonst den Tool-Scope.
3. **Sicherheitsnetz** (`content_filter`): vertrauliche Inhalte in `extern/allgemein`
werden hart abgelehnt (ausser `trusted: true` fuer oeffentliche Quellen wie INB).
Allgemeines Wissen steht getrennt in `config/general.yaml`.
## Komponenten
- `src/connectors/` Extract: confluence (inkl. eingebundene PDF-Anhaenge als
eigene Dokumente `kind: attachment` mit `parent_url`/`attachment_name`; Bilder bleiben
als `[Bild: ...]`-Text), web_crawler (crawler+sitemap), pdf_parser (direkte PDF-URL,
PDF-Links einer Seite ODER Sitemap+Regex -> jeweils neueste Version), gitlab_md, file_source
- `src/transformers/` Transform: md_converter, tagger, content_filter (Filter/Redaction),
chunker (deterministisches, embedding-freies Heading-Chunking)
- `src/review/` Gate: staging (Routing approved/pending) + **Dedup** je
`(scope, domaene, page_identity)` - gleiche Seite aus mehreren Quellen landet nur
einmal im Feed (spezifischere Aufbereitung gewinnt: FAQ > Seite)
- `src/main.py` Orchestrator (`--only` fuer Vorschau)
- `src/chunk.py` Standalone-Chunking (offline auf `output/processed` -> `output/chunks/`;
Default aus, pro Quelle ueber `options.chunk` aktivierbar; zentrale Defaults in
`config/chunking.yaml`)
- `src/strategy_detect.py` erkennt pro URL die Strategie (unbekannt -> neue Strategie noetig)
- `src/store.py` liest den Bestand aus `output/processed` + `staging/pending`
(Frontmatter-Parser); inkrementelles
Re-Tagging bestehender Dateien (`reconcile_meta`), Aufraeumen alter Ablagen nach
Scope-Wechsel (`prune_scope_orphans`) und alter Seiten-Duplikate (`prune_duplicate_files`)
- `src/quality.py` Quality-Score (0-100) pro Dokument
- `src/site.py` GitLab-Pages-Seiten -> `public/` (Uebersicht, Hilfe, Chatbot, Wissensquellen)
## Chunking (optional, abgeleitetes Artefakt)
Grosse, stark gegliederte Dokumente (v.a. INB) werden zusaetzlich zur Voll-Datei in
**Chunks entlang der Ueberschriften** zerlegt. Grundsaetze:
- **Default = AUS.** Nur Quellen mit `options.chunk != off` werden gechunkt (aktuell INB).
- **Voll-Dokument bleibt unangetastet** in `output/processed/`. Chunks sind ein
**zusaetzliches, jederzeit neu erzeugbares** Artefakt in `output/chunks/<scope>/<domaene>/<docslug>/`
(Parent-Document-Muster). Der Anschliesser waehlt: Voll-Dokument, Chunks oder beides.
- **Nur deterministische, embedding-freie** Strategien (`headings | faq | recursive`).
Semantisches Chunking gehoert in die Vektor-DB des Anschliessers (die wir nicht stellen).
- **Contextual Retrieval (Anthropic, deterministisch):** jedem Chunk wird eine kurze
Kontextzeile (`> Kontext: <Dokument> > <Abschnitt>`) vorangestellt - ohne LLM/Embedding.
- Gemeinsame Defaults in **`config/chunking.yaml`**, pro Quelle in `options` ueberschreibbar.
Drei wichtige Feinsteuerungen (Default jeweils aus):
`chunk_min_doc_chars` (Dokumente unter dieser Zeichenzahl bleiben ganz),
`chunk_kind` (`""` alle, `"attachment"` nur Anhang-PDFs, `"document"` nur Voll-Seiten) und
`chunk_component` (`""` alle, sonst nur dieser component_type, z.B. `"faq"`).
**Mehrere Konfigurationen je Ort** sind erlaubt: pro Dokument greift die erste passende
(Filter `chunk_kind`/`chunk_component`).
- **Aktive Konfigurationen:** INB 2026/2027 (`chunk: headings`), pathOS am Ort `*/pathos`
zweigleisig: Anhang-PDFs via `chunk: headings, chunk_kind: attachment, chunk_min_doc_chars: 3000`
und die interne FAQ-Seite via `chunk: faq, chunk_component: faq` (ein Chunk je Frage);
normale Seiten bleiben ganz.
- **Inkrementell:** Chunking laeuft am Ende von `src.main` automatisch mit. Pro Dokument
wird nur neu gechunkt, wenn sich der Inhalt (`parent_hash`) ODER die wirksamen Optionen
(`chunk_fingerprint`, inkl. Strategie und Logik-Version `_CHUNKER_VERSION`) geaendert
haben. Deaktivierte Orte (`chunk: off`) und verwaiste Chunks (Voll-Dokument geloescht)
werden automatisch entfernt.
- Lauf: `python -m src.chunk --data output` (offline auf dem vorhandenen `output/processed`).
```mermaid
flowchart LR
PROC[output/processed/ - Voll-Dokument] --> CK[chunker<br/>headings + Kontext-Vorspann]
CFG[config/chunking.yaml<br/>+ options.chunk je Quelle] --> CK
CK --> CH[output/chunks/<scope>/<domaene>/<docslug>/]
PROC --> VDB[(Vektor-DB: waehlt processed ODER chunks ODER beides)]
CH --> VDB
```