angepasster retriever
This commit is contained in:
parent
8b5af5ec65
commit
466ce3ca82
|
|
@ -131,9 +131,8 @@ def _build_explanation(
|
||||||
) -> Explanation:
|
) -> Explanation:
|
||||||
"""
|
"""
|
||||||
Erstellt ein detailliertes Explanation-Objekt für einen Treffer.
|
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()
|
sem_w, edge_w, cent_w = _get_scoring_weights()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -143,7 +142,6 @@ def _build_explanation(
|
||||||
|
|
||||||
note_type = payload.get("type", "unknown")
|
note_type = payload.get("type", "unknown")
|
||||||
|
|
||||||
# 2. Score Breakdown berechnen
|
|
||||||
breakdown = ScoreBreakdown(
|
breakdown = ScoreBreakdown(
|
||||||
semantic_contribution=(sem_w * semantic_score * type_weight),
|
semantic_contribution=(sem_w * semantic_score * type_weight),
|
||||||
edge_contribution=(edge_w * edge_bonus),
|
edge_contribution=(edge_w * edge_bonus),
|
||||||
|
|
@ -157,76 +155,87 @@ def _build_explanation(
|
||||||
reasons: List[Reason] = []
|
reasons: List[Reason] = []
|
||||||
edges_dto: List[EdgeDTO] = []
|
edges_dto: List[EdgeDTO] = []
|
||||||
|
|
||||||
# 3. Semantische Gründe
|
# 1. Semantische Gründe
|
||||||
if semantic_score > 0.85:
|
if semantic_score > 0.85:
|
||||||
reasons.append(Reason(
|
reasons.append(Reason(
|
||||||
kind="semantic",
|
kind="semantic",
|
||||||
message="Sehr hohe textuelle Übereinstimmung mit der Anfrage.",
|
message="Sehr hohe textuelle Übereinstimmung.",
|
||||||
score_impact=breakdown.semantic_contribution
|
score_impact=breakdown.semantic_contribution
|
||||||
))
|
))
|
||||||
elif semantic_score > 0.75:
|
elif semantic_score > 0.70:
|
||||||
reasons.append(Reason(
|
reasons.append(Reason(
|
||||||
kind="semantic",
|
kind="semantic",
|
||||||
message="Gute textuelle Übereinstimmung.",
|
message="Gute textuelle Übereinstimmung.",
|
||||||
score_impact=breakdown.semantic_contribution
|
score_impact=breakdown.semantic_contribution
|
||||||
))
|
))
|
||||||
|
|
||||||
# 4. Typ-Gründe
|
# 2. Typ-Gründe
|
||||||
if type_weight > 1.0:
|
if type_weight != 1.0:
|
||||||
|
msg = "Bevorzugt" if type_weight > 1.0 else "Leicht abgewertet"
|
||||||
reasons.append(Reason(
|
reasons.append(Reason(
|
||||||
kind="type",
|
kind="type",
|
||||||
message=f"Bevorzugt aufgrund des Typs '{note_type}' (Gewicht: {type_weight}).",
|
message=f"{msg} aufgrund des Typs '{note_type}' (Gewicht: {type_weight}).",
|
||||||
score_impact=(sem_w * semantic_score * (type_weight - 1.0)) # Delta
|
score_impact=(sem_w * semantic_score * (type_weight - 1.0))
|
||||||
))
|
|
||||||
elif type_weight < 1.0:
|
|
||||||
reasons.append(Reason(
|
|
||||||
kind="type",
|
|
||||||
message=f"Abgewertet aufgrund des Typs '{note_type}' (Gewicht: {type_weight}).",
|
|
||||||
score_impact=None
|
|
||||||
))
|
))
|
||||||
|
|
||||||
# 5. Graph-Gründe (Incoming Edges)
|
# 3. Graph-Gründe (Edges)
|
||||||
if subgraph and node_key and edge_bonus > 0:
|
if subgraph and node_key and edge_bonus > 0:
|
||||||
# Wir suchen nach eingehenden Kanten, die diesen Bonus verursacht haben.
|
# Wir sammeln die stärksten Kanten (egal ob rein oder raus),
|
||||||
# graph_adapter.py (v0.4.0) muss get_incoming_edges bereitstellen.
|
# 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"):
|
if hasattr(subgraph, "get_incoming_edges"):
|
||||||
incoming = subgraph.get_incoming_edges(node_key)
|
incoming = subgraph.get_incoming_edges(node_key)
|
||||||
|
for edge in incoming:
|
||||||
# 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]):
|
|
||||||
src = edge.get("source", "Unknown")
|
src = edge.get("source", "Unknown")
|
||||||
kind = edge.get("kind", "edge")
|
kind = edge.get("kind", "edge")
|
||||||
weight = edge.get("weight", 0.0)
|
weight = edge.get("weight", 0.0)
|
||||||
|
|
||||||
msg = f"Verbunden mit '{src}' via '{kind}'"
|
if weight > 0.05:
|
||||||
reasons.append(Reason(
|
edges_dto.append(EdgeDTO(
|
||||||
kind="edge",
|
id=f"{src}->{node_key}:{kind}",
|
||||||
message=msg,
|
kind=kind, source=src, target=node_key, weight=weight, direction="in"
|
||||||
score_impact=(edge_w * weight),
|
))
|
||||||
details={"source": src, "kind": kind, "weight": weight}
|
|
||||||
))
|
|
||||||
|
|
||||||
# EdgeDTO für die API
|
# Sortieren nach Gewicht und Top-3 als Reasons generieren
|
||||||
edges_dto.append(EdgeDTO(
|
all_edges = sorted(edges_dto, key=lambda e: e.weight, reverse=True)
|
||||||
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."))
|
|
||||||
|
|
||||||
# 6. Centrality Gründe
|
for top_edge in all_edges[:3]:
|
||||||
if cent_bonus > 0.05:
|
# 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(
|
reasons.append(Reason(
|
||||||
kind="centrality",
|
kind="centrality",
|
||||||
message="Knoten ist ein zentraler Hub im Kontext der Anfrage.",
|
message="Knoten liegt zentral im Kontext.",
|
||||||
score_impact=breakdown.centrality_contribution
|
score_impact=breakdown.centrality_contribution
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
@ -235,7 +244,6 @@ def _build_explanation(
|
||||||
reasons=reasons,
|
reasons=reasons,
|
||||||
related_edges=edges_dto if edges_dto else None
|
related_edges=edges_dto if edges_dto else None
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- End Explanation Logic ---
|
# --- End Explanation Logic ---
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user