This commit is contained in:
team3
2026-06-18 16:45:33 +02:00
parent 9c0f622e0c
commit d067cb8b54
27 changed files with 329 additions and 327 deletions

View File

@@ -23,7 +23,6 @@ log = logging.getLogger("creator.lernen")
NOETIG = 3 # gute Antworten bis "absolviert" (Tier 1)
MASTERY = 10 # Score bis "verstanden" (Tier 2)
MEISTERN = 25 # Score bis "gemeistert" (Tier 3, Maximum)
VERTIEFUNG_TIMEOUT = 600
CHAT_TIMEOUT = 240
PRUEFUNG_TIMEOUT = 120 # kurze JSON-Turns; deckelt die Serien-Latenz pro Prüfungs-Schritt
GRUENDLICH_TIMEOUT = 600 # „Gründlich prüfen": starkes Modell (role guide) braucht länger
@@ -84,37 +83,13 @@ def _transcript(messages: list[dict]) -> str:
) or "(leer)"
async def vertiefung_generieren(topic: str, baustein: str, section: str, art: str = "vertiefung", provider: str = DEFAULT_PROVIDER) -> str | None:
"""Ausführlichere Fassung des Bausteins als Markdown · None bei Fehler.
art "vertiefung" = gleicher Stoff, nur umfangreicher;
"deepdive" = Label „Amateur": gleicher Stoff, für Einsteiger aufbereitet.
"""
try:
prompt = _prompt(
"Baustein-Deepdive" if art == "deepdive" else "Baustein-Vertiefung",
topic=topic, baustein=baustein,
section_block=section.strip() or "(keine Guide-Fassung übergeben)",
)
returncode, stdout, _ = await run_agent(
"vertiefung-" + str(uuid.uuid4()), prompt, VERTIEFUNG_TIMEOUT,
provider=provider, role="fast", capabilities="none", lane="interactive",
)
if returncode != 0:
return None
return stdout.strip() or None
except Exception:
log.warning("[%s] Vertiefung fehlgeschlagen (%s)", topic, baustein, exc_info=True)
return None
async def baustein_chat(topic: str, baustein: str, section: str, vertiefung: str | None, messages: list[dict], provider: str = DEFAULT_PROVIDER) -> str:
async def baustein_chat(topic: str, baustein: str, section: str, kompakt: str | None, messages: list[dict], provider: str = DEFAULT_PROVIDER) -> str:
try:
prompt = _prompt(
"Baustein-Chat",
topic=topic, baustein=baustein,
section_block=section.strip() or "(keine Guide-Fassung übergeben)",
vertiefung_block=(vertiefung or "").strip() or "(keine)",
kompakt_block=(kompakt or "").strip() or "(keine)",
transcript=_transcript(messages),
)
returncode, stdout, _ = await run_agent(
@@ -197,7 +172,7 @@ def _doppelfrage_mangel(frage: str) -> str | None:
async def _frage_mit_kritik(
topic: str, baustein: str, section_block: str, vertiefung_block: str,
topic: str, baustein: str, section_block: str, kompakt_block: str,
transcript: str, provider: str,
) -> str | None:
"""Frage generieren, vom Kritiker prüfen lassen, bei Mängeln neu (max KRITIK_MAX_RUNDEN)."""
@@ -207,7 +182,7 @@ async def _frage_mit_kritik(
data = await _gen_call(
"Baustein-Frage", "fast", _frage_schema, provider,
topic=topic, baustein=baustein, section_block=section_block,
vertiefung_block=vertiefung_block, transcript=transcript, kritik_block=kritik_block,
kompakt_block=kompakt_block, transcript=transcript, kritik_block=kritik_block,
)
if data is None:
return None
@@ -215,7 +190,7 @@ async def _frage_mit_kritik(
probleme = await _kritik_call(
"Baustein-Frage-Kritik", provider, role="guide", # starke KI prüft die Regeln
topic=topic, baustein=baustein, section_block=section_block,
vertiefung_block=vertiefung_block, transcript=transcript, frage=frage,
kompakt_block=kompakt_block, transcript=transcript, frage=frage,
)
hart = _doppelfrage_mangel(frage) # erzwingt Neugenerierung, auch wenn der KI-Kritiker es übersah
if hart:
@@ -227,7 +202,7 @@ async def _frage_mit_kritik(
async def _bewertung_mit_kritik(
topic: str, baustein: str, section_block: str, vertiefung_block: str,
topic: str, baustein: str, section_block: str, kompakt_block: str,
frage: str, transcript: str, gute_antworten: int, provider: str, role: str = "judge",
) -> dict | None:
"""Antwort bewerten (gut/schlecht), vom Kritiker prüfen lassen, bei Fehlurteil neu.
@@ -243,7 +218,7 @@ async def _bewertung_mit_kritik(
bew = await _gen_call(
"Baustein-Bewertung", role, _bewertung_schema, provider, timeout,
topic=topic, baustein=baustein, section_block=section_block,
vertiefung_block=vertiefung_block, frage=frage, transcript=transcript,
kompakt_block=kompakt_block, frage=frage, transcript=transcript,
gute_antworten=gute_antworten, noetig=NOETIG, kritik_block=kritik_block,
)
if bew is None:
@@ -251,7 +226,7 @@ async def _bewertung_mit_kritik(
probleme = await _kritik_call(
"Baustein-Bewertung-Kritik", provider, role=role, timeout=timeout,
topic=topic, baustein=baustein, section_block=section_block,
vertiefung_block=vertiefung_block, frage=frage, transcript=transcript,
kompakt_block=kompakt_block, frage=frage, transcript=transcript,
bewertung_block=_bewertung_text(bew),
)
if not probleme:
@@ -260,29 +235,29 @@ async def _bewertung_mit_kritik(
return bew # best-effort nach der letzten Runde
def _bloecke(section: str, vertiefung: str | None) -> tuple[str, str]:
def _bloecke(section: str, kompakt: str | None) -> tuple[str, str]:
return (
section.strip() or "(keine Guide-Fassung übergeben)",
(vertiefung or "").strip() or "(keine)",
(kompakt or "").strip() or "(keine)",
)
async def pruefung_frage(
topic: str, baustein: str, section: str, vertiefung: str | None,
topic: str, baustein: str, section: str, kompakt: str | None,
messages: list[dict], provider: str = DEFAULT_PROVIDER,
) -> str | None:
"""Aktion 'frage': nächste Frage generieren (Generator + Kritiker) · None bei Fehler."""
try:
section_block, vertiefung_block = _bloecke(section, vertiefung)
section_block, kompakt_block = _bloecke(section, kompakt)
transcript = _transcript(messages) if messages else "(leer)"
return await _frage_mit_kritik(topic, baustein, section_block, vertiefung_block, transcript, provider)
return await _frage_mit_kritik(topic, baustein, section_block, kompakt_block, transcript, provider)
except Exception:
log.warning("[%s] Frage fehlgeschlagen (%s)", topic, baustein, exc_info=True)
return None
async def pruefung_bewertung(
topic: str, baustein: str, section: str, vertiefung: str | None,
topic: str, baustein: str, section: str, kompakt: str | None,
frage: str, messages: list[dict], gute_antworten: int, provider: str = DEFAULT_PROVIDER,
role: str = "judge",
) -> dict | None:
@@ -292,10 +267,10 @@ async def pruefung_bewertung(
Gibt {"feedback", "bewertung": gut|schlecht, "bestanden"} · None bei Fehler.
"""
try:
section_block, vertiefung_block = _bloecke(section, vertiefung)
section_block, kompakt_block = _bloecke(section, kompakt)
transcript = _transcript(messages) if messages else "(leer)"
return await _bewertung_mit_kritik(
topic, baustein, section_block, vertiefung_block,
topic, baustein, section_block, kompakt_block,
frage.strip() or "(keine Frage übergeben)", transcript, gute_antworten, provider, role,
)
except Exception:
@@ -304,7 +279,7 @@ async def pruefung_bewertung(
async def pruefung_bewertung_schnell(
topic: str, baustein: str, section: str, vertiefung: str | None,
topic: str, baustein: str, section: str, kompakt: str | None,
frage: str, messages: list[dict], gute_antworten: int, provider: str = DEFAULT_PROVIDER,
) -> dict | None:
"""Aktion 'antwort' (Vorschau): nur Evaluator, KEIN Kritiker — sofortiges Urteil.
@@ -312,12 +287,12 @@ async def pruefung_bewertung_schnell(
Wird optimistisch angezeigt; 'antwort_pruefen' liefert danach das geprüfte Urteil.
"""
try:
section_block, vertiefung_block = _bloecke(section, vertiefung)
section_block, kompakt_block = _bloecke(section, kompakt)
transcript = _transcript(messages) if messages else "(leer)"
return await _gen_call(
"Baustein-Bewertung", "judge", _bewertung_schema, provider,
topic=topic, baustein=baustein, section_block=section_block,
vertiefung_block=vertiefung_block, frage=frage.strip() or "(keine Frage übergeben)",
kompakt_block=kompakt_block, frage=frage.strip() or "(keine Frage übergeben)",
transcript=transcript, gute_antworten=gute_antworten, noetig=NOETIG, kritik_block="(keine)",
)
except Exception:
@@ -326,7 +301,7 @@ async def pruefung_bewertung_schnell(
async def baustein_diskussion(
topic: str, baustein: str, section: str, vertiefung: str | None,
topic: str, baustein: str, section: str, kompakt: str | None,
frage: str, letzte_bewertung: str | None, messages: list[dict], provider: str = DEFAULT_PROVIDER,
) -> str | None:
"""Aktion 'diskussion': Tutor erklärt/diskutiert die Frage oder eine Bewertung.
@@ -334,11 +309,11 @@ async def baustein_diskussion(
Kein Bewerten, kein Kritiker — hier ist der Mensch der Prüfer. None bei Fehler.
"""
try:
section_block, vertiefung_block = _bloecke(section, vertiefung)
section_block, kompakt_block = _bloecke(section, kompakt)
prompt = _prompt(
"Baustein-Pruefung-Diskussion",
topic=topic, baustein=baustein,
section_block=section_block, vertiefung_block=vertiefung_block,
section_block=section_block, kompakt_block=kompakt_block,
frage=frage.strip() or "(keine Frage übergeben)",
letzte_bewertung_block=(letzte_bewertung or "").strip() or "(noch keine)",
transcript=_transcript(messages) if messages else "(leer)",