Commit graph

3 commits

Author SHA1 Message Date
Jeuner
cc569d1ecb fix: bekannte Whisper-Halluzinationen filtern statt als Anliegen zu werten
"Untertitelung des ZDF, 2020" (Whisper-Trainingsdaten-Artefakt bei Stille/
sofortigem Auflegen) tauchte 5x identisch auf sehr kurzen Aufnahmen (1,7-4s)
auf und wurde von der Kategorisierung als echtes Anliegen fehlinterpretiert.
transcribe.py erkennt jetzt eine kleine Liste bekannter Whisper-
Halluzinationsphrasen (ZDF/WDR/NDR/SWR-Untertitel-Boilerplate u.ae.) und
leert den Text stattdessen.

Dabei aufgefallen: categorize.kategorisiere() wirft bei leerem Transkript
absichtlich einen Fehler - das griff bisher nie, weil Whisper nie leer
zurueckkam. Ohne Fix waere jedes "Anrufer legt nach dem Signalton sofort
auf" (normales, haeufiges Verhalten) jetzt als Kategorisierungs-Fehler nach
telefon/fehler/ verschwunden statt auf dem Board zu erscheinen.
process_call.verarbeite() faengt das jetzt ab und legt einen regulaeren
"kein Anliegen hinterlassen"-Datensatz an (sonstiges/niedrig).

Die 5 bereits gespeicherten ZDF-Fehleintraege in ablage/ wurden manuell
nachkorrigiert (transkript/auswertung), Stapel-/Archiv-Status blieb
unangetastet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vtvcyNkapuDh583643hNs
2026-08-24 20:50:30 +02:00
Jeuner
3749d60aa4 fix: Watcher griff Aufnahmen mitten im Schreiben (Race Condition)
Der Fehler "[Errno 2] No such file or directory: '...stt_out_*.json'"
sah wie ein Transkriptions-Bug aus, war aber ein Timing-Problem:
_fertig() prüfte nur, ob die Dateigröße 2 Sekunden (RUHE_S) unveraendert
blieb - eine normale Sprechpause reicht dafuer schon aus, waehrend
FreeSWITCH den Anruf noch aktiv aufnimmt. Belegt im Verlauf-Log: die
Aufnahme wurde um 10:15:56 als "fertig" gegriffen, der Anrufer legte laut
Log aber erst um 10:16:01 auf.

ffmpeg/whisper-cli lasen dabei einen mitten im Schreiben befindlichen
RIFF-Header, produzierten keine Ausgabe, und process_call brach mit einem
FileNotFoundError auf die erwartete JSON-Datei ab. Die Original-Audiodatei
selbst blieb dabei unbeschaedigt (FreeSWITCH schrieb einfach in die schon
nach telefon/fehler/ verschobene Datei weiter) - deshalb sah eine manuelle
Nachpruefung der "kaputten" Datei immer intakt aus und liess sich nicht
reproduzieren.

_fertig() prueft jetzt zusaetzlich per `lsof -t`, ob noch ein Prozess die
Datei offen haelt - nur wenn weder die Groesse waechst noch ein Handle
offen ist, gilt die Aufnahme als abgeschlossen.

Nebenbei: tempfile.mkstemp() in transcribe.py gab einen offenen File-
Descriptor zurueck, der nie geschlossen wurde (fd-Leak in beiden
Tempdatei-Erzeugungen) - behoben.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PuP3zfgYqhnrwyEne2ejG7
2026-08-23 10:25:22 +02:00
Jeuner
1a1280e79d Praxis-Telefon-Agent: Anrufannahme, Transkription, Triage — vollständig on-prem
Nimmt Anrufe einer Hausarztpraxis entgegen, transkribiert sie lokal,
kategorisiert das Anliegen und legt es strukturiert ab. Keine Patientendaten
verlassen den Rechner: Spracherkennung, Sprachmodell und Sprachsynthese laufen
lokal, im Datenpfad steht kein Cloud-Dienst.

Aufbau:
- FreeSWITCH nimmt über einen SIP-Trunk an, spielt die Ansage und nimmt auf.
- pipe.watch erkennt fertige Aufnahmen und schiebt sie durch die Pipe.
- whisper.cpp transkribiert, ein lokales Sprachmodell (Ollama) ordnet in
  Kategorie und Dringlichkeit ein — mit erzwungenem JSON-Schema.
- Ablage lokal, optional Nextcloud (WebDAV) und ein Deck-Board.
- Leitstand (pipe.server): Board mit Eingang/In Bearbeitung/Rückfragen/Erledigt,
  Aufnahmen zum Anhören, Verlauf und Ampeln für alle beteiligten Dienste.

Zugangsdaten und Anrufdaten liegen außerhalb des Repositorys (.env, ablage/,
telefon/). Konfiguration siehe .env.example, Einrichtung siehe README.
2026-08-22 22:37:17 +02:00