Mac-Worker: - install-launchagent.sh legt jetzt selbst venv + Dependencies an, statt das vorauszusetzen — ein Befehl reicht für komplette Erstinstallation - modernes launchctl bootout/bootstrap/kickstart statt unload/load (alte Variante wirft auf neueren macOS-Versionen "Input/output error 5") - neuer Sub-Befehl "restart" zum Neustarten ohne Reinstall - neues update.sh: git pull + pip install + kickstart + Health in einem Schritt LXC: - install.sh ALLOWED_EXTS-Check verhindert wiederholtes ",webm"-Anhängen - bessere Abschluss-Ausgabe mit echter Host-IP, Update-Anweisung, Diagnose-Hinweis Docs: - Haupt-README: aktueller Funktionsstand (Auth, PDF, Profile, Mobile/PWA, Diagnose, Retry alle als done markiert), Installation auf neuen Stand gebracht - Profile-Tabelle mit allen 17 Profilen - API-Listen vervollständigt (diag, preload, log, pdf, profiles) - Datenfluss-Skizze mit Preload + Heartbeat + X-Job-Id beschrieben Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Voraussetzungen
- Apple Silicon (M1/M2/M3/M4), 16 GB RAM minimum
- macOS mit Xcode-CLI-Tools (
xcode-select --install) - Homebrew
- Python 3.11+ (
brew install python@3.11— falls nicht vorhanden)
Schritt 1 — Ollama vorbereiten
# 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
Schritt 2 — Repo + LaunchAgent installieren
# Repo holen
git clone https://git.cynfo.net/christian/voice-agent.git ~/voice-agent
cd ~/voice-agent/mac-worker
# Vollständige Installation in einem Befehl:
./deploy/install-launchagent.sh
Das Skript macht alles in einem Rutsch, idempotent:
- legt
.venvan (wenn fehlt) und installiertrequirements.txt - legt
.envaus Vorlage an (wenn fehlt) - generiert
~/Library/LaunchAgents/com.voiceagent.worker.plistmit den richtigen Pfaden - registriert den Agent via modernem
launchctl bootstrap - wartet auf
http://127.0.0.1:8080/healthund gibt die Status-Antwort aus
Verwaltung:
| Aktion | Befehl |
|---|---|
| Status + letzte Logs | ./deploy/install-launchagent.sh status |
| Worker neu starten (kein Reinstall) | ./deploy/install-launchagent.sh restart |
| Update (git-pull + pip + restart + Health) | ./deploy/update.sh |
| Vollständige Neuinstallation | ./deploy/install-launchagent.sh |
| Entfernen | ./deploy/install-launchagent.sh uninstall |
| Live-Logs | tail -f ~/Library/Logs/voice-agent-worker.{out,err}.log |
Hinweis (modernes macOS):
launchctl unload -w …wirft auf neueren Versionen oftInput/output error 5. Die Skripte nutzen daherlaunchctl bootout/bootstrap/kickstart -k— das funktioniert zuverlässig.
Eigenschaften des Agents:
RunAtLoad— startet beim LoginKeepAlivemitCrashed=true,SuccessfulExit=false— Restart nach Crash, kein Loop bei sauberem ExitThrottleInterval=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.
Worker im Vordergrund starten (für Entwicklung)
cd ~/voice-agent/mac-worker
source .venv/bin/activate
uvicorn app.main:app --host 0.0.0.0 --port 8080
API-Endpunkte
| Methode | Pfad | Zweck |
|---|---|---|
| GET | /health |
Status + Ollama-Reachability |
| GET | /api/profiles |
Verfügbare YAML-Profile auflisten |
| POST | /api/transcribe |
multipart: audio, language → Transkript-JSON |
| POST | /api/summarize |
JSON: transcript, title, profile → Summary-JSON |
| POST | /api/protocol |
JSON: transcript, summary, title, profile → Protokoll-JSON |
| POST | /api/export/docx |
JSON: data, profile → DOCX-Binary |
| POST | /api/export/pdf |
JSON: data, profile → PDF-Binary |
| POST | /api/preload |
JSON: profile → forciert Ollama-Modell-Load (keep_alive 30 m) |
| GET | /api/jobs/{id}/log |
Letzte 200 Logzeilen für genau diesen Job |
Alle relevanten Calls dürfen einen Header X-Job-Id: <int> mitschicken — der
Mac-Worker schreibt dann jede Logzeile dieses Requests in einen per-Job
Ringbuffer, den /api/jobs/{id}/log für die LXC-UI ausliefert.
Firewall am Mac
# 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 |
faster |
Intel-Mac, Linux oder CUDA — 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.cppmit 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) — 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:
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
# 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.