update
This commit is contained in:
@@ -10,9 +10,10 @@ import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import belege
|
||||
import db
|
||||
import llm
|
||||
from config import ARTEFAKT_CHUNK_ATOME, BEISPIEL_FORMEN, VERIFY_PANEL
|
||||
from config import ARTEFAKT_CHUNK_ATOME, AUSSAGEN_JE_ATOM, BEISPIEL_FORMEN, VERIFY_PANEL
|
||||
|
||||
log = logging.getLogger("creator2.artefakte")
|
||||
|
||||
@@ -39,8 +40,10 @@ def _referenziert_quelle(inhalt: dict) -> bool:
|
||||
|
||||
|
||||
def _zitate(atom_id: int) -> str:
|
||||
rows = db.query("SELECT zitat FROM anker WHERE atom_id=? AND start>=0", (atom_id,))
|
||||
return "\n".join(f"> {r['zitat']}" for r in rows) or "(kein Anker)"
|
||||
# Quelltext-FENSTER um die Anker (belege.py) — nur das Zitat war zu schmal:
|
||||
# inhaltliche Karten zu Verfahren galten als unbelegt und churnten endlos.
|
||||
bloecke = belege.fenster_liste(atom_id)
|
||||
return "\n---\n".join(bloecke) or "(kein Anker)"
|
||||
|
||||
|
||||
def _atom_block(a: dict) -> str:
|
||||
@@ -267,6 +270,78 @@ def _formen_gate(topic: str, atom_ids: list[int] | None = None) -> None:
|
||||
db.update("artefakte", "id", b["id"], inhalt=db.j(inh_neu))
|
||||
|
||||
|
||||
async def _aussagen_generieren(ctx: llm.Kontext, chunk: list[dict]) -> None:
|
||||
"""Prüfungs-PAARE je Atom: wahre Aussage (ankerprüfbare Paraphrase) + falsche
|
||||
(GENAU EIN Aspekt gekippt — Mutations-Muster auf Satzebene). Der Code baut je
|
||||
Paar 2 Zeilen; die wahre Aussage ist gratis die Feedback-Erklärung der falschen."""
|
||||
bloecke = "\n\n".join(_atom_block(a) for a in chunk)
|
||||
res = await llm.call(ctx, stage="aussagen_gen", template="Aussagen-Generate",
|
||||
werte={"atome": bloecke, "paare": AUSSAGEN_JE_ATOM},
|
||||
role="guide", n=len(chunk), item=f"as{chunk[0]['id']}",
|
||||
erwartet=list)
|
||||
gueltig = {a["id"] for a in chunk}
|
||||
zaehler: dict[int, int] = {}
|
||||
for e in res or []:
|
||||
if not isinstance(e, dict) or e.get("atom") not in gueltig:
|
||||
continue
|
||||
atom_id = e["atom"]
|
||||
wahr, falsch = str(e.get("wahr", "")).strip(), str(e.get("falsch", "")).strip()
|
||||
if not wahr or not falsch or wahr == falsch:
|
||||
continue
|
||||
if _referenziert_quelle({"text": f"{wahr} {falsch}"}):
|
||||
continue
|
||||
i = zaehler.get(atom_id, 0)
|
||||
zaehler[atom_id] = i + 1
|
||||
paar = f"{atom_id}-{i}"
|
||||
db.insert("artefakte", atom_id=atom_id, typ="aussage", status="kandidat",
|
||||
inhalt=db.j({"text": wahr, "wahr": True, "paar": paar,
|
||||
"erklaerung": wahr}))
|
||||
db.insert("artefakte", atom_id=atom_id, typ="aussage", status="kandidat",
|
||||
inhalt=db.j({"text": falsch, "wahr": False, "paar": paar,
|
||||
"erklaerung": wahr}))
|
||||
|
||||
|
||||
async def _aussagen_verifizieren(ctx: llm.Kontext, chunk: list[dict]) -> None:
|
||||
"""Getrennter Maßstab je SOLL (Lehre aus der Fehlersuche): wahre müssen aus den
|
||||
Belegen folgen, falsche EINDEUTIG widersprechen. Panel einstimmig → verifiziert;
|
||||
unvollständiges Panel → kandidat lassen (Lektion 20)."""
|
||||
bloecke, kandidaten = [], []
|
||||
for a in chunk:
|
||||
rows = db.query("SELECT * FROM artefakte WHERE atom_id=? AND typ='aussage'"
|
||||
" AND status='kandidat'", (a["id"],))
|
||||
if not rows:
|
||||
continue
|
||||
zeilen = [f"ATOM {a['id']} Belege:\n{_zitate(a['id'])}"]
|
||||
for k in rows:
|
||||
inh = db.uj(k["inhalt"], {})
|
||||
soll = "wahr" if inh.get("wahr") else "falsch"
|
||||
zeilen.append(f"AUSSAGE {k['id']} [SOLL: {soll}]: {inh.get('text', '')}")
|
||||
kandidaten.append(k)
|
||||
bloecke.append("\n".join(zeilen))
|
||||
if not kandidaten:
|
||||
return
|
||||
stimmen = await llm.panel(ctx, VERIFY_PANEL, stage="aussagen_verify",
|
||||
template="Aussagen-Verify", role="judge",
|
||||
werte={"aussagen": "\n\n".join(bloecke)},
|
||||
n=len(kandidaten), item=f"asv{chunk[0]['id']}",
|
||||
erwartet=list)
|
||||
|
||||
def urteil_fuer(kid: int):
|
||||
def urteil(stimme):
|
||||
for e in stimme:
|
||||
if isinstance(e, dict) and e.get("aussage") == kid:
|
||||
return bool(e.get("ok"))
|
||||
return None
|
||||
return urteil
|
||||
|
||||
for k in kandidaten:
|
||||
votes = [u for s in stimmen if (u := urteil_fuer(k["id"])(s)) is not None]
|
||||
if len(votes) < VERIFY_PANEL:
|
||||
continue
|
||||
db.update("artefakte", "id", k["id"],
|
||||
status="verifiziert" if all(votes) else "verworfen")
|
||||
|
||||
|
||||
async def _kette(ctx: llm.Kontext, chunk: list[dict]) -> None:
|
||||
"""gen→gate→verify EINES Chunks verkettet — der Verify braucht nur die eigenen
|
||||
Kandidaten, nicht die Generierung fremder Chunks (Stage-Barriere kostete
|
||||
@@ -278,6 +353,12 @@ async def _kette(ctx: llm.Kontext, chunk: list[dict]) -> None:
|
||||
await _generieren(ctx, ohne)
|
||||
_formen_gate(ctx.topic, [a["id"] for a in chunk])
|
||||
await _verifizieren(ctx, chunk)
|
||||
ohne_aussage = [a for a in chunk if not db.one(
|
||||
"SELECT id FROM artefakte WHERE atom_id=? AND typ='aussage'"
|
||||
" AND status!='verworfen' LIMIT 1", (a["id"],))]
|
||||
if ohne_aussage:
|
||||
await _aussagen_generieren(ctx, ohne_aussage)
|
||||
await _aussagen_verifizieren(ctx, chunk)
|
||||
|
||||
|
||||
async def bauen(ctx: llm.Kontext) -> None:
|
||||
@@ -295,6 +376,10 @@ def messen(ctx: llm.Kontext) -> list[dict]:
|
||||
if not any(k["status"] == "verifiziert" for k in karten):
|
||||
befunde.append({"art": "atom_ohne_flashcard", "item": str(a["id"]),
|
||||
"detail": a["titel"]})
|
||||
if not db.one("SELECT id FROM artefakte WHERE atom_id=? AND typ='aussage'"
|
||||
" AND status='verifiziert' LIMIT 1", (a["id"],)):
|
||||
befunde.append({"art": "atom_ohne_aussage", "item": str(a["id"]),
|
||||
"detail": a["titel"]})
|
||||
offen = db.query("SELECT id FROM artefakte WHERE atom_id=? AND status='kandidat'",
|
||||
(a["id"],))
|
||||
for k in offen:
|
||||
|
||||
Reference in New Issue
Block a user