80 lines
2.4 KiB
Python
80 lines
2.4 KiB
Python
"""
|
|
app/services/feedback_service.py
|
|
Service zum Loggen von Suchanfragen und Feedback (WP-04c).
|
|
Speichert Daten als JSONL für späteres Self-Tuning (WP-08).
|
|
"""
|
|
import json
|
|
import os
|
|
import time
|
|
from pathlib import Path
|
|
from typing import Dict, Any, List
|
|
from app.models.dto import QueryRequest, QueryResponse, FeedbackRequest
|
|
|
|
# Pfad für Logs (lokal auf dem Beelink/PC)
|
|
LOG_DIR = Path("data/logs")
|
|
SEARCH_LOG_FILE = LOG_DIR / "search_history.jsonl"
|
|
FEEDBACK_LOG_FILE = LOG_DIR / "feedback.jsonl"
|
|
|
|
def _ensure_log_dir():
|
|
if not LOG_DIR.exists():
|
|
os.makedirs(LOG_DIR, exist_ok=True)
|
|
|
|
def log_search(req: QueryRequest, res: QueryResponse):
|
|
"""
|
|
Speichert den "Snapshot" der Suche.
|
|
WICHTIG: Wir speichern die Scores (Breakdown), damit wir später wissen,
|
|
warum das System so entschieden hat.
|
|
"""
|
|
_ensure_log_dir()
|
|
|
|
# Wir reduzieren die Datenmenge etwas (z.B. keine vollen Texte)
|
|
hits_summary = []
|
|
for hit in res.results:
|
|
# Falls Explanation an war, speichern wir den Breakdown, sonst die Scores
|
|
breakdown = None
|
|
if hit.explanation and hit.explanation.breakdown:
|
|
breakdown = hit.explanation.breakdown.model_dump()
|
|
|
|
hits_summary.append({
|
|
"node_id": hit.node_id,
|
|
"note_id": hit.note_id,
|
|
"total_score": hit.total_score,
|
|
"breakdown": breakdown, # Wichtig für Training!
|
|
"rank_semantic": hit.semantic_score,
|
|
"rank_edge": hit.edge_bonus
|
|
})
|
|
|
|
entry = {
|
|
"timestamp": time.time(),
|
|
"query_id": res.query_id,
|
|
"query_text": req.query,
|
|
"mode": req.mode,
|
|
"top_k": req.top_k,
|
|
"hits": hits_summary
|
|
}
|
|
|
|
try:
|
|
with open(SEARCH_LOG_FILE, "a", encoding="utf-8") as f:
|
|
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
except Exception as e:
|
|
print(f"ERROR logging search: {e}")
|
|
|
|
def log_feedback(fb: FeedbackRequest):
|
|
"""
|
|
Speichert das User-Feedback.
|
|
"""
|
|
_ensure_log_dir()
|
|
|
|
entry = {
|
|
"timestamp": time.time(),
|
|
"query_id": fb.query_id,
|
|
"node_id": fb.node_id,
|
|
"score": fb.score,
|
|
"comment": fb.comment
|
|
}
|
|
|
|
try:
|
|
with open(FEEDBACK_LOG_FILE, "a", encoding="utf-8") as f:
|
|
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
except Exception as e:
|
|
print(f"ERROR logging feedback: {e}") |