Files
perinco-CRM/docs/04-diktat.md
T
Sascha WolfandClaude Opus 5 7973dc07b1 docs: echte Umlaute in der Dokumentation
893 Stellen in Fliesstext, Tabellen und Mermaid-Beschriftungen. Dazu die
sichtbaren ss-Fälle (ausschließlich, groß, außen, schließt, heißt,
draußen, weiße, anstoßen, Straße).

Nicht angetastet, damit die Doku weiter beschreibt, was wirklich in der
Datenbank steht:

- Codeblöcke und Code-Spans - dort stehen Bezeichner, SQL, Enum-Werte
  und Dateinamen: `mail_pool`, `'uebernommen'`, `extract.js`
- in Mermaid nur der Text in Anführungszeichen; Knotennamen, Pfeile und
  Werte wie source = 'Mail' bleiben, wie sie sind
- `empfaenger` im ER-Diagramm von crm_mail_contact_candidates: die
  Aufzählung dort nennt die Datenbankwerte, nicht die Anzeige

Die Menü-Skizze in der Benutzeranleitung zeigt jetzt die aufgeklappten
Kontakte mit "Aus Mails" samt Zähler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 09:45:58 +02:00

86 lines
3.2 KiB
Markdown

# 04 Diktat (Whisper im Browser)
Überall, wo längerer Text entsteht - Projektbeschreibung, Interaktionsnotiz,
nächster Schritt, interne Notizen - sitzt ein Mikrofon-Button. Die Erkennung
läuft **vollständig im Browser des Benutzers**.
Das Backend ist daran nicht beteiligt und sieht keine Audiodaten: kein API-Key,
keine Kosten pro Minute, kein Datenabfluss. Aufnahmen werden nirgends gespeichert.
## Weg der Aufnahme
```mermaid
flowchart LR
MIC(("Mikrofon")) --> MR["MediaRecorder"]
MR --> AC["AudioContext<br/>16 kHz Mono PCM"]
AC --> W["Web Worker"]
W --> ONNX["Whisper als ONNX<br/>WebGPU, sonst WASM"]
ONNX --> T["Text"]
T --> F["Formularfeld"]
HF["Hugging-Face-CDN"] -.->|"einmalig 759 MB"| CACHE[("Cache-Speicher<br/>der Seite")]
CACHE --> ONNX
```
Die Erkennung liegt im Web Worker (`src/lib/whisper.worker.ts`), damit die
Oberfläche währenddessen nicht einfriert. Die ONNX-Laufzeit wird mit
ausgeliefert (Vite bundelt die `.wasm`), nur die Modelldateien kommen beim ersten
Laden vom CDN und bleiben danach im Cache-Speicher der Seite.
## Lebenszyklus des Modells
```mermaid
stateDiagram-v2
[*] --> idle : Anmeldung
idle --> loading : "Sprachmodell laden" oder<br/>automatisch, wenn schon im Cache
loading --> ready : im Worker initialisiert
loading --> error : Abbruch, Haenger, kein Speicher
error --> loading : erneut versuchen
ready --> idle : "Sprachmodell löschen"
ready --> ready : Diktat benutzen
note right of loading
Phase download: Dateien holen
Phase init: Modell im Worker aufbauen
end note
```
Beim Anmelden wird das Modell automatisch bereitgestellt, **wenn** es schon in
diesem Browser liegt. Ein Download von 759 MB startet nie von allein, und der
Automatismus greift bewusst nur einmal pro Seitenaufruf: wer das Modell löscht,
bekommt es nicht sofort wieder heruntergeladen.
## Wo es sitzt
Reiter **Whisper** in den Einstellungen - für **jede Rolle** sichtbar, denn jeder
richtet sein Diktat auf seinem eigenen Gerät ein. Genau deshalb sind die
Einstellungen überhaupt für alle Rollen zugänglich; welche Reiter erscheinen,
entscheidet die Rolle:
```mermaid
flowchart LR
E["Einstellungen"] --> A["Mein Konto<br/>(alle)"]
E --> W["Whisper<br/>(alle)"]
E --> N["Nutzerverwaltung<br/>(admin)"]
E --> S["Scanner<br/>(admin)"]
```
Ist das Modell noch nicht geladen, wird der Mikrofon-Button zu *Diktat
einrichten* und springt per `goTo('Einstellungen', { tab: 'whisper' })` direkt
dorthin - ein offener Dialog wird vorher geschlossen (`onBeforeNavigate`).
## Warum keine Auswahl
Modell, Genauigkeit, Rechenweg und Sprache stehen als Konstanten in
`src/lib/whisper.tsx`: `large-v3-turbo`, `q4`, Grafikkarte wenn vorhanden,
Deutsch. Jede andere Kombination war in den Messungen entweder schlechter oder
kaputt - bei `small` in fp16 auf WebGPU reagiert der ganze Browser-Tab nicht mehr,
und **kein Timeout in der Seite kann das abfangen**, weil das Skript selbst nicht
mehr läuft.
Eine Auswahl hätte also nur Gelegenheit geboten, etwas falsch zu machen. Die
Messwerte stehen im [README](../README.md#warum-small-und-q4---die-messungen).
Als Netz bleibt eine Frist pro Erkennung (15x Aufnahmedauer, mindestens 45 s,
höchstens 5 min) und eine Hänger-Erkennung beim Laden.