Files
ICON-CRM/docs/04-diktat.md
T

133 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 04 Diktat (Whisper im Browser)
Überall dort, wo längerer Text entsteht – Notiz am Kontakt, Notizen an Lead und
Unternehmen, Projektnotizen, Ausschreibungstext – sitzt ein Mikrofon-Knopf. Die
Erkennung läuft **vollständig im Browser des Benutzers**.
Das Backend ist daran nicht beteiligt und sieht keine Audiodaten: kein
API-Schlüssel, keine Kosten pro Minute, kein Datenabfluss. Aufnahmen werden
nirgends gespeichert.
Die Umsetzung ist aus dem perinco CRM übernommen und technisch identisch – wer
das eine kennt, findet sich im anderen zurecht.
## Weg der Aufnahme
```mermaid
flowchart LR
MIC(("Mikrofon")) --> MR["MediaRecorder<br/>webm / ogg / mp4"]
MR --> AC["AudioContext + OfflineAudioContext<br/>16 kHz Mono PCM"]
AC --> W["Web Worker"]
W --> ONNX["Whisper als ONNX<br/>WebGPU, sonst WASM"]
ONNX --> T["Text"]
T --> F["Formularfeld<br/>angehaengt, nie ersetzt"]
HF["Hugging-Face-CDN"] -.->|"einmalig ~300 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 bündelt die `.wasm`, rund 21 MB unter `dist/assets/`), nur
die Modelldateien kommen beim ersten Laden vom CDN und bleiben danach im
Cache-Speicher der Seite.
## Festlegungen
Es gibt bewusst nichts einzustellen (`src/lib/whisper.tsx`):
| Festlegung | Wert | Warum |
| --- | --- | --- |
| Modell | `onnx-community/whisper-large-v3-turbo` | beste Erkennung im Deutschen, auch bei Fachbegriffen („S4 HANA"); `small` verliert genau dort, `tiny` ist unbrauchbar |
| Genauigkeit | `q4` | `q8` liefert auf der Grafikkarte Unsinn, `fp16` blockiert den Tab, `fp32` passt nicht in einen Speicherblock |
| Rechenweg | WebGPU, sonst WASM | automatisch; ohne Grafikbeschleunigung dauert es länger, funktioniert aber |
| Sprache | Deutsch | fest |
## Lebenszyklus des Modells
```mermaid
stateDiagram-v2
[*] --> idle: Anmeldung
idle --> loading: Klick auf "Sprachmodell laden" / "Diktat aktivieren"
loading --> ready: Worker meldet "ready"
loading --> error: Speicher, Abbruch, 2 min ohne Regung
ready --> idle: "Entladen"
ready --> idle: "Sprachmodell loeschen" (Cache geleert)
error --> loading: erneuter Versuch
```
* **Es lädt nichts von selbst** – weder der Download noch das Aktivieren aus dem
Cache. Beides braucht einen Klick.
* Der Grund ist gemessen, nicht vermutet: das Anlegen der ONNX-Sitzung übersetzt
hunderte Shader und legt dabei Zeiger und Darstellung für einige Sekunden
still. Lief das beim Anmelden von selbst, sah die ganze Anwendung aus, als
hinge sie – und zwar genau dann, wenn jemand gerade eine Lead-Kachel ziehen
wollte.
* Liegt das Modell im Cache, heißt der Knopf *Diktat aktivieren* und der
Download entfällt.
* Vor dem Download bittet die Anwendung um **dauerhaften Speicher**
(`navigator.storage.persist`). Chrome darf ablehnen; dann funktioniert alles
genauso, nur ohne diese Zusicherung.
### Hänger werden erkannt
Zwei Fristen, beide mit verwertbarer Meldung statt endlosem „Lädt …":
| Frist | Wann |
| --- | --- |
| 120 s ohne Regung des Workers | beim Laden – meist scheitert das Anlegen der ONNX-Sitzung am Speicher |
| max(45 s, 15 × Aufnahmedauer), höchstens 300 s | bei einer Erkennung |
In beiden Fällen wird der Worker beendet; ein neuer Versuch bekommt einen
frischen.
## Bedienung
*Einstellungen → Diktat* (für jede Rolle sichtbar, denn jeder richtet sein Gerät
selbst ein):
1. **Sprachmodell laden** – zeigt die tatsächliche Downloadgröße, bei Hugging
Face erfragt (`src/lib/whisperModels.ts`), und einen Fortschrittsbalken.
2. **Probe sprechen** – kurz etwas diktieren und den erkannten Text ansehen.
3. **Sprachmodell löschen** – entfernt es aus diesem Browser.
Danach steht in den Formularen ein Mikrofon-Knopf. Während der Aufnahme läuft
die Zeit, daneben liegt ein Kreuz zum Verwerfen.
Der Knopf zeigt drei Zustände:
| Zustand | Was zu sehen ist |
| --- | --- |
| kein Mikrofon / kein HTTPS | gar nichts – ein toter Knopf wäre schlechter |
| Modell fehlt | *Diktat einrichten*, führt direkt in den Reiter |
| bereit | Mikrofon, dann laufende Zeit, dann *Erkenne …* |
Erkannter Text wird **angehängt**, nie ersetzt: in den WYSIWYG-Feldern als
eigener Absatz am Ende, in den Textfeldern hinter dem Vorhandenen. Ins DOM des
Editors zu schreiben wäre die Alternative – dann hinge das Ergebnis aber an der
Schreibmarke, die während der Aufnahme längst woanders stehen kann.
## Dateien
| Datei | Aufgabe |
| --- | --- |
| `src/lib/whisper.tsx` | Kontext: Zustand, Fortschritt, Cache-Erkennung, Fähigkeiten des Geräts |
| `src/lib/whisper.worker.ts` | Pipeline, Erkennung, Fehler auch außerhalb der await-Kette |
| `src/lib/dictation.tsx` | `useRecorder` – Mikrofon, Dauer, Abbruch |
| `src/lib/audio.ts` | webm/ogg/mp4 → 16 kHz Mono Float32 |
| `src/lib/whisperModels.ts` | Downloadgröße, Formatierung |
| `src/components/ui/Dictate.tsx` | `DictateButton`, `DictateTextarea`, `appendParagraph` |
| `src/components/settings/WhisperSettings.tsx` | der Reiter |
Abhängigkeit: `@huggingface/transformers` (^3.8.1).
## Störungssuche
| Symptom | Ursache |
| --- | --- |
| Kein Mikrofon-Knopf zu sehen | kein `MediaRecorder` oder keine sichere Verbindung (HTTPS) |
| „Kein Zugriff aufs Mikrofon" | Berechtigung im Browser verweigert |
| Erkennung sehr langsam | keine WebGPU – der Reiter sagt es ausdrücklich |
| Modell nach Neustart wieder weg | privates Fenster oder Speicherbeschränkung; der Reiter weist darauf hin |
| „Der Speicher reicht … nicht" | zu wenig Arbeitsspeicher für die ONNX-Sitzung |