This commit is contained in:
team3
2026-06-24 19:41:34 +02:00
parent d223cc6a07
commit 2ba76e40dd
7 changed files with 22 additions and 17 deletions

View File

@@ -25,7 +25,7 @@ LESBARKEIT_HART_ANTEIL = 0.30 # … ODER wenn dieser Anteil der Sätze hart ist
# Eigene Spur für interaktive Aufrufe (Chat, Elemente), damit sie nicht hinter # Eigene Spur für interaktive Aufrufe (Chat, Elemente), damit sie nicht hinter
# laufenden Writern in der Warteschlange hängen. # laufenden Writern in der Warteschlange hängen.
MAX_CONCURRENT_AGENTS = 10 MAX_CONCURRENT_AGENTS = 10
MAX_CONCURRENT_INTERACTIVE = 4 MAX_CONCURRENT_INTERACTIVE = 8
# Grace-Fenster der Konsens-Races (Bausteine, Guide, OnePager): Nach dem ersten # Grace-Fenster der Konsens-Races (Bausteine, Guide, OnePager): Nach dem ersten
# gültigen Ergebnis dürfen die übrigen Agenten noch so viele Sekunden fertig # gültigen Ergebnis dürfen die übrigen Agenten noch so viele Sekunden fertig

View File

@@ -148,21 +148,22 @@ def _bewertung_schema(data) -> dict | None:
return {"feedback": feedback, "niveau": niveau} return {"feedback": feedback, "niveau": niveau}
async def _gen_call(name: str, role: str, schema, provider: str, timeout: int = PRUEFUNG_TIMEOUT, **kwargs) -> dict | None: async def _gen_call(name: str, role: str, schema, provider: str, timeout: int = PRUEFUNG_TIMEOUT, lane: str = "interactive", **kwargs) -> dict | None:
"""Generator-Agent: Template füllen, laufen lassen, per schema parsen · None bei Fehler.""" """Generator-Agent: Template füllen, laufen lassen, per schema parsen · None bei Fehler.
lane="batch" für Hintergrund (Vorladen, Genau-Bewertung) → eigene Slot-Schlange."""
returncode, stdout, _ = await run_agent( returncode, stdout, _ = await run_agent(
name.lower() + "-" + str(uuid.uuid4()), _prompt(name, **kwargs), timeout, name.lower() + "-" + str(uuid.uuid4()), _prompt(name, **kwargs), timeout,
provider=provider, role=role, capabilities="none", lane="interactive", provider=provider, role=role, capabilities="none", lane=lane,
) )
return schema(_parse_json_text(stdout)) if returncode == 0 else None return schema(_parse_json_text(stdout)) if returncode == 0 else None
async def _kritik_call(name: str, provider: str, role: str = "judge", timeout: int = PRUEFUNG_TIMEOUT, **kwargs) -> list[str]: async def _kritik_call(name: str, provider: str, role: str = "judge", timeout: int = PRUEFUNG_TIMEOUT, lane: str = "interactive", **kwargs) -> list[str]:
"""Kritiker-Agent (Default role judge): leere Liste = in Ordnung. Fail-open: Ausfall des """Kritiker-Agent (Default role judge): leere Liste = in Ordnung. Fail-open: Ausfall des
Kritikers darf den Turn nicht blockieren, also dann ebenfalls leere Liste.""" Kritikers darf den Turn nicht blockieren, also dann ebenfalls leere Liste."""
returncode, stdout, _ = await run_agent( returncode, stdout, _ = await run_agent(
name.lower() + "-" + str(uuid.uuid4()), _prompt(name, **kwargs), timeout, name.lower() + "-" + str(uuid.uuid4()), _prompt(name, **kwargs), timeout,
provider=provider, role=role, capabilities="none", lane="interactive", provider=provider, role=role, capabilities="none", lane=lane,
) )
if returncode != 0: if returncode != 0:
return [] return []
@@ -204,7 +205,7 @@ async def _frage_mit_kritik(
frage = None frage = None
for _ in range(KRITIK_MAX_RUNDEN): for _ in range(KRITIK_MAX_RUNDEN):
data = await _gen_call( data = await _gen_call(
"Baustein-Frage", "guide", _frage_schema, provider, "Baustein-Frage", "guide", _frage_schema, provider, lane="batch",
topic=topic, baustein=baustein, section_block=section_block, topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, transcript=transcript, vermeide_block=vermeide_block, kompakt_block=kompakt_block, transcript=transcript, vermeide_block=vermeide_block,
typ_block=typ_block, fokus_block=fokus_block, kritik_block=kritik_block, typ_block=typ_block, fokus_block=fokus_block, kritik_block=kritik_block,
@@ -213,7 +214,7 @@ async def _frage_mit_kritik(
return None return None
frage = data["frage"] frage = data["frage"]
probleme = await _kritik_call( probleme = await _kritik_call(
"Baustein-Frage-Kritik", provider, role="guide", # starke KI prüft die Regeln "Baustein-Frage-Kritik", provider, role="guide", lane="batch", # starke KI prüft die Regeln
topic=topic, baustein=baustein, section_block=section_block, topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, transcript=transcript, vermeide_block=vermeide_block, kompakt_block=kompakt_block, transcript=transcript, vermeide_block=vermeide_block,
typ_block=typ_block, fokus_block=fokus_block, frage=frage, typ_block=typ_block, fokus_block=fokus_block, frage=frage,
@@ -238,11 +239,13 @@ async def _bewertung_mit_kritik(
`role` = "judge" (schnell) oder "guide" (gründlich, starkes Modell mit Thinking). `role` = "judge" (schnell) oder "guide" (gründlich, starkes Modell mit Thinking).
""" """
timeout = GRUENDLICH_TIMEOUT if role == "guide" else PRUEFUNG_TIMEOUT timeout = GRUENDLICH_TIMEOUT if role == "guide" else PRUEFUNG_TIMEOUT
# Gründlich (role guide) = Nutzer wartet → interaktiv. Hintergrund-Genau (judge) → batch.
lane = "interactive" if role == "guide" else "batch"
kritik_block = "(keine)" kritik_block = "(keine)"
bew = None bew = None
for _ in range(KRITIK_MAX_RUNDEN): for _ in range(KRITIK_MAX_RUNDEN):
bew = await _gen_call( bew = await _gen_call(
"Baustein-Bewertung", role, _bewertung_schema, provider, timeout, "Baustein-Bewertung", role, _bewertung_schema, provider, timeout, lane=lane,
topic=topic, baustein=baustein, section_block=section_block, topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, frage=frage, transcript=transcript, kompakt_block=kompakt_block, frage=frage, transcript=transcript,
begruendung_block=begruendung_block, kritik_block=kritik_block, begruendung_block=begruendung_block, kritik_block=kritik_block,
@@ -250,7 +253,7 @@ async def _bewertung_mit_kritik(
if bew is None: if bew is None:
return None return None
probleme = await _kritik_call( probleme = await _kritik_call(
"Baustein-Bewertung-Kritik", provider, role=role, timeout=timeout, "Baustein-Bewertung-Kritik", provider, role=role, timeout=timeout, lane=lane,
topic=topic, baustein=baustein, section_block=section_block, topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, frage=frage, transcript=transcript, kompakt_block=kompakt_block, frage=frage, transcript=transcript,
bewertung_block=_bewertung_text(bew), bewertung_block=_bewertung_text(bew),
@@ -309,7 +312,7 @@ async def pruefung_frage_variante(
try: try:
section_block, kompakt_block = _bloecke(section, kompakt) section_block, kompakt_block = _bloecke(section, kompakt)
data = await _gen_call( data = await _gen_call(
"Baustein-Frage-Variante", "guide", _frage_schema, provider, "Baustein-Frage-Variante", "guide", _frage_schema, provider, lane="batch",
topic=topic, baustein=baustein, section_block=section_block, topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, muster=muster, kompakt_block=kompakt_block, muster=muster,
) )
@@ -378,7 +381,7 @@ async def quiz_generieren(
"GENAU EINE Option ist richtig, die anderen drei sind klar falsch." "GENAU EINE Option ist richtig, die anderen drei sind klar falsch."
) )
return await _gen_call( return await _gen_call(
"Baustein-Quiz", "guide", (lambda d: _quiz_schema(d, schwer=schwer)), provider, "Baustein-Quiz", "guide", (lambda d: _quiz_schema(d, schwer=schwer)), provider, lane="batch",
topic=topic, baustein=baustein, section_block=section_block, topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, muster=muster, anzahl_block=anzahl_block, kompakt_block=kompakt_block, muster=muster, anzahl_block=anzahl_block,
) )
@@ -396,7 +399,7 @@ async def lueckwahl_generieren(
try: try:
section_block, kompakt_block = _bloecke(section, kompakt) section_block, kompakt_block = _bloecke(section, kompakt)
return await _gen_call( return await _gen_call(
"Baustein-Lueckwahl", "guide", _lueckwahl_schema, provider, "Baustein-Lueckwahl", "guide", _lueckwahl_schema, provider, lane="batch",
topic=topic, baustein=baustein, section_block=section_block, topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, muster=muster, kompakt_block=kompakt_block, muster=muster,
) )
@@ -427,7 +430,7 @@ async def lueckentext_generieren(
try: try:
section_block, kompakt_block = _bloecke(section, kompakt) section_block, kompakt_block = _bloecke(section, kompakt)
return await _gen_call( return await _gen_call(
"Baustein-Lueckentext", "guide", _lueck_schema, provider, "Baustein-Lueckentext", "guide", _lueck_schema, provider, lane="batch",
topic=topic, baustein=baustein, section_block=section_block, topic=topic, baustein=baustein, section_block=section_block,
kompakt_block=kompakt_block, muster=muster, kompakt_block=kompakt_block, muster=muster,
) )

View File

@@ -16,6 +16,7 @@ HARTE REGELN:
- Gleiche Kernfrage wie das Muster, nur anders formuliert — KEINE neue Sache, KEINE wörtliche Kopie. - Gleiche Kernfrage wie das Muster, nur anders formuliert — KEINE neue Sache, KEINE wörtliche Kopie.
- Frag nur, was aus dem Material beantwortbar ist. Keine erfundenen Zusatzannahmen. - Frag nur, was aus dem Material beantwortbar ist. Keine erfundenen Zusatzannahmen.
- Sprich den Lerner direkt an, klares Deutsch, keine Floskeln. - Sprich den Lerner direkt an, klares Deutsch, keine Floskeln.
- `$…$` NUR für echte Mathematik. Code, Pfade, Namespaces, Dateinamen, JSON-Schlüssel/-Werte und Bezeichner gehören in Backticks oder Klartext — NIE in `$…$` (nicht `$Acme\Example$`, sondern `Acme\Example` in Backticks).
Gib NUR dieses JSON aus (kein weiterer Text): Gib NUR dieses JSON aus (kein weiterer Text):
{{"frage": "genau eine kurze Frage"}} {{"frage": "genau eine kurze Frage"}}

View File

@@ -31,6 +31,7 @@ HARTE REGELN FÜR DIE FRAGE — wichtiger als alles andere:
FACHLICHE REFERENZ — WICHTIG: FACHLICHE REFERENZ — WICHTIG:
- Die Guide-Fassung und die Vertiefung oben sind die Referenz. Deine Frage darf ihr NIE widersprechen. - Die Guide-Fassung und die Vertiefung oben sind die Referenz. Deine Frage darf ihr NIE widersprechen.
- Erfinde keine Zusatzannahmen (z. B. fehlende Eingaben, geänderte Definitionen). Frag nur, was aus dem Material folgt. - Erfinde keine Zusatzannahmen (z. B. fehlende Eingaben, geänderte Definitionen). Frag nur, was aus dem Material folgt.
- `$…$` NUR für echte Mathematik. Code, Pfade, Namespaces, Dateinamen, JSON-Schlüssel/-Werte und Bezeichner gehören in Backticks oder Klartext — NIE in `$…$` (nicht `$Acme\Example$`, sondern `Acme\Example` in Backticks).
HINWEISE DES PRÜFERS ZUR LETZTEN FASSUNG: HINWEISE DES PRÜFERS ZUR LETZTEN FASSUNG:
{kritik_block} {kritik_block}

View File

@@ -17,7 +17,7 @@ AUFGABE:
von `$…$` (nie innerhalb einer Formel). von `$…$` (nie innerhalb einer Formel).
- Die Lücke prüft aktives Abrufen — sie steht für einen zentralen Fachbegriff, nicht für ein Füllwort. - Die Lücke prüft aktives Abrufen — sie steht für einen zentralen Fachbegriff, nicht für ein Füllwort.
- Der Satz gibt genug Kontext, dass die Lösung eindeutig ist (keine Raterei). - Der Satz gibt genug Kontext, dass die Lösung eindeutig ist (keine Raterei).
- Fachbegriffe/Formeln dürfen als $…$ gesetzt werden (z. B. $V(w,c)$). - `$…$` NUR für echte Mathematik (Variablen, Symbole, Formeln, z. B. $V(w,c)$). Code, Pfade, Namespaces, Dateinamen, JSON-Schlüssel/-Werte und Bezeichner gehören in Backticks oder Klartext — NIE in `$…$`. Beispiel: nicht `$Acme\Example$`, sondern `Acme\Example` in Backticks.
- `loesung` = der erwartete Begriff (kurz, wie er in die Lücke gehört). - `loesung` = der erwartete Begriff (kurz, wie er in die Lücke gehört).
- `alternativen` = akzeptierte Synonyme/Schreibweisen (kann leer sein), KEINE falschen. - `alternativen` = akzeptierte Synonyme/Schreibweisen (kann leer sein), KEINE falschen.
- Klares Deutsch, ein Satz, genau ein `___`. - Klares Deutsch, ein Satz, genau ein `___`.

View File

@@ -16,7 +16,7 @@ AUFGABE:
- Biete GENAU VIER Begriffe als Optionen — **genau einer** passt in die Lücke. - Biete GENAU VIER Begriffe als Optionen — **genau einer** passt in die Lücke.
- Die drei falschen sind plausibel, aber klar falsch (Verwechslung, verwandter Begriff) — keine Albernheiten. - Die drei falschen sind plausibel, aber klar falsch (Verwechslung, verwandter Begriff) — keine Albernheiten.
- Optionen kurz (Begriff oder kurzer Halbsatz), ähnlich lang. - Optionen kurz (Begriff oder kurzer Halbsatz), ähnlich lang.
- Fachbegriffe/Formeln dürfen als $…$ gesetzt werden. - `$…$` NUR für echte Mathematik (Variablen, Symbole, Formeln). Code, Pfade, Namespaces, Dateinamen, JSON-Schlüssel/-Werte und Bezeichner gehören in Backticks oder Klartext — NIE in `$…$`. Beispiel: nicht `$Acme\Example$`, sondern `Acme\Example` in Backticks.
Gib NUR dieses JSON aus (kein weiterer Text): Gib NUR dieses JSON aus (kein weiterer Text):
{{"satz": "Ein ___ ist eine endliche Menge von Symbolen.", "optionen": [ {{"satz": "Ein ___ ist eine endliche Menge von Symbolen.", "optionen": [

View File

@@ -18,7 +18,7 @@ AUFGABE:
KEINE ganzen Sätze, KEIN „Weil …"-Vorspann, keine Begründungs-Prosa. Nur der Kern. KEINE ganzen Sätze, KEIN „Weil …"-Vorspann, keine Begründungs-Prosa. Nur der Kern.
- Alle vier Optionen ähnlich lang und im gleichen Stil. Keine verräterischen Hinweise. - Alle vier Optionen ähnlich lang und im gleichen Stil. Keine verräterischen Hinweise.
- Die Frage selbst knapp (1 Satz). Klares Deutsch. - Die Frage selbst knapp (1 Satz). Klares Deutsch.
- Fachbegriffe/Formeln dürfen als $…$ gesetzt werden (z. B. $V(w,c)=A(w)$). - `$…$` NUR für echte Mathematik (Variablen, Symbole, Formeln, z. B. $V(w,c)=A(w)$). Code, Pfade, Namespaces, Dateinamen, JSON-Schlüssel/-Werte und Bezeichner gehören in Backticks oder Klartext — NIE in `$…$`. Beispiel: nicht `$Acme\Example$`, sondern `Acme\Example` in Backticks.
Gib NUR dieses JSON aus (kein weiterer Text): Gib NUR dieses JSON aus (kein weiterer Text):
{{"frage": "die Frage", "optionen": [ {{"frage": "die Frage", "optionen": [