This commit is contained in:
team3
2026-06-15 08:46:28 +02:00
parent 25a07ede4d
commit 33a4440404
6 changed files with 39 additions and 75 deletions

View File

@@ -26,7 +26,6 @@ VERTIEFUNG_TIMEOUT = 600
CHAT_TIMEOUT = 240
PRUEFUNG_TIMEOUT = 120 # kurze JSON-Turns; deckelt die Serien-Latenz pro Prüfungs-Schritt
KRITIK_MAX_RUNDEN = 2 # Generator → Kritiker → ggf. Neu, höchstens so oft
MAX_NACHFRAGEN = 2 # mündliche Prüfung: höchstens so viele Folgefragen, dann Urteil erzwingen
def score_berechnen(
@@ -115,20 +114,14 @@ def _frage_schema(data) -> dict | None:
def _bewertung_schema(data) -> dict | None:
"""{"status": "gut"|"schlecht"|"nachfrage", "feedback": str, "frage": str, "bestanden": bool}.
Bei status "nachfrage" muss `frage` (die Folgefrage) gefüllt sein. · sonst None.
"""
"""{"feedback": str, "bewertung": "gut"|"schlecht", "bestanden": bool} · sonst None."""
if not isinstance(data, dict):
return None
feedback = str(data.get("feedback", "")).strip()
status = data.get("status")
frage = str(data.get("frage", "")).strip()
if not feedback or status not in ("gut", "schlecht", "nachfrage"):
bewertung = data.get("bewertung")
if not feedback or bewertung not in ("gut", "schlecht"):
return None
if status == "nachfrage" and not frage:
return None
return {"status": status, "feedback": feedback, "frage": frage, "bestanden": data.get("bestanden") is True}
return {"feedback": feedback, "bewertung": bewertung, "bestanden": data.get("bestanden") is True}
async def _gen_call(name: str, role: str, schema, provider: str, **kwargs) -> dict | None:
@@ -161,7 +154,7 @@ def _kritik_block(vorversion: str, probleme: list[str]) -> str:
def _bewertung_text(bew: dict) -> str:
return f"Bewertung: {bew['status']}\nFeedback: {bew['feedback']}"
return f"Bewertung: {bew['bewertung']}\nFeedback: {bew['feedback']}"
async def _frage_mit_kritik(
@@ -193,14 +186,12 @@ async def _frage_mit_kritik(
async def _bewertung_mit_kritik(
topic: str, baustein: str, section_block: str, vertiefung_block: str,
frage: str, transcript: str, gute_antworten: int, rest_nachfragen: int, provider: str,
frage: str, transcript: str, gute_antworten: int, provider: str,
) -> dict | None:
"""Antwort beurteilen: gut/schlecht (mit Kritiker) ODER nachfrage (Folgefrage, ohne Kritiker).
"""Antwort bewerten (gut/schlecht), vom Kritiker prüfen lassen, bei Fehlurteil neu.
`frage` ankert die geprüfte Frage; der Dialog liefert Antwort + etwaige Folgefragen.
`rest_nachfragen` = wie viele Folgefragen noch erlaubt sind (0 → muss entscheiden).
Eine „nachfrage" wird sofort zurückgegeben (kein Verdikt zu prüfen). Verdikte
durchlaufen den Kritiker-Loop wie bisher.
`frage` ankert die geprüfte Frage; der Dialog (transcript) liefert die Antwort und
eine etwaige Diskussion — so kann eine Re-Bewertung das Argument sehen.
"""
kritik_block = "(keine)"
bew = None
@@ -209,13 +200,10 @@ async def _bewertung_mit_kritik(
"Baustein-Bewertung", "judge", _bewertung_schema, provider,
topic=topic, baustein=baustein, section_block=section_block,
vertiefung_block=vertiefung_block, frage=frage, transcript=transcript,
gute_antworten=gute_antworten, noetig=NOETIG, rest_nachfragen=rest_nachfragen,
kritik_block=kritik_block,
gute_antworten=gute_antworten, noetig=NOETIG, kritik_block=kritik_block,
)
if bew is None:
return None
if bew["status"] == "nachfrage":
return bew # Folgefrage → kein Kritiker, keine Wertung
probleme = await _kritik_call(
"Baustein-Bewertung-Kritik", provider,
topic=topic, baustein=baustein, section_block=section_block,
@@ -251,26 +239,19 @@ async def pruefung_frage(
async def pruefung_bewertung(
topic: str, baustein: str, section: str, vertiefung: str | None,
frage: str, messages: list[dict], gute_antworten: int, nachfrage_runde: int = 0,
provider: str = DEFAULT_PROVIDER,
frage: str, messages: list[dict], gute_antworten: int, provider: str = DEFAULT_PROVIDER,
) -> dict | None:
"""Aktion 'antwort': Antwort beurteilen (Evaluator + Kritiker).
"""Aktion 'antwort': Antwort bewerten (Evaluator + Kritiker).
Gibt {"status": gut|schlecht|nachfrage, "feedback", "frage", "bestanden"} · None bei Fehler.
`nachfrage_runde` = bisherige Folgefragen dieser Frage; bei erschöpftem Budget wird
ein erneutes „nachfrage" zu „schlecht" gezwungen (der Lerner konnte es nicht zeigen).
Gibt {"feedback", "bewertung": gut|schlecht, "bestanden"} · None bei Fehler.
"""
try:
section_block, vertiefung_block = _bloecke(section, vertiefung)
transcript = _transcript(messages) if messages else "(leer)"
rest = max(0, MAX_NACHFRAGEN - nachfrage_runde)
bew = await _bewertung_mit_kritik(
return await _bewertung_mit_kritik(
topic, baustein, section_block, vertiefung_block,
frage.strip() or "(keine Frage übergeben)", transcript, gute_antworten, rest, provider,
frage.strip() or "(keine Frage übergeben)", transcript, gute_antworten, provider,
)
if bew and bew["status"] == "nachfrage" and rest <= 0:
return {"status": "schlecht", "feedback": bew["feedback"], "frage": "", "bestanden": False}
return bew
except Exception:
log.warning("[%s] Bewertung fehlgeschlagen (%s)", topic, baustein, exc_info=True)
return None