- com.voiceagent.worker.plist.template — User-LaunchAgent mit RunAtLoad, KeepAlive(Crashed=true), ProcessType=Interactive für MLX/Metal-Zugriff - install-launchagent.sh — generiert plist mit Pfaden, lädt via launchctl, prüft Health-Endpoint; unterstützt install/uninstall/status - READMEs: Abschnitt "Worker als Dienst" mit Verwaltungs-Befehlen
205 lines
8.0 KiB
Markdown
205 lines
8.0 KiB
Markdown
# Voice-Agent Mac-Worker
|
||
|
||
Stateless FastAPI-Service auf dem Mac. Macht die schwere Arbeit: Whisper-Transkription und Ollama-Zusammenfassung. Wird vom LXC-Frontend per HTTP angesprochen.
|
||
|
||
## Setup (Apple Silicon)
|
||
|
||
```bash
|
||
cd mac-worker
|
||
python3.11 -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install --upgrade pip
|
||
pip install -r requirements.txt
|
||
cp .env.example .env
|
||
# Bei Bedarf .env anpassen
|
||
```
|
||
|
||
## Ollama vorbereiten
|
||
|
||
```bash
|
||
# Ollama installieren, falls noch nicht geschehen
|
||
brew install ollama
|
||
|
||
# Modell ziehen (Default — siehe unten für Alternativen)
|
||
ollama pull llama3.1:8b
|
||
|
||
# Ollama netzwerkweit erreichbar machen (LaunchAgent)
|
||
launchctl setenv OLLAMA_HOST "0.0.0.0:11434"
|
||
brew services restart ollama
|
||
```
|
||
|
||
## Worker starten — Vordergrund (Test)
|
||
|
||
```bash
|
||
uvicorn app.main:app --host 0.0.0.0 --port 8080
|
||
```
|
||
|
||
Health-Check vom LXC:
|
||
```bash
|
||
curl http://<MAC-IP>:8080/health
|
||
```
|
||
|
||
## Worker als Dienst (LaunchAgent — empfohlen für Dauerbetrieb)
|
||
|
||
Der Worker soll dauerhaft laufen, nach Login automatisch starten und bei Crash automatisch neu starten. Dafür ein **LaunchAgent** (kein LaunchDaemon — MLX/Metal-GPU-Zugriff braucht User-Context).
|
||
|
||
**Installation** (einmalig, nachdem `.venv` und `.env` eingerichtet sind):
|
||
|
||
```bash
|
||
cd ~/voice-agent/mac-worker
|
||
./deploy/install-launchagent.sh
|
||
```
|
||
|
||
Das Skript:
|
||
|
||
1. prüft, ob `.venv/bin/uvicorn` existiert
|
||
2. legt `.env` aus Vorlage an, falls fehlt
|
||
3. generiert `~/Library/LaunchAgents/com.voiceagent.worker.plist` mit den passenden Pfaden
|
||
4. lädt den Agent (`launchctl load -w …`)
|
||
5. testet `http://127.0.0.1:8080/health`
|
||
|
||
**Verwaltung:**
|
||
|
||
| Aktion | Befehl |
|
||
|---|---|
|
||
| Status + Logs anzeigen | `./deploy/install-launchagent.sh status` |
|
||
| Entfernen | `./deploy/install-launchagent.sh uninstall` |
|
||
| Update (neue Version pullen) | `git pull && pip install -r requirements.txt && ./deploy/install-launchagent.sh` |
|
||
| Stoppen | `launchctl unload -w ~/Library/LaunchAgents/com.voiceagent.worker.plist` |
|
||
| Starten | `launchctl load -w ~/Library/LaunchAgents/com.voiceagent.worker.plist` |
|
||
| Live-Logs | `tail -f ~/Library/Logs/voice-agent-worker.{out,err}.log` |
|
||
|
||
**Eigenschaften des Agents:**
|
||
|
||
- `RunAtLoad` — startet beim Login
|
||
- `KeepAlive` mit `Crashed=true`, `SuccessfulExit=false` — Restart nach Crash, kein Loop bei sauberem Exit
|
||
- `ThrottleInterval=10` — frühestens 10 s nach Restart erneut starten
|
||
- Logs in `~/Library/Logs/voice-agent-worker.{out,err}.log`
|
||
|
||
**Wichtig:** der Agent läuft nur, wenn der Mac-User eingeloggt ist. Für headless-Server-Betrieb (Mac Mini ohne Login) entweder:
|
||
|
||
- Auto-Login aktivieren (System-Settings → Users), oder
|
||
- in einen LaunchDaemon umbauen — dann fällt allerdings die Apple-Neural-Engine-Beschleunigung weg, MLX läuft nur auf der CPU.
|
||
|
||
## API-Endpunkte
|
||
|
||
| Methode | Pfad | Zweck |
|
||
|---|---|---|
|
||
| GET | `/health` | Status + Ollama-Reachability |
|
||
| POST | `/api/transcribe` | multipart: `audio`, `language` → Transkript-JSON |
|
||
| POST | `/api/summarize` | JSON: `transcript`, `title` → Summary-JSON |
|
||
| POST | `/api/protocol` | JSON: `transcript`, `summary`, `title` → Protokoll-JSON |
|
||
| POST | `/api/export/docx` | JSON: Protokoll → DOCX-Binary |
|
||
|
||
## Firewall am Mac
|
||
|
||
```bash
|
||
# Eingehende Verbindungen auf Port 8080 erlauben (System-Settings > Network > Firewall)
|
||
# oder via pf — siehe Apple-Doku.
|
||
```
|
||
|
||
Der Worker hat **keine Authentifizierung** — Betrieb nur im internen Netz.
|
||
|
||
---
|
||
|
||
## Whisper-Engine wählen
|
||
|
||
In `.env`:
|
||
|
||
```
|
||
WHISPER_ENGINE=mlx # mlx | faster | mock
|
||
WHISPER_MODEL=large-v3 # Modellname (siehe Tabellen unten)
|
||
WHISPER_BATCH_SIZE=12 # MLX: höher = schneller + mehr RAM
|
||
WHISPER_LANGUAGE=de
|
||
```
|
||
|
||
| `WHISPER_ENGINE` | Wann nutzen |
|
||
|---|---|
|
||
| `mlx` (Default) | Apple Silicon — nutzt Apple Neural Engine via [`lightning-whisper-mlx`](https://github.com/mustafaaljadery/lightning-whisper-mlx) |
|
||
| `faster` | Intel-Mac, Linux oder CUDA — [`faster-whisper`](https://github.com/SYSTRAN/faster-whisper) (CTranslate2) |
|
||
| `mock` | Testbetrieb ohne Modell-Download |
|
||
|
||
### MLX-Modelle (`WHISPER_ENGINE=mlx`)
|
||
|
||
| `WHISPER_MODEL` | Größe | Geschwindigkeit (M2) | Qualität (Deutsch) |
|
||
|---|---|---|---|
|
||
| `tiny` | 75 MB | ~20× Realzeit | Niedrig — nur für Voice-Commands |
|
||
| `base` | 145 MB | ~15× Realzeit | Niedrig |
|
||
| `small` | 466 MB | ~6× Realzeit | Mittel |
|
||
| `medium` | 1.5 GB | ~2× Realzeit | Gut, schwächelt bei Fachbegriffen |
|
||
| `large-v3` (Default) | 3 GB | ~0.3× Realzeit | **Sehr gut** — Standard für Sitzungsprotokolle |
|
||
| `distil-large-v3` | 1.5 GB | ~1.5× Realzeit | Fast wie `large-v3`, ~5× schneller |
|
||
| `large-v3-turbo` | 1.6 GB | ~3× Realzeit | Wie `large-v3` für lange Inhalte, schwächer bei kurzen |
|
||
|
||
Empfehlung: `large-v3` für maximale Qualität, `distil-large-v3` wenn Geschwindigkeit wichtiger ist.
|
||
|
||
Modelle werden beim ersten Aufruf automatisch von HuggingFace gezogen (`mlx-community/whisper-...`).
|
||
|
||
### Alternative Transkriptions-Engines
|
||
|
||
Diese sind im Standard-Setup **nicht** aktiviert, lassen sich aber statt `lightning-whisper-mlx` einbauen:
|
||
|
||
- **`whisper.cpp` mit CoreML** — pure C++, sehr ressourceneffizient, kann das Whisper-Modell auf der Apple Neural Engine ausführen. Eigener Subprozess statt Python-Bindings.
|
||
- **WhisperX** ([github.com/m-bain/whisperX](https://github.com/m-bain/whisperX)) — basiert auf `faster-whisper`, fügt **Wort-genaue Timestamps** und **Sprecher-Diarization** (via pyannote) hinzu. Erfordert HuggingFace-Token. Sinnvoll, sobald „Wer hat was gesagt?" wichtiger wird.
|
||
- **Voxtral** (Mistral, 2025) — multimodales Sprach-LLM, kann Audio direkt zusammenfassen ohne separaten Whisper-Schritt. Reduziert die Pipeline auf einen einzigen Modell-Call, braucht aber andere Library und höheren VRAM.
|
||
- **NVIDIA Parakeet TDT** — extrem schnell, aber primär englisch. Für deutsche Sitzungen nicht empfehlenswert.
|
||
|
||
Für reinen Geschwindigkeits-Boost ohne Architektur-Änderung: einfach `WHISPER_MODEL=distil-large-v3` setzen.
|
||
|
||
---
|
||
|
||
## Ollama-Modell wählen
|
||
|
||
In `.env`:
|
||
|
||
```
|
||
OLLAMA_MODEL=llama3.1:8b
|
||
OLLAMA_TIMEOUT=600
|
||
```
|
||
|
||
Anschließend nur noch:
|
||
|
||
```bash
|
||
ollama pull <neues-modell>
|
||
# Worker neu starten
|
||
```
|
||
|
||
### Empfehlung nach Mac-RAM
|
||
|
||
| Mac-RAM | Empfohlenes Modell | Begründung |
|
||
|---|---|---|
|
||
| 16 GB | `llama3.1:8b` oder `qwen2.5:7b` | Genug Spielraum neben Whisper-large-v3 |
|
||
| 24 GB | `qwen2.5:14b` oder `gemma3:12b` | Großer Qualitätssprung bei deutscher JSON-Ausgabe |
|
||
| 32 GB | `mistral-small:24b` (Q4) oder `gpt-oss:20b` | Beste Kompromiss-Wahl |
|
||
| 64 GB+ | `llama3.3:70b` oder `qwen2.5:72b` | Top-Qualität, aber langsamer (~20–60 s pro Summary) |
|
||
|
||
### Detailbewertung der Kandidaten
|
||
|
||
| Modell | Größe | Deutsch | JSON-Stabilität | Geschwindigkeit | Notizen |
|
||
|---|---|---|---|---|---|
|
||
| `llama3.1:8b` (Default) | 8 B | Gut | Mittel — gelegentlich Codefences | Schnell (~5 s) | Ausreichend für die meisten Sitzungen |
|
||
| `qwen2.5:7b` | 7 B | Sehr gut | Hoch | Schnell | Beste 7B-Wahl für Deutsch |
|
||
| `qwen2.5:14b` | 14 B | Sehr gut | Sehr hoch | Mittel (~10 s) | **Empfehlung für 24–32 GB Macs** |
|
||
| `gemma3:12b` | 12 B | Gut | Hoch | Mittel | Google, gute Strukturierung |
|
||
| `mistral-small:24b` | 24 B | Sehr gut | Sehr hoch | Mittel (~15 s) | Europäisch trainiert, sehr stark bei Fachsprache |
|
||
| `gpt-oss:20b` | 20 B | Gut | Sehr hoch | Mittel | OpenAI Open-Weight (2025), explizit auf Tool-Use trainiert |
|
||
| `phi4:14b` | 14 B | Mittel | Hoch | Mittel | Microsoft, stark im Reasoning, schwächer im Deutschen |
|
||
| `llama3.3:70b` | 70 B | Exzellent | Sehr hoch | Langsam (~30–60 s) | Top-Qualität, nur mit 48 GB+ RAM sinnvoll |
|
||
|
||
### Modell wechseln
|
||
|
||
```bash
|
||
# 1. Modell ziehen
|
||
ollama pull qwen2.5:14b
|
||
|
||
# 2. .env anpassen
|
||
sed -i '' 's/^OLLAMA_MODEL=.*/OLLAMA_MODEL=qwen2.5:14b/' .env
|
||
|
||
# 3. Worker neu starten
|
||
# (z.B. Ctrl-C im Terminal und uvicorn erneut)
|
||
```
|
||
|
||
### Tipp: JSON-Modus erzwingen
|
||
|
||
Der aktuelle Worker parst die Antwort manuell und greift bei Fehlern auf einen Text-Fallback zurück. Mit Ollama lässt sich JSON-Output erzwingen, indem im Generate-Call `format: "json"` gesetzt wird — das senkt Parse-Fehler bei kleineren Modellen drastisch. Bei Bedarf in `app/core/ollama_client.py` ergänzen.
|