fix: QC-Ratings aus 7 älteren Artikeln entfernt (CHECK 10)
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user