8.3 KiB
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 ausoutput/, 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:
- Tool-
scopeinconfig/tools.yaml:intern|extern|allgemein|mixed(mixed = Quellen mit unterschiedlichem Scope; im Zweifel zwei Seiten). - Source-
scope:intern|extern|allgemein|"intern,extern"(ganze Seite fuer beide, dupliziert). Quellen erben sonst den Tool-Scope. - Sicherheitsnetz (
content_filter): vertrauliche Inhalte inextern/allgemeinwerden hart abgelehnt (aussertrusted: truefuer oeffentliche Quellen wie INB).
Allgemeines Wissen steht getrennt in config/general.yaml.
Komponenten
src/connectors/Extract: confluence (inkl. eingebundene PDF-Anhaenge als eigene Dokumentekind: attachmentmitparent_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_sourcesrc/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.pyOrchestrator (--onlyfuer Vorschau)src/chunk.pyStandalone-Chunking (offline aufoutput/processed->output/chunks/; Default aus, pro Quelle ueberoptions.chunkaktivierbar; zentrale Defaults inconfig/chunking.yaml)src/strategy_detect.pyerkennt pro URL die Strategie (unbekannt -> neue Strategie noetig)src/store.pyliest den Bestand ausoutput/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.pyQuality-Score (0-100) pro Dokumentsrc/site.pyGitLab-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 != offwerden gechunkt (aktuell INB). - Voll-Dokument bleibt unangetastet in
output/processed/. Chunks sind ein zusaetzliches, jederzeit neu erzeugbares Artefakt inoutput/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 inoptionsueberschreibbar. 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) undchunk_component(""alle, sonst nur dieser component_type, z.B."faq"). Mehrere Konfigurationen je Ort sind erlaubt: pro Dokument greift die erste passende (Filterchunk_kind/chunk_component). - Aktive Konfigurationen: INB 2026/2027 (
chunk: headings), pathOS am Ort*/pathoszweigleisig: Anhang-PDFs viachunk: headings, chunk_kind: attachment, chunk_min_doc_chars: 3000und die interne FAQ-Seite viachunk: faq, chunk_component: faq(ein Chunk je Frage); normale Seiten bleiben ganz. - Inkrementell: Chunking laeuft am Ende von
src.mainautomatisch 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 vorhandenenoutput/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