feat(recommendation): extend Score with SimilarityScore + SimilarityWeight

This commit is contained in:
2026-04-29 08:01:53 -04:00
parent 362cb2c9ce
commit bb3f911761
3 changed files with 47 additions and 6 deletions
+11 -6
View File
@@ -11,23 +11,26 @@ import (
// ContextualMatchScore is in [0, 1] — max similarity between the user's
// current session vector and any non-seed contextual_like row for this
// track. Set by LoadCandidates after a bulk fetch.
// SimilarityScore is in [0, 1]; 0 when no signal (random fill).
type ScoringInputs struct {
IsGeneralLiked bool
LastPlayedAt *time.Time // nil = never played
PlayCount int // total play_events
SkipCount int // play_events with was_skipped=true
ContextualMatchScore float64 // [0, 1]; 0 when no signal
SimilarityScore float64 // [0, 1]; 0 when no signal (random fill)
}
// ScoringWeights are the operator-tunable knobs. Defaults live in
// config.RecommendationConfig and are propagated here per request.
type ScoringWeights struct {
BaseWeight float64
LikeBoost float64
RecencyWeight float64
SkipPenalty float64
JitterMagnitude float64
ContextWeight float64
BaseWeight float64
LikeBoost float64
RecencyWeight float64
SkipPenalty float64
JitterMagnitude float64
ContextWeight float64
SimilarityWeight float64
}
// Score computes the weighted-shuffle score per spec §6:
@@ -37,6 +40,7 @@ type ScoringWeights struct {
// + recency_decay * RecencyWeight
// - skip_ratio * SkipPenalty
// + contextual_match_score * ContextWeight
// + similarity_score * SimilarityWeight
// + small_random_jitter
//
// Higher score = more likely to surface. rng is a function returning a
@@ -50,6 +54,7 @@ func Score(in ScoringInputs, w ScoringWeights, now time.Time, rng func() float64
s += recencyDecay(in.LastPlayedAt, now) * w.RecencyWeight
s -= skipRatio(in.PlayCount, in.SkipCount) * w.SkipPenalty
s += in.ContextualMatchScore * w.ContextWeight
s += in.SimilarityScore * w.SimilarityWeight
s += (rng()*2 - 1) * w.JitterMagnitude
return s
}