diff --git a/app/core/graph/graph_derive_edges.py b/app/core/graph/graph_derive_edges.py index 05ee59b..687de6d 100644 --- a/app/core/graph/graph_derive_edges.py +++ b/app/core/graph/graph_derive_edges.py @@ -4,7 +4,7 @@ DESCRIPTION: Hauptlogik zur Kanten-Aggregation und De-Duplizierung. AUDIT: - Nutzt parse_link_target - Übergibt Section als 'variant' an ID-Gen - - FIXED: Semantische De-Duplizierung via 'sem_key' (löst das Callout-Problem) + - Dedup basiert jetzt auf Edge-ID (erlaubt Multigraph für Sections) """ from typing import List, Optional, Dict, Tuple from .graph_utils import ( @@ -124,6 +124,7 @@ def build_edges_for_note( if sec: def_payload["target_section"] = sec edges.append(_edge(rel, "chunk", cid, r, note_id, def_payload)) + # Für Note-Scope Sammlung nutzen wir den Original-String zur Dedup, aber gesäubert refs_all.extend([parse_link_target(r, note_id)[0] for r in refs]) # 3) Note-Scope & De-Duplizierung @@ -144,32 +145,13 @@ def build_edges_for_note( "provenance": "rule", "confidence": PROVENANCE_PRIORITY["derived:backlink"] })) - # ---------------------------------------------------------------------------------- - # FIX: Semantische Deduplizierung - # Hier lösen wir das Problem, dass Callout-Kanten andere überschreiben. - # Wir nutzen einen Key aus (Source, Target, Kind, Section), um Duplikate - # aus verschiedenen Regeln (z.B. callout vs. wikilink) zusammenzuführen. - # ---------------------------------------------------------------------------------- + # Deduplizierung: Wir nutzen jetzt die EDGE-ID als Schlüssel. + # Da die Edge-ID nun 'variant' (Section) enthält, bleiben unterschiedliche Sections erhalten. unique_map: Dict[str, dict] = {} - for e in edges: - # Semantischer Schlüssel: Unabhängig von rule_id oder edge_id - src = e.get("source_id", "") - tgt = e.get("target_id", "") - kind = e.get("kind", "") - sec = e.get("target_section", "") - - # Dieser Key ist für alle Einträge im Callout-Block UNTERSCHIEDLICH, - # da 'sec' (1) Integrität, 3) Disziplin...) unterschiedlich ist. - sem_key = f"{src}->{tgt}:{kind}@{sec}" - - if sem_key not in unique_map: - unique_map[sem_key] = e - else: - # Konfliktlösung: Die Kante mit der höheren Confidence gewinnt - curr_conf = unique_map[sem_key].get("confidence", 0.0) - new_conf = e.get("confidence", 0.0) - if new_conf > curr_conf: - unique_map[sem_key] = e + eid = e["edge_id"] + # Bei Konflikt (gleiche ID = exakt gleiche Kante und Section) gewinnt die höhere Confidence + if eid not in unique_map or e.get("confidence", 0) > unique_map[eid].get("confidence", 0): + unique_map[eid] = e return list(unique_map.values()) \ No newline at end of file