feat: surface recommendation rationale

This commit is contained in:
2026-06-27 17:55:38 +02:00
parent 1d9840f2b9
commit 6d5e9d0a2c
5 changed files with 47 additions and 2 deletions

View File

@@ -24,7 +24,7 @@ func rerankRecommendationCandidates(candidates []recommendationCandidate, limit
continue
}
selected = append(selected, domain.Anime{Anime: candidate.anime})
selected = append(selected, domain.Anime{Anime: candidate.anime, RecommendationRationale: candidate.rationale})
features := diversityFeatures(candidate.anime)
seen.add(features)
recent = append(recent, features)

View File

@@ -48,9 +48,23 @@ func scoreRecommendationCandidate(
themeMatches: themes,
studioMatches: studios,
demographicMatches: demos,
rationale: buildRecommendationRationale(profile, candidate),
}
}
func buildRecommendationRationale(profile userTasteProfile, candidate jikan.Anime) []string {
rationale := make([]string, 0, 4)
rationale = append(rationale, matchedEntityNames(profile.genres, candidate.Genres)...)
rationale = append(rationale, matchedEntityNames(profile.themes, candidate.Themes)...)
rationale = append(rationale, matchedEntityNames(profile.studios, candidate.Studios)...)
rationale = append(rationale, matchedEntityNames(profile.demographics, candidate.Demographics)...)
if len(rationale) > 4 {
return rationale[:4]
}
return rationale
}
func recommendationCandidateScoreAdjustments(now time.Time, profile userTasteProfile, candidate jikan.Anime) float64 {
var score float64
@@ -115,3 +129,22 @@ func weightedEntityMatch(weights map[int]float64, entities []jikan.NamedEntity)
return matches, score
}
func matchedEntityNames(weights map[int]float64, entities []jikan.NamedEntity) []string {
if len(weights) == 0 {
return []string{}
}
names := make([]string, 0, 1)
for _, entity := range entities {
if entity.Name == "" || weights[entity.MalID] <= 0 {
continue
}
names = append(names, entity.Name)
if len(names) >= 1 {
break
}
}
return names
}

View File

@@ -25,6 +25,7 @@ type recommendationCandidate struct {
themeMatches int
studioMatches int
demographicMatches int
rationale []string
}
type userTasteProfile struct {

View File

@@ -9,6 +9,7 @@ import (
type Anime struct {
jikan.Anime
RecommendationRationale []string
}
type Genre struct {