This commit is contained in:
team3
2026-06-22 12:24:44 +02:00
parent 2d1a3d3e37
commit 04900eef55
23 changed files with 53 additions and 1076 deletions

View File

@@ -1,4 +1,4 @@
"""Guide-Generierung als Konsens-Pipeline (Roadmap hat einen eigenen Weg).
"""Guide-Generierung als Konsens-Pipeline.
Auswahl: 5 Agenten (min. 3, Grace) → Code-Voting (Mehrheit = Konsens) →
Mapping-Agent sortiert Strittiges → Klärungs-Loop (max. KONSENS_MAX_RUNDEN).
@@ -24,7 +24,6 @@ import lesbarkeit
from database import list_guides, update_guide
from fsutil import atomic_write_json
from jsonio import read_json_file as _json_datei
from roadmap import _generate_roadmap
from paths import bausteine_path, guide_content_path, project_dir, subbausteine_path
from pipeline import (
CANCELLED, FAILED, GenContext, _claude_error, _extra,
@@ -429,7 +428,7 @@ async def _generate_sections(
def _hat_relevanz(num, art):
return any(isinstance(s, dict) and s.get("relevanz") == art for s in subs_raw.get(_titel(entries[num]), []))
# Schritt 0: Auswahl. Deterministisch je Format (Roadmap hat einen eigenen Weg):
# Schritt 0: Auswahl. Deterministisch je Format:
# Guide=relevante Bausteine · FullGuide=alle · Rest=Rand-Bausteine.
if format_name == "FullGuide":
auswahl = list(entries)
@@ -528,10 +527,10 @@ async def _generate_sections(
# Garantie: jeder gewählte Baustein steht im Plan (gegen weglassende Agenten/Judges).
plan = _mit_resten(plan, sel_entries)
# Chunks festlegen — Inhalte, Inhalts-Prüfung, Writer und Lese-Check nutzen
# dieselbe Aufteilung: 1 Agent je ~30 Bausteine (gedeckelt).
total_sections = sum(len(c["nums"]) for c in plan)
chunks = _split_chunks(plan, min(WRITER_MAX, max(1, math.ceil(total_sections / WRITER_SECTIONS))))
# Chunks festlegen: 1 Chunk = 1 Baustein. So läuft JEDER Section-Schritt (Inhalte,
# Inhalts-Prüfung, Writer, Lese-Check) mit genau einem Agent je Baustein. Der Writer
# rationiert sein Output-Budget dann nicht über viele Sections → längere, variable Texte.
chunks = [[{"title": ch["title"], "nums": [num]}] for ch in plan for num in ch["nums"]]
# 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).
if format_name == "FullGuide":
@@ -727,10 +726,9 @@ async def _generate_sections(
if is_cancelled():
return None
if res is None:
if runde == 1:
await _fail(guide_id, "Lese-Prüfung fehlgeschlagen")
return None
_log(topic, f"Lese-Prüfung Runde {runde} fehlgeschlagen — Stand bleibt")
# Nicht hart failen: bei 1 Agent je Baustein darf ein einzelner Check-Ausfall
# nicht den ganzen Guide kippen. Sections sind geschrieben; Lese-Prüfung ist Politur.
_log(topic, f"Lese-Prüfung Runde {runde} ohne vollständiges Ergebnis — Stand bleibt")
break
probleme_by_num: dict[int, str] = {}
@@ -859,25 +857,19 @@ async def generate_guide(guide_id: str, topic: str, format_name: str, instructio
for p_alt in guide_slot_dateien(content_path):
p_alt.unlink(missing_ok=True)
if format_name == "Roadmap":
graph = await _generate_roadmap(guide_id, topic, instructions, provider, content_path)
if graph is None or is_guide_cancelled(guide_id):
return
content = {"topic": topic, "format": format_name, "graph": graph}
else:
alle = _lade_bausteine(bausteine_path(topic).read_text(encoding="utf-8"))
if not alle:
await _fail(guide_id, "Keine Bausteine gefunden")
return
entries = _eindeutige_titel(alle)
facts = _prompt("Guide-Fakten-Projekt", project=project) if project else _prompt("Guide-Fakten-Thema")
chapters = await _generate_sections(
guide_id, topic, format_name, entries,
facts, instructions, provider, content_path,
)
if chapters is None or is_guide_cancelled(guide_id):
return
content = {"topic": topic, "format": format_name, "chapters": chapters}
alle = _lade_bausteine(bausteine_path(topic).read_text(encoding="utf-8"))
if not alle:
await _fail(guide_id, "Keine Bausteine gefunden")
return
entries = _eindeutige_titel(alle)
facts = _prompt("Guide-Fakten-Projekt", project=project) if project else _prompt("Guide-Fakten-Thema")
chapters = await _generate_sections(
guide_id, topic, format_name, entries,
facts, instructions, provider, content_path,
)
if chapters is None or is_guide_cancelled(guide_id):
return
content = {"topic": topic, "format": format_name, "chapters": chapters}
atomic_write_json(content_path, content, indent=1)