letzte anpassungen
This commit is contained in:
parent
8490911958
commit
7fa9ce81bd
|
|
@ -1,16 +1,25 @@
|
||||||
"""
|
"""
|
||||||
FILE: app/core/graph/graph_subgraph.py
|
FILE: app/core/graph/graph_subgraph.py
|
||||||
DESCRIPTION: In-Memory Repräsentation eines Graphen für Scoring und Analyse.
|
DESCRIPTION: In-Memory Repräsentation eines Graphen für Scoring und Analyse.
|
||||||
|
Zentrale Komponente für die Graph-Expansion (BFS) und Bonus-Berechnung.
|
||||||
|
MODULARISIERUNG: Teil des graph-Pakets (WP-14).
|
||||||
|
VERSION: 1.1.0
|
||||||
|
STATUS: Active
|
||||||
"""
|
"""
|
||||||
import math
|
import math
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Dict, List, Optional, DefaultDict, Any, Set
|
from typing import Dict, List, Optional, DefaultDict, Any, Set
|
||||||
from qdrant_client import QdrantClient
|
from qdrant_client import QdrantClient
|
||||||
|
|
||||||
|
# Lokale Paket-Imports
|
||||||
from .graph_weights import EDGE_BASE_WEIGHTS, calculate_edge_weight
|
from .graph_weights import EDGE_BASE_WEIGHTS, calculate_edge_weight
|
||||||
from .graph_db_adapter import fetch_edges_from_qdrant
|
from .graph_db_adapter import fetch_edges_from_qdrant
|
||||||
|
|
||||||
class Subgraph:
|
class Subgraph:
|
||||||
"""Leichtgewichtiger Subgraph mit Adjazenzlisten & Kennzahlen."""
|
"""
|
||||||
|
Leichtgewichtiger Subgraph mit Adjazenzlisten & Kennzahlen.
|
||||||
|
Wird für die Berechnung von Graph-Boni im Retriever genutzt.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.adj: DefaultDict[str, List[Dict]] = defaultdict(list)
|
self.adj: DefaultDict[str, List[Dict]] = defaultdict(list)
|
||||||
|
|
@ -19,7 +28,10 @@ class Subgraph:
|
||||||
self.out_degree: DefaultDict[str, int] = defaultdict(int)
|
self.out_degree: DefaultDict[str, int] = defaultdict(int)
|
||||||
|
|
||||||
def add_edge(self, e: Dict) -> None:
|
def add_edge(self, e: Dict) -> None:
|
||||||
"""Fügt eine Kante hinzu und aktualisiert Indizes."""
|
"""
|
||||||
|
Fügt eine Kante hinzu und aktualisiert Indizes.
|
||||||
|
Unterstützt Kontext-Notes für verbesserte Graph-Konnektivität.
|
||||||
|
"""
|
||||||
src = e.get("source")
|
src = e.get("source")
|
||||||
tgt = e.get("target")
|
tgt = e.get("target")
|
||||||
kind = e.get("kind")
|
kind = e.get("kind")
|
||||||
|
|
@ -29,15 +41,15 @@ class Subgraph:
|
||||||
if not src or not tgt:
|
if not src or not tgt:
|
||||||
return
|
return
|
||||||
|
|
||||||
# 1. Forward
|
# 1. Forward-Kante
|
||||||
self.adj[src].append({"target": tgt, "kind": kind, "weight": weight})
|
self.adj[src].append({"target": tgt, "kind": kind, "weight": weight})
|
||||||
self.out_degree[src] += 1
|
self.out_degree[src] += 1
|
||||||
self.in_degree[tgt] += 1
|
self.in_degree[tgt] += 1
|
||||||
|
|
||||||
# 2. Reverse (WP-04b Explanation)
|
# 2. Reverse-Kante (für WP-04b Explanation Layer)
|
||||||
self.reverse_adj[tgt].append({"source": src, "kind": kind, "weight": weight})
|
self.reverse_adj[tgt].append({"source": src, "kind": kind, "weight": weight})
|
||||||
|
|
||||||
# 3. Kontext-Note Handling
|
# 3. Kontext-Note Handling (erhöht die Zentralität der Parent-Note)
|
||||||
if owner and owner != src:
|
if owner and owner != src:
|
||||||
self.adj[owner].append({"target": tgt, "kind": kind, "weight": weight})
|
self.adj[owner].append({"target": tgt, "kind": kind, "weight": weight})
|
||||||
self.out_degree[owner] += 1
|
self.out_degree[owner] += 1
|
||||||
|
|
@ -54,16 +66,21 @@ class Subgraph:
|
||||||
return self.aggregate_edge_bonus(node_id)
|
return self.aggregate_edge_bonus(node_id)
|
||||||
|
|
||||||
def centrality_bonus(self, node_id: str) -> float:
|
def centrality_bonus(self, node_id: str) -> float:
|
||||||
"""Log-gedämpfte Zentralität (In-Degree)."""
|
"""
|
||||||
|
Log-gedämpfte Zentralität basierend auf dem In-Degree.
|
||||||
|
Begrenzt auf einen maximalen Boost von 0.15.
|
||||||
|
"""
|
||||||
indeg = self.in_degree.get(node_id, 0)
|
indeg = self.in_degree.get(node_id, 0)
|
||||||
if indeg <= 0:
|
if indeg <= 0:
|
||||||
return 0.0
|
return 0.0
|
||||||
return min(math.log1p(indeg) / 10.0, 0.15)
|
return min(math.log1p(indeg) / 10.0, 0.15)
|
||||||
|
|
||||||
def get_outgoing_edges(self, node_id: str) -> List[Dict[str, Any]]:
|
def get_outgoing_edges(self, node_id: str) -> List[Dict[str, Any]]:
|
||||||
|
"""Gibt alle ausgehenden Kanten einer Node zurück."""
|
||||||
return self.adj.get(node_id, [])
|
return self.adj.get(node_id, [])
|
||||||
|
|
||||||
def get_incoming_edges(self, node_id: str) -> List[Dict[str, Any]]:
|
def get_incoming_edges(self, node_id: str) -> List[Dict[str, Any]]:
|
||||||
|
"""Gibt alle eingehenden Kanten einer Node zurück."""
|
||||||
return self.reverse_adj.get(node_id, [])
|
return self.reverse_adj.get(node_id, [])
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -74,7 +91,10 @@ def expand(
|
||||||
depth: int = 1,
|
depth: int = 1,
|
||||||
edge_types: Optional[List[str]] = None,
|
edge_types: Optional[List[str]] = None,
|
||||||
) -> Subgraph:
|
) -> Subgraph:
|
||||||
"""Expandiert ab Seeds entlang von Edges bis zu einer bestimmten Tiefe."""
|
"""
|
||||||
|
Expandiert ab Seeds entlang von Edges bis zu einer bestimmten Tiefe.
|
||||||
|
Nutzt fetch_edges_from_qdrant für den Datenbankzugriff.
|
||||||
|
"""
|
||||||
sg = Subgraph()
|
sg = Subgraph()
|
||||||
frontier = set(seeds)
|
frontier = set(seeds)
|
||||||
visited = set()
|
visited = set()
|
||||||
|
|
@ -83,6 +103,7 @@ def expand(
|
||||||
if not frontier:
|
if not frontier:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Batch-Abfrage der Kanten für die aktuelle Ebene
|
||||||
payloads = fetch_edges_from_qdrant(client, prefix, list(frontier), edge_types)
|
payloads = fetch_edges_from_qdrant(client, prefix, list(frontier), edge_types)
|
||||||
next_frontier: Set[str] = set()
|
next_frontier: Set[str] = set()
|
||||||
|
|
||||||
|
|
@ -91,12 +112,14 @@ def expand(
|
||||||
if not src or not tgt: continue
|
if not src or not tgt: continue
|
||||||
|
|
||||||
sg.add_edge({
|
sg.add_edge({
|
||||||
"source": src, "target": tgt,
|
"source": src,
|
||||||
|
"target": tgt,
|
||||||
"kind": pl.get("kind", "edge"),
|
"kind": pl.get("kind", "edge"),
|
||||||
"weight": calculate_edge_weight(pl),
|
"weight": calculate_edge_weight(pl),
|
||||||
"note_id": pl.get("note_id"),
|
"note_id": pl.get("note_id"),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# BFS Logik: Neue Ziele in die nächste Frontier aufnehmen
|
||||||
if tgt not in visited:
|
if tgt not in visited:
|
||||||
next_frontier.add(str(tgt))
|
next_frontier.add(str(tgt))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user