- Add FrequencyTracker: increment(), get_frequency_score(), get_recency_score(), get_time_decay() - Add EmotionAnalyzer: EMOTION_KEYWORDS dict, extract(), calculate_score(), get_emotion_profile() - Add ImpactEvaluator: evaluate(), get_topic_overlap(), rank_by_impact() - Add ImportanceScorer: composite scoring (freq 35% + recency 20% + emotion 25% + impact 20%) - Update UserMemory model: frequency_count, emotion_tags, importance_score, importance_level, associated_topics - Integrate ImportanceScorer into memory_service.py (recall + importance update) - Add 37 tests for all memory scoring components - Fix urgency patterns: remove overly broad '今天' that matched neutral text - Update memory-update checklist: mark all M.1 tasks complete
53 lines
1.7 KiB
Python
53 lines
1.7 KiB
Python
"""
|
|
ImpactEvaluator
|
|
|
|
Evaluates the breadth of impact a memory has based on associated topics.
|
|
"""
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from app.models.memory import UserMemory
|
|
|
|
|
|
class ImpactEvaluator:
|
|
"""Evaluate the impact breadth of a memory"""
|
|
|
|
# Threshold for maximum impact score
|
|
IMPACT_THRESHOLD = 5 # Number of associated topics for max impact
|
|
|
|
def evaluate(self, memory: "UserMemory") -> float:
|
|
"""Calculate impact score (0.0 - 1.0)
|
|
|
|
The more associated topics a memory has, the higher its impact.
|
|
Topics represent "what this memory is about" — if it touches
|
|
many aspects of the user's life, it has high impact.
|
|
"""
|
|
associated_topics = memory.associated_topics or []
|
|
if not associated_topics:
|
|
return 0.0
|
|
|
|
# Normalize: IMPACT_THRESHOLD topics = full impact (1.0)
|
|
raw_score = len(associated_topics) / self.IMPACT_THRESHOLD
|
|
return min(1.0, raw_score)
|
|
|
|
def get_topic_overlap(self, memory_a: "UserMemory", memory_b: "UserMemory") -> float:
|
|
"""Calculate topic overlap between two memories (0.0 - 1.0)
|
|
|
|
Used for finding related memories.
|
|
"""
|
|
topics_a = set(memory_a.associated_topics or [])
|
|
topics_b = set(memory_b.associated_topics or [])
|
|
|
|
if not topics_a or not topics_b:
|
|
return 0.0
|
|
|
|
intersection = topics_a & topics_b
|
|
union = topics_a | topics_b
|
|
|
|
return len(intersection) / len(union) if union else 0.0
|
|
|
|
def rank_by_impact(self, memories: list["UserMemory"]) -> list["UserMemory"]:
|
|
"""Rank memories by impact score (descending)"""
|
|
return sorted(memories, key=lambda m: self.evaluate(m), reverse=True)
|