update
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
mit Sättigungslogik (Nullrunde nur mit neuen Queries gültig, R11)."""
|
||||
import hashlib
|
||||
|
||||
from . import config, db, engine, laden, llm, textkit
|
||||
from . import config, db, engine, graph, laden, llm, textkit
|
||||
|
||||
|
||||
def _thema(topic: str) -> str:
|
||||
@@ -11,25 +11,25 @@ def _thema(topic: str) -> str:
|
||||
return (t.get("titel") or t["name"]) if t else topic
|
||||
|
||||
|
||||
@engine.worker("korpus.recherche_plan", buendel=True)
|
||||
@engine.worker("korpus.recherche_plan")
|
||||
async def recherche_plan(task: dict) -> engine.Ergebnis:
|
||||
mitglieder = [task] + task.get("_buendel", [])
|
||||
lens_von = {m["id"]: db.uj(m["payload"], {}).get("lens", "")
|
||||
for m in mitglieder}
|
||||
"""EIN Task je Runde plant alle Blickwinkel (das Lens-Auffächern gehört
|
||||
hierher, nicht in die Engine)."""
|
||||
lenses = config.RECHERCHE_LENSES
|
||||
seed = []
|
||||
if task["runde"] == 1:
|
||||
# Wikipedia-Seed: kuratierter Grundstock ist Pflicht, nicht Suchglück
|
||||
# (Audit Run 7: wikipedia_de fehlte komplett) — einmalig, idempotent
|
||||
seed = [{"knoten": "suche", "item": "r1:wikiseed",
|
||||
# Startquellen-Seed: kuratierter Grundstock ist Pflicht, nicht
|
||||
# Suchglück — einmalig, idempotent; läuft durchs Inhalts-Urteil
|
||||
seed = [{"knoten": "suche", "item": "r1:seed",
|
||||
"payload": {"seed": True}}]
|
||||
bisherige = [db.uj(t["ergebnis"], {}).get("query", "") for t in db.query(
|
||||
"SELECT ergebnis FROM tasks WHERE topic=? AND knoten='suche'", task["topic"])]
|
||||
text = await llm.call(
|
||||
run_id=task["run_id"], stufe="korpus", knoten="recherche_plan",
|
||||
item=task["item"], role="judge", n=len(mitglieder),
|
||||
skill_namen=["planer", "deutsch-praezise", "recherche-plan"],
|
||||
item=task["item"], role="judge", n=len(lenses),
|
||||
skill_namen=graph.skills_von(task["knoten"]),
|
||||
werte={"thema": _thema(task["topic"]),
|
||||
"lenses": "\n".join(f"- {l}" for l in lens_von.values()),
|
||||
"lenses": "\n".join(f"- {l}" for l in lenses),
|
||||
"runde": task["runde"],
|
||||
"bisherige_queries": "\n".join(f"- {q}" for q in bisherige if q) or "-"})
|
||||
if text is None:
|
||||
@@ -39,19 +39,16 @@ async def recherche_plan(task: dict) -> engine.Ergebnis:
|
||||
q = block.get("text", "").strip()
|
||||
if q:
|
||||
je_lens.setdefault(block.get("lens", "").strip(), []).append(q)
|
||||
neue, teil = [], {}
|
||||
for m in mitglieder:
|
||||
queries = je_lens.get(lens_von[m["id"]], [])[:config.QUERIES_JE_LENS]
|
||||
if not queries: # Lens fehlt in der Antwort → neuer Versuch, nie stumm
|
||||
teil[m["id"]] = "neu"
|
||||
continue
|
||||
teil[m["id"]] = "fertig"
|
||||
neue = []
|
||||
for lens in lenses:
|
||||
queries = je_lens.get(lens, [])[:config.QUERIES_JE_LENS]
|
||||
if not queries: # Blickwinkel fehlt in der Antwort → Retry, nie stumm
|
||||
raise RuntimeError(f"recherche_plan: Blickwinkel '{lens}' ohne Query")
|
||||
for q in queries:
|
||||
h = hashlib.sha256(q.encode()).hexdigest()[:8]
|
||||
neue.append({"knoten": "suche", "item": f"r{m['runde']}:q:{h}",
|
||||
neue.append({"knoten": "suche", "item": f"r{task['runde']}:q:{h}",
|
||||
"payload": {"query": q}})
|
||||
return engine.Ergebnis(daten={"queries": len(neue)},
|
||||
neue_tasks=seed + neue, teil_status=teil)
|
||||
return engine.Ergebnis(daten={"queries": len(neue)}, neue_tasks=seed + neue)
|
||||
|
||||
|
||||
@engine.worker("korpus.soll_extraktion", buendel=True)
|
||||
@@ -66,7 +63,7 @@ async def soll_extraktion(task: dict) -> engine.Ergebnis:
|
||||
antwort = await llm.call(
|
||||
run_id=task["run_id"], stufe="korpus", knoten="soll_extraktion",
|
||||
item=task["item"], role="extraktion", n=len(mitglieder),
|
||||
skill_namen=["extraktor", "deutsch-praezise", "soll-extraktion"],
|
||||
skill_namen=graph.skills_von(task["knoten"]),
|
||||
werte={"thema": _thema(task["topic"]),
|
||||
"abschnitte": "\n\n".join(f"=== ABSCHNITT {i} ===\n{c}"
|
||||
for i, (_, _, c) in chunks.items())})
|
||||
@@ -117,7 +114,7 @@ async def soll_konsens(task: dict) -> engine.Ergebnis:
|
||||
antwort = await llm.call(
|
||||
run_id=task["run_id"], stufe="korpus", knoten="soll_konsens",
|
||||
item=f"{task['item']}:n{len(punkte)}", role="judge", n=len(punkte),
|
||||
skill_namen=["richter", "deutsch-praezise", "soll-konsens"],
|
||||
skill_namen=graph.skills_von(task["knoten"]),
|
||||
werte={"punkte": liste, "ziel": ziel})
|
||||
if antwort is None:
|
||||
raise RuntimeError("soll_konsens ohne Ergebnis")
|
||||
@@ -131,17 +128,11 @@ async def soll_konsens(task: dict) -> engine.Ergebnis:
|
||||
belege = []
|
||||
for i in ids:
|
||||
belege += db.uj(bekannt[i]["belege"], [])
|
||||
quellen = {b["quelle"] for b in belege}
|
||||
# DER CODE zählt — ≥2 unabhängige Quellen ODER 1 kuratierte
|
||||
# (Wikipedia = vertrauenswürdig, creator2-uni-Prinzip; sonst
|
||||
# verlieren Einzelquellen-Facetten wie „Overflow-Pomodoros")
|
||||
kuratiert = db.one(
|
||||
"SELECT id FROM quellen WHERE id IN ({}) AND backend LIKE "
|
||||
"'wikipedia%'".format(",".join("?" * len(quellen))),
|
||||
*quellen) if quellen else None
|
||||
noetig = config.SOLL_MIN_BELEGE_WIKI if kuratiert \
|
||||
else config.SOLL_MIN_BELEGE
|
||||
if len(quellen) >= noetig:
|
||||
# Vollständigkeit vor Vorsicht: 1 wörtlicher Beleg genügt (das
|
||||
# Zitat-Gate schützt vor Halluzination; die ≥2-Quellen-Regel
|
||||
# verwarf echte Facetten — Lektion 104). Dedup/Verdichtung/
|
||||
# Prüfungen fangen Überschuss hinten ab.
|
||||
if belege:
|
||||
kopf = ids[0]
|
||||
db.update("soll", "id=?", (kopf,), status="bestaetigt",
|
||||
punkt=gruppe.get("text", bekannt[kopf]["punkt"])[:300],
|
||||
@@ -173,25 +164,31 @@ async def gate(task: dict) -> engine.Ergebnis:
|
||||
if bestaetigt < config.SOLL_PUNKTE_MIN:
|
||||
befunde.append({"art": "soll_zu_wenig", "item": topic,
|
||||
"detail": f"{bestaetigt} < {config.SOLL_PUNKTE_MIN}"})
|
||||
runden_max = graph.get().knoten[task["knoten"]].runden_max
|
||||
gesaettigt = bestaetigt == vorher # Nullrunde (mit frischen Queries gelaufen)
|
||||
if not gesaettigt and runde < config.RECHERCHE_RUNDEN_MAX:
|
||||
if not gesaettigt and runde < runden_max:
|
||||
befunde.append({"art": "unsaettigt", "item": topic,
|
||||
"detail": f"Runde {runde}: {vorher}→{bestaetigt}"})
|
||||
|
||||
if befunde:
|
||||
if runde < config.RECHERCHE_RUNDEN_MAX:
|
||||
neue = [{"knoten": "recherche_plan", "item": f"r{runde + 1}:lens:{lens}",
|
||||
"runde": runde + 1, "art": "ruecklauf",
|
||||
"payload": {"lens": lens}}
|
||||
for lens in config.RECHERCHE_LENSES]
|
||||
if runde < runden_max:
|
||||
neue = [{"knoten": "recherche_plan", "item": f"r{runde + 1}:plan",
|
||||
"runde": runde + 1, "art": "ruecklauf"}]
|
||||
return engine.Ergebnis(gate_status="rot", befunde=befunde,
|
||||
neue_tasks=neue,
|
||||
daten={"bestaetigt": bestaetigt})
|
||||
|
||||
# grün → Rest-Kandidaten sind entschieden (Sättigung): verwerfen statt
|
||||
# als unadjudizierter Ballast liegen zu lassen
|
||||
db.execute("UPDATE soll SET status='verworfen' WHERE topic=? AND "
|
||||
"status='kandidat'", topic)
|
||||
# Harte Vollständigkeits-Stops (Lektion 103): Vollständigkeit ist DAS
|
||||
# Kernziel — fehlt die Basis, ist die Pipeline kaputt → PAUSE statt
|
||||
# kleiner Guide. Sichtbar als Run-Grund, Resume prüft erneut.
|
||||
if config.SEED_PFLICHT and not db.one(
|
||||
"SELECT id FROM quellen WHERE topic=? AND zweck='seed' AND "
|
||||
"status='geladen'", topic):
|
||||
raise llm.LaufPause("vollstaendigkeit:seed_fehlt")
|
||||
if quellen < config.QUELLEN_MIN:
|
||||
raise llm.LaufPause(
|
||||
f"vollstaendigkeit:quellen {quellen}<{config.QUELLEN_MIN}")
|
||||
|
||||
# Stufe inventar: Extraktion NUR belegnah (Lektion 92) — 2 Reader je
|
||||
# Fenster um die Soll-Belege. Quellen ohne Beleg liefern keine Atome.
|
||||
db.update("topics", "name=?", (topic,), status="korpus_fertig")
|
||||
@@ -201,7 +198,7 @@ async def gate(task: dict) -> engine.Ergebnis:
|
||||
for b in db.uj(s["belege"], []):
|
||||
belege_je_quelle.setdefault(b["quelle"], []).append(b["zitat"])
|
||||
for q in db.query("SELECT * FROM quellen WHERE topic=? AND status='geladen' "
|
||||
"AND zweck='korpus'", topic):
|
||||
"AND zweck IN ('korpus','seed')", topic):
|
||||
zitate = belege_je_quelle.get(q["id"])
|
||||
if not zitate:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user