From 03d3173ca6cbe5c69c87d97988ea62315bac9357 Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 29 Dec 2025 12:42:26 +0100 Subject: [PATCH] =?UTF-8?q?neu=20deduplizierung=20f=C3=BCr=20callout-edges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/graph/graph_derive_edges.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/core/graph/graph_derive_edges.py b/app/core/graph/graph_derive_edges.py index 2d20530..05ee59b 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 (ignoriert rule_id bei Konflikten) + - FIXED: Semantische De-Duplizierung via 'sem_key' (löst das Callout-Problem) """ from typing import List, Optional, Dict, Tuple from .graph_utils import ( @@ -63,6 +63,7 @@ def build_edges_for_note( payload = { "chunk_id": cid, + # Variant=sec sorgt für eindeutige ID pro Abschnitt "edge_id": _mk_edge_id(k, cid, t, "chunk", "inline:rel", variant=sec), "provenance": "explicit", "rule_id": "inline:rel", "confidence": PROVENANCE_PRIORITY["inline:rel"] } @@ -127,6 +128,8 @@ def build_edges_for_note( # 3) Note-Scope & De-Duplizierung if include_note_scope_refs: + # refs_all ist jetzt schon gesäubert (nur Targets) + # note_level_references müssen auch gesäubert werden cleaned_note_refs = [parse_link_target(r, note_id)[0] for r in (note_level_references or [])] refs_note = _dedupe_seq((refs_all or []) + cleaned_note_refs) @@ -141,9 +144,12 @@ 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. + # ---------------------------------------------------------------------------------- unique_map: Dict[str, dict] = {} for e in edges: @@ -153,6 +159,8 @@ def build_edges_for_note( 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: