mirror of
https://github.com/Jeuners/Logpy-AgentOne.git
synced 2026-09-09 15:02:33 +02:00
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
This commit is contained in:
parent
2e767f3737
commit
cc569d1ecb
2 changed files with 49 additions and 2 deletions
|
|
@ -28,7 +28,24 @@ def verarbeite(audio: str, anrufer_nummer: str | None = None,
|
|||
print(f" Text: {t.text[:160]}{'…' if len(t.text) > 160 else ''}")
|
||||
|
||||
print("▸ Kategorisiere …")
|
||||
if t.text.strip():
|
||||
auswertung = categorize.kategorisiere(t.text)
|
||||
else:
|
||||
# Kein Text erkannt: Anrufer hat nach dem Signalton nicht gesprochen
|
||||
# oder sofort aufgelegt (oder eine bekannte Whisper-Halluzination
|
||||
# wurde rausgefiltert, siehe transcribe._ist_bekannte_halluzination).
|
||||
# Das ist ein normaler, haeufiger Fall - kein Kategorisierungs-Fehler,
|
||||
# der die Aufnahme nach telefon/fehler/ verschwinden lassen sollte.
|
||||
print(" Kein Text erkannt - kein Anliegen hinterlassen.")
|
||||
auswertung = {
|
||||
"kategorie": "sonstiges", "dringlichkeit": "niedrig",
|
||||
"anrufer_name": None, "rueckrufnummer": None,
|
||||
"rueckruf_gewuenscht": False, "sprache": t.sprache or "de",
|
||||
"anliegen_kurz": "Kein Anliegen hinterlassen - Anrufer hat nach "
|
||||
"dem Signalton nicht gesprochen oder sofort "
|
||||
"aufgelegt.",
|
||||
"stichworte": [],
|
||||
}
|
||||
print(f" Kategorie: {auswertung['kategorie']} · Dringlichkeit: {auswertung['dringlichkeit']}")
|
||||
protokoll.schreibe(
|
||||
"notfall" if auswertung["dringlichkeit"] == "notfall" else "anruf",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from __future__ import annotations
|
|||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
from dataclasses import dataclass, field
|
||||
|
|
@ -30,6 +31,30 @@ class Transkript:
|
|||
segmente: list[dict] = field(default_factory=list)
|
||||
|
||||
|
||||
# Bekannte Whisper-Halluzinationen: bei Stille/Rauschen (z.B. Anrufer legt
|
||||
# gleich nach dem Signalton auf) erfindet Whisper haeufig auswendig gelernte
|
||||
# Untertitel-Textbausteine aus den Trainingsdaten, statt "kein Text" zu
|
||||
# liefern - "Untertitelung des ZDF, 2020" trat bei uns mehrfach auf identisch
|
||||
# kurzen (1,7-4s) Aufnahmen auf und wurde anschliessend von der Kategorisierung
|
||||
# als echtes Anliegen fehlinterpretiert. whisper.cpp's eingebauter
|
||||
# no-speech-Schwellwert (--no-speech-thold, Default 0.6) faengt das nicht
|
||||
# zuverlaessig ab. Statt einer echten VAD (zusaetzliches Modell noetig) reicht
|
||||
# hier ein Blocklist-Abgleich gegen die dokumentiert haeufigsten Phrasen.
|
||||
_BEKANNTE_HALLUZINATIONEN = [
|
||||
re.compile(p, re.IGNORECASE) for p in (
|
||||
r"untertitel(ung)?\s+(des|im auftrag des|von)\s+(zdf|wdr|ndr|swr|br|mdr|hr|rbb)",
|
||||
r"copyright\s+(wdr|zdf|ndr|swr|br)\s*\d{4}",
|
||||
r"untertitel\s+von\s+stephanie\s+geiges",
|
||||
r"vielen\s+dank\s+f(ü|u)rs?\s+zuschauen",
|
||||
r"amara\.org",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def _ist_bekannte_halluzination(text: str) -> bool:
|
||||
return any(m.search(text) for m in _BEKANNTE_HALLUZINATIONEN)
|
||||
|
||||
|
||||
def _letzte_zeile(ausgabe: str | None) -> str:
|
||||
"""Letzte nicht-leere Zeile einer Fehlerausgabe (für knappe Meldungen)."""
|
||||
zeilen = [z for z in (ausgabe or "").strip().splitlines() if z.strip()]
|
||||
|
|
@ -117,6 +142,8 @@ def _mit_whispercpp(audio: Path) -> Transkript:
|
|||
text_ganz = " ".join(s["text"] for s in segmente).strip()
|
||||
sprache = roh.get("result", {}).get("language", config.WHISPER_SPRACHE)
|
||||
dauer = segmente[-1]["ende"] if segmente else 0.0
|
||||
if _ist_bekannte_halluzination(text_ganz):
|
||||
text_ganz, segmente = "", []
|
||||
return Transkript(text=text_ganz, sprache=sprache,
|
||||
sprache_wahrscheinlichkeit=1.0, dauer=dauer, segmente=segmente)
|
||||
|
||||
|
|
@ -139,8 +166,11 @@ def _mit_faster_whisper(audio: Path) -> Transkript:
|
|||
if t:
|
||||
teile.append(t)
|
||||
seg_liste.append({"start": round(s.start, 2), "ende": round(s.end, 2), "text": t})
|
||||
text_ganz = " ".join(teile).strip()
|
||||
if _ist_bekannte_halluzination(text_ganz):
|
||||
text_ganz, seg_liste = "", []
|
||||
return Transkript(
|
||||
text=" ".join(teile).strip(), sprache=info.language,
|
||||
text=text_ganz, sprache=info.language,
|
||||
sprache_wahrscheinlichkeit=round(info.language_probability, 3),
|
||||
dauer=round(info.duration, 2), segmente=seg_liste,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue