This commit is contained in:
team3
2026-06-19 09:06:45 +02:00
parent bf6ab18ffd
commit 1fba9ba898
6 changed files with 113 additions and 19 deletions

View File

@@ -5,6 +5,7 @@ der Chat-/Prüfungs-Verlauf kommt vom Frontend, persistiert wird nur der
Prüfungs-Zähler (DB) und die Vertiefung (DB).
"""
import asyncio
import logging
import re
import uuid
@@ -113,6 +114,14 @@ def _frage_schema(data) -> dict | None:
return {"frage": frage} if frage else None
def _fragen_schema(data) -> list[str] | None:
"""{"fragen": [str, …]} → Liste nicht-leerer Fragen · sonst None."""
if not isinstance(data, dict) or not isinstance(data.get("fragen"), list):
return None
fragen = [str(f).strip() for f in data["fragen"] if str(f).strip()]
return fragen or None
def _bewertung_schema(data) -> dict | None:
"""{"feedback": str, "bewertung": "gut"|"schlecht", "bestanden": bool} · sonst None."""
if not isinstance(data, dict):
@@ -264,6 +273,56 @@ async def pruefung_frage(
return None
async def _frage_ok(topic, baustein, section_block, kompakt_block, transcript, vermeide_block, frage, provider) -> bool:
"""Eine geplante Frage prüfen: Doppelfrage-Guard + Kritiker. True = in Ordnung."""
if _doppelfrage_mangel(frage):
return False
probleme = await _kritik_call(
"Baustein-Frage-Kritik", provider, role="guide",
topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, transcript=transcript, vermeide_block=vermeide_block, frage=frage,
)
return not probleme
async def pruefung_fragen(
topic: str, baustein: str, section: str, kompakt: str | None,
messages: list[dict], vermeide: list[str] | None = None, anzahl: int = 5,
provider: str = DEFAULT_PROVIDER,
) -> list[str]:
"""Aktion 'fragen': EIN Planer entwirft `anzahl` verschiedene Fragen, parallel geprüft.
Diversität kommt vom Planer; der Kritiker prüft Qualität + Ähnlichkeit zu Gestelltem.
→ Liste gültiger Fragen (kann < anzahl sein)."""
try:
section_block, kompakt_block = _bloecke(section, kompakt)
transcript = _transcript(messages) if messages else "(leer)"
vermeide = [f for f in (vermeide or []) if f and f.strip()]
gesammelt: list[str] = []
for _ in range(2): # Plan + eine Nachrunde für Abgelehnte
brauche = anzahl - len(gesammelt)
if brauche <= 0:
break
vblock = _vermeide_block(vermeide + gesammelt)
kandidaten = await _gen_call(
"Baustein-Fragen-Plan", "fast", _fragen_schema, provider, CHAT_TIMEOUT,
topic=topic, baustein=baustein, section_block=section_block, kompakt_block=kompakt_block,
transcript=transcript, vermeide_block=vblock, anzahl=brauche,
)
if not kandidaten:
break
oks = await asyncio.gather(
*[_frage_ok(topic, baustein, section_block, kompakt_block, transcript, vblock, f, provider) for f in kandidaten],
return_exceptions=True,
)
for f, ok in zip(kandidaten, oks):
if ok is True and f not in gesammelt:
gesammelt.append(f)
return gesammelt[:anzahl]
except Exception:
log.warning("[%s] Fragen-Plan fehlgeschlagen (%s)", topic, baustein, exc_info=True)
return []
async def pruefung_bewertung(
topic: str, baustein: str, section: str, kompakt: str | None,
frage: str, messages: list[dict], gute_antworten: int, provider: str = DEFAULT_PROVIDER,