docs: Bedienungsanleitung an aktuelle Features angepasst

§6.6 Sprache: Flex-Modus + language_mode entfernt; feste Sprache pro
Nutzer, automatische Übersetzung bei Fremdsprache und Admin-gesteuerte
erlaubte Sprachen (allowed_languages) dokumentiert.

§10 Notruf: automatische Erkennung (EMERGENCY_LLM_*) ersetzt durch
nutzerausgelösten 🆘-Knopf mit /api/emergency (category=manual) und
SMTP-E-Mail-Benachrichtigung (EMERGENCY_CONTACT_EMAIL, SMTP_*).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-25 09:32:11 +02:00
commit 9cfaaab7b0

View file

@ -793,7 +793,7 @@ https://va.beispiel.de/ ← remote über Reverse-Proxy (alles, inkl. Mikr
| Element | Funktion |
|---------|----------|
| **👄 / Titel** | Marke; Mund-Symbol als Wiedererkennung |
| **Sprache ▾** | Antwortsprache (→ § 6.6): `🔄 Flex` = folgt der gesprochenen Sprache · feste Sprache (🇩🇪/🇬🇧/…) = Antwort + Stimme in dieser Sprache |
| **Sprache ▾** | Antwortsprache (fest, → § 6.6). Diktat in einer Fremdsprache wird automatisch in die Zielsprache übersetzt. Welche Sprachen erscheinen, legt der Admin pro Nutzer fest. |
| **⋮ Menü** | Öffnet die Einstellungen (unten) |
**Identitätsleiste (direkt unter der Kopfzeile):** links „Angemeldet als …" (SSO-Identität;
@ -1395,52 +1395,44 @@ print(result) # → 'Chluteur kommt.'
---
### 6.6 Sprache wechseln (Fix / Flex)
### 6.6 Sprache (fest, pro Nutzer)
Die Antwortsprache wird in der Web-Oberfläche über **ein einziges Dropdown** („Sprache")
gesteuert. Es kennt zwei Arten von Werten:
Die Sprache ist **immer fest** — den früheren „Flex"-Modus gibt es nicht mehr. Im Dropdown
„Sprache" wählt man eine konkrete Sprache; **Antwort und vorlesende Stimme sind immer in
dieser Sprache.**
| Auswahl | Bedeutung | Whisper (STT) | LLM-Antwort + Stimme |
|---------|-----------|---------------|----------------------|
| **🔄 Flex** | keine feste Sprache — folgt automatisch der gesprochenen | erkennt die Sprache, **übersetzt nicht** | in der **erkannten** Sprache (blaue Blase zeigt das Original) |
| **🇩🇪 🇬🇧 🇫🇷 🇪🇸 🇮🇹 🇧🇷 🇵🇱 🇸🇦 🇷🇺 🇨🇳** | „Fix": System bleibt bei dieser Sprache | bekommt die feste Sprache → **übersetzt** die Eingabe | immer in der **festen** Sprache, egal worin gefragt wurde |
**Fremdsprache diktieren → Übersetzung:** Spricht man in einer **anderen** als der
eingestellten Sprache, erkennt das System die gesprochene Sprache automatisch und
**übersetzt die Anfrage in die Zielsprache**. Sie wird dann auch in der Zielsprache
angezeigt und beantwortet — hilfreich beim Sprachenlernen und über Sprachgrenzen hinweg.
**Beispiele** (Eingabe auf Französisch gesprochen):
Beispiel (Zielsprache 🇩🇪 DE, auf Französisch gesprochen): Die Blase zeigt die **deutsche**
Übersetzung; Antwort + Stimme sind Deutsch.
- **Flex** → blaue Blase: französischer Originaltext · Antwort + Stimme: Französisch.
- **🇩🇪 DE** → blaue Blase: deutsche Übersetzung · Antwort + Stimme: Deutsch.
Die vorlesende Piper-Stimme folgt der Sprache automatisch; die ♂/♀-Buttons bestimmen die
Variante (→ `LANG_TO_PIPER_VOICE`, `PIPER_VOICE_GENDERED`).
Die vorlesende Piper-Stimme folgt immer der Antwortsprache automatisch (z. B. `thorsten`/`kerstin`
für Deutsch, `siwis`/`tom` für Französisch — Zuordnung → `LANG_TO_PIPER_VOICE` und
`PIPER_VOICE_GENDERED`). Die aktive Geschlechtspräferenz (♂/♀-Buttons) bestimmt, welche
Variante gewählt wird. Bei Text-Chat
im Flex-Modus (keine Audio-Erkennung möglich) bekommt das LLM **keine** Sprachvorgabe und
antwortet von selbst in der Sprache der Eingabe; als Fallback gilt `DEFAULT_LANGUAGE`.
> **Hinweis:** Es gibt kein separates „Fix/Flex"-Menü mehr — eine konkrete Sprache zu
> wählen *ist* der Fix-Modus, „🔄 Flex" der flexible. Intern bleiben zwei Felder
> erhalten: `language` (ISO-Code) und `language_mode` (`fix` | `flex`).
**Erlaubte Sprachen pro Nutzer (Admin):** Im Admin → **Nutzer** legt der Admin per
Sprach-Chips fest, welche Sprache(n) ein Nutzer wählen darf. Die App zeigt dann nur diese;
bei **genau einer** erlaubten Sprache verschwindet das Sprachmenü ganz (kein Stress).
**Konfiguration außerhalb der Web-UI:**
```bash
DEFAULT_LANGUAGE=de # global in .env (Fallback-/Standardsprache)
DEFAULT_LANGUAGE_MODE=fix # global: fix | flex (Admin: Tab „⚙ Einstellungen")
DEFAULT_LANGUAGE=de # globale Standardsprache (Admin: „⚙ Konfiguration")
```
Pro Nutzer (dauerhaft):
```bash
# Feste Sprache (Fix):
# Standardsprache des Nutzers:
curl -s -X PUT $URL/api/me/prefs -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"language":"en","language_mode":"fix"}' | jq
-H 'Content-Type: application/json' -d '{"language":"en"}' | jq
# Flex (Sprache folgt automatisch):
curl -s -X PUT $URL/api/me/prefs -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"language_mode":"flex"}' | jq
# Erlaubte Sprachen setzt der Admin (CSV, leer = alle):
curl -s -X PUT $URL/api/admin/users/$USER_ID/prefs -H "X-Admin-Key: $ADMIN_KEY" \
-H 'Content-Type: application/json' -d '{"allowed_languages":"de,en"}' | jq
```
Pro Aufruf: `{"text":"…","language":"en","language_mode":"fix"}` im Body.
---
### 6.7 Audio-Geräte (Mikrofon, Lautsprecher, Bluetooth)
@ -2006,34 +1998,48 @@ Pro Nutzer übersteuern: `daily_request_limit` in `PUT /api/me/prefs`.
---
## 10. Notfall-Erkennung und Eskalation
## 10. Notruf (vom Nutzer ausgelöst)
> 🔧 Admin
Das System erkennt Notlagen-Signale (medizinisch, Sturz, Hilferuf) in der Nutzereingabe.
Die Erkennung ist **zweistufig**:
Es gibt **keine automatische** Notfallerkennung mehr (früher Stichwörter + LLM) — die war
unzuverlässig (Fehlalarme **wie** verpasste Notfälle) und intransparent. Ein Notruf entsteht
**nur durch eine bewusste Nutzeraktion**:
1. **Stichwort-Heuristik** — im Hot-Path, sofort, ohne Latenz.
2. **LLM-Klassifikation** — läuft als Hintergrund-Task, **nur** wenn Stufe 1 nichts fand.
Erkennt verpasste Formulierungen (metaphorische Suizidalität, Schlaganfall-Symptome
ohne Schlüsselwort) mit Konfidenz-Schwelle.
- In der App oben links ein dezenter roter **🆘-Knopf** („Hilfe rufen").
- Tippen → Rückfrage „Wirklich Hilfe rufen?" → **erst nach Bestätigung** wird ausgelöst.
- Der Notruf wird protokolliert (Admin → System → **Notrufe**); der Nutzer erhält einen
klaren Hinweis in seiner Sprache.
Bei Treffer: Protokolleintrag + Metrik + optionaler Webhook + Signal im Response
(`X-Emergency`-Header, `emergency`-Feld, WebSocket-`emergency`-Event).
**Benachrichtigung (Testphase):** Ist SMTP konfiguriert, geht beim Notruf eine **E-Mail**
an die Kontaktperson(en) (`EMERGENCY_CONTACT_EMAIL`). Ohne SMTP wird nichts versendet und
der Nutzer sieht „ACHTUNG: Es ist noch keine Benachrichtigung eingebaut.". SMS/Anruf folgen
später.
```bash
EMERGENCY_WEBHOOK_URL=https://example.org/alert # optional
EMERGENCY_LLM_ENABLED=true # Stufe 2 (Standard: an)
EMERGENCY_LLM_MIN_CONFIDENCE=0.6 # Schwelle gegen Fehlalarme
EMERGENCY_LLM_PROVIDER= # leer = Default-LLM
EMERGENCY_CONTACT_EMAIL=dieter.schlueter@linix.de # Default-Kontaktperson
SMTP_HOST= # ohne Host kein Versand — KEINE Inline-Kommentare hinter Werten!
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM= # muss dem SMTP_USER gehören (sonst 553 Sender rejected)
SMTP_STARTTLS=true
EMERGENCY_WEBHOOK_URL= # optional, für spätere Eskalation
```
> ⚠️ **Wichtig:** Die Erkennung ist **keine verlässliche Lebensrettung** und kein Ersatz
> für einen echten Notruf. Sie kann Notlagen verpassen oder Fehlalarme auslösen.
> Erkannte Texte sind hochsensibel (DSGVO Art. 9: Einwilligung, Aufbewahrung, Zugriff).
Auslösen per API (z. B. zum Testen):
```bash
curl -s -X POST $URL/api/emergency -H 'Content-Type: application/json' \
-d '{"language":"de"}' | jq
# → {"category":"manual","notice":"…","email_sent":true|false}
```
> ⚠️ **Wichtig:** Der Notruf ist **kein Ersatz** für einen echten Rettungsdienst. In der
> Testphase ist die Zustellung nur E-Mail (best effort, abhängig vom Mailserver).
---
## 11. Remote-Zugang und Deployment
> 🔧 Admin
@ -2283,10 +2289,13 @@ Logs: Terminal von `make run`. Mehr Details: `LOG_LEVEL=debug` in `.env`.
| Variable | Default | Bedeutung |
|----------|---------|-----------|
| `DAILY_REQUEST_LIMIT` | `0` | Anfragen/Nutzer/Tag (0 = unbegrenzt) |
| `EMERGENCY_WEBHOOK_URL` | (leer) | Webhook-URL für Notfall-Eskalation |
| `EMERGENCY_LLM_ENABLED` | `true` | LLM-Klassifikation (Stufe 2) ein/aus |
| `EMERGENCY_LLM_PROVIDER` | (leer = Default-LLM) | Provider für Klassifikation |
| `EMERGENCY_LLM_MIN_CONFIDENCE` | `0.6` | Konfidenz-Schwelle gegen Fehlalarme |
| `EMERGENCY_CONTACT_EMAIL` | `dieter.schlueter@linix.de` | Kontaktperson für die Notruf-E-Mail |
| `SMTP_HOST` | (leer) | Mailserver für Notruf-E-Mail (leer = kein Versand) |
| `SMTP_PORT` | `587` | SMTP-Port |
| `SMTP_USER` / `SMTP_PASSWORD` | (leer) | SMTP-Zugangsdaten |
| `SMTP_FROM` | (leer → `SMTP_USER`) | Absender (muss dem SMTP_USER gehören) |
| `SMTP_STARTTLS` | `true` | STARTTLS verwenden |
| `EMERGENCY_WEBHOOK_URL` | (leer) | optionaler Webhook (spätere Eskalation) |
---
@ -2316,8 +2325,7 @@ Wichtige Body-Felder für `/api/chat` und `/api/speak`:
| Feld | Typ | Bedeutung |
|------|-----|-----------|
| `text` | string | Eingabe-Text (Pflicht) |
| `language` | string | Sprache, z. B. `de`, `en` (im Fix-Modus maßgeblich) |
| `language_mode` | string | `fix` (feste Sprache, Eingabe wird übersetzt) oder `flex` (folgt der erkannten Sprache) — → § 6.6 |
| `language` | string | Zielsprache, z. B. `de`, `en` — Antwort + Stimme (→ § 6.6) |
| `stt_provider` | string | Provider für diese Anfrage |
| `llm_provider` | string | Provider für diese Anfrage |
| `tts_provider` | string | Provider für diese Anfrage |
@ -2340,7 +2348,7 @@ Body-Felder: `input_endpoint`, `output_endpoint`, `stt_provider`, `llm_provider`
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
| `GET` | `/api/me` | Aktueller Nutzer + Präferenzen |
| `PUT` | `/api/me/prefs` | Dauerhafte Routing-Präferenzen setzen (Merge; Felder u. a. `language`, `language_mode`, `tts_provider` — → § 6.6) |
| `PUT` | `/api/me/prefs` | Dauerhafte Nutzer-Präferenzen setzen (Merge; Felder u. a. `language`, `tts_provider` — → § 6.6) |
| `GET` | `/api/me/memories` | Alle Langzeit-Erinnerungen |
| `POST` | `/api/me/memories` | Erinnerung hinzufügen |
| `DELETE` | `/api/me/memories/{id}` | Erinnerung löschen |