This commit is contained in:
team3
2026-07-04 03:25:02 +02:00
parent c4caf31ed0
commit 2f5d5b9ca1
11 changed files with 176 additions and 18 deletions

View File

@@ -32,6 +32,12 @@ NOTE_GEWICHTE_GUIDE = {"fachlich_falsch": 3.0, "ziel_ohne_anker": 2.0, "marker_f
_MARKER = re.compile(r"<!--\s*sub:\s*\w+\s*\|\s*(.*?)\s*-->")
def _mnorm(s: str) -> str:
"""Marker-/Sub-Norm ohne Backslashes — escapte Titel (`h\\~2\\~o`) erzeugten
falsch-positive „Marker fehlt"-Befunde, weil Writer und DB verschieden escapen."""
return _norm_title(s.replace("\\", ""))
def _ausfuehrlich(md: str) -> str:
"""Der Lern-Fließtext einer Karte (hinter dem ausführlich-Marker, sonst alles)."""
teile = re.split(r"<!--\s*ausführlich\s*-->", md or "", maxsplit=1)
@@ -42,9 +48,10 @@ def marker_fehlend(cards: list[dict], subs_rel: dict[str, set]) -> list[str]:
"""Relevante Subs ohne Sub-Marker in der Section — der Level-Filter verliert sie."""
out = []
for c in cards:
marker = {_norm_title(m) for m in _MARKER.findall(c["md"] or "")}
marker = {_mnorm(m) for m in _MARKER.findall(c["md"] or "")}
for sn in sorted(subs_rel.get(c["block_norm"], set())):
if sn not in marker and not any(m.startswith(sn) or sn.startswith(m) for m in marker):
mn = _mnorm(sn)
if mn not in marker and not any(m.startswith(mn) or mn.startswith(m) for m in marker):
out.append(f"{c['block']} · {sn}")
return out
@@ -101,21 +108,37 @@ def lesbarkeit(cards: list[dict]) -> list[str]:
async def _fachlich_falsch(topic: str, cards: list[dict]) -> list[str]:
"""LLM-Stichprobe: Section enthält eine fachlich falsche Aussage? (eigenes Template)."""
"""LLM-Stichprobe: Section enthält eine fachlich falsche Aussage? Zwei unabhängige
Durchgänge, nur DOPPELT bestätigte zählen — ein Einzel-Judge schwankte zwischen
0 und 5 Befunden am selben Guide und kippte die Note (Gewicht 3.0) auf 0."""
from agents import run_agent
from jsonio import parse_json_text
from pipeline import _yesno_schema
out = []
for lo in range(0, len(cards), 5):
chunk = cards[lo:lo + 5]
listing = "\n\n".join(f"{k}. SECTION {c['block']}:\n{_ausfuehrlich(c['md'])[:LLM_SECTION_CHARS]}"
for k, c in enumerate(chunk, 1))
rc, txt, _err = await run_agent(
f"qa-guide-{topic}-fakten-{lo}", qa._qa_prompt("QA-Guide-Fakten", topic=topic, extra="", sections=listing),
600, role="judge", capabilities="none", scope=topic, label=f"Guide-QA Fakten {lo}")
v = (_yesno_schema(parse_json_text(txt)) or {}) if rc == 0 else {}
out += [c["block"] for k, c in enumerate(chunk, 1) if v.get(k) == "ja"]
return out
async def _pass(kandidaten: list[dict], tag: str) -> list[str]:
out = []
for lo in range(0, len(kandidaten), 5):
chunk = kandidaten[lo:lo + 5]
listing = "\n\n".join(f"{k}. SECTION {c['block']}:\n{_ausfuehrlich(c['md'])[:LLM_SECTION_CHARS]}"
for k, c in enumerate(chunk, 1))
rc, txt, _err = await run_agent(
f"qa-guide-{topic}-fakten{tag}-{lo}", qa._qa_prompt("QA-Guide-Fakten", topic=topic, extra="", sections=listing),
600, role="judge", capabilities="none", scope=topic, label=f"Guide-QA Fakten{tag} {lo}")
v = (_yesno_schema(parse_json_text(txt)) or {}) if rc == 0 else {}
out += [c["block"] for k, c in enumerate(chunk, 1) if v.get(k) == "ja"]
return out
verdacht = await _pass(cards, "")
if not verdacht:
return []
# ZWEI unabhängige Bestätiger, beide müssen zustimmen — mit nur einem sprang die
# Note desselben Guides weiter zwischen 2.0 und 6.6 (ein Zufalls-ja kostet 1.5 Punkte)
kandidaten = [c for c in cards if c["block"] in set(verdacht)]
b1 = set(await _pass(kandidaten, "-2"))
if not b1:
return []
b2 = set(await _pass([c for c in kandidaten if c["block"] in b1], "-3"))
return [b for b in verdacht if b in b1 and b in b2]
async def guide_qa_report(topic: str, llm: bool = False) -> dict | None: