plan: Task 3.1 erledigt, 3.2 vorbereitet; Sprach-Auftrag NEX als 6.2, HDD-Befund in 5.1
ci / test (push) Successful in 28s

ops-LXC, vfio auf allen GPU-Knoten (Bildschirm danach schwarz), Templates
9044/9046/9048/9052, API-Benutzer tofu@pve. Auftrag Sprachmodelle mit
Abgleich gegen den echten Cluster unter docs/auftraege/. Die 4-TB-HDD ist
die Toshiba BACKUP und scheidet für den Cluster aus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Rapp
2026-10-12 19:10:36 +02:00
co-authored by Claude Opus 5.5
parent e5d1fa45b1
commit 47816bda68
2 changed files with 90 additions and 9 deletions
@@ -0,0 +1,77 @@
# Auftrag: Sprachmodelle (Spracherkennung + Sprachausgabe) für NEX auf dem knaster-Cluster
Eingang 12.10.2026 (vom NEX-Team, ohne Schlüssel). Ausführung **nach** Task 3.2 (GPU-VMs mit Treiber), gern durch einen Subagenten. Im Plan als Task 6.5 geführt.
## Abgleich mit dem echten Cluster (Stand 12.10.2026, vor Ausführung lesen)
Der Auftrag geht von einem älteren Bild aus. Es gilt:
| Auftrag sagt | Wirklichkeit |
|---|---|
| 2 Knoten + QDevice | 5 Knoten (pve44, pve46, pve48, pve52, pve61), 5 Stimmen, kein QDevice |
| Knoten A 2× P2000, Knoten B 2× M4000, GPU per LXC oder VM | Eine Ubuntu-24.04-VM pro GPU-Knoten mit allen Karten (D2): `gpu-pve44`, `gpu-pve46`, `gpu-pve48` je M4000 + P2000, `gpu-pve52` nur M4000. Treiber R580 in der VM |
| Feste IP per DHCP-Reservierung | D5 ist zurückgestellt. Gäste laufen per DHCP, der Nutzer hat keinen Zugriff auf den DHCP-Server. **Vor der Abnahme klären:** Reservierung durch die DHCP-Verwaltung oder statische IP außerhalb des DHCP-Bereichs |
Daraus folgt für die Umsetzung:
- **Erkennung:** whisper.cpp (CUDA 12, `CMAKE_CUDA_ARCHITECTURES="52;61"`) auf einer **M4000**. Naheliegend ist `gpu-pve52`: Dort steckt nur die M4000, und Placement verteilt keine andere Rolle dorthin. Die P2000 bleiben für embed/rerank (pve44, pve46) und `gpu-small` (pve48). Die M4000 sind im Inventar `disabled` bis zum Benchmark-Tag (Task 3.3); die Messwerte aus 4. Abnahme liefern genau diesen Benchmark für whisper und gehören in Task 3.3.
- **Ausgabe Stufe 1 (Piper, CPU):** laut Registry `tts-de` im Infra-LXC (`infra`, Task 5.2). Solange `infra` nicht steht, als Container in derselben GPU-VM wie die Erkennung (CPU reicht).
- **Firewall „nur von 192.168.100.29“:** Proxmox-Firewall an der Netzkarte der VM (`firewall=1`, Regeln für 8178/8179) plus `ufw` in der VM.
- Globale Vorgabe aus dem Plan passt: whisper-server auf Port 8178, nur von `.29`, `stt.openai.base_url` nur als IP-Literal.
## Auftrag (Originaltext)
NEX sind Hermes-Agenten auf appsrv08 (192.168.100.29). Sprache läuft dort: Hermes → nex-router → LiteLLM → Sprach-Backend. Das Backend ist austauschbar (Eintrag in LiteLLM). Der Dienst auf dem Cluster ist das **Rückfall-Modell**. Solange kein Cloud-Anbieter Guthaben hat, ist er der einzige aktive Weg. Die NEX-Seite trägt danach nur IP:Port und Modellnamen in LiteLLM ein. Gebraucht werden zwei **OpenAI-kompatible** HTTP-Dienste im internen Netz. Ein Dienst für beides ist auch in Ordnung.
Formate laut Hermes-Code: mp3 als Standard, Opus für Sprachnachrichten, rohes PCM mit Sample-Rate-Header für das Streaming.
### Hardware und Grenzen
- Treiber **R580**, die letzte Serie für Maxwell/Pascal.
- **faster-whisper/CTranslate2 laufen nicht auf CC 5.2.** Auf der M4000 nur whisper.cpp, und zwar als **CUDA-12-Build**, nicht CUDA 13 (`-DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES="52;61"`).
- PyTorch-Unterstützung für sm_50/sm_61 in aktuellen Wheels **vorher prüfen** (`python -c "import torch; print(torch.cuda.get_arch_list())"`), nicht annehmen. Keine Tensor-Cores: bei faster-whisper `int8` oder `int8_float32`.
### 1. Spracherkennung (Pflicht)
- Modell **Whisper large-v3-turbo**, Sprache fest `de`.
- Empfehlung M4000: whisper.cpp `whisper-server` mit `-m ggml-large-v3-turbo.bin -l de -bs 5 --convert --inference-path /v1/audio/transcriptions`, **ohne** `--print-realtime`, `--tmp-dir` auf tmpfs (z. B. `/run/nex-whisper`). Nach dem Start einmal mit 1 s Stille aufwärmen.
- Alternative P2000: speaches (faster-whisper) mit `deepdml/faster-whisper-large-v3-turbo-ct2`, `int8_float32`.
- Optional mitmessen: `primeline/whisper-large-v3-turbo-german`.
- Schnittstelle: `POST /v1/audio/transcriptions`, multipart
- Felder: `file`, `model`, `language=de`, `response_format`.
- `file` muss wav, mp3, ogg/opus, webm und m4a annehmen (Umwandlung mit ffmpeg).
- `response_format`: `json` → `{"text": "…"}`, `text`, `verbose_json` (wenn möglich mit Wort-Wahrscheinlichkeiten).
- Unbekannte Felder ignorieren, nicht mit Fehler antworten.
### 2. Sprachausgabe (Pflicht Stufe 1, optional Stufe 2)
- **Stufe 1:** Piper Deutsch, CPU reicht. Stimmen: `de_DE-thorsten-high` und eine weibliche deutsche Stimme. Genauen Namen nennen. Server OpenAI-kompatibel, z. B. speaches (kann Piper) oder ein schlanker Wrapper.
- Schnittstelle: `POST /v1/audio/speech`
- JSON-Body `{model, voice, input, response_format, speed}`.
- `response_format` muss können: `mp3` (Standard), `opus` (im Ogg-Container), `wav`, `flac`, `pcm` (rohes int16, little-endian, mono; Sample-Rate im Header `X-Audio-Sample-Rate` oder in `Content-Type: audio/pcm; rate=<Hz>`).
- Antwort möglichst gestreamt (chunked), damit die Wiedergabe früh beginnt.
- **Stufe 2 (optional, GPU, nach PyTorch-Prüfung):** eine natürlichere deutsche Stimme.
- **Lizenz für kommerzielle Nutzung prüfen.** Nach Stand des NEX-Teams sind XTTS-v2 (Coqui Public Model License) und die Gewichte von Fish Speech/OpenAudio **nicht kommerziell**.
- Chatterbox Multilingual (MIT) ist ein Kandidat.
- Kokoro hat nach diesem Stand keine deutsche Stimme. Prüfen, nicht voraussetzen.
### 3. Für beide Dienste
- `GET /health` → 200.
- Feste IP in `192.168.100.0/24` (siehe Abgleich oben). Ports zum Beispiel 8178 (Erkennung) und 8179 (Ausgabe).
- Firewall: Zugriff **nur von 192.168.100.29**. LiteLLM läuft dort in einem Bridge-Netz und erscheint mit der Host-IP.
- Kein API-Key nötig. Erzwingt der Server einen, einen festen Dummy-Wert nennen (kein Geheimnis).
- Autostart nach Neustart (systemd oder Docker `restart: unless-stopped`), Energiesparen aus, Sicherheitsupdates.
- **Datenschutz:** keine Ablage von Audio oder Text; Temp-Dateien nur im RAM, alte regelmäßig löschen; **keine Transkripte oder vorgelesenen Texte in Logs.**
### 4. Abnahme
Von appsrv08 aus prüfen, Testaudio ohne Kundendaten, danach löschen.
- **Erkennung:**
- `curl -s -w ' %{time_total}s\n' -F file=@t10.wav -F model=whisper -F language=de -F response_format=json http://IP:8178/v1/audio/transcriptions`
- Text muss deutsch und plausibel sein.
- Median aus 3 Läufen: 10 s Audio **≤ 4 s** (Ziel ≤ 3 s), 60 s Audio **≤ 12 s**. Den ersten Aufruf getrennt notieren.
- **Ausgabe:** `curl -s -o t.mp3 -w ' TTFB %{time_starttransfer}s gesamt %{time_total}s\n' -H 'Content-Type: application/json' -d '{"model":"piper","voice":"de_DE-thorsten-high","input":"Guten Tag, hier ist Ihr NEX-Assistent. Die Baubesprechung in Haslach ist am vierten November.","response_format":"mp3"}' http://IP:8179/v1/audio/speech`
- Ergebnis abspielbar, Zeit bis zum ersten Byte möglichst ≤ 1 s.
- Dasselbe mit `pcm` (Sample-Rate-Header prüfen) und `opus`.
- **Last:** 2 Erkennungen gleichzeitig. Mit `nvidia-smi` prüfen, dass GPU-Last und VRAM steigen.
### 5. Rückmeldung (genau so)
Je Dienst: Basis-URL (IP:Port) · Knoten und GPU · Software mit Version oder Image-Digest · Modellname(n), wie im Feld `model` erwartet · Stimmen-IDs · unterstützte `response_format` · PCM-Sample-Rate · Messwerte (Median, erster Aufruf, VRAM, RAM) · Autostart nach Neustart getestet (ja/nein) · Auffälligkeiten.
### Gegenseite (NEX, zur Information)
Die Agenten fragen nur noch neutrale Namen an: `nex-stt` für die Erkennung, `nex-tts` für die Ausgabe. Welches Backend dahinter antwortet und in welcher Reihenfolge, legt allein LiteLLM fest: heute das Cluster, später ein Cloud-Anbieter davor und das Cluster als Rückfall. nex-router 0.2.3 wird um die Sprachausgabe (gestreamt) erweitert. Nach der Rückmeldung trägt das NEX-Team die Endpunkte in LiteLLM ein und testet die Kette. Live erst mit dem neuen Router-Image.