feat: Bewertungssystem (👍/👎) als Trainingsdaten-Export

Jede Karte im Leitstand bekommt Daumen hoch/runter fuer die Kategorisierung.
Bei 👎 optional Korrektur (richtige Kategorie/Dringlichkeit). Gespeichert
als bewertung.json pro Anruf-Ordner (gleiches Muster wie stapel.json,
pipe/bewertung.py).

Export als JSONL (pipe/export.py, neuer "📤 Bewertungen exportieren"-Knopf
+ automatisch beim "Tag archivieren"): ein Datensatz pro bewertetem Anruf
mit Transkript, Modell-Ausgabe, Backend/Modell, Bewertung und Korrektur -
fuer externe Auswertung (z.B. um Kategorisierungs-Fehler systematisch statt
zufaellig zu finden, wie das Notfall-Sicherheitsnetz vom letzten Fund).

Export-Knopf verlangt Bestaetigung ("Transkripte und Namen verlassen den
Rechner") - bewusst kein automatischer Export ohne Hinweis, das steht sonst
im Widerspruch zum On-Prem-Versprechen. training/ ist neu gitignored (volle
Transkripte/Namen drin, siehe README-Hinweis zu Testdaten).

Nebenbei: process_call.py legt jetzt auch backend/modell/dauer_s der
Kategorisierung dauerhaft in meta.json ab (vorher nur im fluechtigen Log) -
noetig, damit der Export nachvollziehbar ist, welches Modell lief.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vtvcyNkapuDh583643hNs
This commit is contained in:
Jeuner 2026-08-24 23:47:20 +02:00
parent 7e4505d9b7
commit 90548944a2
5 changed files with 219 additions and 7 deletions

40
pipe/bewertung.py Normal file
View file

@ -0,0 +1,40 @@
"""Bewertung eines Anrufs durch das Praxispersonal (👍/👎 im Leitstand).
Dient gleichzeitig als Trainingsdaten fuer die Modellverbesserung - siehe
pipe.export fuer den Export als JSONL. Gleiches Muster wie stapel.py: eine
kleine JSON-Datei pro Anruf-Ordner, unabhaengig von meta.json (das aendert
sich nach der Ablage nie mehr, die Bewertung schon).
"""
from __future__ import annotations
import json
from datetime import datetime
from pathlib import Path
DATEI = "bewertung.json"
def lies(ordner: Path) -> dict | None:
"""Bewertung eines Anrufs, oder None wenn (noch) unbewertet."""
pfad = ordner / DATEI
if not pfad.is_file():
return None
try:
return json.loads(pfad.read_text(encoding="utf-8"))
except Exception:
return None
def setze(ordner: Path, bewertung: str, korrektur: dict | None = None) -> bool:
"""Speichert eine Bewertung. False bei ungueltigem Wert."""
if bewertung not in {"gut", "schlecht"}:
return False
try:
(ordner / DATEI).write_text(json.dumps({
"bewertung": bewertung,
"korrektur": korrektur,
"zeitpunkt": datetime.now().isoformat(timespec="seconds"),
}, ensure_ascii=False), encoding="utf-8")
return True
except Exception:
return False