update
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -218,6 +218,7 @@ class BausteinPruefungRequest(BaseModel):
|
||||
frage: str = Field(default="", max_length=2000) # aktuell geprüfte Frage (für diskussion/antwort); Anker der Basis
|
||||
letzte_bewertung: str = Field(default="", max_length=2000) # Feedback der letzten Bewertung (Kontext für diskussion)
|
||||
vermeide: list[str] = [] # schon gestellte + vorgemerkte Fragen — sinngemäß nicht wiederholen
|
||||
anzahl: int = 5 # aktion 'fragen': wie viele neue Fragen der Planer entwerfen soll
|
||||
# Basis/Streak werden serverseitig je Frage geführt (offene_frage-Anker) — nicht mehr vom Client.
|
||||
tier2: bool = False # ganzer Guide absolviert (alle ≥3) → −1 bei falsch, Deckel 10
|
||||
tier3: bool = False # ganzer Guide verstanden (alle ≥10) → Meisterpfad, −2 bei falsch, Deckel 25
|
||||
@@ -228,6 +229,7 @@ class BausteinPruefungRequest(BaseModel):
|
||||
|
||||
class BausteinPruefungResponse(BaseModel):
|
||||
frage: str | None = None
|
||||
fragen: list[str] | None = None # aktion 'fragen': geplante, geprüfte Fragen
|
||||
reply: str | None = None
|
||||
feedback: str | None = None
|
||||
bewertung: Literal["gut", "schlecht"] | None = None
|
||||
|
||||
@@ -18,7 +18,7 @@ from database import (
|
||||
)
|
||||
from bausteine import generate_bausteine, cancel_bausteine, bausteine_status, active_bausteine, reset_bausteine, lade_quelle, lade_uebersicht
|
||||
from elements import generate_element, chat_with_guide, chat_with_element, check_element, style_element, refine_suggestion
|
||||
from lernen import NOETIG, MASTERY, MEISTERN, baustein_chat, baustein_diskussion, baustein_element_anlegen, pruefung_bewertung, pruefung_bewertung_schnell, pruefung_frage, score_berechnen, streak_berechnen
|
||||
from lernen import NOETIG, MASTERY, MEISTERN, baustein_chat, baustein_diskussion, baustein_element_anlegen, pruefung_bewertung, pruefung_bewertung_schnell, pruefung_frage, pruefung_fragen, score_berechnen, streak_berechnen
|
||||
from guide import generate_guide, guide_slot_dateien
|
||||
from pipeline import cancel_guide
|
||||
from regeln import FORMATE, formate_stats, guide_lock, ist_absolviert, lade_lernstand, thema_abgeschlossen
|
||||
@@ -273,6 +273,10 @@ async def baustein_pruefung_route(req: BausteinPruefungRequest):
|
||||
raise HTTPException(502, "Frage fehlgeschlagen — bitte erneut versuchen")
|
||||
return {"frage": frage, "gute_antworten": gute, "absolviert": absolviert, "verstanden": verstanden, "gemeistert": gemeistert}
|
||||
|
||||
if req.aktion == "fragen":
|
||||
fragen = await pruefung_fragen(req.topic, req.baustein, req.section, kompakt, msgs, vermeide=req.vermeide, anzahl=req.anzahl, provider=req.provider)
|
||||
return {"fragen": fragen, "gute_antworten": gute, "absolviert": absolviert, "verstanden": verstanden, "gemeistert": gemeistert}
|
||||
|
||||
if req.aktion == "diskussion":
|
||||
if not req.frage.strip():
|
||||
raise HTTPException(400, "Diskussion braucht eine laufende Frage")
|
||||
|
||||
Reference in New Issue
Block a user