Prompt
I need help designing content recommendations for a streaming app. The system should use watch history, session signals, and recency, and it has to explain why each title was shown.
Latest observation
Here’s a practical way to design a recommendation system for a streaming app that uses watch history, session signals, and recency, while also providing a clear “why was this shown?” explanation for each title.
1) What the system should optimize for
Your recommendation stack should balance:
- Relevance: show titles the user is likely to watch
- Freshness/recency: prioritize what matches the user’s current mood
- Diversity: avoid showing 20 near-identical items
- Explainability: every recommendation needs a human-readable reason
A good approach is a multi-stage recommender:
- Candidate generation
Pull a few hundred/thousand potentially relevant titles from multiple sources. - Ranking
Score and sort candidates based on history, session context, and recency. - Post-processing
Deduplicate, diversify, enforce business rules, and attach explanations.
2) Core signals to use
A. Watch history signals
These are long-term preference signals.
Use:
- Genres watched most often
- Actors/directors/franchises repeatedly watched
- Completion rate
- Rewatch behavior
- Preferred runtime
- Language, region, maturity rating
- Time-of-day/day-of-week habits
- “Like pattern” from what the user finishes vs abandons
Useful derived features:
- Affinity score between user and title metadata
- Embedding similarity from past watched titles to candidate title
- Sequence patterns: user watched thriller → true crime → mystery
- Satisfaction score: based on completion, rewatch, thumbs up/down, skips
B. Session signals
These capture the user’s current intent.
Use:
- What they watched in this session
- What they hovered over or clicked
- Search terms
- Trailer plays
- Browse behavior, scroll depth
- Dwell time on title details
- Time since last action
- Device type and context, if relevant
Examples:
- If the user just watched a comedy special, boost more comedy.
- If they searched “space drama,” boost sci-fi/drama titles.
- If they hovered over one actor multiple times, boost that actor’s films.
Session signals should usually carry more weight than historical signals because they reflect current intent.
C. Recency signals
Recency can mean both content freshness and user behavior freshness.
Use:
- Newly added titles
- Recently released titles
- Recently trending titles
- Titles recently watched by similar users
- Time decay on old preferences
Good practice:
- Let recent behavior matter more than old behavior
- Decay watch history signals over time
- Give a small boost to newly released or newly added content if it matches profile
3) A practical scoring model
You can compute a final score like:
[ Score(title) = w_h \cdot History + w_s \cdot Session + w_r \cdot Recency + w_b \cdot Business + w_d \cdot DiversityPenalty ]
Where:
- History = long-term user-title affinity
- Session = current intent match
- Recency = freshness, trending, time decay
- Business = editorial/promotional priorities, availability, localization
- DiversityPenalty = lowers near-duplicates
A simple version:
- History: 0.45
- Session: 0.35
- Recency: 0.15
- Business: 0.05
For a “continue watching” or active browsing session, you might shift more weight to session signals:
- History: 0.30
- Session: 0.50
- Recency: 0.15
- Business: 0.05
4) Candidate generation sources
Generate candidates from multiple “reason channels” so explanations are easier later.
Examples:
History-based candidates
- Similar to titles watched to completion
- Same genre / same cast / same franchise
- From a user profile cluster
Session-based candidates
- Similar to the last title watched in the session
- Matches search query
- Matches current browsing category
Recency-based candidates
- Newly added in liked genres
- Trending among similar users
- Recently released titles in watched categories
Each candidate should retain provenance, e.g.:
source = "because you watched X"source = "because you searched for Y"source = "new release in drama"source = "popular with viewers who liked Z"
This provenance becomes the explanation layer.
5) Explainability design: “Why was this shown?”
Every recommendation should have a primary reason and optionally a secondary reason.
Good explanation patterns
- “Because you watched Dark City”
- “Because you’ve watched several sci-fi thrillers”
- “Because you searched for courtroom dramas”
- “New this week in your favorite genre”
- “Popular among people who watched The Crown”
- “Because you often finish episodes of limited series”
Best practices
- Make explanations specific
- Use observable signals
- Keep them short and natural
- Avoid exposing sensitive inferences
- Don’t mention data the user didn’t clearly provide if it feels creepy
Example explanation objects
For internal use:
{
"title_id": "tt123",
"score": 0.87,
"explanations": [
{
"type": "history",
"text": "Because you watched several sci-fi thrillers",
"weight": 0.42
},
{
"type": "session",
"text": "Based on what you just watched tonight",
"weight": 0.31
},
{
"type": "recency",
"text": "New this month",
"weight": 0.14
}
]
}
For the UI, show only one reason:
- “Because you watched several sci-fi thrillers” or
- “New this month in sci-fi”
6) A good explanation hierarchy
Use this order of preference:
- Session reason
“Because you just watched X” or “because you searched for Y” - History reason
“Because you often watch Z” - Recency reason
“New this week” or “Trending now” - Collaborative reason
“Because people who watched A also watched B” - Fallback
“Recommended for you”
This makes explanations feel personalized and timely.
7) Avoiding weak or misleading explanations
Don’t say:
- “Because you are a female user aged 32”
- “Because similar users like it” if that’s the only signal and it feels opaque
- “Because our algorithm thought so”
Avoid explanations that are:
- Too generic
- Too technical
- Based on protected attributes
- Inconsistent with the actual scoring signal
Important: the explanation should reflect the dominant reason in the ranking, not a random plausible reason.
8) Architecture recommendation
Offline pipeline
- Build user profiles from watch history
- Update content embeddings and metadata
- Compute recency/trending stats
- Train ranking model if using ML
Online request flow
- Fetch recent session events
- Retrieve candidates from multiple sources
- Score candidates
- Diversify and apply business rules
- Generate explanation from top contributing feature/source
- Return titles + reason text
Latency matters, so explanation generation should be lightweight and preferably derived from already computed features.
9) Example recommendation logic
Example user behavior
- Watches crime documentaries
- Frequently finishes limited series
- Just searched “political thriller”
- Recently watched a new release about investigations
Possible ranked recommendations
- Political thriller limited series
Reason: “Because you searched for political thrillers” - New crime docuseries
Reason: “New this week in true crime” - Investigative drama
Reason: “Because you watched similar documentaries” - Award-winning mini-series
Reason: “Because you often finish limited series”
10) Metrics to evaluate
Ranking metrics
- CTR on recommendations
- Play start rate
- Watch time
- Completion rate
- Long-term retention
Explanation quality metrics
- Explanation click-through
- User trust/acceptance surveys
- “Hide this” or “not interested” rate after explanation
- A/B test of explanation formats
11) Implementation tip: use explanation tags
For each title, store a list of candidate explanations with scores:
[
{"reason_code": "watched_similar_titles", "score": 0.82},
{"reason_code": "searched_keyword_match", "score": 0.77},
{"reason_code": "new_release", "score": 0.31}
]
Then map reason codes to templates:
watched_similar_titles→ “Because you watched similar titles”searched_keyword_match→ “Because you searched for {query}”new_release→ “New on the platform”
This keeps explanations consistent and easy to localize.
12) Recommended starting point
If you want a strong first version, do this:
- Candidate generation
- similar-to-history
- session-based matching
- trending/new releases
- Ranking
- weighted sum of history/session/recency
- Explanations
- choose top contributing source
- map to a short human-readable template
That gives you:
- Good relevance
- Strong personalization
- Simple explanations
- Easy iteration
If you want, I can also provide:
- a sample data schema,
- a scoring formula with feature definitions, or
- a pseudo-code ranking pipeline for this system.