# Projektplan — Setup phy-z-srv-gpu01 (LLM-Server mit SMB-Wissensbasis) Date: 2026-07-10 (Stand-Update: 2026-09-03) Status: **In Umsetzung** — Server geliefert, Basis-Setup + GPU-Treiber fertig Basiert auf: `20260706-hardware-assessment.md`, `20260706-software-assessment.md`, `20260707-deep-dive.md` --- ## Stand 2026-09-03 (Kurzfassung — Details in den jeweiligen Abschnitten) **Erledigt:** Server geliefert und im Rack; Basis-Setup (Ubuntu 24.04.4, Nutzer, Netz, RAID/Filesystem — s. `SETUP.md` im Repo-Root); GPU-Treiber via Ansible-Rolle `nvidia_gpu` (595er-Branch, **open** Kernel-Module — Blackwell läuft nicht mit den proprietären Modulen, s. `manuals/20260714-nvidia-driver-install.md`); Docker installiert. Share-Analyse gelaufen (§2.1). **Als Nächstes:** CIFS-Mounts + Container-Stack (Rollen `cifs_mounts`, `llm_stack`) — entspricht Phasen 3–5 in §3. **Kritischer offener Punkt:** Der Bestand ist rund **12× größer** als die in §1 gesetzte Schwelle. Ohne Eingrenzung des Korpus (Kundengespräch) ist Phase 5 nicht sinnvoll umsetzbar — s. §2.1 und §2.6. ## 0. Rahmenbedingungen (fixiert) | Punkt | Entscheidung | |---|---| | Hardware | HPE DL380 Gen12, 2× Xeon 6714P, 128 GB RAM, RTX PRO 6000 96 GB, 2× 960 GB NVMe — s. `HW.md` | | Betrieb | **Bare Metal**, keine Virtualisierung des LLM-Stacks | | Nutzer | ~25, gemischt (technisch versiert + Nicht-Techniker) | | Auth | **AD/LDAP** (Details Service-Account/Basis-DN als Pre-Work klären) | | Berechtigungen | **Einfach: alle Nutzer sehen den gesamten indizierten Bestand** (Kunde bestätigt; Shares ohne HR-/Personal-Inhalte halten) | | Sprache | Dokumente & Nutzer überwiegend **Deutsch** → Modell- und Embedding-Wahl daran ausrichten | | Inference | **vLLM** mit fester GPU-Auslastung (kein Ollama als Basis) | | Wissensbasis | SMB-Shares (einige TB, PDF/Office), **Sync ist die Kernanforderung** | | Später | Jira-Anbindung u. a. Workloads; dafür ggf. **MIG** | | Vorgehen | Setup vollständig über **Ansible** (dieses Repo), PoC vorab: nein — reine Planung | ## 1. Produktentscheidung: Open WebUI statt Onyx **Empfehlung: Open WebUI** (bestätigt und verschärft die Empfehlung aus dem Deep Dive §4.7). Ausschlaggebend, Stand 2026-07-10 verifiziert: 1. **Onyx' File-Connector ist reiner manueller Upload** — kein Verzeichnis-Watching, kein periodischer Re-Sync eines gemounteten Shares ([Doku](https://docs.onyx.app/admins/connectors/official/file)). Die Kernanforderung „Shares synchron halten" hieße bei Onyx: Eigenbau gegen deren API. Open WebUI hat dafür das offizielle Sync-Tool [`oikb`](https://github.com/open-webui/oikb) (inkrementell, Löschungen, Cron-fähig — Deep Dive §4.3). 2. **Onyx: SSO (OIDC/SAML), User Groups, RBAC, Permission-Sync = Enterprise Edition (kostenpflichtig)** ([Doku](https://docs.onyx.app/deployment/miscellaneous/enterprise_edition)). Open WebUI: LDAP/AD-Anbindung und Gruppen sind frei. 3. Onyx bringt einen zweiten schweren Stack mit (Vespa-Index, Postgres, Worker) — auf 128 GB RAM **neben** vLLM spürbar; widerspricht „it just works". 4. Bedienung: ein UI im ChatGPT-Stil, vorkonfigurierte Assistenten mit fester Knowledge Base → passt für den gemischten Nutzerkreis. **Wann die Entscheidung zu revidieren ist** (Trigger, nach der Share-Analyse §2.1): - Der *relevante* Bestand liegt deutlich über ~10⁵ Dokumenten und lässt sich nicht sinnvoll filtern → Suchqualität/Skalierung von Open WebUI prüfen, Onyx (mit Eigenbau-Sync) oder Eigenbau-Pipeline (Deep Dive §4.5) neu bewerten. - Datei-genaue NTFS-ACLs werden doch Anforderung → Neubewertung (Deep Dive §4.4/4.5). **Status 2026-09-03:** Trigger 1 ist **eingetreten** (1,26 Mio. Office-Dateien, §2.1). Die Produktentscheidung bleibt dennoch bei Open WebUI — aber **nur unter der Bedingung**, dass der Korpus auf einen kuratierten Teilbestand eingegrenzt wird (§2.6). Ein ungefilterter Vollindex über 1,26 Mio. Dokumente ist mit Open WebUI nicht sinnvoll zu betreiben (und liefert auch inhaltlich schlechte Antworten — RAG über einen kompletten, ungeordneten Fileserver retourniert überwiegend Rauschen). Jira später: für *Indizierung* von Jira-Inhalten hat Onyx zwar native Konnektoren, Open WebUI deckt das aber über Tools/MCP (Atlassian-MCP) bzw. API-Workloads direkt gegen vLLM ab — kein Entscheidungskriterium für heute. ## 2. Phase 0 — Pre-Work (jetzt, ohne Server) ### 2.1 Share-Analyse (wichtigster Punkt — ja, unbedingt vorab) Read-only-Skript auf einer beliebigen Maschine mit Share-Zugriff (kein GPU-Server nötig). Zu erheben: - Volumen & Dateianzahl **pro Share und Dateityp** (pdf/docx/xlsx/…) - Anteil **gescannter PDFs** (Stichprobe: PDFs ohne Textlayer) → OCR-Bedarf - Duplikate/Altversionen („Kopie von …", Versionsordner) → Filterregeln - Änderungsrate (mtime-Verteilung) → Sync-Frequenz - Anteil nicht extrahierbarer Formate (CAD, Bilder, Archive) → Erwartungsmanagement - Sonderzeichen/Pfadlängen, Encoding (Umlaute!) Ergebnis bestimmt: Korpus-Umfang (→ §1-Trigger), OCR-Pipeline ja/nein, Index-Größe (→ Storage-Check §2.3), Erst-Indexierungsdauer. **Ergebnis (Lauf 2026-07-14, Skript `scripts/share-analysis.ps1` auf `D:` des Fileservers Z-FILESERVER):** | Kennzahl | Wert | Konsequenz | |---|---|---| | Gesamt | **3.322.175 Dateien, 2.224 GB** | Scan lief über `D:` gesamt, nicht je Share → Obergrenze, enthält evtl. nicht freigegebene Daten | | `office` (extrahierbar) | **1.258.642 Dateien, 587 GB** | **12× über der §1-Schwelle** → Eingrenzung zwingend (§2.6) | | `other` (unklassifiziert) | 1.575.752 Dateien, 934 GB | größter Block, **noch unidentifiziert** → `D-file-types.csv` auswerten | | Bilder / CAD / Archive / Medien | 403k / 60k / 12,6k / 6,2k Dateien | nicht extrahierbar → Erwartungsmanagement | | PDFs | **924.076**, davon ~**18 % ohne Textlayer** (Stichprobe 200) | ≈ **166.000 Scan-PDFs → OCR**; eigener Zeit-/GPU-Aufwand, konkurriert mit vLLM | | Änderungsrate | 94 % älter als 1 Jahr; nur ~158k in den letzten 365 Tagen | Sync-Frequenz unkritisch; **Alter ≠ Irrelevanz** (Datenblätter, Normen) → Filter primär ordner-/inhaltsbasiert, Aktualität nur sekundär | | Duplikat-/Altversions-Muster | 33.179 Dateien | Filterregeln | | Pfade > 240 Zeichen | 25.169 | Extraktions-Toolchain gezielt testen | | Nicht-ASCII-Dateinamen | 126.111 | UTF-8 durchgängig (CIFS-Mount-Optionen → Extraktion → Index) | **Noch auszuwerten** (CSVs liegen auf dem Fileserver, noch nicht gesichtet): `D-toplevel-folders.csv` (Größe/Anzahl je Top-Level-Ordner — die Grundlage für die Include-/Exclude-Liste) und `D-file-types.csv` (Auflösung der `other`-Kategorie). ### 2.2 Mit dem Kunden zu klären (Checkliste) — Stand 2026-09-03 - [x] AD/LDAP: AD-Gruppe **`llm_users`** wird angelegt. *Offen:* Bind-Service-Account und Basis-DN (werden für die LDAP-Konfiguration in Open WebUI gebraucht) - [x] **SMB-Service-Account read-only** wird separat angelegt (kein Nutzer-Account) - [ ] **Welche Shares/Unterordner in den Index?** — offen, wird besprochen; **wichtigster Punkt**, s. §2.1/§2.6 - [x] DNS-Name **`chat.phytron.local`** (vorerst); **kein TLS/keine lokalen Zertifikate** zu Beginn → reines HTTP im LAN. *Hinweis:* AD-Passwörter gehen damit im Klartext über das Netz; für später ein internes CA-Zertifikat einplanen (Reverse Proxy ist vorbereitet, nur Zertifikat fehlt) - [x] Internetzugang: **unbeschränkt**, keine Einschränkung nötig - [ ] Protokollierung/Datenschutz: **noch zu besprechen** (Chat-Logs = Verhaltensdaten; Betriebsrat/DSGVO) — vor dem Rollout klären, nicht vor der technischen Umsetzung - [x] Betrieb/Wartung/Updates: **wir** (Softbox) - [x] „Andere Workloads": **Jira vorerst nicht relevant** → MIG bleibt aus, die volle GPU geht an den LLM-Stack ### 2.3 Storage-Check — ✅ erledigt, unkritisch Ist-Stand auf dem Server: **876 GB nutzbar** (`/`, LVM), davon 818 GB frei. Gegenrechnung mit §2.1: selbst ein Vollindex des `office`-Bestands (1,26 Mio. Dokumente → grob 150–250 GB inkl. extrahiertem Text, Embeddings und Index-Overhead) plus Modelle (~100 GB) passt. Strategie bleibt: **kein lokaler Spiegel der Shares**, nur Index. **Keine zusätzlichen NVMe nötig.** Der begrenzende Faktor ist damit *nicht* der Plattenplatz, sondern Retrieval-Qualität und Indexgröße (§2.6). ### 2.4 Ansible vorbereiten (im Repo, testbar ohne GPU) - `run.yml`: Play für `phy_z_srv_gpu01` ergänzen - Rollen-Skelett unter `ansible/roles/` (Details erst bei Umsetzung): - `nvidia_gpu` — Treiber (≥ 580), Container Toolkit, optional MIG, DCGM - `cifs_mounts` — ro-Mounts, Credentials aus `group_vars/secrets.yml` - `llm_stack` — Docker Compose: vLLM (feste `--gpu-memory-utilization`), Embedding-Server, Open WebUI + pgvector, Reverse Proxy (TLS), oikb-Timer - Bestehendes nachnutzen: `geerlingguy.security` (Basis-Härtung), `geerlingguy.docker` - `group_vars/phy_z_srv_gpu01.yml`: nur Overrides (Modellname, VRAM-Quote, Share-Liste, LDAP-Parameter) - Alles außer GPU-Rolle ist vorab in einer Wegwerf-VM testbar (`just run`-Pfad) ### 2.5 Modell-Shortlist — Entscheidung 2026-09-03 **Startmodell: Qwen3-32B FP8.** Begründung: ~35 GB VRAM, damit reichlich Reserve für KV-Cache (~25 parallele Nutzer) und das Embedding-Modell daneben; gutes Deutsch; hoher Durchsatz. Alternative bei Qualitätsbedarf: gpt-oss-120b (MoE, ~60–65 GB) — weniger Parallelität, engerer Fit. Embeddings: bge-m3 / multilingual-e5 (mehrsprachig, zwingend für Deutsch). Das ist der **Startwert als Rollen-Variable**, keine endgültige Festlegung: Die finale Wahl fällt gegen das Eval-Set (s. u.) und nach dem vLLM-Smoke-Test. ⚠️ **Offener technischer Punkt:** Die offiziellen vLLM-Docker-Images unterstützen SM120 (Blackwell Workstation/Server) laut aktueller Quellenlage **nicht zuverlässig out of the box**; auf RTX-PRO-6000-Systemen werden verbreitet Nightly- oder Community-Builds eingesetzt ([vLLM-Forum](https://discuss.vllm.ai/t/support-for-rtx-6000-blackwell-96gb-card/1707), [rtx6kpro](https://github.com/local-inference-lab/rtx6kpro/blob/master/inference-engines/vllm.md)). Vor dem Bau der Rolle `llm_stack` daher **ein Container manuell auf dem Server testen** und das funktionierende Image pinnen — dieselbe Fehlerklasse wie beim Treiber (proprietäre vs. open Kernel-Module). Kleines deutsches Eval-Set (20–30 Fragen mit erwarteten Antworten aus dem Bestand) mit dem Kunden sammeln — das ist später das Abnahmekriterium. ### 2.6 Korpus eingrenzen (neu, wichtigster offener Punkt) Die Share-Analyse (§2.1) hat 1,26 Mio. potenziell extrahierbare Dokumente ergeben. „Alles indizieren" ist weder technisch sinnvoll noch inhaltlich wünschenswert. Vorschlag für das Kundengespräch: 1. **Ordner statt Alter als Filter.** Aus `D-toplevel-folders.csv` gemeinsam mit dem Kunden die Ordner markieren, die tatsächlich Wissensbasis sind (Doku, Normen, Datenblätter, Handbücher, Projektdoku) — Rest bleibt außen vor. Reiner Aktualitätsfilter wäre falsch: Datenblätter und Normen sind über Jahre gültig. 2. **Ausschlusslisten** für Duplikate/Altversionen (33k Treffer), Archiv- und Backup-Ordner, nicht extrahierbare Formate (CAD, Bilder, Medien). 3. **Zielgröße nennen.** Als Orientierung: eine gut funktionierende Wissensbasis liegt eher im Bereich **10.000–100.000 Dokumente**. Das ist kein hartes Limit, aber der Bereich, in dem Retrieval-Qualität und Indexierungsdauer beherrschbar bleiben. 4. **Iterativ starten.** Phase 5 zuerst mit *einem* gut abgegrenzten Share/Ordner, Qualität am Eval-Set (§2.5) messen, dann erweitern. Das erspart einen tagelangen Fehlversuch über den Gesamtbestand. **Erwartungsmanagement gegenüber dem Kunden:** Die ursprüngliche Anforderung lautete sinngemäß „unsere Dokumente auslesen". Realistisch ist „ein kuratierter Teil unserer Dokumente, dafür mit guten Antworten". Das früh sagen — nicht erst, wenn der Vollindex schlechte Treffer liefert. ## 3. Phasen ab Server-Lieferung | Phase | Inhalt | Ergebnis/Abnahme | |---|---|---| | **1. Basis** (Woche 1) | Rack/Strom (600-W-GPU!), iLO, Firmware, RAID, Ubuntu 24.04 LTS, Eintrag in Ansible-Basis-Setup (Security, Pakete, Nutzer) | `just run phy_z_srv_gpu01` läuft grün | | **2. GPU-Stack** (Woche 1–2) | Rolle `nvidia_gpu`: Treiber, Container Toolkit, DCGM; **Burn-in unter Dauerlast** (SM120-Risiken, Deep Dive §1.6); MIG erst mal **aus** | `nvidia-smi` ok, 48 h-Lasttest ohne Reset | | **3. Inference** (Woche 2) | vLLM-Container (NVIDIA-Build) mit gewähltem Modell, feste VRAM-Quote, Embedding-Server daneben; Benchmark mit Eval-Set | deutsche Antworten ok, Ziel-Parallelität erreicht | | **4. UI + Auth** (Woche 2–3) | Open WebUI + pgvector, LDAP-Login, Reverse Proxy + TLS, vorkonfigurierter „Phytron-Assistent" | Login mit AD-Konto, Chat läuft | | **5. Wissensbasis** (Woche 3–5) | CIFS-Mounts (ro), Extraktion inkl. OCR-Engine (Tika/Docling), oikb-Sync erst auf **einen Teilbestand**, Qualität prüfen, dann Vollindex (Dauer aus §2.1 abschätzen) | Fragen aus dem Eval-Set werden mit korrekten Quellen beantwortet | | **6. Pilot → Rollout** (Woche 5–7) | 3–5 Pilotnutzer (gemischt), Feedback, dann alle 25; Onboarding-Einseiter (was kann es, was nicht — CAD/Scans!) | Abnahme durch Kunden | | **7. Betrieb** | Monitoring (GPU/DCGM, Dienste), Backup (Konfig + DBs — Modelle nicht), Update-Runbook `manuals/`, Doku im README | Runbooks vorhanden | | **später** | Jira & weitere Workloads; **erst dann MIG-Layout** festlegen (bis 4 Instanzen) statt jetzt raten | — | ## 4. Risiken | Risiko | Umgang | |---|---| | ~~Storage zu klein für Index~~ | ✅ erledigt — 876 GB nutzbar, reicht (§2.3) | | **Korpus 12× über der Planungsschwelle (§2.1)** | **Eingrenzung auf kuratierten Teilbestand (§2.6); ohne Kundenentscheid keine Phase 5** | | ~166.000 Scan-PDFs → OCR-Aufwand & -Qualität | eigener Arbeitsblock, GPU-Konkurrenz zu vLLM einplanen; Erwartungen dämpfen | | ~~SM120/Treiber-Kinderkrankheiten~~ | ✅ erledigt — Treiber 595 **open** läuft (proprietäre Module scheitern an `RmInitAdapter`) | | Kein TLS zu Beginn (§2.2) | AD-Passwörter im Klartext im LAN; internes CA-Zertifikat nachrüsten, Reverse Proxy vorbereiten | | Software-Stand veraltet bis Lieferung | Versionen **erst bei Installation pinnen**; Wiedereinstiegs-Checkliste | | Erst-Indexierung dauert Tage | Teilbestand zuerst; Sync nachts; Dauer vorab abschätzen | | RAM-Konkurrenz Extraktion/OCR vs. vLLM | vLLM hat feste VRAM-Quote; Indexer-Jobs drosseln (nice/cgroups); 128 GB reichen für OWUI-Stack | | Datenschutz/Betriebsrat bei Chat-Logs | Früh klären (§2.2), Logging-Policy dokumentieren | | Kein HA — ein Server | Erwartung managen: Wartungsfenster = Dienst weg; Backup-Restore-Runbook | ## 5. Wiedereinstiegs-Checkliste (wenn der Server da ist) 1. [x] Dieses Dokument + Deep Dive lesen; offene Punkte aus §2.2 abhaken 2. [x] Ergebnis der Share-Analyse vorliegen? → §2.1 3. [ ] Neu sichten & **dann erst pinnen**: vLLM-Version (RTX-PRO-6000-/SM120-Support), Open WebUI + oikb Release Notes, Modell-Shortlist §2.5 — NVIDIA-Treiber ✅ (595-open) 4. [x] Storage-Entscheidung §2.3 → unkritisch, nichts nachzubestellen 5. [ ] Dann Phasenplan §3 von oben abarbeiten; jede Phase = Ansible-Commit + ggf. Runbook unter `manuals/` ## 6. Bewusst offen gelassen (kein Jetzt-Entscheid nötig) - Exaktes Modell & Quantisierung, vLLM-Flags, MIG-Layout, OCR-Engine-Wahl, Monitoring-Stack-Details, Reverse-Proxy-Produkt — alles bei Umsetzung mit aktuellem Stand entscheiden; Rahmen steht oben.