my_voice_assistant_v3_jamulix/BEDIENUNGSANLEITUNG.md
Dieter Schlüter 1eb79c1f09 feat: Tageskontingent und Notfall-Eskalation (#6)
- Quota (app/quota.py): Anfragen pro Nutzer/Tag (usage-Tabelle), DAILY_REQUEST_LIMIT,
  pro Nutzer via prefs uebersteuerbar; 429 (REST) bzw. error-Event (WS); Metrik
- Notfall (app/safety/emergency.py): heuristische Erkennung (de/en); Log im Store
  (emergency_events) + optionaler Webhook (best-effort) + X-Emergency/emergency-Event;
  Metrik emergency_total; Notfaelle umgehen das Kontingent
- verdrahtet in chat/speak/transcribe + WS-Turns
- Tests: 64 gruen (+6); Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur, .env.example)

Hinweis: Notfall-Erkennung ist eine Heuristik (kein Lebensretter); erkannte Texte
sind sensibel -> DSGVO beachten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 05:29:30 +02:00

12 KiB

Bedienungsanleitung — Voice Assistant Gateway

Diese Anleitung führt Schritt für Schritt durch Installation, Start, Konfiguration und Fehlerbehebung. Technische Hintergründe stehen im Architektur-Dokument, eine kompakte Übersicht im README.


1. Voraussetzungen

  • Python 3.11 oder neuer (python3 --version)
  • Ein OpenRouter-API-Key — nur nötig, wenn ein Profil entfernte KI nutzt (hybrid, cloud). Für rein lokalen Betrieb (local-dev) nicht erforderlich.
  • Optional: Docker, falls im Container betrieben.

2. Installation

cd voice-assistant-scaffold
python3 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .[test]

Danach die zentrale Konfigurationsdatei anlegen:

cp config/voice-assistant.example.toml config/voice-assistant.toml

3. API-Key hinterlegen (für Cloud/Hybrid)

Der Schlüssel wird aus der Umgebung gelesen und gehört nicht in eine Datei. Dauerhaft am besten in ~/.bashrc:

echo 'export OPENROUTER_API_KEY=sk-or-v1-DEIN_KEY' >> ~/.bashrc
chmod 600 ~/.bashrc
source ~/.bashrc

Prüfen, ob er ankommt:

echo ${OPENROUTER_API_KEY:0:8}    # zeigt nur den Anfang

Sicherheit: Den Key niemals in .env oder config/*.toml schreiben. Wird ein Key versehentlich öffentlich, im OpenRouter-Dashboard löschen (= widerrufen) und neu erzeugen.


4. Betriebsart (Profil) wählen

Profile bestimmen, welche KI-Module genutzt werden:

Profil Bedeutung Key nötig?
local-dev alles lokal (eigene KI/Hardware) nein
hybrid STT/TTS über Cloud, Haupt-LLM lokal ja
cloud alles über OpenRouter (Standardbetrieb) ja

Profil einmalig für einen Start:

VA_PROFILE=cloud make run

Profil dauerhaft — in .env eintragen:

VA_PROFILE=cloud

Hinweis: Stehen in .env noch DEFAULT_STT_PROVIDER / DEFAULT_LLM_PROVIDER / DEFAULT_TTS_PROVIDER, überschreiben diese das Profil. Für profilbasiertes Umschalten sollten sie auskommentiert sein.


5. Starten und Stoppen

make run

Standard-Adresse: http://localhost:8080 (Port änderbar, siehe Abschnitt 8). Beenden mit Strg + C.

Schnelltest in einem zweiten Terminal:

curl http://localhost:8080/health
# {"status":"ok"}

curl http://localhost:8080/api/config
# zeigt aktives Profil und die aufgelöste Standard-Route

6. Tägliche Bedienung — typische Aufgaben

a) Text sprechen lassen (/api/speak)

curl -X POST http://localhost:8080/api/speak \
  -H 'Content-Type: application/json' \
  -d '{"text":"Guten Morgen, wie geht es Ihnen?"}' \
  --output antwort.pcm

b) Chatten (Text rein, gesprochene Antwort raus) (/api/chat)

Nur den Trace als JSON ansehen (ohne Audio):

curl -X POST "http://localhost:8080/api/chat?debug=true" \
  -H 'Content-Type: application/json' \
  -d '{"text":"Wie wird das Wetter morgen?"}'

Komfortabler mit dem mitgelieferten Client (spielt die Antwort ab):

python chat_client.py "Erzähl mir einen guten Morgen-Spruch"

chat_client.py erwartet den Dienst auf Port 8003 — bei Bedarf im Skript GATEWAY_URL anpassen oder den Dienst mit PORT=8003 make run starten.

Fortlaufendes Gespräch (Gedächtnis): Wird eine session_id mitgegeben, merkt sich der Assistent den Verlauf und bezieht ihn in die nächste Antwort ein:

curl -X POST "http://localhost:8080/api/chat?session_id=oma-anna&debug=true" \
  -H 'Content-Type: application/json' -d '{"text":"Ich heiße Anna."}'
curl -X POST "http://localhost:8080/api/chat?session_id=oma-anna&debug=true" \
  -H 'Content-Type: application/json' -d '{"text":"Wie war noch mein Name?"}'

Ohne session_id ist jeder Aufruf eigenständig (kein Gedächtnis). Wie viele zurückliegende Nachrichten einfließen, steuert HISTORY_MAX_MESSAGES (Standard 10).

c) Audio transkribieren (/api/transcribe)

curl -X POST http://localhost:8080/api/transcribe \
  -F "file=@aufnahme.wav" -F "language=de"

d) Gerät oder Provider einmalig umstellen (pro Aufruf)

curl -X POST http://localhost:8080/api/speak \
  -H 'Content-Type: application/json' \
  -d '{"text":"Test","tts_provider":"piper","output_endpoint":"loopback"}'

e) Präferenzen für eine Session festlegen

# einmal setzen
curl -X POST http://localhost:8080/api/sessions/oma-anna/route \
  -H 'Content-Type: application/json' \
  -d '{"llm_provider":"openrouter","language":"de"}'

# danach mit dieser Session nutzen
curl -X POST "http://localhost:8080/api/chat?session_id=oma-anna&debug=true" \
  -H 'Content-Type: application/json' -d '{"text":"Hallo!"}'

7. Verfügbare Geräte und Bausteine ansehen

curl http://localhost:8080/api/devices    # Audio-Endpunkte mit Fähigkeiten
curl http://localhost:8080/api/config     # Profil, Route, Provider, Endpunkte

8. Port ändern

PORT=8003 make run                      # einmalig
sed -i 's/^PORT=.*/PORT=8003/' .env     # dauerhaft

9. Mit Docker betreiben

export OPENROUTER_API_KEY=sk-or-v1-...
docker compose up --build

Der Key wird aus der Shell in den Container durchgereicht; fehlt er, bricht der Start mit klarer Meldung ab.


10. Authentifizierung & Mehrbenutzer

Im Produktivbetrieb ist AUTH_ENABLED=true (Standard). Dann brauchen chat/speak/transcribe/sessions/me ein Bearer-Token pro Nutzer. Nutzer und Sessions werden in einer SQLite-Datei gespeichert (DB_PATH, Standard data/voice-assistant.db).

Schritt 1 — Admin-Schlüssel setzen (nur über die Umgebung):

export ADMIN_API_KEY=ein-langes-geheimnis

Schritt 2 — Nutzer anlegen (Token erscheint nur einmal, sicher notieren):

curl -X POST http://localhost:8080/api/admin/users \
  -H "X-Admin-Key: $ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"display_name":"Oma Anna"}'

Schritt 3 — mit Token nutzen:

TOKEN=<das-token-von-oben>
curl http://localhost:8080/api/me -H "Authorization: Bearer $TOKEN"

curl -X POST http://localhost:8080/api/speak \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"text":"Guten Morgen!"}'

Dauerhafte Vorlieben eines Nutzers (Gerät/Provider/Sprache) setzen:

curl -X PUT http://localhost:8080/api/me/prefs \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"language":"de","llm_provider":"openrouter"}'

Diese Vorlieben gelten automatisch für alle Aufrufe dieses Nutzers (Ebene zwischen Profil und Session). Eine fremde Session zu nutzen, wird mit 403 abgelehnt.

Langzeit-Erinnerungen (dauerhafte Fakten/Vorlieben, gelten über alle Gespräche):

# anlegen
curl -X POST http://localhost:8080/api/me/memories \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{"content":"Mag morgens Kamillentee."}'
# auflisten / löschen
curl http://localhost:8080/api/me/memories -H "Authorization: Bearer $TOKEN"
curl -X DELETE http://localhost:8080/api/me/memories/1 -H "Authorization: Bearer $TOKEN"

Diese Erinnerungen gibt der Assistent bei jedem Chat als Kontext mit — auch ohne session_id.

Echtzeit-Chat über WebSocket (/ws/chat): dauerhafter Kanal, pro Nachricht {"text": "..."}; Antwort kommt als Event-Folge (ack, semantic, Audio, done). Token per Query (?token=…), Gedächtnis per ?session_id=…. Mit {"text": "...", "stream": true} kommt die Antwort schon während der Generierung als token-Events (geringere wahrgenommene Latenz). Mit "audio_stream": true kommt zusätzlich das Audio satzweise (audio-Event + binärer Frame), sobald ein Satz fertig ist.

Sprach-Eingang (/ws/voice): Mikrofon-Audio als binäre Frames senden, dann {"type":"end"}. Der Server schickt ein transcript-Event und danach die Antwort wie bei /ws/chat (stream/audio_stream im end-Frame möglich). Mit {"type":"start","vad":true,"format":"pcm","sample_rate":16000} erkennt der Server das Äußerungsende automatisch an einer Sprechpause (kein end nötig).

Unterbrechen (Barge-in): Während der Assistent antwortet, {"type":"interrupt"} senden — die laufende Antwort wird abgebrochen (interrupted-Event).

Für lokale Entwicklung ist in der mitgelieferten .env AUTH_ENABLED=false gesetzt — dann ist kein Token nötig (anonymer Nutzer).


11. Resilienz & Metriken (Betrieb)

Fallback bei Provider-Ausfall: Pro Modul eine Ersatzliste setzen (in .env). Fällt der primäre Provider aus, übernimmt der nächste automatisch:

LLM_FALLBACK=local-openai-compatible
STT_FALLBACK=faster-whisper
TTS_FALLBACK=piper

Metriken ansehen:

curl http://localhost:8080/api/metrics                 # JSON
curl http://localhost:8080/api/metrics?format=prometheus

Enthält Request-Zahlen/-Laufzeiten, Pipeline-Stufen (stt/llm/tts) und Fallback-/Fehlerzähler. Die Werte gelten pro laufendem Prozess.

Tageskontingent (Kostenbremse) in .env:

DAILY_REQUEST_LIMIT=200    # Anfragen pro Nutzer/Tag; 0 = unbegrenzt

Bei Überschreitung antwortet der Dienst mit 429. Notfall-Eingaben werden nie blockiert.

Notfall-Eskalation: Erkennt der Dienst in einer Chat-/Sprach-Eingabe ein Notlagen-Signal (z. B. „Schmerzen in der Brust", „gestürzt", „kann nicht atmen"), protokolliert er das, macht es sichtbar (X-Emergency / emergency-Event) und ruft optional einen Webhook auf:

# EMERGENCY_WEBHOOK_URL=https://example.org/alert

⚠️ Nur eine Heuristik — kein Ersatz für einen echten Notruf. Erkannte Texte sind sensibel; auf Einwilligung und Datenschutz achten.


12. Fehlerbehebung

Symptom Ursache Lösung
OPENROUTER_API_KEY is empty Key nicht in der Umgebung export OPENROUTER_API_KEY=…, neues Terminal / source ~/.bashrc
HTTP 401 „Bearer token required/Invalid token" Auth an, Token fehlt/falsch gültiges Token im Header Authorization: Bearer …, oder AUTH_ENABLED=false für dev
HTTP 401 bei /api/admin/users falscher/fehlender Admin-Key X-Admin-Key mit ADMIN_API_KEY abgleichen
HTTP 403 bei ?session_id=… Session gehört anderem Nutzer eigene session_id verwenden
HTTP 503 bei /api/admin/users ADMIN_API_KEY nicht gesetzt Admin-Key in der Umgebung setzen
HTTP 422 „Unbekannter …-Provider/Endpunkt" Tippfehler in *_provider / *_endpoint gültige Werte via GET /api/config prüfen
VA_PROFILE wirkt nicht DEFAULT_*_PROVIDER in .env überschreibt es diese Zeilen in .env auskommentieren
LLM-Timeout / Connection refused (lokal) lokaler LLM-Server (Port 11434) läuft nicht LLM-Server starten oder Profil cloud wählen
Address already in use Port belegt anderen PORT setzen (Abschnitt 8)
chat_client.py bekommt keine Antwort Client nutzt Port 8003 Dienst mit PORT=8003 starten oder GATEWAY_URL anpassen
Profil greift nicht / Standardwerte config/voice-assistant.toml fehlt Datei aus *.example.toml kopieren (Abschnitt 2)

Logs erscheinen im Terminal, in dem make run läuft. Für mehr Details LOG_LEVEL=debug in .env setzen.


13. Tests ausführen

make test

Alle Tests sollten grün sein. Schlägt etwas fehl, gibt die Ausgabe den genauen Testnamen und die Ursache an.