# 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=`). - 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.