Spezialisierung je Thema: begrenzt den Umfang auf einen Ausschnitt
Drittes Feld neben Thema und Hinweisen. Hinweise fügen hinzu, die Spezialisierung nimmt weg: Recherche, Gliederung, Kapitel und Prüfung bekommen sie als verbindliche Grenze; nur Stoff, der dafür gebraucht wird, Grundlagen nur soweit nötig. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ app.add_middleware(GZipMiddleware, minimum_size=1000) # Buchdaten sind 1–2 MB
|
||||
class ThemaNeu(BaseModel):
|
||||
name: str
|
||||
hinweise: str = ""
|
||||
spezialisierung: str = ""
|
||||
|
||||
|
||||
class Position(BaseModel):
|
||||
@@ -67,7 +68,7 @@ def _thema_info(t: dict) -> dict | None:
|
||||
if not vs:
|
||||
return None
|
||||
neueste = vs[-1]
|
||||
return {"slug": t["slug"], "name": t["name"], "titel": t["titel"], "hinweise": t.get("hinweise", ""),
|
||||
return {"slug": t["slug"], "name": t["name"], "titel": t["titel"], "hinweise": t.get("hinweise", ""), "spezialisierung": t.get("spezialisierung", ""),
|
||||
"angelegt": t.get("angelegt", ""), "status": neueste["status"], "laeuft": neueste["laeuft"],
|
||||
"stufe": neueste["stufe"], "versionen": vs}
|
||||
|
||||
@@ -83,7 +84,7 @@ async def thema_anlegen(body: ThemaNeu):
|
||||
if not state.name_ok(name):
|
||||
raise HTTPException(400, "Ungültiger Name")
|
||||
try:
|
||||
return _thema_info(state.anlegen(name, body.hinweise))
|
||||
return _thema_info(state.anlegen(name, body.hinweise, body.spezialisierung))
|
||||
except ValueError as e:
|
||||
raise HTTPException(409, str(e))
|
||||
|
||||
@@ -114,7 +115,7 @@ async def zustand(slug: str, n: int):
|
||||
key = _key(slug, n)
|
||||
t = _thema(slug)
|
||||
st = state.zustand(key)
|
||||
return {"slug": slug, "name": t["name"], "version": n, "titel": t["titel"], "hinweise": t.get("hinweise", ""), **st,
|
||||
return {"slug": slug, "name": t["name"], "version": n, "titel": t["titel"], "hinweise": t.get("hinweise", ""), "spezialisierung": t.get("spezialisierung", ""), **st,
|
||||
"deutung": st.get("deutung") or state.deutung(key), "ereignisse": st["ereignisse"][-100:]}
|
||||
|
||||
|
||||
|
||||
@@ -72,9 +72,9 @@ def _offen(name: str) -> None:
|
||||
s["status"] = "offen"
|
||||
|
||||
|
||||
def _thema(name: str) -> tuple[str, str]:
|
||||
def _thema(name: str) -> tuple[str, str, str]:
|
||||
t = state.thema(name) or {}
|
||||
return t.get("titel") or name, t.get("hinweise", "")
|
||||
return t.get("titel") or name, t.get("hinweise", ""), t.get("spezialisierung", "")
|
||||
|
||||
|
||||
def _kapitel(name: str) -> list[dict]:
|
||||
@@ -108,12 +108,13 @@ async def _parallel(name: str, sid: str, aufgaben: list) -> None:
|
||||
# ---- Stufen -------------------------------------------------------------------
|
||||
|
||||
async def st_recherche(name: str) -> None:
|
||||
thema, hinweise = _thema(name)
|
||||
thema, hinweise, spez = _thema(name)
|
||||
s = state.stufe(name, "recherche")
|
||||
s.update(gesamt=1, fertig=0)
|
||||
v = state.verzeichnis(name)
|
||||
if not state.bericht_lesen(name, "recherche"):
|
||||
p = prompts.laden("recherche", thema=thema, hinweise_block=prompts.hinweise_block(hinweise),
|
||||
spezialisierung_block=prompts.spezialisierung_block(spez),
|
||||
stil_basis=(config.PROMPTS / "stil_basis.md").read_text())
|
||||
r = await agenten.laufen(name, "recherche", "recherche", "Thema deuten und Quellen beschaffen", p)
|
||||
if r["status"] != "fertig":
|
||||
@@ -132,7 +133,7 @@ async def st_recherche(name: str) -> None:
|
||||
|
||||
|
||||
async def st_gliederung(name: str) -> None:
|
||||
thema, hinweise = _thema(name)
|
||||
thema, hinweise, spez = _thema(name)
|
||||
s = state.stufe(name, "gliederung")
|
||||
s.update(gesamt=1, fertig=0)
|
||||
p = state.verzeichnis(name) / "buch" / "GLIEDERUNG.md"
|
||||
@@ -140,7 +141,8 @@ async def st_gliederung(name: str) -> None:
|
||||
for versuch in (1, 2):
|
||||
if p.exists() and not gliederung.parsen(p.read_text())["fehler"] and state.bericht_lesen(name, "gliederung"):
|
||||
break
|
||||
pr = prompts.laden("gliederung", thema=thema, hinweise_block=prompts.hinweise_block(hinweise)) + hinweis
|
||||
pr = prompts.laden("gliederung", thema=thema, hinweise_block=prompts.hinweise_block(hinweise),
|
||||
spezialisierung_block=prompts.spezialisierung_block(spez)) + hinweis
|
||||
r = await agenten.laufen(name, "gliederung", "gliederung", "Gliederung entwerfen", pr)
|
||||
if r["status"] != "fertig":
|
||||
raise Abbruch("Gliederung gescheitert: " + r["bericht"][:200])
|
||||
@@ -156,14 +158,15 @@ async def st_gliederung(name: str) -> None:
|
||||
|
||||
|
||||
async def st_schreiben(name: str) -> None:
|
||||
thema, _ = _thema(name)
|
||||
thema, _, spez = _thema(name)
|
||||
kap = _kapitel(name)
|
||||
s = state.stufe(name, "schreiben")
|
||||
offen = [k for k in kap if not state.bericht_lesen(name, f"kapitel-{k['n']:02d}")]
|
||||
s.update(gesamt=len(kap), fertig=len(kap) - len(offen))
|
||||
aufgaben = []
|
||||
for k in offen:
|
||||
p = prompts.laden("kapitel", thema=thema, n=str(k["n"]), kapitel_titel=k["titel"], datei=k["datei"])
|
||||
p = prompts.laden("kapitel", thema=thema, n=str(k["n"]), kapitel_titel=k["titel"], datei=k["datei"],
|
||||
spezialisierung_block=prompts.spezialisierung_block(spez))
|
||||
if (state.verzeichnis(name) / "buch" / k["datei"]).exists():
|
||||
p += "\n\nHinweis: `buch/" + k["datei"] + "` existiert bereits aus einem abgebrochenen Lauf – prüfe sie und vervollständige oder ersetze sie."
|
||||
aufgaben.append((f"kapitel-{k['n']:02d}", f"Kapitel {k['n']} schreiben", p))
|
||||
@@ -171,7 +174,7 @@ async def st_schreiben(name: str) -> None:
|
||||
|
||||
|
||||
async def st_pruefung(name: str) -> None:
|
||||
thema, _ = _thema(name)
|
||||
thema, _, spez = _thema(name)
|
||||
kap = _kapitel(name)
|
||||
s = state.stufe(name, "pruefung")
|
||||
offen = [k for k in kap if not state.bericht_lesen(name, f"pruefung-{k['n']:02d}")]
|
||||
@@ -179,14 +182,15 @@ async def st_pruefung(name: str) -> None:
|
||||
aufgaben = []
|
||||
for k in offen:
|
||||
autor = state.bericht_lesen(name, f"kapitel-{k['n']:02d}") or "(kein Autorenbericht vorhanden)"
|
||||
p = prompts.laden("pruefung", thema=thema, n=str(k["n"]), kapitel_titel=k["titel"], datei=k["datei"], autor_bericht=autor)
|
||||
p = prompts.laden("pruefung", thema=thema, n=str(k["n"]), kapitel_titel=k["titel"], datei=k["datei"], autor_bericht=autor,
|
||||
spezialisierung_block=prompts.spezialisierung_block(spez))
|
||||
aufgaben.append((f"pruefung-{k['n']:02d}", f"Kapitel {k['n']} prüfen", p))
|
||||
await _parallel(name, "pruefung", aufgaben)
|
||||
|
||||
|
||||
async def st_lektorat(name: str) -> None:
|
||||
"""Geprüfte Kapitel sprachlich glätten; Marker: berichte/lektorat-NN.md."""
|
||||
thema, _ = _thema(name)
|
||||
thema, _, _ = _thema(name)
|
||||
kap = [k for k in _kapitel(name) if (state.verzeichnis(name) / "buch" / k["datei"]).exists()]
|
||||
s = state.stufe(name, "lektorat")
|
||||
offen = [k for k in kap if state.bericht_lesen(name, f"pruefung-{k['n']:02d}") and not state.bericht_lesen(name, f"lektorat-{k['n']:02d}")]
|
||||
@@ -209,7 +213,7 @@ async def st_lektorat(name: str) -> None:
|
||||
|
||||
|
||||
async def st_konsistenz(name: str) -> None:
|
||||
thema, _ = _thema(name)
|
||||
thema, _, _ = _thema(name)
|
||||
kap = _kapitel(name)
|
||||
gruppen = [kap[i:i + config.KONSISTENZ_GRUPPE] for i in range(0, len(kap), config.KONSISTENZ_GRUPPE)]
|
||||
s = state.stufe(name, "konsistenz")
|
||||
|
||||
@@ -11,3 +11,11 @@ def laden(name: str, **werte: str) -> str:
|
||||
|
||||
def hinweise_block(hinweise: str) -> str:
|
||||
return f"Hinweise des Auftraggebers (verbindlich): {hinweise}\n" if hinweise else ""
|
||||
|
||||
|
||||
def spezialisierung_block(spezialisierung: str) -> str:
|
||||
"""Hinweise fügen hinzu, die Spezialisierung nimmt weg: sie begrenzt den Umfang des Buchs auf einen Ausschnitt des Themas."""
|
||||
if not spezialisierung:
|
||||
return ""
|
||||
return (f"Spezialisierung (verbindliche Grenze des Umfangs): {spezialisierung}. Das Buch behandelt vom Thema nur, was dafür "
|
||||
"gebraucht wird; alles andere entfällt, auch wenn es zum Thema gehört. Grundlagen nur, soweit sie dafür nötig sind.\n")
|
||||
|
||||
@@ -119,12 +119,12 @@ def themen() -> list[dict]:
|
||||
return aus
|
||||
|
||||
|
||||
def anlegen(name: str, hinweise: str) -> dict:
|
||||
def anlegen(name: str, hinweise: str, spezialisierung: str = "") -> dict:
|
||||
d = config.STORAGE / name
|
||||
if d.exists():
|
||||
raise ValueError("Thema existiert schon")
|
||||
d.mkdir(parents=True)
|
||||
t = {"name": name, "slug": slug_frei(name), "titel": name, "hinweise": hinweise.strip(), "angelegt": jetzt()}
|
||||
t = {"name": name, "slug": slug_frei(name), "titel": name, "hinweise": hinweise.strip(), "spezialisierung": spezialisierung.strip(), "angelegt": jetzt()}
|
||||
_schreiben(d / "thema.json", t)
|
||||
version_anlegen(name)
|
||||
return t
|
||||
|
||||
Reference in New Issue
Block a user