Files
Orchestrator/bahn/wissensdatenbank/.kiro/steering/architecture.md
T

8.3 KiB
Raw Blame History

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)

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.

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).
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