# 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//...` 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///] 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
Mensch: 4-Augen] --> E[Extract
connectors/] E --> T[Transform
md_converter, tagger] T --> G1{Auto-Filter
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//)] ``` ## 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///[/]` | freigegebenes Wissen (Markdown) | ja | | `staging/raw//` | heruntergeladene Roh-PDFs | ja | | `staging/pending/` | wartet auf manuelle Sichtung | ja (Transparenz) | | `output/chunks////` | 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////` (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: > `) 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
headings + Kontext-Vorspann] CFG[config/chunking.yaml
+ options.chunk je Quelle] --> CK CK --> CH[output/chunks////] PROC --> VDB[(Vektor-DB: waehlt processed ODER chunks ODER beides)] CH --> VDB ```