Files
Orchestrator/README.md
T
ankn cfaf670100 Squashed 'bahn/wissensdatenbank/' content from commit 07a8196e
git-subtree-dir: bahn/wissensdatenbank
git-subtree-split: 07a8196e5f9e55d027f90485beb95f4006387669
2026-06-30 21:19:25 +02:00

294 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Wissensdatenbank ETL-Pipeline
## Worum geht es? (in einfach)
Dies ist die **zentrale Wissenssammlung** fuer den 1st-Level-Support und Chatbots rund um
DB InfraGO. Wir **sammeln hier das Wissen** aus vielen Quellen (Confluence, Webseiten,
PDFs), pruefen es und legen es **versioniert im Git-Repo** ab. Damit ist dieses Repo die
**Single Source of Truth**: Wenn etwas hier steht und freigegeben ist, gilt es - und genau
das lesen die Chatbots.
**Was ist „ETL"?** Drei einfache Schritte, die bei jeder Aufnahme passieren:
1. **Extract = Sammeln.** Wissen aus den Quellen holen (Confluence-Seiten, Webseiten, PDFs).
2. **Transform = Aufbereiten.** In einheitliches Markdown umwandeln, mit Schlagworten/Scope
versehen und vertrauliche Inhalte herausfiltern.
3. **Load = Ablegen.** Das geprueefte Wissen versioniert nach `output/processed/` schreiben -
die zentrale Wahrheit, aus der Chatbots/Vektor-DB lesen.
Kurz: **Quellen rein → aufbereiten/pruefen → als Single Source of Truth ablegen.**
## Ziel
Klar definieren, **welches Wissen in Chatbots darf und welches nicht.** Wissen wird bei
der Aufnahme speziell verarbeitet und bereitgestellt, sodass Chatbots es nutzen koennen.
Klassifikation nach Scope:
- **allgemein** darf intern **und** extern genutzt werden; nicht toolspezifisch
(z.B. Regulierung/INB, Kundeninformationen).
- **intern** nur DB InfraGO intern.
- **extern** public im Internet bzw. fuer EVUs/EIUs.
Technisch eine **ETL-Pipeline fuer LLM/RAG**: holt Wissen aus Confluence, Webseiten und
PDFs, klassifiziert es nach Scope, filtert vertrauliche Inhalte und gibt nur geprueftes
Wissen frei (`output/processed/<scope>/...` = Feed fuer die Vektor-DB).
> Architektur & vollstaendiger Wissensfluss inkl. Diagramme:
> [`.kiro/steering/architecture.md`](.kiro/steering/architecture.md)
> · Manuelle Einrichtungsschritte: [`docs/SETUP.md`](docs/SETUP.md)
## Denkmodell
```
Domaene -> Scope (intern|extern|allgemein) -> Tool -> Dokument(e)
```
- **Domaene**: fachlicher Bereich (jedes Tool ist eigene Domaene; `regulierung`, `kundeninfo` fuer allgemeines Wissen).
- **Scope**: `intern` | `extern` | `allgemein` (allgemein gilt fuer intern UND extern).
- **Tool**: eine Fachanwendung aus dem 1st-Level-Support (Katalog automatisch aus Seite 428909879).
- **Strategie pro Quelle** siehe Tabelle unten.
### Strategien (Verarbeitung pro Quelle)
| Strategie | Was sie tut | Wichtige Options |
|-----------|-------------|------------------|
| `confluence_page` | genau diese eine Seite | `scope` (auch `intern,extern`), `incremental`, `attachments` |
| `confluence_tree` | Seite inkl. Unterseiten | `max_depth` (-1=alle, 0=nur Seite), `incremental`, `attachments` |
| `confluence_faq` | FAQs aus Tabellen | `incremental` |
| `crawler` | Webseite: Index → Detailseiten | `detail_pattern`, `selector`, `max_pages` |
| `sitemap` | Detailseiten aus (gz-)Sitemap | `sitemap_url`, `url_pattern`, `limit`, `incremental` |
| `gitlab_md` | Markdown-Dateien aus einem GitLab-Repo | `ref`, `path`, `max_depth` (Token: `GITLAB_TOKEN`) |
| `file` | Dateien (md/pdf) aus dem Repo-Ordner `files/` | `max_depth` |
| `pdf` | PDFs einer Seite → Markdown (1→n) | `split: headings\|pages`, `heading_pattern`, `keep_raw`, `redact` |
### Confluence-Anhaenge (eingebundene PDFs)
Bei `confluence_page`/`confluence_tree` werden **eingebundene PDF-Anhaenge**
(`view-file`/`viewpdf`-Makros) automatisch geparst und als **eigene Dokumente** im
selben Feed abgelegt (Frontmatter `kind: attachment`, `parent_url` zeigt auf die Seite).
Scope/Domain/Owner werden von der Seite geerbt. Inkrementell ueber die Attachment-Version
(unveraenderter Anhang -> kein Re-Parse). Default an, pro Quelle ueber
`options: { attachments: false }` abschaltbar.
Bilder werden nicht binaer uebernommen; der **alt-Text/Dateiname** bleibt als
`[Bild: ...]` im Markdown - gibt dem RAG Kontext ohne Volumen. Datei-Embeds ohne
Parsing-Pfad (z.B. Office-Docs) tauchen als `[Anhang: ...]`-Marker im Seitentext auf.
### Mehrere Quellen pro Tool
Ein Tool kann beliebig viele, gemischte Quellen haben einfach weitere Eintraege
unter `sources:` anhaengen (z.B. mehrere Confluence-Quellen mit `max_depth`, eine
interne und eine externe Seite, oder zusaetzlich eine FAQ-Seite). Siehe die Tools
`pathos` (mehrere Quellen inkl. `confluence_faq` und `intern,extern`) und `nur`.
### intern vs. extern wie wird klassifiziert?
Es gibt **keine Inhaltstrennung innerhalb einer Seite** mehr. Klassifiziert wird pro
Tool und pro Quelle:
**Tool-`scope`** (in `config/tools.yaml`):
| Tool-`scope` | Bedeutung |
|--------------|-----------|
| `intern` | alles intern (Default, restriktiv z.B. Salesforce, NuR) |
| `extern` | alles extern |
| `allgemein` | gilt fuer intern UND extern |
| `mixed` | Tool hat Quellen mit **unterschiedlichem** Scope (z.B. eine interne und eine externe Seite) |
**Source-`scope`** (pro Quelle, gilt fuer die ganze Seite):
- `intern` | `extern` | `allgemein`
- `"intern,extern"` → die Seite wird fuer **beide** Scopes genutzt (dupliziert).
Quellen ohne eigenen `scope` erben den Tool-Scope (bei `mixed` => intern).
**Wichtig:** Hat ein Tool gemischtes Wissen, braucht es im Zweifel **zwei Seiten**
(eine interne, eine externe) als zwei Quellen statt einer gemischten Seite.
Allgemeines, tool-uebergreifendes Wissen steht separat in **`config/general.yaml`**.
Der Tool-Katalog wird **initial** mit `scripts/bootstrap_tools.py` erzeugt und danach
**manuell** in `config/tools.yaml` gepflegt. Uebersicht aller Quellen: GitLab-Pages-Seite
**„Wissensquellen"** (`config.html`, geparst aus tools.yaml/general.yaml/approvals.yaml).
## Neues Wissen hinzufuegen (Workflow)
Jedes Wissen hat **owners** (intern Verantwortliche, Accountability) und einen
**contact** (herausgebbare Kontaktadresse, Default `einfachbahn@deutschebahn.com`).
Beide stehen im Frontmatter jedes Dokuments und auf der Pages-Seite, damit klar ist,
wer verantwortlich ist und an wen man sich wenden darf.
1. **Issue anlegen** mit der Vorlage „Neues Wissen" (`.gitlab/issue_templates/`):
Tool/Domaene, **Quellen je Zeile** (`URL | Strategie | Scope`), **Verantwortliche**
(`owners`) und optional **Kontakt** (Default `einfachbahn@deutschebahn.com`).
2. **Reviewer uebernimmt** die Angaben nach `config/tools.yaml` bzw. `config/general.yaml`
(neuer Tool-/Quellen-Eintrag inkl. `owners:`) per GitLab Web-IDE (Variante A) oder
manuell (Variante B).
3. **Merge Request** öffnen. Die **MR-Freigabe ist das Quality Gate** (4-Augen, via
`CODEOWNERS` + Protected Branch).
4. **Vorschau** im MR: den `preview`-Job manuell starten und `PREVIEW_ONLY` auf
Tool-Id/Domaene/URL der neuen Quelle setzen. Er verarbeitet nur diese Quelle und
stellt die erzeugten Markdowns als Job-Artefakt bereit. Lokal alternativ
`python -m src.main --only "<tool|domaene|url>" --data preview`.
5. **Merge** → der ETL-Schedule verarbeitet die Quelle (stuendlich MoFr, inkrementell) und
committet das Wissen; die Vektor-DB liest direkt aus `output/processed/<scope>/...`.
## Freigabe-Prozess
```
MR mergen (Mensch) -> ETL: Extract -> Transform -> Auto-Filter -> output/processed/ -> Vektor-DB
```
- **Freigabe = Merge Request (Mensch, das eigentliche Gate):** Wer eine Quelle
(Link + Strategie + Scope) in `config/tools.yaml`/`general.yaml` eintraegt, stellt einen
MR. Beim **Merge** (4-Augen via `CODEOWNERS` + Protected Branch) wird entschieden,
welche Quelle mit welchem Scope aufgenommen wird. Danach laeuft der ETL automatisch.
- **Auto-Filter im ETL (Sicherheitsnetz, kein Freigabe-Knopf):** setzt pro Dokument
`review_status` standardmaessig **approved**:
- `approved` -> `output/processed/...` (**direkt live**, sobald gemergt + ETL gelaufen)
- `pending` -> `staging/pending/` (Blacklist-Treffer, Scope-Warnung, oder
Inhalt < 50 Zeichen Grund im Frontmatter `review_notes`)
- `trusted: true` (z.B. INB) bleibt trotz Treffer approved; `redact: false` schaltet
die Redaction pro Quelle ab.
- **`config/approvals.yaml` (nachtraegliche Korrektur):** hebt einzelne
`pending`-Dokumente per URL oder `hash:<content_hash>` doch auf `approved`.
Kein Schritt, den jedes Dokument durchlaeuft. Den Hash zeigt die Pages-Seite (Detail-Fenster).
- **Vektor-DB liest direkt aus `output/processed/<scope>/<domaene>/`** (implementiert):
Nur freigegebenes Wissen liegt dort, der Scope steckt im Ordnerpfad **und** im
Frontmatter (inkl. `owners`/`contact`). Kein separater Export-/Ingestion-Schritt
`processed/` IST der Feed. Der externe Index nimmt nur `extern/` + `allgemein/`.
- **`output/_meta.json`**: globale Statusdatei fuer nachgelagerte Systeme mit
`last_run` (letzter ETL-Lauf), `last_change` (wann sich der Bestand zuletzt
inhaltlich/metadatenseitig geaendert hat), `documents`, `by_scope` und einer
`content_signature`. Ein Index-/RAG-Consumer kann daran erkennen, ob ein erneutes
Einlesen ueberhaupt noetig ist.
- **`output/_index.json`**: dokument-genauer Katalog des gesamten Bestands.
Pro Voll-Dokument: `domain`/`tool`/`scope`, `url`, `path`, `content_hash`,
`last_updated` und - falls vorhanden - die zugehoerigen `chunks` (Anzahl + Pfad)
und `attachments` (Anhang-Dokumente: Name + Pfad). Anhang-Dokumente haben zudem
`kind: "attachment"` und `parent_url`. Aggregate: `documents_total`,
`chunks_total`, `attachments_total`, `by_domain` (mit `documents`/`chunks`/`attachments`),
`by_scope`. So sieht ein Anschliesser auf einen Blick, was pro Domaene/Tool wo liegt
und welche Dokumente zusaetzlich als Chunks oder Anhaenge vorliegen (z.B. INB =
Voll-Dokument **und** Chunks; pathOS-Seite = Voll-Dokument **und** Anhang-PDF).
Deterministisch (kein Zeitstempel) -> aendert sich nur bei echten Bestandsaenderungen.
- **`output/run_log.jsonl`**: append-only **Lauf-Historie** (1 JSON-Zeile je ETL-Lauf):
Zeitstempel, verarbeitete Dokumente, Status-Counts und **Fehler je Quelle**
(z.B. fehlgeschlagene Confluence-Abrufe). Auf die letzten 500 Laeufe gekappt. So sieht
man - auch historisch - ob ein Lauf sauber durchlief, ohne die fluechtigen CI-Job-Logs
zu durchsuchen. Die Uebersichtsseite zeigt zusaetzlich eine kompakte Health-Zeile
(„N Dok verarbeitet, M Quellen mit Fehler") in der Fusszeile.
### Wo liegt das final freigegebene Wissen?
Ausschliesslich unter **`output/processed/<scope>/<domaene>/[<tool>/]`** im Repo
(versioniert, = Single Source of Truth). `pending`-Dokumente liegen in
`staging/pending/` und werden zur **Transparenz ebenfalls committet** (auf der
Pages-Seite einsehbar). Der Audit-Trail steht in `staging/review_report.json` und in
der Git-Historie.
## Lokal testen
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
ruff check src tests # Lint
python -m pytest -q # Offline-Tests
python -m src.main --config config/tools.yaml --data output --staging staging # Live-Lauf
python -m src.chunk --data output # Chunks erzeugen (offline, optional)
```
## Confluence aktivieren
```bash
export CONFLUENCE_URL="https://arija-confluence.jaas.service.deutschebahn.com"
export CONFLUENCE_TOKEN="<PAT>" # Bearer-PAT (Server/DC)
pip install atlassian-python-api
```
Ohne Credentials werden Confluence-Quellen sauber uebersprungen.
## Betrieb (GitLab CI)
- `quality`-Stage: `ruff` (Lint) + `gitleaks` (Secret-Scan) bei jedem Push/MR.
- `etl`-Stage: **stuendlich MoFr 817 Uhr** (Cron `0 8-17 * * 1-5`), baut Wissen,
committet `data/` nach `main` (per `GIT_PUSH_TOKEN`); der Commit loest automatisch
einen `pages`-Refresh aus.
- `pages`-Stage: baut `public/` (Uebersicht, Hilfe, Chatbot-Anschluss, Wissensquellen)
fuer GitLab Pages auf `main`.
- `preview`-Job (MR, manuell): verarbeitet eine neue/geaenderte Quelle via
`--only` und stellt die erzeugten Markdowns als Job-Artefakt bereit (zum Pruefen
vor dem Merge). `PREVIEW_ONLY` beim Start auf Tool-Id/Domaene/URL setzen.
- **Kundeninfos inkrementell**: `incremental: true` crawlt nur NEUE Detailseiten und
ueberschreibt vorhandene nicht Re-Runs bleiben schnell.
- **Confluence inkrementell**: `options: { incremental: true }` prueft pro Seite die
Confluence-Versionsnummer (`source_version` im Frontmatter). Unveraenderte Seiten
werden uebersprungen, nur geaenderte/neue Seiten neu verarbeitet spart API-Last.
- **Re-Tagging bei Metadaten-Aenderung**: Aenderst du in `tools.yaml`/`general.yaml`
nur `tags`/`owners`/`contact` einer Quelle, weicht deren `meta_fingerprint`
(im Frontmatter) vom gespeicherten ab. Der naechste inkrementelle Lauf passt dann
die bestehenden Dateien dieser Quelle in-place an (nur Frontmatter, kein erneuter
Download/API-Call).
- **Scope-Wechsel**: Aenderst du den `scope` einer Quelle, wird die Seite neu
verarbeitet und an `output/processed/<neuer-scope>/...` geschrieben; die alte Ablage
wird nach erfolgreichem Schreiben **automatisch geloescht** (domaenen-intern, damit
eine URL, die legitim unter mehreren Domaenen liegt z.B. eine pathOS-Seite auch
unter `web` nicht versehentlich entfernt wird).
- **Domain-Umbenennung**: Die neue Ablage entsteht automatisch, die alte Domain-Ablage
bleibt aber stehen (domaenenuebergreifendes Loeschen waere wegen geteilter URLs
unsicher). Den alten `output/processed/<scope>/<alte-domain>/`-Ordner daher bei einer
Domain-Umbenennung manuell loeschen.
- CI/CD-Variablen: `GIT_PUSH_TOKEN`, `CONFLUENCE_URL`, `CONFLUENCE_TOKEN` (Masked).
- `scm-info.yaml`: Pflichtdatei fuer DB-GitLab-Compliance.
## Konfiguration
- `config/tools.yaml` Tools, Domaenen, Quellen, Strategien, Scopes, Tags
- `config/filter_rules.json` Blacklist-Keywords, Regex-Redaction
- `config/chunking.yaml` gemeinsame Chunk-Defaults (pro Quelle ueber `options.chunk` ueberschreibbar)
### Chunking (optional, Default aus)
Grosse, stark gegliederte Dokumente (v.a. **INB**) werden zusaetzlich zur Voll-Datei in
**Chunks entlang der Ueberschriften** zerlegt ein **abgeleitetes, jederzeit neu
erzeugbares** Artefakt unter `output/chunks/<scope>/<domaene>/<docslug>/`. Das Voll-Dokument
in `output/processed/` bleibt unangetastet (Parent-Document-Muster): ein Anschliesser waehlt
Voll-Dokument, Chunks oder beides. Es werden bewusst nur **deterministische, embedding-freie**
Strategien angeboten (`headings | faq | recursive`); semantisches Chunking gehoert in die
Vektor-DB des Anschliessers. Jeder Chunk bekommt eine kurze **Contextual-Retrieval**-Zeile
(`> Kontext: <Dokument> > <Abschnitt>`). Aktivierung pro Quelle via `options: { chunk: headings }`.
**Inkrementell (kein Voll-Rebuild):** Chunking laeuft am Ende jedes ETL-Laufs automatisch
mit (`src.main`) und ist Teil von `data/`. Pro Dokument wird nur dann neu gechunkt, wenn
sich der **Inhalt** (`parent_hash`) ODER die **wirksamen Optionen/Strategie**
(`chunk_fingerprint`) geaendert haben - sonst wird das Dokument uebersprungen (keine
Datei-Aenderung, kein Git-Churn). Aenderst du also bei einer Quelle z.B. `chunk_level`
oder von `headings` auf `recursive`, werden **nur die Dokumente dieser Ablage** neu
erzeugt. Wird `chunk` wieder auf `off` gestellt oder ein Voll-Dokument geloescht, werden
die zugehoerigen Chunks automatisch entfernt.
**Feinsteuerung (Default aus, pro Quelle ueberschreibbar):**
- `chunk_min_doc_chars`: Dokumente unter dieser Zeichenzahl bleiben **ganz** (kleine
FAQ-/How-to-Seiten muessen nicht zwingend gechunkt werden).
- `chunk_kind`: chunkt nur Dokumente mit passendem `kind` (`""` = alle; `attachment` =
nur Anhang-PDFs; `document` = nur Voll-Seiten).
Beispiel **pathOS** (an der Tool-Tree-Quelle in `config/tools.yaml`): grosse Anhang-PDFs
(z.B. EVU-Schnittstellen-Doku, 2,8 MB) werden gechunkt, Seiten und FAQs bleiben ganz.
```yaml
options:
incremental: true
chunk: headings
chunk_kind: attachment # nur Anhang-PDFs chunken
chunk_min_doc_chars: 3000 # < 3k Zeichen bleiben ganz (~750 Token)
```
## Version & Changelog
Aktuelle Version steht in **`VERSION`** (SemVer), die Historie in **`CHANGELOG.md`**
(Format „Keep a Changelog"). Beides wird auf der GitLab-Pages-Seite **„Changelog"**
angezeigt (Version auch in der Navigationsleiste). Pflege-Regeln:
`.kiro/steering/changelog-versioning.md`.