This commit is contained in:
team3
2026-06-22 16:30:39 +02:00
parent 36b7aabdb0
commit c5543c0529
4 changed files with 172 additions and 94 deletions

View File

@@ -33,10 +33,9 @@ from textkit import (
_titel_aufloesen, _titel_index, _vormerge, _titel_aufloesen, _titel_index, _vormerge,
) )
# Subbausteine + Stufen entstehen pro Baustein → wie die Writer chunken: # Subbausteine (Websuche je Baustein) chunken: 1 Agent je ~10 Bausteine, gedeckelt.
# 1 Agent je ~30 Bausteine, gedeckelt. SUBBAUSTEIN_CHUNK = 10
SUBBAUSTEIN_CHUNK = 30 SUBBAUSTEIN_MAX = 40
SUBBAUSTEIN_MAX = 20
# Einstufen ist billig (kurzes Urteil, keine Websuche) → größere Pakete, weniger Dateien/Agenten. # Einstufen ist billig (kurzes Urteil, keine Websuche) → größere Pakete, weniger Dateien/Agenten.
STUFE_CHUNK = 100 STUFE_CHUNK = 100
@@ -474,6 +473,27 @@ def _frage_muster_schema(data) -> list[dict] | None:
return out or None return out or None
def _frage_muster_chunk_schema(data) -> list[dict] | None:
"""{"muster": [{baustein, subbaustein, typ, frage}, …]} → Liste valider Einträge · sonst None.
Wie _frage_muster_schema, aber mit `baustein` (Zuordnung im 10er-Chunk). Ungültige
Einzel-Einträge werden übersprungen (nicht die ganze Liste verworfen)."""
if not isinstance(data, dict) or not isinstance(data.get("muster"), list):
return None
out = []
for e in data["muster"]:
if not isinstance(e, dict):
continue
bau = str(e.get("baustein", "")).strip()
sub = str(e.get("subbaustein", "")).strip()
typ = str(e.get("typ", "")).strip().casefold()
frage = str(e.get("frage", "")).strip()
if not bau or not sub or typ not in FRAGETYPEN or not frage:
continue
out.append({"baustein": bau, "subbaustein": sub, "typ": typ, "frage": frage})
return out or None
def _frage_muster_komplett(topic: str) -> bool: def _frage_muster_komplett(topic: str) -> bool:
"""Frage-Muster-Sidecar existiert (Build gelaufen)? Einzelne leere Bausteine """Frage-Muster-Sidecar existiert (Build gelaufen)? Einzelne leere Bausteine
fallen zur Prüfungszeit auf Live-Generierung zurück — daher genügt die Datei.""" fallen zur Prüfungszeit auf Live-Generierung zurück — daher genügt die Datei."""
@@ -877,9 +897,9 @@ def _match_sub(agent_sub: str, rel: list[str]) -> str:
async def _frage_muster_block(ctx: GenContext, set_p, files: dict, sidecar: dict, instructions: str) -> dict | None: async def _frage_muster_block(ctx: GenContext, set_p, files: dict, sidecar: dict, instructions: str) -> dict | None:
"""Block E: drei Phasen — Finden (1 Generator je Baustein, parallel), Wählen (Code: """Block E (10er-Chunks): Finden (1 Generator je ~10 Bausteine, parallel), Wählen (Code:
relevante Subs + Dedup), Klären (Kritiker je Baustein bereinigt die Tabelle). je Baustein gruppieren + Dedup), Klären (1 Kritiker je Chunk), Prüfen (Nachrunde).
Generativ statt Vote: Muster sind Text, kein 3-Rater-Konsens sinnvoll. Zuordnung je Eintrag über das `baustein`-Feld (Chunk-Datei trägt mehrere Bausteine).
{Baustein-Titel: [{subbaustein, typ, frage}, …]} oder None bei Abbruch.""" {Baustein-Titel: [{subbaustein, typ, frage}, …]} oder None bei Abbruch."""
topic, is_cancelled = ctx.topic, ctx.is_cancelled topic, is_cancelled = ctx.topic, ctx.is_cancelled
arbeit = files["arbeit"] arbeit = files["arbeit"]
@@ -892,99 +912,153 @@ async def _frage_muster_block(ctx: GenContext, set_p, files: dict, sidecar: dict
if not bausteine: if not bausteine:
return {} return {}
typen_block = "\n".join(f"- {k}: {v}" for k, v in FRAGETYPEN.items()) typen_block = "\n".join(f"- {k}: {v}" for k, v in FRAGETYPEN.items())
chunks = _chunk_nums(list(range(len(bausteine))), _n_chunks(len(bausteine))) # ~10 Bausteine/Chunk
def roh_path(c): def roh_path(ci):
return arbeit / f"frage-muster-c{c}.json" return arbeit / f"frage-muster-c{ci}.json"
def final_path(c): def final_path(ci):
return arbeit / f"frage-muster-final-c{c}.json" return arbeit / f"frage-muster-final-c{ci}.json"
# Phase „Fragen finden": pro Baustein 1 Generator, alle parallel. def _chunk_titel(idxs):
async def _finde(c, titel, rel): return [bausteine[i][0] for i in idxs]
fp = roh_path(c)
if _frage_muster_schema(_json_datei(fp)): # Phase „Fragen finden": je Chunk 1 Generator, alle parallel.
async def _finde(ci, idxs):
fp = roh_path(ci)
if _frage_muster_chunk_schema(_json_datei(fp)):
return # Resume return # Resume
sub_block = "\n".join(f"- {s}" for s in rel) block = "\n\n".join(
f"BAUSTEIN: {bausteine[i][0]}\nSUBBAUSTEINE:\n" + "\n".join(f"- {s}" for s in bausteine[i][1])
for i in idxs
)
subs_total = sum(len(bausteine[i][1]) for i in idxs)
status, _ = await run_single_slot( status, _ = await run_single_slot(
ctx, f"Frage-Muster {c}", ctx, f"Frage-Muster {ci}",
key=f"bausteine-{topic}-frage-muster-c{c}", key=f"bausteine-{topic}-frage-muster-c{ci}",
prompt=_prompt("Frage-Muster-Recherche", topic=topic, baustein=titel, prompt=_prompt("Frage-Muster-Recherche", topic=topic, bausteine=block,
subbausteine=sub_block, typen=typen_block, out_path=fp, extra=_extra(instructions)), typen=typen_block, out_path=fp, extra=_extra(instructions)),
role="fast", capabilities="files", role="fast", capabilities="files",
payload=lambda result, p=fp: _frage_muster_schema(_json_datei(p)), payload=lambda result, p=fp: _frage_muster_chunk_schema(_json_datei(p)),
timeout=_timeout("frage_muster", len(rel)), timeout=_timeout("frage_muster", subs_total),
) )
if status == FAILED: if status == FAILED:
_log(topic, f"Frage-Muster Baustein {c} fehlgeschlagen — kein Muster (Fallback Live)") _log(topic, f"Frage-Muster Chunk {ci} fehlgeschlagen — Bausteine im Fallback (Nachrunde/Live)")
await _gather_fortschritt([_finde(c, t, r) for c, (t, r) in enumerate(bausteine, 1)], len(bausteine), _melde_p(set_p, topic, "Fragen finden")) async def finde_alle(ci_list):
ci_list = list(ci_list)
await _gather_fortschritt([_finde(ci, chunks[ci]) for ci in ci_list], len(ci_list), _melde_p(set_p, topic, "Fragen finden"))
await finde_alle(range(len(chunks)))
if is_cancelled(): if is_cancelled():
return None return None
# Phase „Fragen wählen": Code — Dubletten je Baustein raus, Subbaustein-Titel locker # Phase „Fragen wählen": Code — Chunk-Dateien je Baustein gruppieren, Dubletten raus,
# auf die relevanten mappen (nichts wegen Titel-Abweichung verwerfen). # Baustein-/Subbaustein-Titel locker auf die Vorgaben mappen (nichts wegen Abweichung verwerfen).
def _waehle(c, rel): def _waehle_chunk(ci):
eintraege = _frage_muster_schema(_json_datei(roh_path(c))) or [] idxs = chunks[ci]
gesehen, sauber = set(), [] ctitel = _chunk_titel(idxs)
for e in eintraege: rel_by = {bausteine[i][0]: bausteine[i][1] for i in idxs}
out, gesehen = {}, {}
for e in _frage_muster_chunk_schema(_json_datei(roh_path(ci))) or []:
titel = _match_sub(e["baustein"], ctitel)
if titel not in rel_by:
continue # nicht zuordenbar → verwerfen
norm = _norm_frage(e["frage"]) norm = _norm_frage(e["frage"])
if norm in gesehen: seen = gesehen.setdefault(titel, set())
if norm in seen:
continue continue
gesehen.add(norm) seen.add(norm)
sauber.append({**e, "subbaustein": _match_sub(e["subbaustein"], rel)}) out.setdefault(titel, []).append(
return sauber {"subbaustein": _match_sub(e["subbaustein"], rel_by[titel]), "typ": e["typ"], "frage": e["frage"]})
return out
def _waehle_all(ci_list):
roh = {}
for ci in ci_list:
for titel, eintraege in _waehle_chunk(ci).items():
roh.setdefault(titel, []).extend(eintraege)
return roh
set_p("Fragen wählen…", step=_step_idx(topic, "Fragen wählen")) set_p("Fragen wählen…", step=_step_idx(topic, "Fragen wählen"))
roh_by_c = {c: _waehle(c, rel) for c, (titel, rel) in enumerate(bausteine, 1)} roh_by_titel = _waehle_all(range(len(chunks)))
# Phase „Fragen klären": Kritiker je Baustein bereinigt die Tabelle (eindeutig, distinkt). # Phase „Fragen klären": je Chunk 1 Kritiker bereinigt die Tabellen (nach Baustein gruppiert).
async def _klaere(c, titel): async def _klaere(ci, idxs):
roh = roh_by_c[c] fp = final_path(ci)
fp = final_path(c) if _frage_muster_chunk_schema(_json_datei(fp)):
if _frage_muster_schema(_json_datei(fp)) or not roh: return # Resume
return bloecke = []
tabelle = "\n".join(f"{i}. [{e['typ']}] ({e['subbaustein']}) {e['frage']}" for i, e in enumerate(roh, 1)) for i in idxs:
t = bausteine[i][0]
eintraege = roh_by_titel.get(t) or []
if not eintraege:
continue
zeilen = "\n".join(f"- [{e['typ']}] ({e['subbaustein']}) {e['frage']}" for e in eintraege)
bloecke.append(f"BAUSTEIN: {t}\n{zeilen}")
if not bloecke:
return # nichts zu klären in diesem Chunk
subs_total = sum(len(bausteine[i][1]) for i in idxs)
status, _ = await run_single_slot( status, _ = await run_single_slot(
ctx, f"Frage-Muster-Klärung {c}", ctx, f"Frage-Muster-Klärung {ci}",
key=f"bausteine-{topic}-frage-muster-final-c{c}", key=f"bausteine-{topic}-frage-muster-final-c{ci}",
prompt=_prompt("Frage-Muster-Kritik", topic=topic, baustein=titel, tabelle=tabelle, out_path=fp, extra=_extra(instructions)), prompt=_prompt("Frage-Muster-Kritik", topic=topic, tabelle="\n\n".join(bloecke), out_path=fp, extra=_extra(instructions)),
role="judge", capabilities="files", role="judge", capabilities="files",
payload=lambda result, p=fp: _frage_muster_schema(_json_datei(p)), payload=lambda result, p=fp: _frage_muster_chunk_schema(_json_datei(p)),
timeout=_timeout("frage_muster_check", len(roh)), timeout=_timeout("frage_muster_check", subs_total),
) )
if status == FAILED: if status == FAILED:
_log(topic, f"Frage-Muster-Klärung Baustein {c} fehlgeschlagen — Roh-Muster übernommen") _log(topic, f"Frage-Muster-Klärung Chunk {ci} fehlgeschlagen — Roh-Muster übernommen")
await _gather_fortschritt([_klaere(c, t) for c, (t, _) in enumerate(bausteine, 1)], len(bausteine), _melde_p(set_p, topic, "Fragen klären")) async def klaere_alle(ci_list):
ci_list = list(ci_list)
await _gather_fortschritt([_klaere(ci, chunks[ci]) for ci in ci_list], len(ci_list), _melde_p(set_p, topic, "Fragen klären"))
await klaere_alle(range(len(chunks)))
if is_cancelled(): if is_cancelled():
return None return None
# Geklärte Tabelle je Baustein, Fallback auf Roh-Muster. Subbaustein-Titel locker mappen. # Geklärte Chunk-Tabelle je Baustein, Fallback auf Roh-Muster. Titel locker mappen.
def _finalisiere(c, rel): def _final_by_titel(ci_list):
final = _frage_muster_schema(_json_datei(final_path(c))) or roh_by_c[c] out = {}
return [{**e, "subbaustein": _match_sub(e["subbaustein"], rel)} for e in final] for ci in ci_list:
idxs = chunks[ci]
ctitel = _chunk_titel(idxs)
rel_by = {bausteine[i][0]: bausteine[i][1] for i in idxs}
for e in _frage_muster_chunk_schema(_json_datei(final_path(ci))) or []:
titel = _match_sub(e["baustein"], ctitel)
if titel not in rel_by:
continue
out.setdefault(titel, []).append(
{"subbaustein": _match_sub(e["subbaustein"], rel_by[titel]), "typ": e["typ"], "frage": e["frage"]})
return out
ergebnis = {titel: _finalisiere(c, rel) for c, (titel, rel) in enumerate(bausteine, 1)} final_by_titel = _final_by_titel(range(len(chunks)))
ergebnis = {t: (final_by_titel.get(t) or roh_by_titel.get(t) or []) for t, _ in bausteine}
# Phase „Fragen prüfen": Bausteine mit relevanten Subs aber 0 Mustern eine Runde nachholen. # Phase „Fragen prüfen": Chunks mit ≥1 leeren Baustein eine Runde nachholen.
set_p("Fragen prüfen…", step=_step_idx(topic, "Fragen prüfen")) set_p("Fragen prüfen…", step=_step_idx(topic, "Fragen prüfen"))
leer = [(c, t, r) for c, (t, r) in enumerate(bausteine, 1) if not ergebnis.get(t)] leer_titel = {t for t, _ in bausteine if not ergebnis.get(t)}
if leer: if leer_titel:
_log(topic, f"Frage-Muster: {len(leer)} Baustein(e) ohne Muster — Nachrunde") nach = [ci for ci, idxs in enumerate(chunks) if any(bausteine[i][0] in leer_titel for i in idxs)]
for c, t, r in leer: _log(topic, f"Frage-Muster: {len(leer_titel)} Baustein(e) ohne Muster — Nachrunde über {len(nach)} Chunk(s)")
roh_path(c).unlink(missing_ok=True) for ci in nach:
final_path(c).unlink(missing_ok=True) roh_path(ci).unlink(missing_ok=True)
await _gather_fortschritt([_finde(c, t, r) for c, t, r in leer], len(leer), _melde_p(set_p, topic, "Fragen prüfen")) final_path(ci).unlink(missing_ok=True)
await finde_alle(nach)
if is_cancelled(): if is_cancelled():
return None return None
for c, t, r in leer: for titel, eintraege in _waehle_all(nach).items():
roh_by_c[c] = _waehle(c, r) roh_by_titel[titel] = eintraege
await _gather_fortschritt([_klaere(c, t) for c, t, r in leer], len(leer), _melde_p(set_p, topic, "Fragen prüfen")) await klaere_alle(nach)
if is_cancelled(): if is_cancelled():
return None return None
for c, t, r in leer: for titel, eintraege in _final_by_titel(nach).items():
ergebnis[t] = _finalisiere(c, r) final_by_titel[titel] = eintraege
rest = [t for c, t, r in leer if not ergebnis.get(t)] for t, _ in bausteine:
if not ergebnis.get(t):
ergebnis[t] = final_by_titel.get(t) or roh_by_titel.get(t) or []
rest = [t for t, _ in bausteine if not ergebnis.get(t)]
if rest: if rest:
_log(topic, f"Frage-Muster: {len(rest)} Baustein(e) bleiben leer (Fallback Live): {rest[:5]}") _log(topic, f"Frage-Muster: {len(rest)} Baustein(e) bleiben leer (Fallback Live): {rest[:5]}")
return ergebnis return ergebnis

View File

@@ -43,8 +43,9 @@ GUIDE_STEPS = ("Auswahl", "Gliederung", "Inhalte", "Inhalts-Check", "Schreiben",
# Writer skalieren mit der Section-Zahl: 1 Writer je ~30 Sections (gedeckelt). # Writer skalieren mit der Section-Zahl: 1 Writer je ~30 Sections (gedeckelt).
# Kleine Pakete vermeiden Lazy-Output bei langen Listen und begrenzen den Schaden # Kleine Pakete vermeiden Lazy-Output bei langen Listen und begrenzen den Schaden
# eines fehlgeschlagenen Writers. # eines fehlgeschlagenen Writers.
WRITER_SECTIONS = 30 # Inhalte/Inhalts-Check/Lese-Prüfung laufen in Paketen von ~GUIDE_CHUNK Bausteinen je Agent.
WRITER_MAX = 20 # Nur der Writer (Schreiben) bleibt 1 Agent je Baustein (variable Längen, kein Kürzen).
GUIDE_CHUNK = 10
# Lese-Prüfung: nur EINE Runde (Check + Fix). Folgerunden brachten kaum Mehrwert # Lese-Prüfung: nur EINE Runde (Check + Fix). Folgerunden brachten kaum Mehrwert
# (1 Agent je Baustein prüft ohnehin fein), kosten aber extra Agenten. # (1 Agent je Baustein prüft ohnehin fein), kosten aber extra Agenten.
@@ -584,10 +585,10 @@ async def _generate_sections(
plan = _mit_resten(plan, sel_entries) plan = _mit_resten(plan, sel_entries)
_set_fertig(content_path, 1) # Gliederung steht _set_fertig(content_path, 1) # Gliederung steht
# Chunks festlegen: 1 Chunk = 1 Baustein. So läuft JEDER Section-Schritt (Inhalte, # Grobe Chunks (~GUIDE_CHUNK Bausteine je Agent) für Inhalte, Inhalts-Check und Lese-Prüfung.
# Inhalts-Prüfung, Writer, Lese-Check) mit genau einem Agent je Baustein. Der Writer # Der Writer baut darunter pro Baustein (eigene feine Chunks, s.u.) → variable Längen.
# rationiert sein Output-Budget dann nicht über viele Sections → längere, variable Texte. total_sections = sum(len(c["nums"]) for c in plan)
chunks = [[{"title": ch["title"], "nums": [num]}] for ch in plan for num in ch["nums"]] chunks = _split_chunks(plan, max(1, math.ceil(total_sections / GUIDE_CHUNK)))
# Subbausteine je Baustein, je Format gefiltert: Guide=relevant · FullGuide=alle · Rest=Rand. # Subbausteine je Baustein, je Format gefiltert: Guide=relevant · FullGuide=alle · Rest=Rand.
# Subs ohne Relevanz-Feld (Alt-Themen) zählen als „nicht Rand" (→ Guide/FullGuide). # Subs ohne Relevanz-Feld (Alt-Themen) zählen als „nicht Rand" (→ Guide/FullGuide).
if format_name == "FullGuide": if format_name == "FullGuide":
@@ -706,11 +707,14 @@ async def _generate_sections(
_set_fertig(content_path, 3) # Inhalts-Check durch _set_fertig(content_path, 3) # Inhalts-Check durch
# Schritt 4: Schreiben — Writer formuliert die geprüften Inhalte aus (Resume). # Schritt 4: Schreiben — Writer formuliert die geprüften Inhalte aus (Resume).
# FEINE Chunks: genau 1 Baustein je Writer → variable Längen, kein Budget-Rationieren.
def inhalte_text(chunk) -> str: def inhalte_text(chunk) -> str:
nums = [num for ch in chunk for num in ch["nums"] if num in inhalt_by_num] nums = [num for ch in chunk for num in ch["nums"] if num in inhalt_by_num]
return "\n\n".join(f"<!-- section: {_titel(entries[num])} -->\n{inhalt_by_num[num]}" for num in nums) return "\n\n".join(f"<!-- section: {_titel(entries[num])} -->\n{inhalt_by_num[num]}" for num in nums)
paths = [content_path.parent / f"{content_path.stem}.chunk-{i}.md" for i in range(1, writer_count + 1)] w_chunks = [[{"title": ch["title"], "nums": [num]}] for ch in plan for num in ch["nums"]]
w_zuteil = [_zuteilung_subs(c, entries, subs_by_titel) for c in w_chunks]
paths = [content_path.parent / f"{content_path.stem}.chunk-{i}.md" for i in range(1, len(w_chunks) + 1)]
offen = [i for i, p in enumerate(paths) if not p.exists()] offen = [i for i, p in enumerate(paths) if not p.exists()]
if offen: if offen:
async def melde(d, t): await _set_step(guide_id, 4, f"Schreibe Sections {d}/{t}") async def melde(d, t): await _set_step(guide_id, 4, f"Schreibe Sections {d}/{t}")
@@ -719,14 +723,14 @@ async def _generate_sections(
f"{guide_id}-w{i + 1}", f"{guide_id}-w{i + 1}",
_prompt( _prompt(
"Guide-Writer", "Guide-Writer",
topic=topic, format_name=format_name, zuteilung=zuteilungen[i], topic=topic, format_name=format_name, zuteilung=w_zuteil[i],
inhalte=inhalte_text(chunks[i]), inhalte=inhalte_text(w_chunks[i]),
facts=facts, spec=spec, out_path=paths[i], extra=_extra(instructions), facts=facts, spec=spec, out_path=paths[i], extra=_extra(instructions),
), ),
_timeout("writer", chunk_sizes[i]), provider=provider, role="guide", capabilities="full", _timeout("writer", 1), provider=provider, role="guide", capabilities="full",
) )
for i in offen for i in offen
], writer_count, melde, start=writer_count - len(offen)) ], len(w_chunks), melde, start=len(w_chunks) - len(offen))
if is_cancelled(): if is_cancelled():
return None return None
for i, r in zip(offen, results): for i, r in zip(offen, results):

View File

@@ -1,20 +1,20 @@
Du bist Qualitäts-Prüfer für den Fragen-Vorrat einer Lern-Prüfung zum Thema "{topic}", Baustein "{baustein}". Ein anderer Agent hat Frage-Muster erstellt. Prüfe sie und gib eine **bereinigte** Endfassung zurück. Du bist Qualitäts-Prüfer für den Fragen-Vorrat einer Lern-Prüfung zum Thema "{topic}". Ein anderer Agent hat Frage-Muster für **mehrere Bausteine** erstellt. Prüfe sie und gib eine **bereinigte** Endfassung zurück.
ZU PRÜFENDE MUSTER (Nummer. [typ] (subbaustein) Frage): ZU PRÜFENDE MUSTER (gruppiert je Baustein; Zeilen: [typ] (subbaustein) Frage):
{tabelle} {tabelle}
PRÜFE UND BEREINIGE GEGEN DIESE KRITERIEN: PRÜFE UND BEREINIGE GEGEN DIESE KRITERIEN (je Baustein getrennt):
- **Stil:** jede Frage GENAU EINE Frage, ein Fragezeichen, eine Sache. Kein "und"/"sowie", keine Aufzählung. Maximal 12 Sätze. Verstöße → umformulieren. - **Stil:** jede Frage GENAU EINE Frage, ein Fragezeichen, eine Sache. Kein "und"/"sowie", keine Aufzählung. Maximal 12 Sätze. Verstöße → umformulieren.
- **Eindeutig beantwortbar:** aus dem Baustein-Wissen klar beantwortbar, keine erfundenen Annahmen. Unbeantwortbares → korrigieren oder entfernen. - **Eindeutig beantwortbar:** aus dem Baustein-Wissen klar beantwortbar, keine erfundenen Annahmen. Unbeantwortbares → korrigieren oder entfernen.
- **Distinkt:** keine zwei Muster, die sinngemäß dieselbe Frage stellen (auch nicht über Typen hinweg). Dubletten → eines behalten, das andere entfernen. - **Distinkt:** keine zwei Muster desselben Bausteins, die sinngemäß dieselbe Frage stellen (auch nicht über Typen hinweg). Dubletten → eines behalten, das andere entfernen.
- **Typ-Treue:** die Frage passt zum angegebenen Typ; sonst Typ anpassen oder Muster entfernen. - **Typ-Treue:** die Frage passt zum angegebenen Typ; sonst Typ anpassen oder Muster entfernen.
- `subbaustein` und `typ` der Einträge unverändert übernehmen (nur `frage` darf sich ändern); nur entfernen, nicht neu erfinden. - `baustein`, `subbaustein` und `typ` der Einträge unverändert übernehmen (nur `frage` darf sich ändern); nur entfernen, nicht neu erfinden.
Behalte gute Muster unverändert. Ändere nur, was wirklich gegen die Kriterien verstößt. Behalte gute Muster unverändert. Ändere nur, was wirklich gegen die Kriterien verstößt.
Schreibe die bereinigte Endfassung als JSON in die Datei {out_path} (nutze dein Schreib-Werkzeug), GENAU in diesem Format: Schreibe die bereinigte Endfassung ALLER Bausteine als EIN JSON in die Datei {out_path} (nutze dein Schreib-Werkzeug), GENAU in diesem Format:
{{"muster": [ {{"muster": [
{{"subbaustein": "<exakter Titel>", "typ": "<Schlüssel>", "frage": "<eine konkrete Frage>"}} {{"baustein": "<exakter Baustein-Titel>", "subbaustein": "<exakter Titel>", "typ": "<Schlüssel>", "frage": "<eine konkrete Frage>"}}
]}} ]}}
Gib sonst keinen Text aus. Gib sonst keinen Text aus.

View File

@@ -1,18 +1,18 @@
Du baust den Fragen-Vorrat für eine Lern-Prüfung zum Thema "{topic}", Baustein "{baustein}". Erzeuge **Frage-Muster** — je eine konkrete Beispielfrage pro Subbaustein und Fragetyp. Aus diesen Mustern werden später bei der Prüfung leicht abgewandelte Fragen formuliert. Du baust den Fragen-Vorrat für eine Lern-Prüfung zum Thema "{topic}". Erzeuge **Frage-Muster** — je eine konkrete Beispielfrage pro Subbaustein und Fragetyp. Aus diesen Mustern werden später bei der Prüfung leicht abgewandelte Fragen formuliert.
RELEVANTE SUBBAUSTEINE (nur zu diesen Fragen bauen): Du bearbeitest **mehrere Bausteine**. Jeder Baustein hat seine eigenen relevanten Subbausteine:
{subbausteine} {bausteine}
FRAGETYPEN (Schlüssel : Bedeutung): FRAGETYPEN (Schlüssel : Bedeutung):
{typen} {typen}
AUFGABE: AUFGABE (für **jeden** der Bausteine oben):
- Erzeuge für **jeden** Subbaustein zu **jedem** passenden Fragetyp **ein** Muster (volles Kreuzprodukt). - Erzeuge für **jeden** seiner Subbausteine zu **jedem** passenden Fragetyp **ein** Muster (volles Kreuzprodukt).
- Lasse eine Kombination **weg**, wenn der Typ klar nicht passt (z.B. Code-Vorhersage bei reiner Theorie ohne Code). Nichts erzwingen. - Lasse eine Kombination **weg**, wenn der Typ klar nicht passt (z.B. Code-Vorhersage bei reiner Theorie ohne Code). Nichts erzwingen.
- Jedes Muster ist EINE konkrete, knappe Frage (12 Sätze, genau ein Fragezeichen, eine Sache) — kein Mehrteiler, keine Aufzählung. - Jedes Muster ist EINE konkrete, knappe Frage (12 Sätze, genau ein Fragezeichen, eine Sache) — kein Mehrteiler, keine Aufzählung.
- Die Frage muss aus dem Baustein-Wissen beantwortbar sein. Erfinde keine Zusatzannahmen. - Die Frage muss aus dem Baustein-Wissen beantwortbar sein. Erfinde keine Zusatzannahmen.
- Innerhalb eines Subbausteins sollen die Muster verschiedener Typen **wirklich verschiedene** Aspekte treffen — keine Umformulierungen derselben Frage. - Innerhalb eines Subbausteins sollen die Muster verschiedener Typen **wirklich verschiedene** Aspekte treffen — keine Umformulierungen derselben Frage.
- Der `typ` ist exakt einer der Schlüssel oben. `subbaustein` ist exakt einer der Titel oben. - Der `typ` ist exakt einer der Schlüssel oben. `baustein` ist exakt einer der Baustein-Titel oben. `subbaustein` ist exakt einer der Subbaustein-Titel des jeweiligen Bausteins.
HARTE STIL-REGELN JE FRAGE: HARTE STIL-REGELN JE FRAGE:
- Genau EINE Frage, ein Fragezeichen, eine Sache. Kein "und"/"sowie", kein "nenne drei …". - Genau EINE Frage, ein Fragezeichen, eine Sache. Kein "und"/"sowie", kein "nenne drei …".
@@ -20,9 +20,9 @@ HARTE STIL-REGELN JE FRAGE:
- Direkte Ansprache, klares Deutsch. - Direkte Ansprache, klares Deutsch.
{extra} {extra}
Schreibe das Ergebnis als JSON in die Datei {out_path} (nutze dein Schreib-Werkzeug), GENAU in diesem Format: Schreibe ALLE Muster aller Bausteine als EIN JSON in die Datei {out_path} (nutze dein Schreib-Werkzeug), GENAU in diesem Format:
{{"muster": [ {{"muster": [
{{"subbaustein": "<exakter Titel>", "typ": "<Schlüssel>", "frage": "<eine konkrete Frage>"}} {{"baustein": "<exakter Baustein-Titel>", "subbaustein": "<exakter Subbaustein-Titel>", "typ": "<Schlüssel>", "frage": "<eine konkrete Frage>"}}
]}} ]}}
Gib sonst keinen Text aus. Gib sonst keinen Text aus.