update
This commit is contained in:
@@ -56,8 +56,12 @@ SUBBAUSTEIN_KAPPE = 900 # Subbaustein-Finde-Loop je Chunk (15 min)
|
|||||||
KONSOLIDIERUNG_CHUNK = 600 # bis hierher EIN globaler Judge (dedupt alles); darüber chunked + Merge-Pass — nur Fallback-Pfad
|
KONSOLIDIERUNG_CHUNK = 600 # bis hierher EIN globaler Judge (dedupt alles); darüber chunked + Merge-Pass — nur Fallback-Pfad
|
||||||
DEDUP_MAX_RUNDEN = 3 # finaler Dedup-Pass: max. Iterationen (die kleinere Liste blockt je Runde neu)
|
DEDUP_MAX_RUNDEN = 3 # finaler Dedup-Pass: max. Iterationen (die kleinere Liste blockt je Runde neu)
|
||||||
DEDUP_MIN_DELTA = 3 # Abbruch, wenn eine Runde weniger als dieses % der Liste entfernt (konvergiert)
|
DEDUP_MIN_DELTA = 3 # Abbruch, wenn eine Runde weniger als dieses % der Liste entfernt (konvergiert)
|
||||||
|
DEDUP_PAAR_FLOOR = 0.6 # Mindest-Cosine fürs Kandidaten-Paar (Complete-Link aggregiert → kein Chaining)
|
||||||
|
DEDUP_PAARE_CHUNK = 40 # Paare je Judge-Paket (Pairwise-Verifikation statt Block-Mischer)
|
||||||
|
FILTER_CHUNK = 35 # zu beurteilende Bausteine je Judge im Degradier-Pass (volle Liste als Kontext)
|
||||||
# Frage-Muster-Chunks per LPT nach Sub-Last balancieren (Makespan), statt nach Baustein-Anzahl.
|
# Frage-Muster-Chunks per LPT nach Sub-Last balancieren (Makespan), statt nach Baustein-Anzahl.
|
||||||
FRAGE_CHUNK_SUBS = 50 # Ziel-Summe relevanter Subs je Chunk
|
FRAGE_CHUNK_SUBS = 50 # Ziel-Summe relevanter Subs je Chunk
|
||||||
|
FRAGE_MAX_RUNDEN = 3 # Nachhol-Runden für Subs ohne Muster (LLM lässt pro Chunk ~18 % aus)
|
||||||
FAKTEN_CHUNK_SUBS = 25 # Fakten-Extraktion: kleinere Chunks (Fakten sind umfangreicher als Muster)
|
FAKTEN_CHUNK_SUBS = 25 # Fakten-Extraktion: kleinere Chunks (Fakten sind umfangreicher als Muster)
|
||||||
FAKTEN_CHECK_PANEL = 3 # Judges je Chunk im Fakten-Check (Mehrheit beanstandet)
|
FAKTEN_CHECK_PANEL = 3 # Judges je Chunk im Fakten-Check (Mehrheit beanstandet)
|
||||||
KONSOLIDIERUNG_PANEL = 3 # Mapping-Judges je Chunk (Panel → Reconcile statt Einzel-Judge)
|
KONSOLIDIERUNG_PANEL = 3 # Mapping-Judges je Chunk (Panel → Reconcile statt Einzel-Judge)
|
||||||
@@ -71,7 +75,7 @@ _bausteine_cancelled: set[str] = set()
|
|||||||
_bausteine_step: dict[str, int] = {}
|
_bausteine_step: dict[str, int] = {}
|
||||||
|
|
||||||
BAUSTEINE_STEPS = (
|
BAUSTEINE_STEPS = (
|
||||||
"Recherche", "Konsolidierung", "Klärung",
|
"Recherche", "Konsolidierung", "Klärung", "Dedup", "Bausteine-Filter",
|
||||||
"Subbausteine finden", "Subbausteine wählen", "Subbausteine klären",
|
"Subbausteine finden", "Subbausteine wählen", "Subbausteine klären",
|
||||||
"Fakten finden", "Fakten prüfen", "Fakten fix",
|
"Fakten finden", "Fakten prüfen", "Fakten fix",
|
||||||
"Stufen finden", "Stufen wählen", "Stufen klären",
|
"Stufen finden", "Stufen wählen", "Stufen klären",
|
||||||
@@ -207,7 +211,7 @@ def _bausteine_steps(topic: str) -> tuple:
|
|||||||
laufen alle Pakete parallel; der Schritt bleibt, bis das letzte Paket fertig ist.
|
laufen alle Pakete parallel; der Schritt bleibt, bis das letzte Paket fertig ist.
|
||||||
"""
|
"""
|
||||||
q = lade_quelle(topic)
|
q = lade_quelle(topic)
|
||||||
base = ("Recherche", "Konsolidierung", "Klärung", "Dedup")
|
base = ("Recherche", "Konsolidierung", "Klärung", "Dedup", "Bausteine-Filter")
|
||||||
rest = (
|
rest = (
|
||||||
"Subbausteine finden", "Subbausteine wählen", "Subbausteine klären",
|
"Subbausteine finden", "Subbausteine wählen", "Subbausteine klären",
|
||||||
"Fakten finden", "Fakten prüfen", "Fakten fix",
|
"Fakten finden", "Fakten prüfen", "Fakten fix",
|
||||||
@@ -237,7 +241,7 @@ def _melde_p(set_p, topic: str, schritt: str):
|
|||||||
# Sonderschritte (Quelle laden, Ergänzung) gehören zur Phase „Inventar".
|
# Sonderschritte (Quelle laden, Ergänzung) gehören zur Phase „Inventar".
|
||||||
PHASEN = (
|
PHASEN = (
|
||||||
("Quelle", ("Quelle aufbereiten",)),
|
("Quelle", ("Quelle aufbereiten",)),
|
||||||
("Inventar", ("Recherche", "Konsolidierung", "Klärung", "Dedup", "Ergänzung")),
|
("Inventar", ("Recherche", "Konsolidierung", "Klärung", "Dedup", "Bausteine-Filter", "Ergänzung")),
|
||||||
("Subbausteine", ("Subbausteine finden", "Subbausteine wählen", "Subbausteine klären")),
|
("Subbausteine", ("Subbausteine finden", "Subbausteine wählen", "Subbausteine klären")),
|
||||||
("Fakten", ("Fakten finden", "Fakten prüfen", "Fakten fix")),
|
("Fakten", ("Fakten finden", "Fakten prüfen", "Fakten fix")),
|
||||||
("Stufen", ("Stufen finden", "Stufen wählen", "Stufen klären")),
|
("Stufen", ("Stufen finden", "Stufen wählen", "Stufen klären")),
|
||||||
@@ -298,7 +302,8 @@ def _alle_slot_dateien(files: dict) -> list[Path]:
|
|||||||
dyn = (list(arbeit.glob("subbaustein-*")) + list(arbeit.glob("fakten-*")) + list(arbeit.glob("stufe-*")) + list(arbeit.glob("relevanz-*"))
|
dyn = (list(arbeit.glob("subbaustein-*")) + list(arbeit.glob("fakten-*")) + list(arbeit.glob("stufe-*")) + list(arbeit.glob("relevanz-*"))
|
||||||
+ list(arbeit.glob("frage-muster-*")) + list(arbeit.glob("gliederung-*")) + list(arbeit.glob("artefakt-*"))
|
+ list(arbeit.glob("frage-muster-*")) + list(arbeit.glob("gliederung-*")) + list(arbeit.glob("artefakt-*"))
|
||||||
+ list(arbeit.glob("recherche-*")) + list(arbeit.glob("konsolidierung-*"))
|
+ list(arbeit.glob("recherche-*")) + list(arbeit.glob("konsolidierung-*"))
|
||||||
+ list(arbeit.glob("klaerung*")) + list(arbeit.glob("dedup-*"))) if arbeit.is_dir() else []
|
+ list(arbeit.glob("klaerung*")) + list(arbeit.glob("dedup-*"))
|
||||||
|
+ list(arbeit.glob("inventar-filter*"))) if arbeit.is_dir() else []
|
||||||
return [
|
return [
|
||||||
*files["recherche"], files["recherche_mapping"],
|
*files["recherche"], files["recherche_mapping"],
|
||||||
*(p for slots in files["auswahl"].values() for p in slots),
|
*(p for slots in files["auswahl"].values() for p in slots),
|
||||||
@@ -1577,31 +1582,79 @@ async def _frage_muster_block(ctx: GenContext, set_p, files: dict, sidecar: dict
|
|||||||
final_by_titel = _final_by_titel(range(len(chunks)))
|
final_by_titel = _final_by_titel(range(len(chunks)))
|
||||||
ergebnis = {t: (final_by_titel.get(t) or roh_by_titel.get(t) or []) for t, _ in bausteine}
|
ergebnis = {t: (final_by_titel.get(t) or roh_by_titel.get(t) or []) for t, _ in bausteine}
|
||||||
|
|
||||||
# Phase „Fragen prüfen": Chunks mit ≥1 leeren Baustein eine Runde nachholen.
|
# Phase „Fragen prüfen": pro-Sub-Vollständigkeit. Generatoren stürzen zufällig ab (~15 %),
|
||||||
|
# je Chunk 1 Agent ohne Retry → Subs (ganze Bausteine) fallen still durch. Darum mehrere
|
||||||
|
# Runden, die NUR die fehlenden Subs gezielt nachfordern (kurze Pakete, Frage-Muster-Recherche).
|
||||||
set_p("Fragen prüfen…", step=_step_idx(topic, "Fragen prüfen"))
|
set_p("Fragen prüfen…", step=_step_idx(topic, "Fragen prüfen"))
|
||||||
leer_titel = {t for t, _ in bausteine if not ergebnis.get(t)}
|
|
||||||
if leer_titel:
|
def _fehlende_subs() -> list[tuple[str, list[str]]]:
|
||||||
nach = [ci for ci, idxs in enumerate(chunks) if any(bausteine[i][0] in leer_titel for i in idxs)]
|
out = []
|
||||||
_log(topic, f"Frage-Muster: {len(leer_titel)} Baustein(e) ohne Muster — Nachrunde über {len(nach)} Chunk(s)")
|
for t, subs in bausteine:
|
||||||
for ci in nach:
|
hab = {_norm_titel(e["subbaustein"]) for e in ergebnis.get(t) or []}
|
||||||
roh_path(ci).unlink(missing_ok=True)
|
miss = [s for s in subs if _norm_titel(s) not in hab]
|
||||||
final_path(ci).unlink(missing_ok=True)
|
if miss:
|
||||||
await finde_alle(nach)
|
out.append((t, miss))
|
||||||
|
return out
|
||||||
|
|
||||||
|
def _nach_block(items): # items: [(baustein_titel, [fehlende sub_titel])]
|
||||||
|
bloecke = []
|
||||||
|
for titel, subs in items:
|
||||||
|
zeilen = []
|
||||||
|
for s in subs:
|
||||||
|
z = f"- {s}"
|
||||||
|
fk = fakten_by.get((titel, _norm_titel(s)))
|
||||||
|
if fk and (ft := _fakten_zeilen(fk)):
|
||||||
|
z += "\n" + "\n".join(" " + l for l in ft.split("\n"))
|
||||||
|
zeilen.append(z)
|
||||||
|
bloecke.append(f"BAUSTEIN: {titel}\nSUBBAUSTEINE:\n" + "\n".join(zeilen))
|
||||||
|
return "\n\n".join(bloecke)
|
||||||
|
|
||||||
|
async def _nachfordere(runde, pi, items):
|
||||||
|
fp = arbeit / f"frage-muster-nach{runde}-c{pi}.json"
|
||||||
|
if _frage_muster_chunk_schema(_json_datei(fp)):
|
||||||
|
return # Resume
|
||||||
|
subs_total = sum(len(s) for _, s in items)
|
||||||
|
await run_single_slot(
|
||||||
|
ctx, f"Frage-Muster Nachholung R{runde}/{pi}",
|
||||||
|
key=f"bausteine-{topic}-frage-muster-nach{runde}-c{pi}",
|
||||||
|
prompt=_prompt("Frage-Muster-Recherche", topic=topic, bausteine=_nach_block(items),
|
||||||
|
out_path=fp, extra=_extra(instructions)),
|
||||||
|
role="fast", capabilities="files",
|
||||||
|
payload=lambda result, p=fp: _frage_muster_chunk_schema(_json_datei(p)),
|
||||||
|
timeout=_timeout("frage_muster", subs_total),
|
||||||
|
)
|
||||||
|
|
||||||
|
for runde in range(1, FRAGE_MAX_RUNDEN + 1):
|
||||||
|
fehlend = _fehlende_subs()
|
||||||
|
if not fehlend:
|
||||||
|
break
|
||||||
|
n_subs = sum(len(s) for _, s in fehlend)
|
||||||
|
_log(topic, f"Frage-Muster Runde {runde}: {n_subs} Sub(s) in {len(fehlend)} Baustein(en) ohne Muster — Nachforderung")
|
||||||
|
pakete = _lpt_chunks([len(s) for _, s in fehlend], FRAGE_CHUNK_SUBS)
|
||||||
|
paket_items = [[fehlend[i] for i in idxs] for idxs in pakete]
|
||||||
|
await _gather_fortschritt(
|
||||||
|
[_nachfordere(runde, pi, items) for pi, items in enumerate(paket_items)],
|
||||||
|
len(paket_items), _melde_p(set_p, topic, "Fragen prüfen"))
|
||||||
if is_cancelled():
|
if is_cancelled():
|
||||||
return None
|
return None
|
||||||
for titel, eintraege in _waehle_all(nach).items():
|
# Output je Paket parsen + neu gewonnene Subs mergen (Vorhandene nicht überschreiben).
|
||||||
roh_by_titel[titel] = eintraege
|
for pi, items in enumerate(paket_items):
|
||||||
await klaere_alle(nach)
|
titel_subs = {t: subs for t, subs in items}
|
||||||
if is_cancelled():
|
ctitel = list(titel_subs.keys())
|
||||||
return None
|
for e in _frage_muster_chunk_schema(_json_datei(arbeit / f"frage-muster-nach{runde}-c{pi}.json")) or []:
|
||||||
for titel, eintraege in _final_by_titel(nach).items():
|
titel = _match_sub(e["baustein"], ctitel)
|
||||||
final_by_titel[titel] = eintraege
|
if titel not in titel_subs:
|
||||||
for t, _ in bausteine:
|
continue
|
||||||
if not ergebnis.get(t):
|
sub = _match_sub(e["subbaustein"], titel_subs[titel])
|
||||||
ergebnis[t] = final_by_titel.get(t) or roh_by_titel.get(t) or []
|
hab = {_norm_titel(x["subbaustein"]) for x in ergebnis.get(titel) or []}
|
||||||
rest = [t for t, _ in bausteine if not ergebnis.get(t)]
|
if _norm_titel(sub) in hab:
|
||||||
|
continue
|
||||||
|
ergebnis.setdefault(titel, []).append({"subbaustein": sub, "frage": e["frage"]})
|
||||||
|
|
||||||
|
rest = _fehlende_subs()
|
||||||
if rest:
|
if rest:
|
||||||
_log(topic, f"Frage-Muster: {len(rest)} Baustein(e) bleiben leer (Fallback Live): {rest[:5]}")
|
n = sum(len(s) for _, s in rest)
|
||||||
|
_log(topic, f"Frage-Muster: {n} Sub(s) in {len(rest)} Baustein(en) bleiben nach {FRAGE_MAX_RUNDEN} Runden leer: {[t for t, _ in rest][:5]}")
|
||||||
return ergebnis
|
return ergebnis
|
||||||
|
|
||||||
|
|
||||||
@@ -1972,6 +2025,21 @@ def _aspekt_marker(titel: str) -> int:
|
|||||||
return sum(1 for m in _ASPEKT_MARKER if m in t)
|
return sum(1 for m in _ASPEKT_MARKER if m in t)
|
||||||
|
|
||||||
|
|
||||||
|
_VERWEIS_RE = re.compile(r'^(Satz|Lemma|Korollar|Bemerkung|Definition)\s*[\d.]+\s*(\([a-z]\)|[a-z])?\s*$', re.I)
|
||||||
|
|
||||||
|
|
||||||
|
def _ist_verweis(titel: str) -> bool:
|
||||||
|
"""True für reine Verweis-/Platzhalter-Titel OHNE sprechenden Inhalt: „Satz 7.18", „Lemma 6.2",
|
||||||
|
„Korollar 6.18" (Nummer ohne Namen) sowie markierte Stellen „Bedingung (**)". NICHT „Satz 6.24:
|
||||||
|
Cook/Levin" (hat Namen) und NICHT kurze Fachsymbole wie „P⊆NP"/„Σ*" (echte Konzepte)."""
|
||||||
|
t = titel.strip()
|
||||||
|
if _VERWEIS_RE.match(t):
|
||||||
|
return True
|
||||||
|
if re.search(r'\(\*+\)', t): # markierte Stelle „(**)" / „(*)"
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _canonical(kandidaten: list[dict], idxs: list[int], gesehen_norm: set[str]) -> dict:
|
def _canonical(kandidaten: list[dict], idxs: list[int], gesehen_norm: set[str]) -> dict:
|
||||||
"""Repräsentant eines Clusters = das Hauptkonzept (wenigste Eigenschafts-Marker — das Problem
|
"""Repräsentant eines Clusters = das Hauptkonzept (wenigste Eigenschafts-Marker — das Problem
|
||||||
selbst, nicht „… ∈ NP"); Tie → häufigster norm-Titel → meiste Reader. Titel global eindeutig
|
selbst, nicht „… ∈ NP"); Tie → häufigster norm-Titel → meiste Reader. Titel global eindeutig
|
||||||
@@ -1983,7 +2051,9 @@ def _canonical(kandidaten: list[dict], idxs: list[int], gesehen_norm: set[str])
|
|||||||
def gewicht(nb: str):
|
def gewicht(nb: str):
|
||||||
ms = by_norm[nb]
|
ms = by_norm[nb]
|
||||||
reader = set().union(*[set(kandidaten[m]["reader"]) for m in ms]) if ms else set()
|
reader = set().union(*[set(kandidaten[m]["reader"]) for m in ms]) if ms else set()
|
||||||
return (-_aspekt_marker(nb), len(ms), len(reader)) # aspekt-arm zuerst = Hauptkonzept
|
# Verweis-/Platzhalter-Titel ("Satz 7.18") zuletzt — sprechendes Mitglied bevorzugen.
|
||||||
|
echt = not _ist_verweis(kandidaten[ms[0]]["titel"])
|
||||||
|
return (echt, -_aspekt_marker(nb), len(ms), len(reader))
|
||||||
|
|
||||||
best = max(by_norm, key=gewicht)
|
best = max(by_norm, key=gewicht)
|
||||||
k = max(by_norm[best], key=lambda m: len(kandidaten[m]["beschreibung"]))
|
k = max(by_norm[best], key=lambda m: len(kandidaten[m]["beschreibung"]))
|
||||||
@@ -2231,10 +2301,17 @@ async def _klaere_inventar(ctx: GenContext, set_p, files: dict) -> bool:
|
|||||||
return True
|
return True
|
||||||
set_p("Klärung läuft…", step=_step_idx(topic, "Klärung"))
|
set_p("Klärung läuft…", step=_step_idx(topic, "Klärung"))
|
||||||
rest_rows = await db.list_bausteine(topic, status="rest")
|
rest_rows = await db.list_bausteine(topic, status="rest")
|
||||||
if rest_rows:
|
# Durchgängiges Gate (EDC „Define"): auch Konsens-Bausteine mit Verweis-/Platzhalter-Titel
|
||||||
|
# prüfen („Satz 7.18", „Korollar 6.18", „Bedingung (**)") — sie umgehen sonst jede Prüfung.
|
||||||
|
verdaechtig = [b for b in await db.list_bausteine(topic, status="konsens") if _ist_verweis(b["titel"])]
|
||||||
|
pruef_rows = rest_rows + verdaechtig
|
||||||
|
if pruef_rows:
|
||||||
arbeit = files["arbeit"]
|
arbeit = files["arbeit"]
|
||||||
paths = [arbeit / f"klaerung-j{j}.json" for j in range(1, KONSOLIDIERUNG_PANEL + 1)]
|
paths = [arbeit / f"klaerung-j{j}.json" for j in range(1, KONSOLIDIERUNG_PANEL + 1)]
|
||||||
offen = [(j, p) for j, p in enumerate(paths, 1) if _runde_schema(_json_datei(p), final=True) is None]
|
# final=False: ein Judge mit versehentlich nicht-leerem `rest` darf nicht komplett ausfallen
|
||||||
|
# (sonst kollabiert das Panel auf 1 Judge). Sein `aufnehmen` zählt; rest-Einträge gelten als
|
||||||
|
# nicht-aufgenommen. Die „rest leer"-Vorgabe steht weiter im Prompt.
|
||||||
|
offen = [(j, p) for j, p in enumerate(paths, 1) if _runde_schema(_json_datei(p)) is None]
|
||||||
for _, p in offen:
|
for _, p in offen:
|
||||||
p.unlink(missing_ok=True)
|
p.unlink(missing_ok=True)
|
||||||
if offen:
|
if offen:
|
||||||
@@ -2242,19 +2319,20 @@ async def _klaere_inventar(ctx: GenContext, set_p, files: dict) -> bool:
|
|||||||
"key": f"bausteine-{topic}-klaerung-j{j}",
|
"key": f"bausteine-{topic}-klaerung-j{j}",
|
||||||
"prompt": _prompt(
|
"prompt": _prompt(
|
||||||
"Bausteine-Klaerung", topic=topic,
|
"Bausteine-Klaerung", topic=topic,
|
||||||
rest="\n".join(f"- {b['titel']}" for b in rest_rows),
|
rest="\n".join(f"- {b['titel']} — {b['beschreibung']}" if b['beschreibung'] else f"- {b['titel']}"
|
||||||
|
for b in pruef_rows),
|
||||||
final="\n- Entscheide JEDEN Eintrag. `rest` MUSS leer sein.",
|
final="\n- Entscheide JEDEN Eintrag. `rest` MUSS leer sein.",
|
||||||
out_path=p,
|
out_path=p,
|
||||||
),
|
),
|
||||||
"role": "judge", "capabilities": "files",
|
"role": "judge", "capabilities": "files",
|
||||||
"payload": (lambda result, p=p: _runde_schema(_json_datei(p), final=True)),
|
"payload": (lambda result, p=p: _runde_schema(_json_datei(p))),
|
||||||
} for j, p in offen]
|
} for j, p in offen]
|
||||||
vorhanden = KONSOLIDIERUNG_PANEL - len(offen)
|
vorhanden = KONSOLIDIERUNG_PANEL - len(offen)
|
||||||
await _race(topic, "Klärung", slots, max(1, 2 - vorhanden),
|
await _race(topic, "Klärung", slots, max(1, 2 - vorhanden),
|
||||||
_timeout("auswahl_mapping", len(rest_rows)), provider, cancelled=is_cancelled, grace=KONSENS_GRACE)
|
_timeout("auswahl_mapping", len(pruef_rows)), provider, cancelled=is_cancelled, grace=KONSENS_GRACE)
|
||||||
if is_cancelled():
|
if is_cancelled():
|
||||||
return False
|
return False
|
||||||
outs = [r for p in paths if (r := _runde_schema(_json_datei(p), final=True))]
|
outs = [r for p in paths if (r := _runde_schema(_json_datei(p)))]
|
||||||
if not outs:
|
if not outs:
|
||||||
_bausteine_errors[topic] = "Klärung fehlgeschlagen"
|
_bausteine_errors[topic] = "Klärung fehlgeschlagen"
|
||||||
return False
|
return False
|
||||||
@@ -2263,18 +2341,85 @@ async def _klaere_inventar(ctx: GenContext, set_p, files: dict) -> bool:
|
|||||||
for aufnehmen, _ in outs:
|
for aufnehmen, _ in outs:
|
||||||
for nt in {_norm_titel(_titel(t)) for t in aufnehmen}:
|
for nt in {_norm_titel(_titel(t)) for t in aufnehmen}:
|
||||||
votes[nt] = votes.get(nt, 0) + 1
|
votes[nt] = votes.get(nt, 0) + 1
|
||||||
for b in rest_rows:
|
# Umbenenn-Vorschläge (additiv aus dem Roh-JSON — _runde_schema kennt das Feld nicht):
|
||||||
|
# behaltene Verweis-/Platzhalter-Titel → sprechender Name aus dem Inhalt. Alt-Titel-Norm
|
||||||
|
# bleibt stabil (bricht den votes-Match nicht); je Alt-Titel der häufigste Vorschlag.
|
||||||
|
umbenenn: dict[str, dict[str, int]] = {}
|
||||||
|
for p in paths:
|
||||||
|
d = _json_datei(p)
|
||||||
|
umb = d.get("umbenennen") if isinstance(d, dict) else None
|
||||||
|
if isinstance(umb, dict):
|
||||||
|
for alt, neu in umb.items():
|
||||||
|
neu = str(neu).strip()
|
||||||
|
if neu:
|
||||||
|
umbenenn.setdefault(_norm_titel(str(alt)), {}).setdefault(neu, 0)
|
||||||
|
umbenenn[_norm_titel(str(alt))][neu] += 1
|
||||||
|
gesehen_norm = {b["titel_norm"] for b in await db.list_bausteine(topic, status="konsens")}
|
||||||
|
for b in pruef_rows:
|
||||||
auf = votes.get(b["titel_norm"], 0) * 2 >= len(outs)
|
auf = votes.get(b["titel_norm"], 0) * 2 >= len(outs)
|
||||||
await db.set_baustein_status(topic, b["titel_norm"], "konsens" if auf else "verworfen")
|
if not auf:
|
||||||
|
await db.set_baustein_status(topic, b["titel_norm"], "verworfen")
|
||||||
|
continue
|
||||||
|
neu_titel = None
|
||||||
|
if _ist_verweis(b["titel"]) and (vors := umbenenn.get(b["titel_norm"])):
|
||||||
|
kand = max(vors, key=lambda k: (vors[k], len(k)))
|
||||||
|
if not _ist_verweis(kand):
|
||||||
|
neu_titel = kand
|
||||||
|
if neu_titel:
|
||||||
|
nn, t, n = _norm_titel(neu_titel), neu_titel, 2
|
||||||
|
while nn in gesehen_norm:
|
||||||
|
t, nn, n = f"{neu_titel} ({n})", _norm_titel(f"{neu_titel} ({n})"), n + 1
|
||||||
|
gesehen_norm.add(nn)
|
||||||
|
await db.set_baustein_status(topic, b["titel_norm"], "konsens", titel=t, neu_norm=nn)
|
||||||
|
else:
|
||||||
|
await db.set_baustein_status(topic, b["titel_norm"], "konsens")
|
||||||
await db.set_step_status(topic, "Klärung", "fertig")
|
await db.set_step_status(topic, "Klärung", "fertig")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _paare_schema(data) -> dict[int, bool] | None:
|
||||||
|
"""{"paare": {"1": "ja", "2": "nein", …}} → {paar_nr: True/False} · sonst None."""
|
||||||
|
if not isinstance(data, dict) or not isinstance(data.get("paare"), dict):
|
||||||
|
return None
|
||||||
|
out: dict[int, bool] = {}
|
||||||
|
for k, v in data["paare"].items():
|
||||||
|
try:
|
||||||
|
nr = int(k)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
out[nr] = str(v).strip().casefold() in ("ja", "yes", "true", "1")
|
||||||
|
return out or None
|
||||||
|
|
||||||
|
|
||||||
|
def _cliquen(n: int, kanten: list[tuple[int, int]]) -> list[list[int]]:
|
||||||
|
"""Complete-Link: greedy maximale Cliquen über die bestätigten Dubletten-Kanten. Eine Gruppe
|
||||||
|
entsteht nur, wenn ALLE ihre Knoten paarweise verbunden sind → kein Chaining (A=B + B=C bildet
|
||||||
|
KEINE Gruppe {A,B,C}, solange A=C fehlt). Nur Cliquen ≥2 werden zurückgegeben."""
|
||||||
|
adj: dict[int, set[int]] = {i: set() for i in range(n)}
|
||||||
|
for a, b in kanten:
|
||||||
|
adj[a].add(b)
|
||||||
|
adj[b].add(a)
|
||||||
|
benutzt: set[int] = set()
|
||||||
|
gruppen: list[list[int]] = []
|
||||||
|
for v in sorted(range(n), key=lambda x: -len(adj[x])):
|
||||||
|
if v in benutzt or not adj[v]:
|
||||||
|
continue
|
||||||
|
clique = {v}
|
||||||
|
for u in sorted(adj[v], key=lambda x: -len(adj[x])):
|
||||||
|
if u not in benutzt and clique <= adj[u] | {u}: # u mit ALLEN bisherigen verbunden
|
||||||
|
clique.add(u)
|
||||||
|
if len(clique) >= 2:
|
||||||
|
gruppen.append(sorted(clique))
|
||||||
|
benutzt |= clique
|
||||||
|
return gruppen
|
||||||
|
|
||||||
|
|
||||||
async def _dedup_inventar(ctx: GenContext, set_p, files: dict) -> bool:
|
async def _dedup_inventar(ctx: GenContext, set_p, files: dict) -> bool:
|
||||||
"""Finaler iterativer Dedup-Pass über die fertige Konsens-Liste: Embedding-Block + LLM-
|
"""Finaler Dedup-Pass über die fertige Konsens-Liste: Pairwise-Verifikation (Entity
|
||||||
Gruppierung. Fängt Dubletten, die Konsolidierung (Block-Grenzen, Cap) und Klärung (lange
|
Resolution). Embedding liefert Kandidaten-PAARE (Cosine ≥ DEDUP_PAAR_FLOOR), ein Judge
|
||||||
Liste, Lost-in-the-Middle) übersehen. Pro Gruppe bleibt EIN Repräsentant, der Rest wird
|
bestätigt JEDES Paar einzeln (ja = dieselbe Dublette). NUR bestätigte Paare werden zu
|
||||||
verworfen. Iteriert, weil die kleinere Liste je Runde neu blockt (Cross-Block-Reste)."""
|
Merge-Kanten (Union-Find) — kein Chaining, kein Aspekt-Über-Mergen wie beim Block-Mischer.
|
||||||
|
Pro Gruppe bleibt EIN Repräsentant (Hauptkonzept), der Rest wird verworfen."""
|
||||||
topic, is_cancelled = ctx.topic, ctx.is_cancelled
|
topic, is_cancelled = ctx.topic, ctx.is_cancelled
|
||||||
if await db.get_step_status(topic, "Dedup") == "fertig":
|
if await db.get_step_status(topic, "Dedup") == "fertig":
|
||||||
return True
|
return True
|
||||||
@@ -2283,39 +2428,178 @@ async def _dedup_inventar(ctx: GenContext, set_p, files: dict) -> bool:
|
|||||||
return True
|
return True
|
||||||
set_p("Dedup…", step=_step_idx(topic, "Dedup"))
|
set_p("Dedup…", step=_step_idx(topic, "Dedup"))
|
||||||
arbeit = files["arbeit"]
|
arbeit = files["arbeit"]
|
||||||
for runde in range(1, DEDUP_MAX_RUNDEN + 1):
|
|
||||||
konsens = await db.list_bausteine(topic, status="konsens")
|
konsens = await db.list_bausteine(topic, status="konsens")
|
||||||
if len(konsens) < 2:
|
if len(konsens) >= 2:
|
||||||
break
|
import numpy as np
|
||||||
texts = [f"{b['titel']} — {b['beschreibung']}" if b["beschreibung"] else b["titel"] for b in konsens]
|
texts = [f"{b['titel']} — {b['beschreibung']}" if b["beschreibung"] else b["titel"] for b in konsens]
|
||||||
sims = await asyncio.to_thread(embedding.embed_sims, texts)
|
sims = await asyncio.to_thread(embedding.embed_sims, texts)
|
||||||
if sims is None:
|
if sims is not None:
|
||||||
break
|
n = len(konsens)
|
||||||
blocks = await asyncio.to_thread(embedding.capped_blocks, sims, None, None)
|
iu = np.triu_indices(n, k=1)
|
||||||
gruppen = await _block_gruppieren(ctx, set_p, arbeit, konsens, blocks,
|
kand = [(int(iu[0][m]), int(iu[1][m])) for m in np.where(sims[iu] >= DEDUP_PAAR_FLOOR)[0]]
|
||||||
praefix=f"dedup-r{runde}", schritt="Dedup")
|
_log(topic, f"Dedup: {len(kand)} Kandidaten-Paare (Cosine ≥ {DEDUP_PAAR_FLOOR}) → Pairwise-Filter")
|
||||||
|
pakete = [kand[i:i + DEDUP_PAARE_CHUNK] for i in range(0, len(kand), DEDUP_PAARE_CHUNK)]
|
||||||
|
|
||||||
|
def paar_path(pi): return arbeit / f"dedup-paar-c{pi}.json"
|
||||||
|
|
||||||
|
async def _filt(pi, paare):
|
||||||
|
fp = paar_path(pi)
|
||||||
|
if _paare_schema(_json_datei(fp)):
|
||||||
|
return # Resume
|
||||||
|
zeilen = "\n\n".join(
|
||||||
|
f"{j + 1}.\nA: {konsens[a]['titel']} — {konsens[a]['beschreibung']}"
|
||||||
|
f"\nB: {konsens[b]['titel']} — {konsens[b]['beschreibung']}"
|
||||||
|
for j, (a, b) in enumerate(paare))
|
||||||
|
await run_single_slot(
|
||||||
|
ctx, f"Dedup-Paare {pi}",
|
||||||
|
key=f"bausteine-{topic}-dedup-paar-c{pi}",
|
||||||
|
prompt=_prompt("Bausteine-Paar-Filter", topic=topic, paare=zeilen, out_path=fp),
|
||||||
|
role="judge", capabilities="files",
|
||||||
|
payload=lambda result, p=fp: _paare_schema(_json_datei(p)),
|
||||||
|
timeout=_timeout("auswahl_mapping", len(paare)),
|
||||||
|
)
|
||||||
|
|
||||||
|
await _gather_fortschritt([_filt(pi, p) for pi, p in enumerate(pakete)],
|
||||||
|
len(pakete), _melde_p(set_p, topic, "Dedup"))
|
||||||
if is_cancelled():
|
if is_cancelled():
|
||||||
return False
|
return False
|
||||||
|
# Bestätigte "ja"-Kanten sammeln, dann COMPLETE-LINK (greedy Cliquen) statt Single-Link
|
||||||
|
# Union-Find — verhindert Chaining (A=B + B=C mergt NICHT A,C ohne direktes A=C).
|
||||||
|
kanten, ja = [], 0
|
||||||
|
for pi, paare in enumerate(pakete):
|
||||||
|
urteil = _paare_schema(_json_datei(paar_path(pi))) or {}
|
||||||
|
for j, (a, b) in enumerate(paare):
|
||||||
|
if urteil.get(j + 1):
|
||||||
|
kanten.append((a, b))
|
||||||
|
ja += 1
|
||||||
|
gruppen = _cliquen(n, kanten)
|
||||||
weg = 0
|
weg = 0
|
||||||
for idxs in gruppen:
|
for idxs in gruppen:
|
||||||
if len(idxs) < 2:
|
# Repräsentant = Hauptkonzept (wenigste Eigenschafts-Marker), dann kürzester Titel.
|
||||||
continue
|
|
||||||
# Repräsentant = Hauptkonzept (wenigste Eigenschafts-Marker), dann kürzester Titel; Rest verwerfen.
|
|
||||||
rep = min(idxs, key=lambda k: (_aspekt_marker(konsens[k]["titel"]), len(konsens[k]["titel"]), k))
|
rep = min(idxs, key=lambda k: (_aspekt_marker(konsens[k]["titel"]), len(konsens[k]["titel"]), k))
|
||||||
for k in idxs:
|
for k in idxs:
|
||||||
if k != rep:
|
if k != rep:
|
||||||
await db.set_baustein_status(topic, konsens[k]["titel_norm"], "verworfen")
|
await db.set_baustein_status(topic, konsens[k]["titel_norm"], "verworfen")
|
||||||
weg += 1
|
weg += 1
|
||||||
atomic_write_json(arbeit / f"dedup-runde-{runde}.json",
|
from collections import Counter
|
||||||
{"vorher": len(konsens), "entfernt": weg,
|
atomic_write_json(arbeit / "dedup-runde-1.json",
|
||||||
"gruppen": [[konsens[k]["titel"] for k in g] for g in gruppen if len(g) > 1]}, indent=1)
|
{"vorher": n, "entfernt": weg, "paare_geprueft": len(kand), "paare_ja": ja,
|
||||||
_log(topic, f"Dedup Runde {runde}: {len(konsens)} → {len(konsens) - weg} (−{weg})")
|
"clique_groessen": dict(sorted(Counter(len(g) for g in gruppen).items())),
|
||||||
if weg <= max(1, len(konsens) * DEDUP_MIN_DELTA // 100): # konvergiert → Schluss
|
"gruppen": [[konsens[k]["titel"] for k in g] for g in gruppen]}, indent=1)
|
||||||
break
|
_log(topic, f"Dedup (pairwise): {n} → {n - weg} (−{weg}); {ja}/{len(kand)} Paare bestätigt")
|
||||||
await db.set_step_status(topic, "Dedup", "fertig")
|
await db.set_step_status(topic, "Dedup", "fertig")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _filter_schema(data) -> dict[int, int] | None:
|
||||||
|
"""{"fragmente": {"3": 7, "12": 8}} → {baustein_nr: eltern_nr} · None bei ungültiger Struktur.
|
||||||
|
Leeres dict = gültig (nichts zu degradieren). Eltern ≠ sich selbst."""
|
||||||
|
if not isinstance(data, dict) or not isinstance(data.get("fragmente"), dict):
|
||||||
|
return None
|
||||||
|
out: dict[int, int] = {}
|
||||||
|
for k, v in data["fragmente"].items():
|
||||||
|
try:
|
||||||
|
nr, eltern = int(k), int(v)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
if nr != eltern:
|
||||||
|
out[nr] = eltern
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# Reine Notation/Symbole ohne eigenständiges Konzept — eng gehalten (FP~0 an aak geprüft;
|
||||||
|
# „KNF"/„MST"/„NP" treffen NICHT). Diese werden autonom verworfen (brauchen keinen Eltern).
|
||||||
|
_FILTER_NOTATION = re.compile(r'^\s*\|.{1,6}\|\s*$|^Güte\s+\d+\s*$')
|
||||||
|
# Eigenschaft-/Laufzeit-Verdacht — markiert Zeilen fürs Judge-Urteil (KEIN Auto-Drop, FP zu hoch:
|
||||||
|
# „NP-Schwere", Reduktionen mit „∈NP" sind echte Bausteine). Ergänzt _aspekt_marker.
|
||||||
|
_FILTER_PRAEDIKAT = re.compile(
|
||||||
|
r'ist NP-(vollständig|schwer)|NP-(Vollständigkeit|Schwere) von|ETH (Konsequenz|Lower Bound)'
|
||||||
|
r'|Approximationsschema nach|Laufzeit O\(|∈ ?NP', re.I)
|
||||||
|
|
||||||
|
|
||||||
|
def _filter_verdacht(b: dict) -> bool:
|
||||||
|
"""Heuristik-Flag: könnte eine Eigenschaft/ein Detail eines anderen Bausteins sein."""
|
||||||
|
return _aspekt_marker(b["titel"]) > 0 or bool(_FILTER_PRAEDIKAT.search(f"{b['titel']} {b['beschreibung'] or ''}"))
|
||||||
|
|
||||||
|
|
||||||
|
async def _filter_inventar(ctx: GenContext, set_p, files: dict) -> bool:
|
||||||
|
"""Degradier-Pass (Granularität): trennt echte Bausteine von Fragmenten (Eigenschaften,
|
||||||
|
Beweis-Gadgets, Notation, Laufzeit-Details). Jeder Judge sieht die VOLLE Baustein-Liste
|
||||||
|
(Self-Containment ist relational) und markiert Fragmente MIT Eltern-Baustein aus der Liste.
|
||||||
|
Fragment + Eltern-in-Liste → verworfen (Inhalt kommt als Subbaustein des Eltern zurück).
|
||||||
|
Ohne Eltern oder im Zweifel → behalten (kein Konzept-Verlust)."""
|
||||||
|
topic, is_cancelled = ctx.topic, ctx.is_cancelled
|
||||||
|
if await db.get_step_status(topic, "Bausteine-Filter") == "fertig":
|
||||||
|
return True
|
||||||
|
set_p("Bausteine-Filter…", step=_step_idx(topic, "Bausteine-Filter"))
|
||||||
|
arbeit = files["arbeit"]
|
||||||
|
konsens_all = await db.list_bausteine(topic, status="konsens")
|
||||||
|
# Sicherheitsnetz: reine Notation autonom verwerfen (FP~0, kein Eltern nötig). Der Judge
|
||||||
|
# übersieht solche Symbole zuverlässig (Recall-Problem), darum deterministisch vorab.
|
||||||
|
konsens, notation_weg = [], []
|
||||||
|
for b in konsens_all:
|
||||||
|
if _FILTER_NOTATION.search(b["titel"]):
|
||||||
|
await db.set_baustein_status(topic, b["titel_norm"], "verworfen")
|
||||||
|
notation_weg.append(b["titel"])
|
||||||
|
else:
|
||||||
|
konsens.append(b)
|
||||||
|
if notation_weg:
|
||||||
|
_log(topic, f"Bausteine-Filter: {len(notation_weg)} reine Notation verworfen: {notation_weg[:6]}")
|
||||||
|
if len(konsens) < 2:
|
||||||
|
await db.set_step_status(topic, "Bausteine-Filter", "fertig")
|
||||||
|
return True
|
||||||
|
n = len(konsens)
|
||||||
|
# ⚠ markiert Verdachts-Zeilen (Eigenschaft/Laufzeit) — der Judge MUSS sie pro Eintrag prüfen.
|
||||||
|
def _zeile(i, b):
|
||||||
|
mark = "⚠ " if _filter_verdacht(b) else ""
|
||||||
|
return f"{i}. {mark}{b['titel']} — {b['beschreibung']}" if b["beschreibung"] else f"{i}. {mark}{b['titel']}"
|
||||||
|
voll = "\n".join(_zeile(i, b) for i, b in enumerate(konsens, 1))
|
||||||
|
chunks = [list(range(i, min(i + FILTER_CHUNK, n + 1))) for i in range(1, n + 1, FILTER_CHUNK)]
|
||||||
|
|
||||||
|
def filt_path(ci): return arbeit / f"inventar-filter-c{ci}.json"
|
||||||
|
|
||||||
|
async def _beurteile(ci, nummern):
|
||||||
|
fp = filt_path(ci)
|
||||||
|
if _filter_schema(_json_datei(fp)) is not None:
|
||||||
|
return # Resume
|
||||||
|
await run_single_slot(
|
||||||
|
ctx, f"Bausteine-Filter {ci}",
|
||||||
|
key=f"bausteine-{topic}-inventar-filter-c{ci}",
|
||||||
|
prompt=_prompt("Bausteine-Filter", topic=topic, liste=voll,
|
||||||
|
von=nummern[0], bis=nummern[-1], out_path=fp),
|
||||||
|
role="judge", capabilities="files",
|
||||||
|
payload=lambda result, p=fp: _filter_schema(_json_datei(p)),
|
||||||
|
timeout=_timeout("auswahl_mapping", len(nummern)),
|
||||||
|
)
|
||||||
|
|
||||||
|
await _gather_fortschritt([_beurteile(ci, nm) for ci, nm in enumerate(chunks)],
|
||||||
|
len(chunks), _melde_p(set_p, topic, "Bausteine-Filter"))
|
||||||
|
if is_cancelled():
|
||||||
|
return False
|
||||||
|
frag: dict[int, int] = {}
|
||||||
|
for ci, nummern in enumerate(chunks):
|
||||||
|
urteil = _filter_schema(_json_datei(filt_path(ci))) or {}
|
||||||
|
nset = set(nummern)
|
||||||
|
for nr, eltern in urteil.items():
|
||||||
|
if 1 <= eltern <= n and nr in nset:
|
||||||
|
frag[nr] = eltern
|
||||||
|
# Ketten-Schutz: ein Baustein, der selbst Eltern eines Fragments ist, bleibt (sein Kind braucht den Anker).
|
||||||
|
eltern_set = set(frag.values())
|
||||||
|
weg, debug = 0, []
|
||||||
|
for nr, eltern in frag.items():
|
||||||
|
if nr in eltern_set:
|
||||||
|
continue
|
||||||
|
b = konsens[nr - 1]
|
||||||
|
await db.set_baustein_status(topic, b["titel_norm"], "verworfen")
|
||||||
|
weg += 1
|
||||||
|
debug.append({"fragment": b["titel"], "eltern": konsens[eltern - 1]["titel"]})
|
||||||
|
atomic_write_json(arbeit / "inventar-filter.json",
|
||||||
|
{"vorher": n, "degradiert": weg, "fragmente": debug}, indent=1)
|
||||||
|
_log(topic, f"Bausteine-Filter: {n} → {n - weg} (−{weg} Fragmente → Subbausteine)")
|
||||||
|
await db.set_step_status(topic, "Bausteine-Filter", "fertig")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# --- Gliederung (Bausteine-Artefakt: Kapitel-Struktur, vom Guide nur gelesen) ---
|
# --- Gliederung (Bausteine-Artefakt: Kapitel-Struktur, vom Guide nur gelesen) ---
|
||||||
|
|
||||||
def _gliederung_komplett(files: dict) -> bool:
|
def _gliederung_komplett(files: dict) -> bool:
|
||||||
@@ -2709,7 +2993,7 @@ async def _reset_db_ab_phase(topic: str, label: str) -> None:
|
|||||||
await db.delete_subbausteine(topic)
|
await db.delete_subbausteine(topic)
|
||||||
if idx <= 1: # Inventar: Inventar + Recherche-Schritte — Sichtung bleibt
|
if idx <= 1: # Inventar: Inventar + Recherche-Schritte — Sichtung bleibt
|
||||||
await db.delete_bausteine(topic)
|
await db.delete_bausteine(topic)
|
||||||
await db.delete_pipeline_state(topic, ["Recherche", "Konsolidierung", "Klärung", "Dedup"])
|
await db.delete_pipeline_state(topic, ["Recherche", "Konsolidierung", "Klärung", "Dedup", "Bausteine-Filter"])
|
||||||
if idx <= 0: # Quelle: Sichtung neu (Coverage/inhalt + Schritt)
|
if idx <= 0: # Quelle: Sichtung neu (Coverage/inhalt + Schritt)
|
||||||
await db.delete_coverage(topic)
|
await db.delete_coverage(topic)
|
||||||
await db.delete_pipeline_state(topic, ["Quelle aufbereiten"])
|
await db.delete_pipeline_state(topic, ["Quelle aufbereiten"])
|
||||||
@@ -2788,6 +3072,10 @@ async def generate_bausteine(topic: str, instructions: str = "", provider: str =
|
|||||||
if is_cancelled():
|
if is_cancelled():
|
||||||
abgebrochen()
|
abgebrochen()
|
||||||
return
|
return
|
||||||
|
if not await _filter_inventar(ctx, set_p, files):
|
||||||
|
if is_cancelled():
|
||||||
|
abgebrochen()
|
||||||
|
return
|
||||||
konsens_rows = await db.list_bausteine(topic, status="konsens")
|
konsens_rows = await db.list_bausteine(topic, status="konsens")
|
||||||
entries = {
|
entries = {
|
||||||
i: (f"{b['titel']} — {b['beschreibung']}" if b["beschreibung"] else b["titel"])
|
i: (f"{b['titel']} — {b['beschreibung']}" if b["beschreibung"] else b["titel"])
|
||||||
|
|||||||
@@ -764,14 +764,18 @@ async def list_bausteine(topic: str, status: str | None = None) -> list[dict]:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
async def set_baustein_status(topic: str, titel_norm: str, status: str, titel: str | None = None, beschreibung: str | None = None) -> None:
|
async def set_baustein_status(topic: str, titel_norm: str, status: str, titel: str | None = None, beschreibung: str | None = None, neu_norm: str | None = None) -> None:
|
||||||
"""Status setzen; optional Titel/Beschreibung aktualisieren (z.B. nach semantischem Merge)."""
|
"""Status setzen; optional Titel/Beschreibung aktualisieren (z.B. nach semantischem Merge).
|
||||||
|
`neu_norm` benennt den Norm-Schlüssel um (Klärung: Verweis-Titel → sprechender Name). Nur
|
||||||
|
sicher, solange noch keine Subbausteine/Fakten am alten `titel_norm` hängen."""
|
||||||
db = await get_db()
|
db = await get_db()
|
||||||
fields = {"status": status, "updated_at": _now()}
|
fields = {"status": status, "updated_at": _now()}
|
||||||
if titel is not None:
|
if titel is not None:
|
||||||
fields["titel"] = titel
|
fields["titel"] = titel
|
||||||
if beschreibung is not None:
|
if beschreibung is not None:
|
||||||
fields["beschreibung"] = beschreibung
|
fields["beschreibung"] = beschreibung
|
||||||
|
if neu_norm is not None and neu_norm != titel_norm:
|
||||||
|
fields["titel_norm"] = neu_norm
|
||||||
sets = ", ".join(f"{k} = :{k}" for k in fields)
|
sets = ", ".join(f"{k} = :{k}" for k in fields)
|
||||||
await db.execute(
|
await db.execute(
|
||||||
f"UPDATE bausteine SET {sets} WHERE topic = :topic AND titel_norm = :titel_norm",
|
f"UPDATE bausteine SET {sets} WHERE topic = :topic AND titel_norm = :titel_norm",
|
||||||
|
|||||||
177
backend/fragen-vorrat-aak.md
Normal file
177
backend/fragen-vorrat-aak.md
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# Frage-Muster für Lern-Prüfung: aak
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BAUSTEIN: CliqueAndIndependentSet-Problem
|
||||||
|
|
||||||
|
### Subbaustein: Independent Set: Knotenmenge ohne Kanten zwischen je zwei Knoten
|
||||||
|
**Muster:** Was ist die formale Definition eines Independent Set in einem Graphen G = (V, E)?
|
||||||
|
|
||||||
|
### Subbaustein: Komplementgraph (V, E) bildet IS und Clique aufeinander ab
|
||||||
|
**Muster:** Wie hängt die Existenz einer Clique in G mit der Existenz eines Independent Set in G' (dem Komplementgraphen) zusammen?
|
||||||
|
|
||||||
|
### Subbaustein: Existenz von Independent Set der Größe k ist NP-vollständig
|
||||||
|
**Muster:** Durch welche Polynomialzeitreduktion lässt sich zeigen, dass Independent Set NP-schwer ist?
|
||||||
|
|
||||||
|
### Subbaustein: Existenz von Clique der Größe k ist NP-vollständig
|
||||||
|
**Muster:** Wie wird in Satz 6.26 die NP-Schwere von k-Clique bewiesen?
|
||||||
|
|
||||||
|
### Subbaustein: Reduktion k-CLIQUE ⪯ k-INDEPENDENT-SET mittels Komplementgraph
|
||||||
|
**Muster:** Wie transformiert man eine Instanz (G, k) von CLIQUE in eine Instanz von INDEPENDENT-SET?
|
||||||
|
|
||||||
|
### Subbaustein: Clique: Knotenmenge, in der je zwei Knoten durch eine Kante verbunden sind
|
||||||
|
**Muster:** Wann ist eine Knotenmenge C ⊆ V eine Clique in einem Graphen G = (V, E)?
|
||||||
|
|
||||||
|
### Subbaustein: Complementärgraph: Independent Set in G ist Clique in G̅
|
||||||
|
**Muster:** Welche Beziehung besteht zwischen einem Independent Set in G und einer Clique in G̅?
|
||||||
|
|
||||||
|
### Subbaustein: Beide Probleme sind NP-vollständig
|
||||||
|
**Muster:** Welche fundamentale Konsequenz ergibt sich aus Satz 6.16, wenn ein NP-vollständiges Problem in P liegt?
|
||||||
|
|
||||||
|
### Subbaustein: Frage: Existiert Independent Set bzw. Clique der Größe k in G?
|
||||||
|
**Muster:** Was ist die Eingabe und was die Ausgabe bei den Entscheidungsproblemen k-Clique und k-Independent-Set?
|
||||||
|
|
||||||
|
### Subbaustein: Komplementarität: Independent Set in G = Clique im Komplementgraphen
|
||||||
|
**Muster:** Warum sind Clique und Independent Set gegenseitig in Polynomialzeit aufeinander reduzierbar?
|
||||||
|
|
||||||
|
### Subbaustein: CLIQUE: Knotenmenge in der je zwei Knoten durch Kante verbunden sind
|
||||||
|
**Muster:** Welche Bedingung müssen alle Knotenpaare einer Clique erfüllen?
|
||||||
|
|
||||||
|
### Subbaustein: Komplementär: IS in G = CLIQUE in G̅
|
||||||
|
**Muster:** Was bleibt bei der Bildung des Komplementgraphen G̅ gleich und was ändert sich?
|
||||||
|
|
||||||
|
### Subbaustein: Existenz von IS bzw. CLIQUE der Größe k ist NP-vollständig
|
||||||
|
**Muster:** Welche untere Schranke für die Laufzeit von Algorithmen für Independent Set folgt aus der ETH?
|
||||||
|
|
||||||
|
### Subbaustein: Formale Sprachen: CLIQUE = {(G,k) | G enthält Clique ≥ k}
|
||||||
|
**Muster:** Wie ist die formale Sprache CLIQUE über dem Alphabet Σ = {0, 1} kodiert?
|
||||||
|
|
||||||
|
### Subbaustein: Formale Sprachen: INDEPENDENT-SET = {(G,k) | G enthält IS ≥ k}
|
||||||
|
**Muster:** Welche Struktur hat die formale Sprache INDEPENDENT-SET?
|
||||||
|
|
||||||
|
### Subbaustein: Komplementgraph G̅: Kantenmenge E̅ = {(u,v) | u≠v und {u,v} ∉ E}
|
||||||
|
**Muster:** Wie unterscheiden sich die Kantenmengen von G und seinem Komplementgraphen G̅?
|
||||||
|
|
||||||
|
### Subbaustein: K-Clique in G entspricht K-Unabhängige-Menge in G̅
|
||||||
|
**Muster:** Bleibt die Größe k bei der Reduktion von k-Clique auf k-Independent-Set erhalten?
|
||||||
|
|
||||||
|
### Subbaustein: Beide Probleme sind in NP (Verifizierer existiert)
|
||||||
|
**Muster:** Welche Eigenschaft müssen Zertifikat und Verifizierer für ein Problem in NP erfüllen?
|
||||||
|
|
||||||
|
### Subbaustein: NP-vollständig via gegenseitige Reduktion über Komplementgraph
|
||||||
|
**Muster:** Wie folgt aus Korollar 6.18 die NP-Vollständigkeit von Independent Set?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BAUSTEIN: Reduktion CLIQUE → CLIQUE-NOMEMBER
|
||||||
|
|
||||||
|
### Subbaustein: Füge isolierten Knoten v zu G hinzu: G' = G ∪ {v}
|
||||||
|
**Muster:** Wie wird bei der Reduktion von CLIQUE auf CLIQUE-NOMEMBER der neue Graph G' konstruiert?
|
||||||
|
|
||||||
|
### Subbaustein: G hat k-Clique ⟺ G' hat (k+1)-Clique mit v
|
||||||
|
**Muster:** Warum kann der hinzugefügte Knoten v in keiner gültigen k-Clique von G' enthalten sein?
|
||||||
|
|
||||||
|
### Subbaustein: Reduktion beweist CLIQUE-NOMEMBER ∈ NP-vollständig
|
||||||
|
**Muster:** Welche drei Bedingungen müssen erfüllt sein, damit CLIQUE-NOMEMBER als NP-vollständig gilt?
|
||||||
|
|
||||||
|
### Subbaustein: CLIQUE: Eingabe Graph G, Frage: existiert K-clique?
|
||||||
|
**Muster:** Was ist die Eingabe und was ist die Frage beim Entscheidungsproblem CLIQUE?
|
||||||
|
|
||||||
|
### Subbaustein: CLIQUE-NOMEMBER: existiert Knoten der nicht in jeder maximalen Clique liegt?
|
||||||
|
**Muster:** Wie ist das Problem CLIQUE-NOMEMBER gemäß Skript 6.50 formal definiert?
|
||||||
|
|
||||||
|
### Subbaustein: Reduktion: isolierten Knoten v zu G hinzufügen → G'
|
||||||
|
**Muster:** Welche Elemente werden bei der Reduktion von CLIQUE auf CLIQUE-NOMEMBER gegenüber der ursprünglichen Instanz verändert?
|
||||||
|
|
||||||
|
### Subbaustein: G' hat Clique der Größe k genau dann wenn G eine hat
|
||||||
|
**Muster:** Warum bleibt die Cliquengröße k bei der Reduktion unverändert?
|
||||||
|
|
||||||
|
### Subbaustein: v ist in G' in keiner k-Clique (isoliert)
|
||||||
|
**Muster:** Welche Eigenschaft hat der Knoten v in der konstruierten Instanz (G', v, k)?
|
||||||
|
|
||||||
|
### Subbaustein: G hat Clique der Größe k ⟺ G' hat Clique der Größe k ohne v
|
||||||
|
**Muster:** Wie hängt eine k-Clique in G mit einer k-Clique in G' zusammen?
|
||||||
|
|
||||||
|
### Subbaustein: Polynomielle Transformation
|
||||||
|
**Muster:** Warum ist die beschriebene Reduktion in polynomieller Zeit berechenbar?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BAUSTEIN: Independent Set
|
||||||
|
|
||||||
|
### Subbaustein: Für alle u, v ∈ S gilt: {u, v} ∉ E
|
||||||
|
**Muster:** Welche Bedingung muss für je zwei Knoten eines Independent Set gelten?
|
||||||
|
|
||||||
|
### Subbaustein: Komplementär zur Clique
|
||||||
|
**Muster:** Wie hängt ein Independent Set in G mit einer Clique im Komplementgraphen G' zusammen?
|
||||||
|
|
||||||
|
### Subbaustein: NP-vollständiges Problem
|
||||||
|
**Muster:** Welche Komplexitätsklasse enthält Independent Set und wie wurde dies bewiesen?
|
||||||
|
|
||||||
|
### Subbaustein: Knotenmenge ohne Kanten zwischen je zwei Knoten der Menge
|
||||||
|
**Muster:** Was bedeutet es, dass die Knoten eines Independent Set paarweise nicht adjazent sind?
|
||||||
|
|
||||||
|
### Subbaustein: Komplementär zum Clique-Problem
|
||||||
|
**Muster:** In welchem Graphen entspricht ein Independent Set einer Clique?
|
||||||
|
|
||||||
|
### Subbaustein: Independent Set S⊆V: keine Kante zwischen je zwei Knoten in S
|
||||||
|
**Muster:** Wie unterscheidet sich ein Independent Set von einer beliebigen Teilmenge von V?
|
||||||
|
|
||||||
|
### Subbaustein: INDEPENDENT-SET = {(G,k) | G enthält unabhängige Menge der Größe ≥k}
|
||||||
|
**Muster:** Welche Sprache formalisiert das Entscheidungsproblem Independent Set?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BAUSTEIN: Tiefensuche (DFS) für Zykluserkennung
|
||||||
|
|
||||||
|
### Subbaustein: Weiß: unbesuchter Knoten, Grau: aktuell in Bearbeitung, Schwarz: fertig
|
||||||
|
**Muster:** Welche Farbe hat ein Knoten während er von der DFS bearbeitet wird und welche nach Abschluss?
|
||||||
|
|
||||||
|
### Subbaustein: DFS-Zykluserkennung in O(V+E) bei adjacency List
|
||||||
|
**Muster:** Warum beträgt die Laufzeit der DFS-Zykluserkennung bei Adjazenzliste Θ(|V|+|E|)?
|
||||||
|
|
||||||
|
### Subbaustein: Schwarz: vollständig bearbeiteter Knoten
|
||||||
|
**Muster:** Wann wird ein Knoten in der DFS schwarz gefärbt?
|
||||||
|
|
||||||
|
### Subbaustein: Rückkante (grau → weiß): signalisiert Zyklus
|
||||||
|
**Muster:** Zu einem Knoten welcher Farbe muss eine Kante führen, um einen Zyklus anzuzeigen?
|
||||||
|
|
||||||
|
### Subbaustein: Tree Edge (weiß): Kante zu unbesuchtem Knoten
|
||||||
|
**Muster:** Welche Kante wird als Tree Edge bezeichnet?
|
||||||
|
|
||||||
|
### Subbaustein: Grau: aktuell in Bearbeitung (in DFS-Weite)
|
||||||
|
**Muster:** Was bedeutet es, wenn ein Knoten während der DFS grau gefärbt ist?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BAUSTEIN: Turingmaschine für 0^n (Zweierpotenz)
|
||||||
|
|
||||||
|
### Subbaustein: Eingabe: n Nullen in unärer Codierung
|
||||||
|
**Muster:** In welcher Codierung wird die Eingabezahl n der TM für 0^n dargestellt?
|
||||||
|
|
||||||
|
### Subbaustein: Akzeptiert nur wenn n = 2^k für ein k ≥ 0
|
||||||
|
**Muster:** Nach welchem Kriterium entscheidet die TM, ob eine Eingabe akzeptiert wird?
|
||||||
|
|
||||||
|
### Subbaustein: Phase 1: Markiere jede zweite 0 mit x (alternierend)
|
||||||
|
**Muster:** Wie markiert die TM die Nullen im ersten Schritt?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BAUSTEIN: 3-SAT zu 3-Färbung Reduktion
|
||||||
|
|
||||||
|
### Subbaustein: Knotenzahl linear in Variablen und Klauseln
|
||||||
|
**Muster:** Aus welchen Komponenten setzt sich die Knotenmenge V der konstruierten Instanz zusammen?
|
||||||
|
|
||||||
|
### Subbaustein: Dreieck erzwingt drei verschiedene Farben für die drei Knoten
|
||||||
|
**Muster:** Warum benötigen die drei Knoten xi, x̄i und vi eines jeden Dreiecks drei verschiedene Farben?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BAUSTEIN: MC-Knapsack
|
||||||
|
|
||||||
|
### Subbaustein: Ziel: Maximierung des Gesamtwerts
|
||||||
|
**Muster:** Was ist die Zielfunktion beim Maximum-Cut Knapsack Problem?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Gesamt: 48 Frage-Muster**
|
||||||
39
templates/Prompt/Bausteine-Filter.md
Normal file
39
templates/Prompt/Bausteine-Filter.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
Unten ist die vollständige Baustein-Liste eines Lern-Guides zum Thema "{topic}", durchnummeriert. Deine Aufgabe: Trenne **echte Bausteine** von **Fragmenten**, die in Wahrheit zu einem anderen Baustein der Liste gehören.
|
||||||
|
|
||||||
|
VOLLSTÄNDIGE LISTE (als Kontext — du brauchst sie, um Eltern zu finden):
|
||||||
|
{liste}
|
||||||
|
|
||||||
|
BEURTEILE die Nummern **{von} bis {bis}** — gehe sie **EINZELN** durch, einen Eintrag nach dem anderen. Die übrigen Einträge sind nur Kontext (mögliche Eltern).
|
||||||
|
|
||||||
|
## Vorgehen pro Eintrag (zwingend für JEDEN einzeln)
|
||||||
|
Für jeden Eintrag {von}–{bis}:
|
||||||
|
1. Was ist das **Subjekt**? (Worüber wird geredet?)
|
||||||
|
2. Ist dieses Subjekt selbst ein anderer Eintrag der Liste — und sagt der Eintrag darüber nur eine EIGENSCHAFT, einen BEWEIS-TEIL, eine NOTATION oder ein LAUFZEIT-DETAIL aus?
|
||||||
|
- **Ja → Fragment**, Eltern = die Nummer dieses Subjekts.
|
||||||
|
- Nein, es steht für sich → Baustein (behalten).
|
||||||
|
Mit **⚠** markierte Zeilen sind Verdachtsfälle (Eigenschaft/Laufzeit/Notation) — prüfe sie besonders sorgfältig. Entscheide am Inhalt, nicht an der Markierung.
|
||||||
|
|
||||||
|
## Was ist ein BAUSTEIN (eigenständige Lerneinheit — behalten)?
|
||||||
|
Ein Baustein ist self-contained: Man kann ihn erklären, OHNE einen anderen Baustein als Subjekt vorauszusetzen.
|
||||||
|
- Ein **Problem**: „3-SAT", „Clique", „Knapsack", „Dominating Set".
|
||||||
|
- Ein **Verfahren/Algorithmus**: „LPT Scheduling", „Christofides", „FPTAS".
|
||||||
|
- Eine **Definition/ein Konzept**: „NP", „Reduktion", „Verifizierer", „KNF".
|
||||||
|
- Ein **benannter Satz MIT eigener Aussage**: „Cook-Levin: SAT ist NP-vollständig".
|
||||||
|
|
||||||
|
## Was ist ein FRAGMENT (gehört zu einem anderen Baustein → degradieren)?
|
||||||
|
Self-Containment-Test: Setzt der Eintrag ein ANDERES Konzept der Liste als Subjekt voraus? Dann ist er dessen Eigenschaft/Teil, kein eigener Baustein.
|
||||||
|
- **Eigenschaft/Status** eines Problems X (das selbst in der Liste steht): „X ist NP-vollständig", „X ∈ NP", „NP-Schwere von X", „Approximationsgüte von X". → Eltern = X.
|
||||||
|
- **Beweis-/Reduktions-Gadget**: „αEnde", „A-Komponente", „Dummy Items", „Schedule D*", „Knoten z", Hilfsvariablen. → Eltern = der Satz/die Reduktion, in deren Beweis es vorkommt.
|
||||||
|
- **Reine Notation/Symbol**: „|x|", „Σ∗", „Güte 2". → Eltern = die definierende Definition.
|
||||||
|
- **Laufzeit-/Größen-Detail**: „O(|V|⁴) Verifizierer-Laufzeit", „|V'| = |V| bei Reduktion", „Reduktion in O(|E|)". → Eltern = der Algorithmus/die Reduktion.
|
||||||
|
|
||||||
|
## Regeln
|
||||||
|
- Ein Fragment wird NUR degradiert, wenn sein **Eltern-Baustein in der Liste steht** (gib dessen Nummer an). Findest du keinen Eltern → behalte es (kein „Eltern").
|
||||||
|
- Zweifel betrifft die EIGENSTÄNDIGKEIT: Ist unklar, ob ein Eintrag für sich steht → behalten. Aber eine klare Eigenschaft/Notation/ein Beweis-Teil MIT Eltern in der Liste IST ein Fragment — nicht aus Vorsicht behalten.
|
||||||
|
- Eine eigenständige **Reduktion zwischen zwei Problemen** ist ein Baustein, KEIN Fragment („3-SAT ≤ Clique").
|
||||||
|
- Urteile am INHALT (nach dem „—"), nicht am Titel.
|
||||||
|
|
||||||
|
Schreibe NUR die JSON-Datei nach: {out_path}
|
||||||
|
|
||||||
|
Format (nur die Fragment-Nummern aus {von}–{bis}, je mit Eltern-Nummer; `fragmente` darf leer sein):
|
||||||
|
{{"fragmente": {{"12": 5, "13": 5, "27": 19}}}}
|
||||||
@@ -1,20 +1,26 @@
|
|||||||
Zum Thema "{topic}" sind unten Bausteine, die in der Recherche nur EINMAL genannt wurden. Die Quelle wurde vollständig durchsucht. Entscheide für JEDEN Eintrag: ist er ein echter, eigenständiger Baustein → `aufnehmen`, oder nicht → verwerfen (in keine Liste).
|
Zum Thema "{topic}" stehen unten Baustein-Kandidaten, jeder als "Titel — Beschreibung". **Beurteile am INHALT (nach dem —), nicht am Titel.** Entscheide für JEDEN Eintrag: echter, eigenständiger Baustein → `aufnehmen`, sonst verwerfen (in keine Liste).
|
||||||
|
|
||||||
EINZELN GENANNTE EINTRÄGE (jeweils entscheiden):
|
KANDIDATEN (jeweils entscheiden):
|
||||||
{rest}
|
{rest}
|
||||||
|
|
||||||
Was ein Baustein IST: eine eigenständige LERNEINHEIT — ein Konzept, ein Verfahren, ein Problem, eine Definition, ein benannter Satz MIT inhaltlicher Aussage.
|
Was ein Baustein IST: eine eigenständige LERNEINHEIT — ein Konzept, ein Verfahren, ein Problem, eine Definition, ein benannter Satz MIT inhaltlicher Aussage.
|
||||||
|
|
||||||
Was KEIN Baustein ist (→ verwerfen):
|
**Define-Test (entscheidend):** Beschreibt der INHALT ein eigenständiges Konzept, das man einem Lernenden erklären kann — OHNE Bezug auf einen konkreten Beweisschritt oder eine bestimmte Skript-Stelle? Ja → aufnehmen. Nein → verwerfen.
|
||||||
- Bloße Verweise/Nummern ohne Inhalt: "Satz 7.18", "Bemerkung 7.22", "Lemma 6.2", "D*".
|
|
||||||
|
Was KEIN Baustein ist (→ verwerfen, AUCH wenn eine Beschreibung dabei steht):
|
||||||
|
- Hilfskonstrukte, die nur INNERHALB eines Beweises/einer Reduktion existieren: "Knoten z", "Hilfsvariable", "Bedingung (**)" (ein Hilfsknoten / eine markierte Bedingung — kein eigenständiges Konzept).
|
||||||
- Beweis-Fragmente/Zwischenschritte: "Beweis (b) Güte", "Beweis ⊂ Richtung", "Eigenschaft (a)".
|
- Beweis-Fragmente/Zwischenschritte: "Beweis (b) Güte", "Beweis ⊂ Richtung", "Eigenschaft (a)".
|
||||||
|
- Generische Platzhalter ohne eigenes Konzept: "Optimale Lösung", "Lösung" (nur im Kontext eines konkreten Problems sinnvoll).
|
||||||
|
- Reine Verweise OHNE inhaltliche Beschreibung: "Satz 7.18" (nichts Sprechendes nach dem —).
|
||||||
- Zu Vages oder erkennbar Erfundenes.
|
- Zu Vages oder erkennbar Erfundenes.
|
||||||
|
|
||||||
|
**Verweis-Titel MIT echtem Inhalt → aufnehmen UND umbenennen:** Hat ein Eintrag einen nichtssagenden Titel ("Satz 7.18", "Korollar 6.18", "Lemma 6.2"), beschreibt die Beschreibung aber ein echtes Konzept → nimm ihn auf und gib im Feld `umbenennen` einen sprechenden Namen aus dem Inhalt. Im `aufnehmen`-Eintrag bleibt der **Original-Titel** stehen (für die Zuordnung); der neue Name steht NUR in `umbenennen`.
|
||||||
|
|
||||||
Beispiele:
|
Beispiele:
|
||||||
- "Clique-Member — k-Clique, die einen festen Knoten v enthält" → **aufnehmen** (eigenständiges Problem).
|
- "Clique-Member — k-Clique, die einen festen Knoten v enthält" → **aufnehmen** (eigenständiges Problem).
|
||||||
- "Satz 7.18" → **verwerfen** (bloßer Verweis, keine Aussage).
|
- "Satz 7.18 — MGA hat Worst-Case-Güte 2" → **aufnehmen** + `umbenennen`: "Satz 7.18" → "MGA-Algorithmus (Güte 2)".
|
||||||
- "Beweis (b) Güte" → **verwerfen** (Fragment).
|
- "Bedingung (**) — Ungleichungen pi ≤ pk im Scheduling-Beweis" → **verwerfen** (Hilfskonstrukt im Beweis).
|
||||||
- "Satz 7.8: ΔTSP1 hat Güte 2" → **aufnehmen** (benannter Satz MIT Aussage).
|
- "Satz 7.18" (keine Beschreibung) → **verwerfen** (bloßer Verweis).
|
||||||
|
|
||||||
Regeln:
|
Regeln:
|
||||||
- Im Zweifel an der Eigenständigkeit → eher aufnehmen. Dubletten werden später separat entfernt; hier zählt nur: echter Baustein oder Müll.
|
- Im Zweifel an der Eigenständigkeit → eher aufnehmen. Dubletten werden später separat entfernt; hier zählt nur: echter Baustein oder Müll.
|
||||||
@@ -22,5 +28,5 @@ Regeln:
|
|||||||
|
|
||||||
Schreibe NUR die JSON-Datei nach: {out_path}
|
Schreibe NUR die JSON-Datei nach: {out_path}
|
||||||
|
|
||||||
Format (kein weiterer Text in der Datei):
|
Format (kein weiterer Text in der Datei; `umbenennen` darf leer sein):
|
||||||
{{"aufnehmen": ["Titel — Kurzbeschreibung"], "rest": []}}
|
{{"aufnehmen": ["Titel — Kurzbeschreibung"], "umbenennen": {{"alter Titel": "sprechender Name"}}, "rest": []}}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ Regeln:
|
|||||||
- Ein Baustein löst GENAU EIN PROBLEM. Varianten derselben Lösung gehören in den einen Baustein, nicht als eigene Einträge (richtig: `<p>` ist ein Baustein, `<input>` mit allen Typen ist ein Baustein, "Ich-Botschaften" ist ein Baustein, "Present Perfect" mit allen Signalwörtern ist ein Baustein; falsch: 21 Einträge für jeden input-Typ oder je ein Eintrag pro unregelmäßigem Verb, aber auch Sammeleinträge, die mehrere Probleme mischen).
|
- Ein Baustein löst GENAU EIN PROBLEM. Varianten derselben Lösung gehören in den einen Baustein, nicht als eigene Einträge (richtig: `<p>` ist ein Baustein, `<input>` mit allen Typen ist ein Baustein, "Ich-Botschaften" ist ein Baustein, "Present Perfect" mit allen Signalwörtern ist ein Baustein; falsch: 21 Einträge für jeden input-Typ oder je ein Eintrag pro unregelmäßigem Verb, aber auch Sammeleinträge, die mehrere Probleme mischen).
|
||||||
- Ein Baustein ist ATOMAR: genau eine Idee, vollständig in sich. Test: Man kann nichts entfernen, ohne ihn unvollständig zu machen — und es fehlt nichts, um ihn zu verstehen.
|
- Ein Baustein ist ATOMAR: genau eine Idee, vollständig in sich. Test: Man kann nichts entfernen, ohne ihn unvollständig zu machen — und es fehlt nichts, um ihn zu verstehen.
|
||||||
- Granularität: ein Baustein ist eine LERNEINHEIT, kein Lexikon-Eintrag. Familien, die zusammen gelernt werden (z. B. die font-*-Eigenschaften, die Modalverben), sind EIN Baustein.
|
- Granularität: ein Baustein ist eine LERNEINHEIT, kein Lexikon-Eintrag. Familien, die zusammen gelernt werden (z. B. die font-*-Eigenschaften, die Modalverben), sind EIN Baustein.
|
||||||
|
- Ebene (wichtig): ein Baustein ist self-contained — erklärbar, OHNE einen ANDEREN Baustein als Subjekt vorauszusetzen. Das sind Probleme, Verfahren/Algorithmen, Definitionen, benannte Sätze mit eigener Aussage. Ein Detail, das ein anderes Konzept voraussetzt, gehört IN dessen Baustein (als Teil), nicht als eigener Eintrag:
|
||||||
|
- Eine EIGENSCHAFT eines Konzepts gehört zum Konzept: „Clique" ist ein Baustein; „Clique ist NP-vollständig", „Clique ∈ NP", „Laufzeit von Clique" sind Teile davon — kein eigener Eintrag.
|
||||||
|
- Ein BEWEIS-/Reduktions-Bauteil gehört zum Satz/zur Reduktion: „A-Komponente", „Dummy-Item", „αEnde", „Hilfsvariable" — kein eigener Eintrag.
|
||||||
|
- Reine NOTATION/Symbole gehören zu ihrer Definition: „|x|", „Σ∗" — kein eigener Eintrag.
|
||||||
|
- Eine eigenständige REDUKTION zwischen zwei Problemen ist dagegen ein eigener Baustein („3-SAT ≤ Clique").
|
||||||
- KEINE Kategorien, KEINE Bewertung, KEINE Reihenfolge nach Wichtigkeit — nur eine flache, durchnummerierte Liste.
|
- KEINE Kategorien, KEINE Bewertung, KEINE Reihenfolge nach Wichtigkeit — nur eine flache, durchnummerierte Liste.
|
||||||
- Es gibt KEINE Ziel-Anzahl. Höre erst auf, wenn die Recherche nichts Neues mehr hergibt.
|
- Es gibt KEINE Ziel-Anzahl. Höre erst auf, wenn die Recherche nichts Neues mehr hergibt.
|
||||||
- Erfinde nichts: nimm nur Bausteine auf, die du in der Recherche belegt hast. Notiere pro Baustein die Quelle (URL bzw. Dateipfad). Gibt es keine Einzel-Quelle, reicht die Sammel-Quelle (Handbuch-Kapitel, Lehrbuch, Übersichtsseite, Verzeichnis).
|
- Erfinde nichts: nimm nur Bausteine auf, die du in der Recherche belegt hast. Notiere pro Baustein die Quelle (URL bzw. Dateipfad). Gibt es keine Einzel-Quelle, reicht die Sammel-Quelle (Handbuch-Kapitel, Lehrbuch, Übersichtsseite, Verzeichnis).
|
||||||
|
|||||||
Reference in New Issue
Block a user