angepasster retriever
This commit is contained in:
parent
8b5af5ec65
commit
466ce3ca82
|
|
@ -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}
|
||||
))
|
||||
|
||||
# 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."))
|
||||
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"
|
||||
))
|
||||
|
||||
# 6. Centrality Gründe
|
||||
if cent_bonus > 0.05:
|
||||
# Sortieren nach Gewicht und Top-3 als Reasons generieren
|
||||
all_edges = sorted(edges_dto, key=lambda e: e.weight, reverse=True)
|
||||
|
||||
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 ---
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user