update
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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 1–2 Sätze. Verstöße → umformulieren.
|
- **Stil:** jede Frage GENAU EINE Frage, ein Fragezeichen, eine Sache. Kein "und"/"sowie", keine Aufzählung. Maximal 1–2 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.
|
||||||
|
|||||||
@@ -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 (1–2 Sätze, genau ein Fragezeichen, eine Sache) — kein Mehrteiler, keine Aufzählung.
|
- Jedes Muster ist EINE konkrete, knappe Frage (1–2 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.
|
||||||
|
|||||||
Reference in New Issue
Block a user