angepasster retriever

This commit is contained in:
Lars 2025-12-07 16:57:01 +01:00
parent 8b5af5ec65
commit 466ce3ca82

View File

@ -131,9 +131,8 @@ def _build_explanation(
) -> Explanation:
"""
Erstellt ein detailliertes Explanation-Objekt für einen Treffer.
Analysiert Scores, Typen und eingehende Kanten.
Analysiert Scores, Typen und Kanten (Incoming & Outgoing).
"""
# 1. Weights & Config laden
sem_w, edge_w, cent_w = _get_scoring_weights()
try:
@ -143,7 +142,6 @@ def _build_explanation(
note_type = payload.get("type", "unknown")
# 2. Score Breakdown berechnen
breakdown = ScoreBreakdown(
semantic_contribution=(sem_w * semantic_score * type_weight),
edge_contribution=(edge_w * edge_bonus),
@ -157,76 +155,87 @@ def _build_explanation(
reasons: List[Reason] = []
edges_dto: List[EdgeDTO] = []
# 3. Semantische Gründe
# 1. Semantische Gründe
if semantic_score > 0.85:
reasons.append(Reason(
kind="semantic",
message="Sehr hohe textuelle Übereinstimmung mit der Anfrage.",
message="Sehr hohe textuelle Übereinstimmung.",
score_impact=breakdown.semantic_contribution
))
elif semantic_score > 0.75:
elif semantic_score > 0.70:
reasons.append(Reason(
kind="semantic",
message="Gute textuelle Übereinstimmung.",
score_impact=breakdown.semantic_contribution
))
# 4. Typ-Gründe
if type_weight > 1.0:
# 2. Typ-Gründe
if type_weight != 1.0:
msg = "Bevorzugt" if type_weight > 1.0 else "Leicht abgewertet"
reasons.append(Reason(
kind="type",
message=f"Bevorzugt aufgrund des Typs '{note_type}' (Gewicht: {type_weight}).",
score_impact=(sem_w * semantic_score * (type_weight - 1.0)) # Delta
))
elif type_weight < 1.0:
reasons.append(Reason(
kind="type",
message=f"Abgewertet aufgrund des Typs '{note_type}' (Gewicht: {type_weight}).",
score_impact=None
message=f"{msg} aufgrund des Typs '{note_type}' (Gewicht: {type_weight}).",
score_impact=(sem_w * semantic_score * (type_weight - 1.0))
))
# 5. Graph-Gründe (Incoming Edges)
# 3. Graph-Gründe (Edges)
if subgraph and node_key and edge_bonus > 0:
# Wir suchen nach eingehenden Kanten, die diesen Bonus verursacht haben.
# graph_adapter.py (v0.4.0) muss get_incoming_edges bereitstellen.
# Wir sammeln die stärksten Kanten (egal ob rein oder raus),
# die zum Score beitragen.
# A) Outgoing (Ich verweise auf...) - Das ist oft der Hub-Score
if hasattr(subgraph, "get_outgoing_edges"):
outgoing = subgraph.get_outgoing_edges(node_key)
for edge in outgoing:
target = edge.get("target", "Unknown")
kind = edge.get("kind", "edge")
weight = edge.get("weight", 0.0)
# Nur relevante Kanten aufnehmen
if weight > 0.05:
edges_dto.append(EdgeDTO(
id=f"{node_key}->{target}:{kind}",
kind=kind, source=node_key, target=target, weight=weight, direction="out"
))
# B) Incoming (Ich werde verwiesen von...)
if hasattr(subgraph, "get_incoming_edges"):
incoming = subgraph.get_incoming_edges(node_key)
# Sortieren nach Gewicht (stärkste zuerst)
incoming_sorted = sorted(incoming, key=lambda e: e.get("weight", 0.0), reverse=True)
# Top-3 Gründe extrahieren
for idx, edge in enumerate(incoming_sorted[:3]):
for edge in incoming:
src = edge.get("source", "Unknown")
kind = edge.get("kind", "edge")
weight = edge.get("weight", 0.0)
msg = f"Verbunden mit '{src}' via '{kind}'"
reasons.append(Reason(
kind="edge",
message=msg,
score_impact=(edge_w * weight),
details={"source": src, "kind": kind, "weight": weight}
))
if weight > 0.05:
edges_dto.append(EdgeDTO(
id=f"{src}->{node_key}:{kind}",
kind=kind, source=src, target=node_key, weight=weight, direction="in"
))
# EdgeDTO für die API
edges_dto.append(EdgeDTO(
id=f"{src}->{node_key}:{kind}", # Synthetische ID für Anzeige
kind=kind,
source=src,
target=node_key,
weight=weight,
direction="in"
))
else:
# Fallback, falls GraphAdapter noch alt
reasons.append(Reason(kind="edge", message="Knoten ist im Kontext-Graphen vernetzt."))
# Sortieren nach Gewicht und Top-3 als Reasons generieren
all_edges = sorted(edges_dto, key=lambda e: e.weight, reverse=True)
# 6. Centrality Gründe
if cent_bonus > 0.05:
for top_edge in all_edges[:3]:
# Impact schätzen (grob, da Edge-Bonus eine Summe ist)
impact = edge_w * top_edge.weight
if top_edge.direction == "out":
msg = f"Verweist auf '{top_edge.target}' via '{top_edge.kind}'"
else:
msg = f"Referenziert von '{top_edge.source}' via '{top_edge.kind}'"
reasons.append(Reason(
kind="edge",
message=msg,
score_impact=impact,
details={"kind": top_edge.kind, "weight": top_edge.weight}
))
# 4. Centrality
if cent_bonus > 0.01:
reasons.append(Reason(
kind="centrality",
message="Knoten ist ein zentraler Hub im Kontext der Anfrage.",
message="Knoten liegt zentral im Kontext.",
score_impact=breakdown.centrality_contribution
))
@ -235,7 +244,6 @@ def _build_explanation(
reasons=reasons,
related_edges=edges_dto if edges_dto else None
)
# --- End Explanation Logic ---