diff --git a/README.md b/README.md index 560634e..673f852 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Dokumentation für die Infrastruktur eines privaten Homelabs mit öffentlich erreichbaren Webdiensten, einem WireGuard-Zugang und einem dedizierten KI-Server. Dieses Repository hält Architekturentscheidungen, den belegten Betriebsstand und Verfahren für Einrichtung, Betrieb und Sicherheitsabnahme fest. -> **Projektstand: 20.09.2026.** Die Dokumentationsrevisionen sind zusammengeführt. Mehrere Sicherheitsmaßnahmen und technische Endabnahmen stehen noch aus. Auf dem KI-Server sind Host-Baseline, Podman, getrennte rootless Workload-Konten und der Gerätezugriff der KI-Konten auf beide AMD-GPUs eingerichtet. Ein gepinnter `llama.cpp`-ROCm-Build erkennt beide Karten im rootless Testing-Container; dauerhafte LLM- und Encoding-Anwendungen sind noch nicht installiert. +> **Projektstand: 21.09.2026.** Die Dokumentationsrevisionen sind zusammengeführt. Mehrere Sicherheitsmaßnahmen und technische Endabnahmen stehen noch aus. Auf dem KI-Server sind Host-Baseline, Podman, getrennte rootless Workload-Konten und der Gerätezugriff der KI-Konten auf beide AMD-GPUs eingerichtet. Ein gepinnter `llama.cpp`-ROCm-Build erkennt beide Karten im rootless Testing-Container; Swift- und Unsloth-Q6_K sind als 128k-A/B-Kandidaten geplant, aber dauerhafte LLM- und Encoding-Anwendungen sind noch nicht installiert. ## Überblick @@ -39,7 +39,7 @@ Der [Dokumentationsindex](docs/GNAI-000-DOC_Index.md) enthält die datierten Que | --- | --- | | [Dokumentationsindex und Projektstand](docs/GNAI-000-DOC_Index.md) | Dokumentenübersicht, bestätigte Entscheidungen und offene Befunde | | [Plattform-Dokumentation](docs/platform/README.md) | Hostbasis, Zugriffsmodell, Reverse Proxy und VPN | -| [KI-Dokumentation](docs/ai/README.md) | KI-Zielarchitektur und nicht finale Ansible-Deployment-Planung | +| [KI-Dokumentation](docs/ai/README.md) | KI-Zielarchitektur, Ansible-Planung, Modellbewertung und technische `llama.cpp`-Sollkonfiguration | | [Video-Encoding](docs/video-encoding/GNENC-000-DOC_Index.md) | getrennter Planungs- und späterer SOP-Bereich für den CPU-Encoding-Workload | | [Ansible-Control-Projekt](ansible/README.md) | lokale Einrichtung, Preflight, Deployment-Account, Host-Baseline, Podman, Workload-Konten, AMD-GPU-Zugriff und rootless ROCm-Probe | diff --git a/docs/GNAI-000-DOC_Index.md b/docs/GNAI-000-DOC_Index.md index 6b24a9a..b7a3af4 100644 --- a/docs/GNAI-000-DOC_Index.md +++ b/docs/GNAI-000-DOC_Index.md @@ -1,6 +1,6 @@ # Ghostnet.AI – Dokumentationsindex und Projektstand -**Stand:** 2026-09-20 +**Stand:** 2026-09-21 **Status:** Revisionen zusammengeführt; Sicherheitsumsetzung und technische Endabnahme offen ## 1. Dokumente und Zielgruppen @@ -27,6 +27,8 @@ | [Bereichsindex](ai/README.md) | Einstieg in die KI-spezifische Dokumentation | | [GNAI-003-INFRA_Architecture.md](ai/GNAI-003-INFRA_Architecture.md) | Gesamtzielarchitektur und Inventar des KI-Stacks; Intermediate IT | | [GNAI-007-PLAN_Ansible-Deployment.md](ai/GNAI-007-PLAN_Ansible-Deployment.md) | nicht finaler Arbeitsentwurf zum Ansible-/Gitea-Deployment und zur geplanten Folgedokumentation; Infrastrukturadministratoren und Mitwirkende | +| [GNAI-008-PLAN_Llama-Model-Evaluation.md](ai/GNAI-008-PLAN_Llama-Model-Evaluation.md) | nicht finaler A/B- und Abnahmeplan für Swift, Unsloth, Kontext und MTP; KI-Administratoren und Tester | +| [GNAI-009-TECH_Llama-Serving.md](ai/GNAI-009-TECH_Llama-Serving.md) | technische Sollkonfiguration für Modellartefakte, rootless Quadlet, ROCm und `llama-server`; Infrastrukturadministratoren | ### Video-Encoding und Automatisierung @@ -65,6 +67,8 @@ Die Rolle `amd_gpu_access` bestätigte am 20.09.2026 zwei Sapphire-Navi-21-Gerä Das getrennte Playbook `verify-rocm-container.yml` lud am 20.09.2026 für `gnai-testing` den offiziellen `llama.cpp`-ROCm-Build `b11058` als unveränderliches amd64-Manifest `sha256:b429b8be10d3d0353a09c98f4ab840fe516f779858a28f9fd4e5bdf10bc63cb4`. Mit `/dev/kfd`, beiden Render-Nodes und `keep-groups` meldete `rocminfo` genau zwei `gfx1030`-Agents; `llama-server --list-devices` meldete `ROCm0` und `ROCm1` als Radeon RX 6900 XT. Der abschließende Lauf endete mit `ok=33 changed=0 unreachable=0 failed=0`. Die Probecontainer wurden entfernt; dauerhafter Container-, Modell- und Inferenzbetrieb sind damit noch nicht abgenommen. +Am 21.09.2026 wurden die Modellrecherche und die daraus abgeleitete Sollkonfiguration in GNAI-008 und GNAI-009 persistiert. Installiert werden sollen Swift-Qwen3.8-27B Q6_K und Unsloth Qwen3.8-27B UD-Q6_K, jeweils mit Q8-K/V, einem Zielkontext von 131.072 Token, einem Slot und zunächst ohne Vision-Projektor oder MTP. Revisionen und SHA-256 beider Artefakte sind festgehalten. Jedes Modell erhält ein eigenes Quadlet; systemd schließt deren gleichzeitigen Betrieb aus und führt den Wechsel als Stop vor Start aus. Der kontrollierte A/B-Test verwendet zunächst `xhigh` für beide Modelle; danach folgt Swift `xhigh` gegen Unsloth `medium`. Dies ist eine Planungsentscheidung und noch kein Nachweis für Modelldownload, 128k-Betrieb oder Inferenz. + ## 3. Sicherheitsbefunde mit Priorität | Befund | dokumentiertes Verfahren / Status | diff --git a/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md b/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md index 9b3a376..8b55528 100644 --- a/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md +++ b/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md @@ -2,12 +2,12 @@ **Dokumenten-ID:** GNAI-007-PLAN **Titel:** Ansible-Deployment für den KI-Server – Planungsstand -**Stand:** 2026-09-20 +**Stand:** 2026-09-21 **Status:** Nicht finaler Arbeitsentwurf zur Persistierung des aktuellen Planungsstands -**Referenzen:** [Projektstand](../GNAI-000-DOC_Index.md), [Zielarchitektur](GNAI-003-INFRA_Architecture.md), [Zugriffskonzept](../platform/GNAI-004-SEC_Access-Control.md) +**Referenzen:** [Projektstand](../GNAI-000-DOC_Index.md), [Zielarchitektur](GNAI-003-INFRA_Architecture.md), [Modellbewertung](GNAI-008-PLAN_Llama-Model-Evaluation.md), [llama.cpp-Sollkonfiguration](GNAI-009-TECH_Llama-Serving.md), [Zugriffskonzept](../platform/GNAI-004-SEC_Access-Control.md) > **Abgrenzung:** Dieses Dokument hält die Ergebnisse der am 19.09.2026 -> begonnenen und am 20.09.2026 fortgeschriebenen Planungssession fest. Es ist +> begonnenen und bis zum 21.09.2026 fortgeschriebenen Planungssession fest. Es ist > weder eine freigegebene technische Spezifikation noch > eine Installationsanleitung oder ein Nachweis über bereits umgesetzte > Komponenten. Entscheidungen, die noch geprüft oder bestätigt werden müssen, @@ -31,6 +31,9 @@ | 2026-09-20 | 0.10 | spektr / Codex | ROCm als Backend für zwei RX 6900 XT, initialen `llama.cpp`-Modellstand und Layer-Split mit gleicher GPU-Gewichtung festgehalten | | 2026-09-20 | 0.11 | spektr / Codex | AMD-Gerätezugriff für beide rootless KI-Konten implementiert, angewendet und idempotent geprüft | | 2026-09-20 | 0.12 | spektr / Codex | gepinnten rootless ROCm-Probelauf für Testing implementiert; zwei `gfx1030`-Agents und zwei `llama.cpp`-ROCm-Geräte nachgewiesen | +| 2026-09-21 | 0.13 | spektr / Codex | Swift-Qwen3.8 Q6_K mit 128k und `xhigh` als Testing-Kandidat übernommen; Modellbewertung und technische Sollkonfiguration ausgelagert | +| 2026-09-21 | 0.14 | spektr / Codex | Unsloth UD-Q6_K als zweites installiertes 128k-Profil und A/B-Kandidat festgelegt | +| 2026-09-21 | 0.15 | spektr / Codex | je Modell ein festes, gegenseitig ausgeschlossenes Quadlet vorgesehen und dynamischen llama.cpp-Router zurückgestellt | --- @@ -417,24 +420,38 @@ Abweichungen als rootless Quadlet erzeugt: SELinux-Konfiguration dies erfordert. Für den dokumentierten Debian-Host wird es nicht vorsorglich gesetzt. -Der erste Testing-Modellstand verwendet vorbehaltlich Artefakt- und -Checksum-Prüfung: +Die ausführliche Kandidatenbewertung steht in +[GNAI-008-PLAN](GNAI-008-PLAN_Llama-Model-Evaluation.md), die technische +Sollkonfiguration einschließlich revisionsgebundener Downloadquelle und +SHA-256 in [GNAI-009-TECH](GNAI-009-TECH_Llama-Serving.md). Der bevorzugte +erste Testing-Kandidat ist: | Parameter | Ausgangswert | | --- | --- | -| Modelldatei | `Qwen3.8-27B-UD-IQ4_XS.gguf` | -| API-Alias | `qwen3.8-27b` | -| Kontext | 51.200 Token | +| Modelldatei | `Swift-Qwen3.8-27B-Q6_K.gguf` | +| API-Alias | `swift-qwen3.8-27b-q6k` | +| Kontext | 131.072 Token; Inbetriebnahme zunächst mit 65.536 | | parallele Slots | 1 | -| maximale Ausgabe | 4.096 Token | +| maximale Ausgabe | zunächst 32.768 Token; gesonderter 65.536-Token-Test | | GPU-Layer | alle | | Flash Attention | aktiviert | | K-/V-KV-Cache | jeweils `q8_0` | -| Reasoning Effort | `medium` | +| Reasoning Effort | `xhigh`, ohne kurzen Reasoning-Cap | +| MTP | zunächst deaktiviert; spätere A/B-Messung mit `n-max=2` | +| Vision-Projektor | zunächst nicht geladen | Samplingwerte und Chat-Template-Parameter gehören zum Modellprofil und nicht zu allgemeinen Rollendefaults. Dadurch können andere Modelle später eigene geprüfte Profile verwenden, ohne die gemeinsame `llama_cpp`-Rolle zu ändern. +Zusätzlich wird `Qwen3.8-27B-UD-Q6_K.gguf` aus dem revisionsgebundenen +Unsloth-Repository installiert. Es läuft ebenfalls mit 131.072 Token Kontext. +Ein kontrollierter A/B-Test verwendet für beide Kandidaten `xhigh`; ein +zweiter Vergleich stellt Swift `xhigh` dem vorgesehenen Unsloth-`medium`- +Praxisprofil gegenüber. Beide Dateien liegen auf dem Host, aber immer nur ein +Modell wird in den VRAM geladen. Jedes Modell erhält ein eigenes festes +Testing-Quadlet; der Wechsel stoppt die aktive Unit vollständig, bevor die +andere startet. Der im gepinnten Build vorhandene dynamische `llama.cpp`- +Router-Modus bleibt für die erste Ausbaustufe bewusst zurückgestellt. Für die beiden gleich großen GPUs wird zunächst `--split-mode layer --tensor-split 1,1` festgelegt. Der Layer-Split minimiert @@ -451,7 +468,7 @@ Der rootless Container-Probelauf hat inzwischen zwei `gfx1030`-Agents sowie Start muss ein begrenzter Modelltest noch die Device-Reihenfolge und die tatsächliche 1:1-Verteilung in den `llama.cpp`-Startlogs bestätigen. Zusätzlich werden Prompt-Verarbeitung, Token-Generierung, VRAM-Nutzung und Stabilität bei -51.200 Token Kontext gemessen. Der 1:1-Wert ist erst nach diesem Test ein +131.072 Token Kontext gemessen. Der 1:1-Wert ist erst nach diesem Test ein abgenommener Betriebsparameter. --- @@ -873,7 +890,7 @@ Store. Modellladen, Layer-Verteilung und Inferenz wurden noch nicht geprüft. ## 15. Abschlussgrenze dieses Entwurfs -Mit Version 0.12 sind der aktuelle Planungsstand, der ausdrücklich begrenzte +Mit Version 0.15 sind der aktuelle Planungsstand, der ausdrücklich begrenzte Ansible-Preflight, der verifizierte Deployment-Account und die angewendete minimale Host-Baseline, die Podman-/Quadlet-Grundruntime und die getrennten Workload-Konten, deren begrenzter AMD-Gerätezugriff sowie der rootless @@ -892,6 +909,8 @@ Insbesondere wurden noch nicht: - die geplante Deploymentkette technisch abgenommen. Der nächste technische Schritt ist die `llama.cpp`-Rolle für Testing. Sie soll -das gepinnte Image als rootless Quadlet betreiben, das initiale Modell mit -geprüfter Checksumme einbinden und den vorgesehenen Layer-Split sowie eine -reale Inferenz abnehmen. Darauf baut anschließend LiteLLM auf. +das gepinnte Image als rootless Quadlet betreiben, beide festgelegten Modelle +mit geprüften Checksummen bereitstellen und jeweils einzeln mit dem +vorgesehenen Layer-Split und realer Inferenz abnehmen. Dafür werden zwei feste, +gegenseitig ausgeschlossene Quadlets aus derselben Rollenlogik erzeugt. Danach +folgt die reproduzierbare A/B-Serie; darauf baut anschließend LiteLLM auf. diff --git a/docs/ai/GNAI-008-PLAN_Llama-Model-Evaluation.md b/docs/ai/GNAI-008-PLAN_Llama-Model-Evaluation.md new file mode 100644 index 0000000..b1c8afc --- /dev/null +++ b/docs/ai/GNAI-008-PLAN_Llama-Model-Evaluation.md @@ -0,0 +1,343 @@ +# Arbeitsentwurf + +**Dokumenten-ID:** GNAI-008-PLAN +**Titel:** Modell- und Laufzeitbewertung für `llama.cpp` +**Stand:** 2026-09-21 +**Status:** Nicht finaler Evaluierungsplan; keine Production-Freigabe +**Referenzen:** [Ansible-Deployment](GNAI-007-PLAN_Ansible-Deployment.md), [technische Sollkonfiguration](GNAI-009-TECH_Llama-Serving.md), [Zielarchitektur](GNAI-003-INFRA_Architecture.md) + +> Dieses Dokument persistiert die Modellrecherche und legt fest, wie daraus +> eine belastbare Entscheidung für den KI-Server entsteht. Veröffentlichte +> Benchmarks, Community-Berichte und lokale Abnahmetests werden getrennt +> bewertet. Ein hier genannter Kandidat ist erst nach erfolgreicher lokaler +> Abnahme für Production freigegeben. + +--- + +## Änderungsverlauf + +| Datum | Version | Autor | Beschreibung | +| --- | --- | --- | --- | +| 2026-09-21 | 0.3 | spektr / Codex | feste, gegenseitig ausgeschlossene Quadlet-Unit je Modell als Umschaltverfahren festgelegt | +| 2026-09-21 | 0.2 | spektr / Codex | Unsloth Qwen3.8 UD-Q6_K als verbindlichen zweiten 128k-Kandidaten festgelegt und kontrollierten A/B-Test ergänzt | +| 2026-09-21 | 0.1 | spektr / Codex | Swift-Qwen3.8, Q6_K, Reasoning, Kontext, MTP und lokales Vergleichsverfahren als ersten Evaluierungsstand aufgenommen | + +--- + +## 1. Ziel + +Gesucht wird ein Modellprofil für Coding-, Tool- und längere agentische +Aufgaben auf zwei Radeon RX 6900 XT mit zusammen 32 GiB VRAM. Das Profil soll: + +- komplexe Aufgaben mit ausreichend langem Reasoning bearbeiten können; +- unnötige Reasoning-Schleifen reduzieren; +- Tool Calls und strukturierte Ausgaben zuverlässig erzeugen; +- mindestens 128 KiToken Kontext als Zielkonfiguration unterstützen; +- vollständig über beide GPUs mit ROCm betrieben werden; +- durch unveränderliche Modell- und Containerartefakte reproduzierbar sein; +- Swift und Unsloth gleichzeitig im Modellbestand vorhalten, aber für Tests + jeweils nur ein Modell in den VRAM laden; +- ohne Modellwechsel zwischen Testing und Production übertragbar sein; und +- später gegen neue Swift-, Base- oder Dynamic-Quantisierungen austauschbar + bleiben. + +Die Entscheidung wird anhand realer Aufgaben getroffen. Herstellerbenchmarks +dienen als Kandidatenauswahl, ersetzen aber nicht die lokale Abnahme. + +## 2. Bewerteter Quellenstand + +### 2.1 Qwen3.8-27B + +Die [Qwen-Modellkarte](https://huggingface.co/Qwen/Qwen3.8-27B) nennt für das +27B-Modell 64 Blöcke, davon 16 Full-Attention-Blöcke, einen nativen Kontext von +262.144 Token und ein trainiertes Multi-Token-Prediction-Head. Thinking ist +standardmäßig aktiv. Qwen empfiehlt für Thinking `temperature=1.0`, +`top_p=0.95`, `top_k=20`, `min_p=0.0`, `presence_penalty=0.0` und +`repetition_penalty=1.0`. + +Qwen unterstützt `xhigh`, `medium` und `low` als Reasoning-Stufen. Die +Modellkarte warnt ausdrücklich, dass ein niedrigeres Reasoning-Niveau bei +mehrstufigen Agentenaufgaben zu Fehlversuchen und Wiederholungen führen kann +und dadurch nicht zwingend Zeit oder Token spart. Ein harter kurzer +Reasoning-Cap wird deshalb nicht als Standardprofil vorgesehen. + +### 2.2 Swift-Qwen3.8-27B + +Der Herausgeber beschreibt +[Swift-Qwen3.8-27B](https://huggingface.co/ukisai/Swift-Qwen3.8-27b) als +reasoning-effiziente Ableitung des Qwen-Basismodells. Nach eigener Darstellung +wurden mit Overthinking korrelierte Reasoning-Muster im Fine-Tuning +benachteiligt und verlorene Qualität durch weitere Trainingsschritte +kompensiert. + +Die veröffentlichten BF16-Vergleiche wurden mit vLLM, 262.144 Token Kontext, +`xhigh`, fünf Seeds und den Qwen-Samplingwerten durchgeführt. Für GPQA Diamond +werden nahezu gleiche Scores bei 41 Prozent weniger mittleren beziehungsweise +58,3 Prozent weniger medianen Thinking-Tokens ausgewiesen. Terminal-Bench 2.1 +liegt geringfügig unter Base, LiveCodeBench v6 darüber. AIME 2026 und HMMT +zeigen hingegen einen messbaren Rückgang. + +Diese Ergebnisse stammen vom Modellhersteller. Sie sind ein guter Grund für +einen lokalen Test, aber noch kein unabhängiger Nachweis für unsere Aufgaben, +für GGUF Q6_K, für llama.cpp oder für ROCm auf Navi 21. Insbesondere darf der +LiveCodeBench-Unterschied nicht ohne Kontrolle der Output-Limits als allgemeine +Qualitätssteigerung interpretiert werden. + +### 2.3 GGUF-Quantisierungen + +Das vom Swift-Herausgeber gepflegte +[GGUF-Repository](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF) +enthält unter anderem folgende Ein-Datei-Quantisierungen: + +| Quantisierung | veröffentlichte Größe | vorgesehene Rolle | +| --- | ---: | --- | +| Q4_K_M | 18,0 GB | speichersparender Vergleich und möglicher Fast-/Vision-Fallback | +| Q5_K_M | 20,2 GB | möglicher Kompromiss für MTP oder zusätzliche Reserve | +| Q6_K | 22,9 GB | bevorzugter Qualitätskandidat für Text-, Coding- und Tool-Aufgaben | +| Q6_K_L | 25,2 GB | derzeit zu geringe Reserve für 128k und Laufzeitpuffer | +| Q8_0 | 29,1 GB | für 32 GiB Gesamt-VRAM mit langem Kontext ungeeignet | + +Der Herausgeber empfiehlt Q6_K oder höher für lange Agentenläufe und strikte +Tool-Formate. Die GGUFs wurden laut Modellkarte mit llama.cpp `b10896` erzeugt; +recurrent-sensitive Tensoren und das MTP-Head verwenden gezielt höhere +Präzision. Die veröffentlichten quantisierten Aufgabenbenchmarks betreffen +jedoch W4A16/AWQ und nicht direkt die Q6_K-GGUF. Die Eignung von Q6_K bleibt +daher Gegenstand unseres Tests. + +### 2.4 Unsloth Qwen3.8-27B UD-Q6_K + +Das zweite Testartefakt stammt aus dem +[Unsloth-GGUF-Repository](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF). +Ausgewählt ist `Qwen3.8-27B-UD-Q6_K.gguf` mit einer veröffentlichten Größe von +22 GB. Das Repository steht laut Modellkarte unter Apache-2.0. Unsloth hat die +Quantisierung mit einer Importance Matrix und dem eigenen Dynamic-Verfahren +erzeugt. + +Damit unterscheidet sich der Kandidat nicht nur durch das ursprüngliche +Qwen-Verhalten von Swift, sondern auch durch das Quantisierungslayout. Der +Vergleich misst deshalb die beiden konkret einsetzbaren Artefakte als Ganzes; +er isoliert nicht wissenschaftlich den Effekt des Swift-Fine-Tunings. Diese +Einschränkung wird bei der Auswertung ausdrücklich festgehalten. + +### 2.5 MTP + +Das MTP-Head ist in den Swift-GGUFs enthalten. Die Modellkarte nennt für +llama.cpp `--spec-type draft-mtp --spec-draft-n-max 3`. Die aktuelle +llama.cpp-Dokumentation führt MTP als unterstützte spekulative Methode. + +MTP wird trotzdem nicht in das erste Abnahmeprofil aufgenommen. Es benötigt +zusätzlichen Draft-Kontext und zusätzliche Laufzeitpuffer. Der verlinkte +Community-Nachweis zeigt, dass MTP deutliche Gewinne liefern kann, verwendet +aber Strix Halo, Vulkan, ein anderes Qwen-Modell und zeitweise einen Fork. Er +ist kein Nachweis für zwei RX 6900 XT mit dem gepinnten ROCm-Image. + +MTP wird erst nach einer stabilen Baseline in derselben Sitzung und mit +identischen Aufgaben ein- und ausgeschaltet. Begonnen wird mit +`--spec-draft-n-max 2`; `3` bleibt eine zweite Messstufe. + +### 2.6 Swift 1.5 und Community-Quants + +Ankündigungen zu Swift 1.5 und kombinierten Swift-/Unsloth-Dynamic-Quants +werden als Beobachtung festgehalten. Am 21.09.2026 wurde daraus noch kein +freigegebenes Projektartefakt ausgewählt. Weder die Ansible-Rolle noch das +Production-Profil warten auf eine angekündigte Version. + +Ein neuer Checkpoint wird als neues, unveränderliches Profil mit eigener +Quelle, Revision, Prüfsumme und Lizenzprüfung aufgenommen. Er ersetzt niemals +automatisch ein bereits abgenommenes Profil. + +## 3. Vorläufige Kandidaten + +### 3.1 Bevorzugter Kandidat + +**Swift-Qwen3.8-27B Q6_K mit `xhigh`** wird als erster Testing-Kandidat +festgelegt. Gründe sind die höhere Gewichtsqualität gegenüber Q4/Q5, die +Herstellerempfehlung für lange Agentenläufe und die Zielsetzung von Swift, +Reasoning-Effizienz ohne kurzen harten Token-Cap zu erreichen. + +Das erste Profil verwendet: + +| Parameter | Wert | +| --- | --- | +| Quantisierung | Q6_K | +| Kontextziel | 131.072 Token | +| K-/V-KV-Cache | `q8_0` / `q8_0` | +| Reasoning | aktiv, `xhigh`, ohne Reasoning-Budget | +| parallele Slots | 1 | +| Flash Attention | aktiv | +| GPU-Offload | alle Layer | +| GPU-Aufteilung | `layer`, `1,1` | +| MTP | zunächst deaktiviert | +| Vision-Projektor | zunächst nicht geladen | +| maximale Ausgabe | zunächst 32.768 Token; gesonderter 65.536-Token-Test | + +128 KiToken sind das Zielprofil. Die Inbetriebnahme prüft dasselbe Modell +zuerst mit 64 KiToken, damit Fehler bei Modellformat, ROCm oder GPU-Aufteilung +von reinem Speicherdruck unterschieden werden können. + +### 3.2 Verbindlicher Vergleichskandidat + +**Unsloth Qwen3.8-27B UD-Q6_K** wird ebenfalls installiert und mit 131.072 +Token Kontext abgenommen. Das Artefakt ist verbindlich festgelegt: + +| Feld | Wert | +| --- | --- | +| Repository | `unsloth/Qwen3.8-27B-GGUF` | +| Revision | `4ca720788d1e01f1bff70c033e0d0028fd02e502` | +| Datei | `Qwen3.8-27B-UD-Q6_K.gguf` | +| veröffentlichte Größe | 22 GB | +| SHA-256 | `c9c206812fbe4ac7b76a729e25928b63f2ae89d37f69da7a71c20aec763cd436` | +| API-Alias | `unsloth-qwen3.8-27b-ud-q6k` | + +Beide Dateien liegen gleichzeitig auf dem Host und jedes Modell besitzt eine +eigene feste Quadlet-Unit. Für einen Messlauf ist immer nur die ausgewählte +Unit aktiv; die Modelle werden nicht gleichzeitig in den VRAM geladen. Der +Wechsel stoppt den bisherigen Container vollständig, bevor der andere startet. + +Der erste kontrollierte A/B-Vergleich verwendet bei beiden Modellen `xhigh`, +131.072 Token Kontext, Q8-K/V, einen Slot, dieselben Samplingwerte und kein +MTP. Danach folgt der praxisnahe Profilvergleich Swift `xhigh` gegen Unsloth +`medium`. Dadurch bleiben die Effekte der Reasoning-Einstellung sichtbar, +statt sie unbemerkt dem Modellartefakt zuzurechnen. + +### 3.3 Ressourcen-Fallback + +**Swift Q5_K_M mit `xhigh`** ist der erste Fallback, falls Q6_K mit 128k, +Laufzeitpuffern oder später MTP nicht stabil in 32 GiB VRAM betrieben werden +kann. Ein Wechsel auf Q5 erfolgt nur aufgrund gemessener Speicher- oder +Leistungsergebnisse, nicht vorsorglich. + +## 4. Testmatrix + +| Stufe | Modellprofil | Kontext | MTP | Zweck | +| --- | --- | ---: | --- | --- | +| A0 | Swift Q6_K, text-only | 65.536 | aus | Modellformat, vollständiger GPU-Offload, API und Grundstabilität | +| A1 | Swift Q6_K, text-only | 131.072 | aus | bevorzugtes Zielprofil und VRAM-Abnahme | +| A2 | Swift Q6_K mit mmproj | 131.072 | aus | optionaler Vision-Betrieb und zusätzlicher VRAM-Bedarf | +| A3 | Swift Q6_K, text-only | 131.072 | `n-max=2` | MTP-A/B gegen A1 | +| A4 | Swift Q6_K, text-only | 131.072 | `n-max=3` | nur nach erfolgreichem A3-Test | +| B1 | Swift Q5_K_M, text-only | 131.072 | aus / 2 | Ressourcen- und Geschwindigkeitsvergleich | +| U0 | Unsloth UD-Q6_K, text-only, `xhigh` | 65.536 | aus | Modellformat, GPU-Offload, API und Grundstabilität | +| U1 | Unsloth UD-Q6_K, text-only, `xhigh` | 131.072 | aus | kontrolliertes Gegenstück zu A1 | +| AB1 | Swift Q6_K gegen Unsloth UD-Q6_K, beide `xhigh` | 131.072 | aus | A/B mit identischen Laufzeitparametern | +| AB2 | Swift Q6_K `xhigh` gegen Unsloth UD-Q6_K `medium` | 131.072 | aus | Vergleich der vorgesehenen Praxisprofile | + +Vision und MTP sind eigenständige Varianten. Sie werden nicht gleichzeitig +erstmals aktiviert, weil andernfalls Speicher- und Performanceeffekte nicht +zugeordnet werden können. + +## 5. Aufgaben und Messwerte + +Die Vergleichssuite enthält 10 bis 20 repräsentative Aufgaben aus dem späteren +Betrieb: + +1. kleine, eindeutig prüfbare Codeänderung; +2. repositoryweite Änderung mit mehreren Dateien; +3. Fehlersuche anhand von Logs und Tests; +4. Tool-Aufruf mit korrektem JSON-Schema; +5. mehrstufige Aufgabe mit fehlgeschlagenem ersten Ansatz; +6. lange Kontextaufgabe mit relevanten Informationen an verschiedenen Stellen; +7. Refactoring mit unverändertem Verhalten; +8. Dokumentations- und Konfigurationsänderung; +9. mathematisch-logische Kontrollaufgabe; und +10. Negativtest, bei dem das Modell fehlende Informationen erkennen soll. + +Pro Aufgabe und Profil werden soweit praktikabel mindestens drei Läufe +durchgeführt. Für Leistungsmessungen werden zusätzlich deterministische +Prompts mit festem Seed verwendet. Reale Agentenaufgaben behalten die von Qwen +empfohlenen Samplingwerte. + +Für AB1 wird die Reihenfolge pro Aufgabe zwischen Swift und Unsloth gewechselt. +Nach jedem Profilwechsel wird ein definierter Warm-up-Request ausgeführt; seine +Zeit fließt nicht in den fachlichen Vergleich ein. Modellladezeit und Warm-up +werden separat erfasst. Container-Digest, Prompt, Systemnachricht, Toolschema, +Seed, Kontext, Sampling und Ausgabegrenze bleiben innerhalb einer Serie +identisch. Die automatischen Prüfungen und die manuelle Bewertungsrubrik werden +vor dem ersten gewerteten Lauf festgelegt. + +Jede abgeschlossene Serie erhält im Repository einen datierten +Evaluierungsdatensatz mit Profilmanifest, Aufgabenrevision, Rohmesswerten und +Kurzbericht. Modell- oder Containerdateien sowie sensible Promptinhalte werden +nicht in Git gespeichert. Der Bericht muss erkennen lassen, welche Läufe wegen +technischer Fehler wiederholt oder aus der Wertung genommen wurden. + +Zu erfassen sind: + +- fachlicher Erfolg und bestandene Tests; +- Zahl der Tool-Aufrufe, Fehlversuche und Wiederholungen; +- Prompt-, Reasoning-, Antwort- und Gesamttoken; +- Time to First Token, Prompt Processing und Generationstempo; +- gesamte Laufzeit bis zum erfolgreichen Abschluss; +- Spitzen- und stabiler VRAM-Verbrauch je GPU; +- CPU- und RAM-Verbrauch; +- GPU-Aufteilung und etwaige CPU-Offloads; +- Abbruch, OOM, fehlerhafte Tool-Formate oder unvollständige Antworten. + +## 6. Vorläufige Abnahmekriterien + +Ein Kandidat wird zum bevorzugten Production-Profil, wenn: + +- das 128k-Profil ohne OOM startet und einen einstündigen gemischten Test + ohne Containerneustart oder GPU-Fehler besteht; +- alle vorgesehenen Modell-Layer auf die GPUs ausgelagert werden; +- beide GPUs genutzt werden und keine Karte ihren verfügbaren VRAM erschöpft; +- API-Antwort, Reasoning-Trennung und Tool Calls dem erwarteten Schema + entsprechen; +- die Aufgabensuite höchstens einen Erfolg weniger als der + jeweils andere Vergleichskandidat erzielt; +- keine systematische Regression bei Mathematik- oder Tool-Aufgaben sichtbar + wird; und +- der Median der generierten Gesamttoken oder der Zeit bis zum erfolgreichen + Abschluss gegenüber dem anderen Profil praktisch relevant sinkt. + +Als Zielwert für die Effizienz gilt zunächst eine Reduktion von mindestens +20 Prozent. Die vom Hersteller berichteten 30 bis 50 Prozent sind eine +Hypothese, kein lokales Abnahmekriterium. + +MTP wird nur übernommen, wenn es in einer A/B-Messung derselben Sitzung den +Median der Generationsleistung oder End-to-End-Laufzeit um mindestens +10 Prozent verbessert, ohne Stabilität, Tool-Format oder Abschlussquote zu +verschlechtern. Einzelne schnelle Läufe reichen nicht als Nachweis. + +## 7. Freigabe und Rollback + +Jedes Modellprofil erhält einen stabilen Namen, eine unveränderliche Quelle, +eine SHA-256-Prüfsumme und einen dokumentierten Lizenzstand. Testing und +Production referenzieren dieselbe geprüfte Datei aus dem gemeinsamen +schreibgeschützten Modellbestand. + +Ein Upgrade erzeugt ein neues Profil. Das bisherige Modell bleibt bis zum +abgeschlossenen Production-Test erhalten. Der Rollback ändert nur den +versionierten Profilverweis und startet die betroffene Umgebung neu. Ein +unbeaufsichtigter Download von `main`, `latest` oder einer beweglichen +Quantisierungsbezeichnung ist ausgeschlossen. + +Swift und Unsloth werden als getrennte Profile installiert. Das Ergebnis kann +ein bevorzugtes Standardprofil und ein weiterhin verfügbares alternatives +Profil sein; beide dürfen nur über den kontrollierten Umgebungswechsel +aktiviert werden. + +## 8. Offene Punkte + +- Lizenz und tatsächlichen Verwendungsrahmen des Swift-Modells vor Production + bestätigen; +- maximal zulässige Antwortdauer und Ausgabegröße für externe Nutzer + festlegen; +- entscheiden, ob Vision Bestandteil des ersten Production-Profils wird; +- Metrikerfassung für Reasoning- und Antworttoken mit LiteLLM abstimmen; +- Aufgabenbestand und erwartete Ergebnisse der lokalen Suite versionieren; und +- nach Veröffentlichung bewerten, ob Swift 1.5 oder eine geprüfte + Swift-Dynamic-Quantisierung als neuer Kandidat aufgenommen wird. + +## 9. Quellen + +- [Qwen3.8-27B – Modellkarte](https://huggingface.co/Qwen/Qwen3.8-27B) +- [Swift-Qwen3.8-27B – Modellkarte und Benchmarks](https://huggingface.co/ukisai/Swift-Qwen3.8-27b) +- [Swift-Qwen3.8-27B-GGUF – Quantisierungen, KV-Angaben und MTP](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF) +- [Unsloth Qwen3.8-27B-GGUF – Modellkarte und Dateien](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF) +- [Unsloth UD-Q6_K – Datei und Prüfsumme](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/blob/4ca720788d1e01f1bff70c033e0d0028fd02e502/Qwen3.8-27B-UD-Q6_K.gguf) +- [llama.cpp Server – aktuelle Parameterreferenz](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md) +- [llama.cpp MTP-/ROCm-Communitybericht](https://github.com/ggml-org/llama.cpp/discussions/27950) + +Community-Berichte dienen ausschließlich der Hypothesenbildung. Freigaben +beruhen auf reproduzierbaren Messungen des gepinnten eigenen Stacks. diff --git a/docs/ai/GNAI-009-TECH_Llama-Serving.md b/docs/ai/GNAI-009-TECH_Llama-Serving.md new file mode 100644 index 0000000..4fe60d1 --- /dev/null +++ b/docs/ai/GNAI-009-TECH_Llama-Serving.md @@ -0,0 +1,558 @@ +# Technischer Arbeitsentwurf + +**Dokumenten-ID:** GNAI-009-TECH +**Titel:** `llama.cpp`-Serving mit ROCm und rootless Podman +**Stand:** 2026-09-21 +**Status:** Technische Sollkonfiguration für Testing; noch nicht implementiert +**Referenzen:** [Modellbewertung](GNAI-008-PLAN_Llama-Model-Evaluation.md), [Ansible-Deployment](GNAI-007-PLAN_Ansible-Deployment.md), [Zielarchitektur](GNAI-003-INFRA_Architecture.md), [Ansible-Anleitung](../../ansible/README.md) + +> Dieses Dokument beschreibt den vorgesehenen technischen Sollzustand. Es ist +> keine Betriebs-SOP und kein Nachweis, dass Modell, Quadlet oder Inferenz +> bereits eingerichtet wurden. Abweichungen aus der Implementierung werden vor +> einer Production-Freigabe hier nachgeführt. + +--- + +## Änderungsverlauf + +| Datum | Version | Autor | Beschreibung | +| --- | --- | --- | --- | +| 2026-09-21 | 0.3 | spektr / Codex | je Modell ein festes Quadlet vorgesehen; Swift und Unsloth durch kontrollierten Stop/Start gegenseitig ausgeschlossen; Router-Modus zurückgestellt | +| 2026-09-21 | 0.2 | spektr / Codex | Unsloth UD-Q6_K als zweites installiertes Modellprofil und kontrollierten Profilwechsel für A/B-Tests ergänzt | +| 2026-09-21 | 0.1 | spektr / Codex | erstes technisches Profil für Swift Q6_K, 128k, Q8-KV, rootless Quadlet und gestufte MTP-Abnahme erstellt | + +--- + +## 1. Geltungsbereich + +Die Spezifikation umfasst zwei feste `llama-server`-Quadlets in der +Testing-Umgebung, von denen immer genau eines aktiv ist. Sie legt fest: + +- welche Container- und Modellartefakte verwendet werden; +- wie Modell und Konfiguration gespeichert werden; +- welche Geräte und Netzwerke der Container erhält; +- welche `llama-server`-Parameter als Ausgangsprofil gelten; +- wie VRAM-Annahmen behandelt werden; und +- welche technischen Prüfungen vor der Übernahme nach Production bestehen + müssen. + +LiteLLM, öffentliche Veröffentlichung, Nutzer-SOPs und die zeitliche +Umschaltung zwischen Production und Testing erhalten eigene Rollen und +Dokumente. `llama-server` wird nicht direkt für LAN, VPN oder Internet +veröffentlicht. + +## 2. Bereits belegte Voraussetzungen + +Am 20.09.2026 wurden auf `ghostnet-ki` folgende Grundlagen nachgewiesen: + +- Debian 13.6 und Podman 5.4.2 mit cgroup v2 und `crun`; +- rootless Konto `gnai-testing` mit UID/GID 981 und persistentem + systemd-User-Manager; +- `/dev/kfd`, `/dev/dri/renderD128` und `/dev/dri/renderD129`; +- zwei RX 6900 XT als getrennte ROCm-Geräte und `gfx1030`-Agents; und +- erfolgreicher Start des offiziellen `llama.cpp`-ROCm-Images ohne + `HSA_OVERRIDE_GFX_VERSION`. + +Modellladen, 128k-Kontext, Multi-GPU-Layer-Verteilung und Inferenz wurden noch +nicht nachgewiesen. + +## 3. Unveränderliche Artefakte + +### 3.1 Container + +| Feld | Wert | +| --- | --- | +| Upstream | `ghcr.io/ggml-org/llama.cpp:server-rocm` | +| verwendete Referenz | `ghcr.io/ggml-org/llama.cpp@sha256:b429b8be10d3d0353a09c98f4ab840fe516f779858a28f9fd4e5bdf10bc63cb4` | +| Architektur | amd64 | +| Build | `b11058` | +| Revision | `f072b103714dfa1eee531f80b24512faf38e3dd2` | +| Status | ROCm-Geräteerkennung unter `gnai-testing` bestanden | + +Der Dienst verwendet ausschließlich den Digest. Ein bewegliches Tag darf in +Testing zur Recherche aufgelöst, aber nicht in ein freigegebenes Profil +übernommen werden. + +### 3.2 Swift-Modellprofil + +| Feld | Wert | +| --- | --- | +| Repository | `ukisai/Swift-Qwen3.8-27B-GGUF` | +| Repository-Revision | `eb0e3a7dc70643c2ab920f5133750d02c20849ff` | +| Datei | `Swift-Qwen3.8-27B-Q6_K.gguf` | +| veröffentlichte Größe | 22,9 GB | +| SHA-256 | `7f4de8abd5446c08b0f975a1b38e43d02639f4ae9ecdd2ddfd5c5b0f612bda59` | +| Download | `https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF/resolve/eb0e3a7dc70643c2ab920f5133750d02c20849ff/Swift-Qwen3.8-27B-Q6_K.gguf` | +| API-Alias | `swift-qwen3.8-27b-q6k` | + +Die Prüfsumme stammt aus der vom Herausgeber veröffentlichten +[`SHA256SUMS`](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF/blob/main/SHA256SUMS)-Datei. +Ansible muss die SHA-256-Prüfsumme während des Downloads prüfen. Eine bereits +vorhandene Datei mit abweichender Prüfsumme führt zum Abbruch; sie wird nicht +stillschweigend überschrieben oder verwendet. + +### 3.3 Unsloth-Vergleichsprofil + +| Feld | Wert | +| --- | --- | +| Repository | `unsloth/Qwen3.8-27B-GGUF` | +| Repository-Revision | `4ca720788d1e01f1bff70c033e0d0028fd02e502` | +| Datei | `Qwen3.8-27B-UD-Q6_K.gguf` | +| veröffentlichte Größe | 22 GB | +| SHA-256 | `c9c206812fbe4ac7b76a729e25928b63f2ae89d37f69da7a71c20aec763cd436` | +| Download | `https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/resolve/4ca720788d1e01f1bff70c033e0d0028fd02e502/Qwen3.8-27B-UD-Q6_K.gguf` | +| API-Alias | `unsloth-qwen3.8-27b-ud-q6k` | + +Die SHA-256-Prüfsumme ist in den Xet-Dateimetadaten des Herausgebers +veröffentlicht. Für Download, vorhandene Dateien und Fehlerfälle gelten +dieselben Regeln wie beim Swift-Artefakt. Beide Modelldateien werden +bereitgestellt, bevor die A/B-Serie beginnt. + +### 3.4 Optionaler Vision-Projektor + +Der erste Text-/Coding-Test lädt keinen Vision-Projektor. Dadurch bleibt rund +ein weiteres GiB für KV-Cache und Laufzeitpuffer verfügbar und die +Grundabnahme erhält weniger Variablen. + +Für eine spätere Vision-Stufe ist vorgesehen: + +| Feld | Wert | +| --- | --- | +| Datei | `mmproj-Swift-Qwen3.8-27B-F16.gguf` | +| veröffentlichte Größe | 928 MB | +| SHA-256 | `daa1116c9422fa390cc8688495da0e91781f92841dfc3b31a378ff252571745a` | +| Download | `https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF/resolve/eb0e3a7dc70643c2ab920f5133750d02c20849ff/mmproj-Swift-Qwen3.8-27B-F16.gguf` | + +Der Projektor wird erst heruntergeladen und mit `--mmproj` eingebunden, wenn +die Vision-Teststufe ausdrücklich aktiviert wird. + +### 3.5 Lizenz und Herkunft + +Das Qwen-Basismodell und das Unsloth-GGUF-Repository stehen laut ihren +Modellkarten unter Apache-2.0. Die Swift-Änderungen werden laut +Modellkarte unter der Swift Open License 1.0 angeboten. Die Modellkarte nennt +eine kostenlose persönliche, Forschungs-, Bildungs-, Evaluierungs- und +kommerzielle Nutzung bis zu einer dort definierten Umsatzgrenze; oberhalb +davon gelten zusätzliche Lizenzbedingungen. + +Vor einer externen oder kommerziellen Production-Nutzung wird der dann +aktuelle Lizenztext gegen den konkreten Betreiber- und Nutzungsrahmen geprüft. +Repository-Revision, Modellkarte, Prüfsummen und Lizenzhinweis werden als +Herkunftsnachweis zum Profil dokumentiert. + +## 4. Verzeichnis- und Rechtekonzept + +Modelle sind große, unveränderliche und nicht geheime Artefakte. Production +und Testing sollen dieselbe geprüfte Datei lesen, ohne sie in getrennten +Podman-Stores zu duplizieren. + +Vorgesehene Hostpfade: + +```text +/var/lib/ghostnet-ai/shared/models/ +├── swift-qwen3.8-27b/ +│ ├── Swift-Qwen3.8-27B-Q6_K.gguf +│ ├── mmproj-Swift-Qwen3.8-27B-F16.gguf # erst in der Vision-Stufe +│ └── SOURCE.yml +└── unsloth-qwen3.8-27b/ + ├── Qwen3.8-27B-UD-Q6_K.gguf + └── SOURCE.yml +``` + +| Objekt | Eigentümer | Modus | Begründung | +| --- | --- | --- | --- | +| Modellverzeichnisse | `root:root` | `0755` | durch Deployment verwaltet, für beide Umgebungen lesbar | +| GGUF-Dateien | `root:root` | `0644` | unveränderlicher, nicht geheimer gemeinsamer Bestand | +| `SOURCE.yml` | `root:root` | `0644` | nachvollziehbarer Herkunftsnachweis | + +Der Container bindet nur das zum aktiven Profil gehörende Modellverzeichnis +als `/models:ro` ein. Er erhält keinen Schreibzugriff auf den gemeinsamen Bestand. +Downloads erfolgen atomar in ein temporäres Ziel und werden erst nach +erfolgreicher Prüfsumme an den endgültigen Pfad verschoben. + +Vor dem Download beider Modelle prüft Ansible mindestens 70 GiB freien Platz +auf dem Zieldateisystem. Das deckt rund 45 GB endgültigen Modellbestand, die +größte temporäre Downloaddatei und einen kleinen Betriebspuffer ab. Bereits +korrekt vorhandene Artefakte werden bei der Berechnung nicht erneut als +Downloadbedarf angesetzt. + +## 5. Rootless Quadlet + +### 5.1 Ablage und Identität + +Ansible rendert für jedes Modell ein eigenes, rootverwaltetes Testing-Quadlet: + +```text +/etc/containers/systemd/users/981/ +├── gnai-testing-llama-swift.container +└── gnai-testing-llama-unsloth.container +``` + +Sie werden vom persistenten systemd-User-Manager von `gnai-testing` +ausgeführt. Die Units heißen `gnai-testing-llama-swift.service` und +`gnai-testing-llama-unsloth.service`. Beide verwenden dasselbe gepinnte Image, +aber einen eigenen Containernamen, Modellpfad, API-Alias und Parametersatz. +Podman speichert das identische Image nur einmal im rootless Image-Store. + +### 5.2 Containeroptionen + +Der `[Container]`-Abschnitt muss mindestens folgende Eigenschaften abbilden: + +| Quadlet-Eigenschaft | Sollwert | +| --- | --- | +| `Image=` | gepinnter Digest aus Abschnitt 3.1 | +| `Pull=` | `never`; Images werden vor dem Dienststart kontrolliert importiert | +| `AddDevice=` | `/dev/kfd`, `/dev/dri/renderD128`, `/dev/dri/renderD129` | +| `GroupAdd=` | `keep-groups` | +| `Volume=` | Modellverzeichnis nach `/models:ro` | +| `Network=` | dediziertes Testing-Netz für `llama.cpp` und LiteLLM | +| `NetworkAlias=` | `llama-cpp`; wegen gegenseitigem Ausschluss immer nur einmal aktiv | +| `PublishPort=` | nicht gesetzt | +| `ReadOnly=` | nach erfolgreichem Starttest `true` | +| `SecurityLabelDisable=` | nicht gesetzt, solange SELinux auf dem Host nicht aktiv ist | + +Podman dokumentiert `AddDevice=` und `GroupAdd=keep-groups` direkt als +Quadlet-Optionen. Freie `PodmanArgs=` werden für diese Funktionen nicht +benötigt. Die Render-Nodes werden weiterhin aus der Hardwareerkennung der +Ansible-Rolle übernommen und nicht unabhängig als zweite Hardwarequelle +konfiguriert. + +### 5.3 systemd-Verhalten und gegenseitiger Ausschluss + +Vorgesehene Servicewerte: + +```ini +[Service] +Restart=on-failure +RestartSec=10 +TimeoutStartSec=900 +``` + +Der erhöhte Start-Timeout berücksichtigt Modellinitialisierung und einen +möglichen kontrollierten Erstimport. Das Image und das Modell sollen dennoch +vor dem Dienststart bereitstehen. Ein normaler administrativer Stop darf nicht +durch eine `always`-Policy sofort rückgängig gemacht werden. + +Das Testing-Quadlet erhält keinen `[Install]`-Abschnitt und wird nicht für +`default.target` aktiviert. Es startet ausschließlich für ein bewusst +geöffnetes Testfenster. Die spätere Production-Unit kann nach bestandener +Umschalt- und Neustartabnahme gezielt aktiviert werden. + +Die Units erhalten folgende negative Abhängigkeit und eine eindeutige +Reihenfolge: + +```ini +# Swift-Unit +[Unit] +Conflicts=gnai-testing-llama-unsloth.service +Before=gnai-testing-llama-unsloth.service + +# Unsloth-Unit +[Unit] +Conflicts=gnai-testing-llama-swift.service +``` + +`Conflicts=` sorgt dafür, dass der Start einer Unit die andere stoppt. Das +einmalig gesetzte `Before=` ergänzt die nötige Ordnungsabhängigkeit; systemd +führt dadurch den Stop vollständig vor dem neuen Start aus. Der reguläre +Profilwechsel bleibt zusätzlich eine explizite, sequenzielle Operation: + +1. aktuell aktive Modell-Unit stoppen; +2. auf vollständiges Ende des Containers warten; +3. prüfen, dass kein `llama-server` mehr die GPUs belegt; +4. gewünschte Modell-Unit starten; und +5. Alias, Health, Modellrevision und GPU-Belegung prüfen. + +Das Ansible-Playbook und die spätere Betriebs-SOP kapseln diese Reihenfolge. +Ein direkter Start der jeweils anderen Unit löst denselben geordneten Wechsel +aus. Der gleichzeitige Start beider Units in einer Transaktion gilt als Fehler +und ist kein unterstützter Betriebsweg. + +### 5.4 Entscheidung gegen den Router-Modus in der ersten Ausbaustufe + +Der gepinnte `llama.cpp`-Build unterstützt bereits einen Router-Modus mit +`--models-preset`, `--models-max`, automatischem Laden sowie den Endpunkten +`/models/load` und `/models/unload`. Technisch wäre damit ein einzelner +Router-Container für beide Modelle möglich. + +Für die erste Abnahme bleibt jedes Quadlet im einfacheren Single-Model-Modus. +Damit sind Startparameter, Logs, Health, VRAM-Verbrauch und Fehlerzustand einem +Modell eindeutig zugeordnet. Außerdem benötigt der interne Dienst keine +dynamische Modellverwaltungs-API. Nach stabiler Abnahme kann der Router-Modus +separat bewertet werden; er ist keine Voraussetzung für LiteLLM. + +## 6. `llama-server`-Ausgangsprofil + +Das erste 128k-Zielprofil entspricht konzeptionell folgender Kommandozeile: + +```text +--host 0.0.0.0 +--port 8080 +--model /models/Swift-Qwen3.8-27B-Q6_K.gguf +--alias swift-qwen3.8-27b-q6k +--parallel 1 +--ctx-size 131072 +--n-predict 32768 +--n-gpu-layers all +--split-mode layer +--tensor-split 1,1 +--flash-attn on +--cache-type-k q8_0 +--cache-type-v q8_0 +--jinja +--reasoning on +--reasoning-effort xhigh +--reasoning-budget -1 +--temp 1.0 +--top-p 0.95 +--top-k 20 +--min-p 0.0 +--presence-penalty 0.0 +--repeat-penalty 1.0 +--no-webui +--metrics +``` + +Die Parameter werden als strukturierte Ansible-Variablen verwaltet und von +einem Template in `Exec=` gerendert. Eine freie, manuell gepflegte +Environment-Datei ist nicht die Konfigurationsquelle. + +Vor der Implementierung wird `llama-server --help` im exakt gepinnten Image +gegen alle Optionen geprüft. Falls dieser Build `all` für +`--n-gpu-layers` nicht akzeptiert, wird der bereits bewährte numerische Wert +`999` verwendet und die Abweichung dokumentiert. + +Das Unsloth-Profil verwendet dieselbe Kommandozeile mit diesen gezielten +Abweichungen: + +| Parameter | kontrollierter A/B-Test | Praxisvergleich | +| --- | --- | --- | +| `--model` | `/models/Qwen3.8-27B-UD-Q6_K.gguf` | identisch | +| `--alias` | `unsloth-qwen3.8-27b-ud-q6k` | identisch | +| `--reasoning-effort` | `xhigh` | `medium` | + +Alle nicht aufgeführten Parameter bleiben gegenüber dem Swift-Profil +unverändert. + +### 6.1 Reasoning + +`xhigh` ist der Ausgangswert für Swift. `--reasoning-budget -1` vermeidet einen +kurzen serverseitigen Thinking-Cap. `--n-predict 32768` begrenzt dagegen die +gesamte Ausgabe eines einzelnen Requests und schützt den zunächst manuell +betriebenen Testing-Dienst vor unbegrenzten Läufen. + +Die Evaluierung enthält zusätzlich einen kontrollierten 65.536-Token-Lauf. +Vor Production wird entschieden, ob die allgemeine Obergrenze erhöht oder +später durch LiteLLM pro Nutzer und Modell gesteuert wird. Ein Default von +2.048 oder 4.096 Token ist für dieses Profil nicht vorgesehen. + +Die API muss Reasoning getrennt von der finalen Antwort liefern. Zunächst wird +das automatische `--reasoning-format` des gepinnten Builds geprüft. Ein fester +Wert wird erst gesetzt, wenn die Kombination aus llama.cpp, LiteLLM und den +verwendeten Clients mit einem reproduzierbaren Request abgenommen wurde. + +Für Unsloth werden zwei Reasoning-Varianten gerendert. `xhigh` dient dem +kontrollierten A/B-Test mit Swift. `medium` bildet das geplante Praxisprofil +ab. Alle übrigen Laufzeitparameter bleiben innerhalb der jeweiligen +Vergleichsserie gleich. + +### 6.2 Sampling + +Die Werte entsprechen der Qwen-Modellkarte für Thinking. Sie bleiben Teil des +Modellprofils. Ein späteres Instruct-/Non-Thinking-Profil erhält eigene Werte +und überschreibt diese nicht stillschweigend. + +### 6.3 Multi-GPU + +`layer` und `1,1` sind die Ausgangskonfiguration für die zwei gleich großen +Karten. Der zweite PCIe-Slot ist langsamer angebunden, weshalb experimentelle +parallele Split-Verfahren nicht vor der stabilen Layer-Baseline aktiviert +werden. + +Die Startlogs müssen für beide Karten den belegten VRAM und die tatsächliche +Layer-Verteilung ausweisen. `1,1` ist eine Gewichtung, keine Garantie für exakt +gleiche Speicherbelegung. Ein abweichender Split wird ausschließlich anhand +gemessener freier Reserven und Durchsatzwerte eingeführt. + +### 6.4 Vision + +Der Haupttest verwendet `--model` mit einer lokalen Datei und übergibt kein +`--mmproj`. Erst die Vision-Stufe bindet den Projektor ein. Dadurch werden +Text-/Tool-Qualität, Modellformat und 128k-Speicherbedarf unabhängig vom +Vision-Encoder geprüft. + +### 6.5 MTP + +MTP bleibt im Ausgangsprofil aus. Die erste Variante ergänzt: + +```text +--spec-type draft-mtp +--spec-draft-n-max 2 +``` + +Erst nach bestandener A/B-Messung folgt optional `n-max=3`. Draft-Kontext, +VRAM-Zuwachs, Akzeptanzrate und tatsächlicher End-to-End-Gewinn werden +protokolliert. MTP wird nicht zusammen mit Vision erstmals aktiviert. + +## 7. VRAM-Modell + +Die Hardware stellt nominell 2 × 16 GiB VRAM bereit. Die Summe allein genügt +nicht als Fit-Nachweis, weil llama.cpp Gewichte, KV-Cache, Rechenpuffer und +gegebenenfalls den Vision- oder Draft-Kontext nicht zwingend gleichmäßig auf +beide Karten verteilt. + +### 7.1 Gewichte + +Die veröffentlichte Swift-Q6_K-Dateigröße von 22,9 GB entspricht grob +21,3 GiB. Unsloth UD-Q6_K ist mit veröffentlichten 22 GB ähnlich groß. Die +tatsächliche GPU-Belegung kann durch Metadaten, Ausrichtung, mmap und +Backend-Puffer abweichen und wird aus den Startlogs übernommen. + +### 7.2 KV-Cache + +Die Swift-Modellkarte berechnet für FP16-K/V 64 KiB pro Token, weil nur 16 der +64 Blöcke Full Attention verwenden: + +| Kontext | FP16-K/V laut Modellkarte | Q8-K/V, rechnerische Näherung | +| ---: | ---: | ---: | +| 65.536 | 4 GiB | etwa 2 GiB | +| 131.072 | 8 GiB | etwa 4 GiB | +| 196.608 | 12 GiB | etwa 6 GiB | +| 262.144 | 16 GiB | etwa 8 GiB | + +Die Q8-Spalte ist eine Halbierungsnäherung und kein gemessener Wert. Block- und +Backend-Overhead sowie die Verteilung je GPU werden beim Modellstart erfasst. + +### 7.3 Grobe Zielrechnung + +Für das textbasierte 128k-Profil ergibt sich vor der Messung: + +```text +Q6_K-Gewichte etwa 21,3 GiB +Q8-K/V bei 128 KiToken etwa 4,0 GiB +ROCm-/Graph-/Compute-Puffer noch zu messen +────────────────────────────────────────── +Zwischensumme vor Puffern etwa 25,3 GiB +``` + +Mit Vision kommen rund 0,9 GB Dateigröße sowie zusätzliche Laufzeitpuffer +hinzu. MTP kann ebenfalls weiteren Draft-Kontext und Puffer belegen. Deshalb +werden beide Funktionen getrennt zugeschaltet und Q5_K_M bleibt als +Ressourcen-Fallback vorgesehen. + +## 8. Netzwerk und API-Grenze + +`llama-server` hört im Container auf TCP 8080. Es gibt kein `PublishPort=` und +keine Host-Portweiterleitung. LiteLLM wird später in dasselbe +umgebungsspezifische rootless Podman-Netz aufgenommen und erreicht den Dienst +über `http://llama-cpp:8080`. + +Direkte Funktionsprüfungen erfolgen aus dem Container-Netz oder über einen +kurzlebigen administrativen Prüfcontainer. Externe Nutzer sprechen später nur +LiteLLM beziehungsweise die dafür vorgesehene veröffentlichte Anwendung an. + +Der integrierte Webserver wird mit `--no-webui` betrieben. Metriken und Health +bleiben ausschließlich im internen Container-Netz. Vor der Health-Quadlet- +Konfiguration wird geprüft, welches HTTP-Werkzeug das gepinnte Image enthält; +die Abnahme darf nicht von einem im Image fehlenden `curl` abhängen. + +## 9. Ansible-Rollenentwurf + +Die spätere Rolle `llama_cpp` soll folgende Zuständigkeiten besitzen: + +1. Variablen und Artefaktmanifest vollständig validieren. +2. Modellverzeichnisse mit festgelegten Rechten anlegen. +3. beide Modelle über revisionsgebundene URLs und SHA-256 atomar + bereitstellen. +4. Gepinntes Containerimage im rootless Store der Zielumgebung sicherstellen. +5. Render-Nodes aus `amd_gpu_access` übernehmen. +6. umgebungsspezifisches Podman-Netz als Quadlet rendern. +7. beide `llama.cpp`-Quadlets rendern und Generator-Dry-Run ausführen. +8. systemd-User-Manager neu laden und den ausgewählten Testing-Dienst bewusst + starten. +9. Startlog, API-Health, Modellalias und GPU-Verteilung prüfen. +10. einen kleinen Inferenztest ausführen und Ergebnis protokollieren. + +Die Rollenvariablen enthalten eine Map unveränderlicher Modellprofile und +einen einzelnen Selektor wie `llama_cpp_active_profile`. Die Rolle rendert +beide Quadlets, startet aber nur die ausgewählte Unit. Ein Profilwechsel +verändert keine Unit-Datei, sondern stoppt die aktive Unit vollständig und +startet anschließend die bereits gerenderte Ziel-Unit. + +Modell-Download und Dienstaktivierung erhalten getrennte Tags. Dadurch kann +ein großes Artefakt vorbereitet werden, ohne den aktiven Stack umzuschalten. +Check-Modus lädt kein Modell und startet keinen Dienst. + +Production und Testing verwenden dieselbe Rollenlogik, aber getrennte +Variablen, Quadlets, Netzwerke, Podman-Stores und systemd-User-Manager. Die +Rolle darf nie beide GPU-Umgebungen gleichzeitig starten. + +## 10. Technische Abnahme + +### 10.1 Vor dem Start + +- Modellrevision, Dateiname, Größe und SHA-256 stimmen mit dem Manifest + überein. +- Das Containerimage stimmt mit Digest und OCI-Labels überein. +- Beide Render-Nodes und `/dev/kfd` sind für `gnai-testing` zugreifbar. +- Production ist vollständig beendet und hält keine GPU-Prozesse. +- Der Quadlet-Generator meldet keine Fehler. +- Die generierte Unit enthält keinen veröffentlichten Host-Port. + +### 10.2 Start mit 64k + +- Container und Unit werden `active` und bleiben stabil. +- Modellformat und eingebettetes Chat-Template werden erkannt. +- alle Layer werden auf ROCm ausgelagert; +- beide GPUs erscheinen in den Startlogs; +- `/health` und `/v1/models` antworten erfolgreich; +- der API-Alias entspricht dem jeweils ausgewählten Profil; +- ein kurzer Thinking-Request liefert Reasoning und finale Antwort; und +- ein definierter Tool-Call ist syntaktisch gültig. + +### 10.3 Start mit 128k + +- kein OOM oder unbeabsichtigter CPU-Offload; +- freier VRAM je GPU und tatsächlicher KV-Verbrauch werden protokolliert; +- Prompt-Prefill wird stufenweise mit 8k, 32k, 64k und 128k geprüft; +- mindestens ein langer Request endet ohne Kontext- oder Parserfehler; und +- ein einstündiger gemischter Test erzeugt keinen Neustart, GPU-Reset oder + dauerhaft hängenden Slot. + +Diese Abnahme wird zuerst für Swift und danach für Unsloth wiederholt. Erst +wenn beide Profile die technische Abnahme bestanden haben, beginnt die +fachliche A/B-Serie aus GNAI-008. + +### 10.4 Wiederanlauf und Idempotenz + +- ein zweiter identischer Ansible-Lauf meldet `changed=0`; +- ein kontrollierter Containerneustart erhält Profil und Modell; +- ein geplanter Hostneustart startet nur die freigegebene Umgebung; +- ein absichtlich gestoppter Testing-Dienst wird nicht ungewollt neu gestartet; +- ein Startversuch der zweiten Modell-Unit kann niemals beide Modelle + gleichzeitig im VRAM belassen; +- `podman ps`, systemd-Status und Health stimmen überein; und +- der Rückwechsel auf das vorherige Profil funktioniert ohne erneuten + Modelldownload. + +## 11. Noch nicht festgelegte Werte + +- genaue Health-Implementierung im gepinnten Containerimage; +- CPU-Thread-, Batch- und UBatch-Tuning für den i5-13600K; +- verbindliche maximale Ausgabe für Production und einzelne Nutzer; +- Aufnahme des Vision-Projektors in das erste Production-Profil; +- gemessener optimaler Split bei unterschiedlicher PCIe-Anbindung; +- Aktivierung und Parameter von MTP; +- Aufbewahrungs- und Löschregeln für abgelöste Modellartefakte. + +## 12. Quellen + +- [Qwen3.8-27B – Architektur, Reasoning und Sampling](https://huggingface.co/Qwen/Qwen3.8-27B) +- [Swift-Qwen3.8-27B – Training und Benchmarks](https://huggingface.co/ukisai/Swift-Qwen3.8-27b) +- [Swift-Qwen3.8-27B-GGUF – Dateien, Quantisierung und MTP](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF) +- [Swift-GGUF-Prüfsummen](https://huggingface.co/ukisai/Swift-Qwen3.8-27B-GGUF/blob/main/SHA256SUMS) +- [Unsloth Qwen3.8-27B-GGUF – Modellkarte](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF) +- [Unsloth UD-Q6_K – Datei und SHA-256](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/blob/4ca720788d1e01f1bff70c033e0d0028fd02e502/Qwen3.8-27B-UD-Q6_K.gguf) +- [llama.cpp Server-Parameter und Router-Modus des gepinnten Builds](https://github.com/ggml-org/llama.cpp/blob/f072b103714dfa1eee531f80b24512faf38e3dd2/tools/server/README.md) +- [Podman Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html) +- [systemd-Unit-Abhängigkeiten](https://manpages.debian.org/trixie/systemd/systemd.unit.5.en.html) diff --git a/docs/ai/README.md b/docs/ai/README.md index aa6f1b3..24a98d4 100644 --- a/docs/ai/README.md +++ b/docs/ai/README.md @@ -10,6 +10,8 @@ Video-Encoding-Workload besitzt einen | --- | --- | | [GNAI-003](GNAI-003-INFRA_Architecture.md) | Zielarchitektur, Komponenten und Datenwege des KI-Stacks | | [GNAI-007](GNAI-007-PLAN_Ansible-Deployment.md) | nicht finaler Arbeitsentwurf für Ansible- und Gitea-Deployment | +| [GNAI-008](GNAI-008-PLAN_Llama-Model-Evaluation.md) | nicht finaler A/B-Plan für Swift, Unsloth, Reasoning-, Kontext- und MTP-Abnahme | +| [GNAI-009](GNAI-009-TECH_Llama-Serving.md) | technische Sollkonfiguration für zwei geprüfte Modellprofile auf rootless `llama.cpp` mit ROCm | Der übergreifende Projektstand steht im [zentralen Dokumentationsindex](../GNAI-000-DOC_Index.md).