update
This commit is contained in:
@@ -29,7 +29,7 @@ KRITIK_MAX_RUNDEN = 2 # Generator → Kritiker → ggf. Neu, höchstens so oft
|
|||||||
|
|
||||||
|
|
||||||
def score_berechnen(
|
def score_berechnen(
|
||||||
score_vor_frage: int, gut: bool, tier2: bool, tier3: bool, absolviert: bool, gemeistert: bool,
|
score_vor_frage: int, gut: bool, streak: int, tier2: bool, tier3: bool, absolviert: bool, gemeistert: bool,
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Neuer Score nach einer Antwort · driftfrei (immer aus dem Basis-Score gerechnet).
|
"""Neuer Score nach einer Antwort · driftfrei (immer aus dem Basis-Score gerechnet).
|
||||||
|
|
||||||
@@ -37,18 +37,24 @@ def score_berechnen(
|
|||||||
- Tier 1 (tier2=False): +1 bei richtig, KEINE Strafe, Deckel NOETIG (3).
|
- Tier 1 (tier2=False): +1 bei richtig, KEINE Strafe, Deckel NOETIG (3).
|
||||||
- Tier 2 (tier2, nicht tier3): +1 / −1, Boden 3, Deckel MASTERY (10).
|
- Tier 2 (tier2, nicht tier3): +1 / −1, Boden 3, Deckel MASTERY (10).
|
||||||
- Tier 3 (tier3, Meisterpfad): +1 / −2, Boden 10, Deckel MEISTERN (25).
|
- Tier 3 (tier3, Meisterpfad): +1 / −2, Boden 10, Deckel MEISTERN (25).
|
||||||
Boden vor dem Absolvieren ist 0 (sonst NOETIG — absolviert bleibt erhalten).
|
Streak-Bonus bei richtig: ab 3 in Folge +2 (Tier 2/3), ab 5 in Folge +3 (Tier 3).
|
||||||
Ist der Baustein gemeistert, friert der Score bei MEISTERN ein (keine Punkte mehr).
|
`streak` = bisherige Folge VOR dieser Antwort; mit dieser Antwort zählt streak+1.
|
||||||
Re-Bewertung nutzt denselben Basis-Score und ersetzt das vorige Ergebnis.
|
Boden vor dem Absolvieren ist 0; gemeistert friert bei MEISTERN ein.
|
||||||
|
Re-Bewertung nutzt denselben Basis-Score + dieselbe Streak (kein Doppelzählen).
|
||||||
"""
|
"""
|
||||||
if gemeistert:
|
if gemeistert:
|
||||||
return MEISTERN
|
return MEISTERN
|
||||||
if not tier2:
|
if not tier2:
|
||||||
delta, floor, cap = (1 if gut else 0), (NOETIG if absolviert else 0), NOETIG
|
floor, cap, strafe = (NOETIG if absolviert else 0), NOETIG, 0
|
||||||
elif not tier3:
|
elif not tier3:
|
||||||
delta, floor, cap = (1 if gut else -1), NOETIG, MASTERY
|
floor, cap, strafe = NOETIG, MASTERY, -1
|
||||||
else:
|
else:
|
||||||
delta, floor, cap = (1 if gut else -2), MASTERY, MEISTERN
|
floor, cap, strafe = MASTERY, MEISTERN, -2
|
||||||
|
if gut:
|
||||||
|
s = streak + 1
|
||||||
|
delta = 3 if (tier3 and s >= 5) else (2 if (tier2 and s >= 3) else 1)
|
||||||
|
else:
|
||||||
|
delta = strafe
|
||||||
return max(floor, min(cap, score_vor_frage + delta))
|
return max(floor, min(cap, score_vor_frage + delta))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ class BausteinPruefungRequest(BaseModel):
|
|||||||
frage: str = Field(default="", max_length=2000) # aktuell geprüfte Frage (für diskussion/antwort)
|
frage: str = Field(default="", max_length=2000) # aktuell geprüfte Frage (für diskussion/antwort)
|
||||||
letzte_bewertung: str = Field(default="", max_length=2000) # Feedback der letzten Bewertung (Kontext für diskussion)
|
letzte_bewertung: str = Field(default="", max_length=2000) # Feedback der letzten Bewertung (Kontext für diskussion)
|
||||||
score_vor_frage: int = 0 # Score, als die Frage gestellt wurde → driftfreies (Re-)Bewerten
|
score_vor_frage: int = 0 # Score, als die Frage gestellt wurde → driftfreies (Re-)Bewerten
|
||||||
|
streak: int = 0 # Folge korrekter Antworten VOR dieser Frage (Streak-Bonus)
|
||||||
tier2: bool = False # ganzer Guide absolviert (alle ≥3) → −1 bei falsch, Deckel 10
|
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
|
tier3: bool = False # ganzer Guide verstanden (alle ≥10) → Meisterpfad, −2 bei falsch, Deckel 25
|
||||||
messages: list[ChatMessage] = [] # Dialog bisher; leer = erste Frage
|
messages: list[ChatMessage] = [] # Dialog bisher; leer = erste Frage
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ async def baustein_pruefung_route(req: BausteinPruefungRequest):
|
|||||||
)
|
)
|
||||||
if data is None:
|
if data is None:
|
||||||
raise HTTPException(502, "Bewertung fehlgeschlagen — bitte erneut versuchen")
|
raise HTTPException(502, "Bewertung fehlgeschlagen — bitte erneut versuchen")
|
||||||
score = score_berechnen(req.score_vor_frage, data["bewertung"] == "gut", req.tier2, req.tier3, absolviert, gemeistert)
|
score = score_berechnen(req.score_vor_frage, data["bewertung"] == "gut", req.streak, req.tier2, req.tier3, absolviert, gemeistert)
|
||||||
return {"feedback": data["feedback"], "bewertung": data["bewertung"], "gute_antworten": score,
|
return {"feedback": data["feedback"], "bewertung": data["bewertung"], "gute_antworten": score,
|
||||||
"absolviert": absolviert, "verstanden": verstanden, "gemeistert": gemeistert}
|
"absolviert": absolviert, "verstanden": verstanden, "gemeistert": gemeistert}
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ async def baustein_pruefung_route(req: BausteinPruefungRequest):
|
|||||||
|
|
||||||
# Score driftfrei aus dem Basis-Score rechnen (Re-Bewertung ersetzt das vorige Ergebnis).
|
# Score driftfrei aus dem Basis-Score rechnen (Re-Bewertung ersetzt das vorige Ergebnis).
|
||||||
score = score_berechnen(
|
score = score_berechnen(
|
||||||
req.score_vor_frage, data["bewertung"] == "gut", req.tier2, req.tier3, absolviert, gemeistert,
|
req.score_vor_frage, data["bewertung"] == "gut", req.streak, req.tier2, req.tier3, absolviert, gemeistert,
|
||||||
)
|
)
|
||||||
gute = await set_baustein_score(req.topic, req.baustein, score)
|
gute = await set_baustein_score(req.topic, req.baustein, score)
|
||||||
if score >= NOETIG and not absolviert:
|
if score >= NOETIG and not absolviert:
|
||||||
|
|||||||
@@ -93,12 +93,13 @@ export async function chatBaustein({ topic, baustein, section, messages, provide
|
|||||||
|
|
||||||
export async function pruefeBaustein({
|
export async function pruefeBaustein({
|
||||||
topic, baustein, section, provider,
|
topic, baustein, section, provider,
|
||||||
aktion = 'frage', frage = '', letzte_bewertung = '', score_vor_frage = 0, tier2 = false, messages = [],
|
aktion = 'frage', frage = '', letzte_bewertung = '', score_vor_frage = 0, streak = 0,
|
||||||
|
tier2 = false, tier3 = false, messages = [],
|
||||||
}) {
|
}) {
|
||||||
const res = await fetch(`${BASE}/bausteine/pruefung`, {
|
const res = await fetch(`${BASE}/bausteine/pruefung`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ topic, baustein, section, aktion, frage, letzte_bewertung, score_vor_frage, tier2, messages, provider }),
|
body: JSON.stringify({ topic, baustein, section, aktion, frage, letzte_bewertung, score_vor_frage, streak, tier2, tier3, messages, provider }),
|
||||||
})
|
})
|
||||||
return jsonOrThrow(res)
|
return jsonOrThrow(res)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ const pruefLoading = ref(false)
|
|||||||
const aktuelleFrage = ref('') // ankert Bewertung/Diskussion
|
const aktuelleFrage = ref('') // ankert Bewertung/Diskussion
|
||||||
const letztesFeedback = ref('') // Kontext für die Diskussion über eine Bewertung
|
const letztesFeedback = ref('') // Kontext für die Diskussion über eine Bewertung
|
||||||
const scoreVorFrage = ref(0) // Score, als die aktuelle Frage gestellt wurde → driftfreies (Re-)Bewerten
|
const scoreVorFrage = ref(0) // Score, als die aktuelle Frage gestellt wurde → driftfreies (Re-)Bewerten
|
||||||
|
const streak = ref(0) // aktuelle Folge korrekter Antworten im Baustein
|
||||||
|
const streakVorFrage = ref(0) // Streak, als die aktuelle Frage gestellt wurde (driftfrei)
|
||||||
const naechsteFrage = ref(null) // im Hintergrund vorbereitete nächste Frage (Prefetch)
|
const naechsteFrage = ref(null) // im Hintergrund vorbereitete nächste Frage (Prefetch)
|
||||||
let prefetchPromise = null // laufender Hintergrund-Abruf (verhindert Doppel-Prefetch)
|
let prefetchPromise = null // laufender Hintergrund-Abruf (verhindert Doppel-Prefetch)
|
||||||
const pruefMessagesEl = ref(null)
|
const pruefMessagesEl = ref(null)
|
||||||
@@ -134,6 +136,7 @@ function frageZeigen(text) {
|
|||||||
aktuelleFrage.value = text
|
aktuelleFrage.value = text
|
||||||
letztesFeedback.value = ''
|
letztesFeedback.value = ''
|
||||||
scoreVorFrage.value = st.value.gute_antworten
|
scoreVorFrage.value = st.value.gute_antworten
|
||||||
|
streakVorFrage.value = streak.value
|
||||||
pruefMessages.value.push({ role: 'assistant', kind: 'frage', content: text })
|
pruefMessages.value.push({ role: 'assistant', kind: 'frage', content: text })
|
||||||
pruefPhase.value = 'frage_offen'
|
pruefPhase.value = 'frage_offen'
|
||||||
}
|
}
|
||||||
@@ -237,7 +240,7 @@ function bewerten(res) {
|
|||||||
function antwortPayload() {
|
function antwortPayload() {
|
||||||
return {
|
return {
|
||||||
aktion: 'antwort', frage: aktuelleFrage.value, score_vor_frage: scoreVorFrage.value,
|
aktion: 'antwort', frage: aktuelleFrage.value, score_vor_frage: scoreVorFrage.value,
|
||||||
tier2: props.tier2, tier3: props.tier3,
|
streak: streakVorFrage.value, tier2: props.tier2, tier3: props.tier3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,12 +248,18 @@ function antwortPayload() {
|
|||||||
async function verdictPruefen() {
|
async function verdictPruefen() {
|
||||||
const meine = ++pruefenRun
|
const meine = ++pruefenRun
|
||||||
const ziel = letzteFeedbackMsg
|
const ziel = letzteFeedbackMsg
|
||||||
|
const warAbsolviert = st.value.absolviert
|
||||||
|
const warVerstanden = st.value.verstanden
|
||||||
try {
|
try {
|
||||||
const res = await pruefeBaustein({
|
const res = await pruefeBaustein({
|
||||||
topic: props.topic, baustein: props.baustein, section: props.section,
|
topic: props.topic, baustein: props.baustein, section: props.section,
|
||||||
provider: props.provider, messages: pruefDialog(), ...antwortPayload(), aktion: 'antwort_pruefen',
|
provider: props.provider, messages: pruefDialog(), ...antwortPayload(), aktion: 'antwort_pruefen',
|
||||||
})
|
})
|
||||||
if (meine !== pruefenRun) return // eine neuere Bewertung läuft → diese verwerfen
|
if (meine !== pruefenRun) return // eine neuere Bewertung läuft → diese verwerfen
|
||||||
|
// Streak driftfrei aus streakVorFrage neu setzen; Reset beim ersten Erreichen von 3/10.
|
||||||
|
let neu = res.bewertung === 'gut' ? streakVorFrage.value + 1 : 0
|
||||||
|
if ((res.absolviert && !warAbsolviert) || (res.verstanden && !warVerstanden)) neu = 0
|
||||||
|
streak.value = neu
|
||||||
applyPruefung(res)
|
applyPruefung(res)
|
||||||
if (ziel) {
|
if (ziel) {
|
||||||
if (res.bewertung !== ziel.bewertung || (res.feedback && res.feedback !== ziel.content)) {
|
if (res.bewertung !== ziel.bewertung || (res.feedback && res.feedback !== ziel.content)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user