# BSN Server-Architektur — Split-Plan **Stand:** 18.06.2026 | **Autor:** Basti (Chef vom Dienst) **Status:** Planungsphase — noch nicht umgesetzt --- ## Ziel Das BSN-Chatsystem von einem Monolithen auf zwei spezialisierte Dienste aufteilen: | Server | Rolle | Optimiert für | |---|---|---| | **Server A** (aktuell, 4C/8GB) | Web-Frontend + Datenbank | I/O, HTTP-Requests | | **Server B** (lokal, 24C/32GB) | Sprachverarbeitung | CPU, parallele Jobs | ## Ist-Analyse ### Aktueller Monolith (Port 5002) ``` app.py (Flask) — EIN Prozess, alles drin: ├── WhatsApp Webhook (I/O-leicht) ├── Telegram Webhook (I/O-leicht) ├── Public Frontend (I/O-leicht) ├── Admin Dashboard (I/O-leicht) ├── faster-whisper Transkription (CPU-schwer, 30–60s, 1 Kern) ├── Piper TTS (CPU-mittel, 1–3s) ├── ffmpeg Video-Thumbnails (CPU-mittel, 2–5s) ├── DeepSeek LLM-Calls (Netzwerk, kein CPU-Problem) └── SQLite DB (I/O) ``` ### Ressourcen-Profil pro Aufgabe | Aufgabe | CPU | RAM | Dauer | Parallelfähig | |---|---|---|---|---| | WhatsApp-Request | <1% | 5 MB | 50ms | ✅ Beliebig | | Frontend-Rendering | 2% | 10 MB | 100ms | ✅ Beliebig | | **Whisper Transkription** | 100% (1 Kern) | 1,5 GB | 30–60s | ⚠️ 2–3 gleichzeitig | | **Piper TTS** | 50% (1 Kern) | 200 MB | 1–3s | ✅ 4–5 | | **ffmpeg Thumbnail** | 80% (1 Kern) | 100 MB | 2–5s | ⚠️ 2–3 | | LLM API-Call | 0% | 20 MB | 2–5s | ✅ Beliebig | ### Kritischer Pfad: SPIEL Essen Messe Während der Messe (4 Tage, ~10h/Tag) ist mit **Spitzen von 20–50 parallelen Requests** zu rechnen. Der aktuelle 4-Kern-Server würde bei 3–4 gleichzeitigen Transkriptionen **alle Kerne blockieren** — Web-Requests würden timeouten. --- ## Ziel-Architektur ``` ┌─────────────────────────────────────────────────────┐ │ Server A — "Web & Data" │ │ 4 Kerne, 8 GB RAM │ │ Port 5002 (intern) │ │ │ │ Flask (app.py), schlank: │ │ ├── WhatsApp/Telegram Webhook │ │ ├── Public Frontend + Admin │ │ ├── SQLite (bsn_intake.db) │ │ ├── DeepSeek LLM-Calls │ │ └── HTTP-Client → Server B │ │ │ │ NUTZT: whisperclient.py (sendet Audio an Server B) │ └──────────────────────┬──────────────────────────────┘ │ HTTP (lokal, <1ms Latenz) POST /transcribe {audio_file} → {text} POST /speak {text} → {audio_file} POST /thumbnail {video_file} → {thumbnail} │ ┌──────────────────────▼──────────────────────────────┐ │ Server B — "Language Processor" │ │ 24 Kerne, 32 GB RAM (lokal) │ │ Port 5003 (intern) │ │ │ │ Flask/FastAPI (language_server.py): │ │ ├── POST /transcribe │ │ │ └── faster-whisper 'base' model │ │ ├── POST /speak │ │ │ └── Piper TTS (de_DE-thorsten-medium) │ │ ├── POST /thumbnail │ │ │ └── ffmpeg -ss 1 -vframes 1 │ │ └── GET /health │ │ │ │ Worker-Pool: 8–10 parallele Transkriptionen │ │ RAM: 1,5 GB pro Whisper-Instanz ≈ 15 GB nutzbar │ └─────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────┐ │ Server C — "Fallback & Test" │ │ 24 Kerne, 32 GB RAM (lokal) │ │ │ │ Optional: │ │ ├── Redundanz: Failover wenn Server B ausfällt │ │ ├── Testumgebung: Staging für neue Features │ │ └── Proxmox-Host: mehrere isolierte VMs │ └─────────────────────────────────────────────────────┘ ``` --- ## API-Spezifikation (Server A ↔ Server B) ### `POST /transcribe` ``` Request: multipart/form-data file: audio.ogg (WhatsApp voice memo) Response: 200 JSON { "ok": true, "text": "Transkribierter deutscher Text...", "duration_seconds": 12.5, "processing_time_ms": 3420 } Fehler: { "ok": false, "error": "..." } ``` ### `POST /speak` ``` Request: JSON { "text": "Hallo Daniel, hier ist deine Antwort.", "voice": "de_DE-thorsten-medium" // optional, default } Response: 200 audio/wav (binary WAV data) Fehler: { "ok": false, "error": "..." } ``` ### `POST /thumbnail` ``` Request: multipart/form-data file: video.mp4 Response: 200 image/jpeg (binary JPEG, 320px wide) Fehler: { "ok": false, "error": "..." } ``` ### `GET /health` ``` Response: 200 JSON { "ok": true, "workers_busy": 2, "workers_available": 8, "uptime_seconds": 86400 } ``` --- ## Dateigrößen & Netzwerk | Medientyp | Typische Größe | Übertragung (1 Gbit/s lokal) | |---|---|---| | WhatsApp Sprachnachricht | 50–500 KB | < 5 ms | | WhatsApp Audio (2 Min.) | 2–5 MB | ~40 ms | | WhatsApp Video (1 Min.) | 5–50 MB | ~400 ms | | WAV-Audio (TTS Output) | 100–500 KB | < 5 ms | | JPEG Thumbnail | 10–50 KB | < 1 ms | **Fazit:** Dateigrößen sind kein Bottleneck. Selbst 50-MB-Videos gehen in <500ms über das lokale Netz. --- ## Migrationsplan (6 Schritte) ### Phase 1: Language Server extrahieren (2h) 1. `language_server.py` schreiben — Flask-App mit `/transcribe`, `/speak`, `/thumbnail`, `/health` 2. Whisper/Piper/ffmpeg-Code aus `app.py` in `language_server.py` verschieben 3. Auf Server B deployen und testen 4. healthcheck einrichten ### Phase 2: Web Server umbauen (1h) 5. `whisperclient.py` schreiben — HTTP-Client für Language Server 6. In `app.py`: direkte Funktionsaufrufe ersetzen durch `whisperclient.transcribe(audio_path)` 7. Fallback: wenn Language Server nicht erreichbar → Submission als "Transkription ausstehend" markieren ### Phase 3: Testen (1h) 8. WhatsApp-Sprachnachricht senden → Transkription via Server B verifizieren 9. TTS via Server B verifizieren 10. Video-Thumbnail via Server B verifizieren 11. Lasttest: 10 parallele Sprachnachrichten ### Phase 4: Proxmox (Daniel) 12. Proxmox auf Server B installieren 13. VM mit 12 Kernen + 16 GB RAM für Language Server 14. SSH-Zugang für Basti ### Phase 5: Redundanz (optional) 15. Language Server auch auf Server C deployen 16. Web Server mit Failover: wenn B nicht antwortet → C anfragen ### Phase 6: Monitoring 17. Health-Checks via cron (alle 60s) 18. Alert an Telegram wenn Language Server down --- ## Voraussetzungen ### Was Daniel bereitstellen muss - [ ] Server B: Proxmox installiert, VM bereit - [ ] Server B: SSH-Zugang für Basti - [ ] Server B: Linux (Debian/Ubuntu), Python 3.11+ - [ ] Lokales Netzwerk: Server A kann Server B erreichen (ping, HTTP) ### Was auf Server B installiert wird ```bash # System apt install python3 python3-pip python3-venv ffmpeg espeak-ng # Python pip install faster-whisper flask requests # Piper TTS (pre-built binary) # Voices: de_DE-thorsten-medium.onnx (~61 MB) ``` --- ## Risiken & Fallbacks | Risiko | Eintritts-WSK | Fallback | |---|---|---| | Server B nicht erreichbar | Niedrig (lokales Netz) | Submission markiert als "Transkription ausstehend", retry in 30s | | Whisper-Modell zu langsam | Niedrig (24 Kerne) | Kleinere `tiny`-Variante laden | | RAM voll (zu viele parallele Jobs) | Mittel (unter Last) | Worker-Pool begrenzen, Queue | | Piper TTS fehlende Library | Niedrig | LD_LIBRARY_PATH + ESPEAK_DATA_PATH gesetzt | --- ## Kosten-Nutzen **Vor dem Split (aktuell):** - 4-Kern-Server: Web + Transkription + TTS = alles in einem - SPIEL Essen: 3 parallele Transkriptionen = alle Kerne blockiert = Web timeouts **Nach dem Split:** - 4-Kern-Server: NUR Web = ~100 gleichzeitige Requests möglich - 24-Kern-Server: NUR Sprache = 8–10 parallele Transkriptionen - Web-Server bleibt IMMER responsiv, egal wie viele Sprachnachrichten reinkommen --- ## Nächste Schritte 1. Daniel installiert Proxmox auf Server B 2. Daniel gibt SSH-Zugang 3. Basti deployt `language_server.py` 4. Basti baut `app.py` um 5. Integrationstest 6. Produktiv setzen