Files
SkinbaseNova/docs/Discover/for-you.md
2026-04-09 08:50:36 +02:00

174 lines
4.6 KiB
Markdown

# For You
## Route
- URL: `GET /discover/for-you`
- Auth required: yes
- Controller: `App\Http\Controllers\Web\DiscoverController::forYou()`
- Entry service: `App\Services\Recommendations\RecommendationFeedResolver`
## High-level flow
`For You` is not a simple search sort.
It is a recommendation pipeline with engine selection, caching, layered candidate generation, reranking, and cursor pagination.
The controller:
1. reads `limit` and `cursor`
2. calls `RecommendationFeedResolver::getFeed()`
3. converts feed items into the artwork card view model
4. returns HTML or JSON depending on request type
## Engine selection
`RecommendationFeedResolver` chooses between two implementations:
- V2: `App\Services\Recommendations\RecommendationServiceV2`
- V1: `App\Services\Recommendations\PersonalizedFeedService`
Selection is based on:
- `config('discovery.v2.enabled')`
- rollout percentage bucket for the current user
- explicit `algo_version` override
## Cache model
Both engines use `user_recommendation_cache`.
At request time:
1. Load cache row for `(user_id, algo_version)`
2. Check cache version and `expires_at`
3. If missing or stale, dispatch `RegenerateUserRecommendationCacheJob`
4. If the row is empty, build fallback recommendations inline for the current request
This means the page is usually cache-backed, but it does not hard-fail if the cache is cold.
## V2 pipeline
V2 is the richer layered engine.
### Candidate layers
The candidate pool is blended from:
- personalized layer
- social layer
- trending layer
- exploration layer
- vector layer (only when V3/vector support is enabled and configured)
Default target ratios from `config/discovery.php`:
- personalized: 50%
- social: 20%
- trending: 20%
- exploration: 10%
### Main V2 score
For each candidate row:
```text
score
= (base_score * weight_base)
+ session_boost
+ social_boost
+ trending_boost
+ exploration_boost
+ creator_boost
+ vector_boost
- negative_penalty
- repetition_penalty
```
Where:
- `session_boost` comes from merged session/profile signals
- `social_boost` comes from followed creators and artworks liked by followed creators
- `trending_boost` is built from `trending_score_1h`, `trending_score_24h`, `trending_score_7d`
- `exploration_boost` rewards fresh uploads, new creators, and unseen tags
- `creator_boost` uses creator follower count plus artwork momentum metrics
- `vector_boost` comes from visual similarity when vector mode is enabled
- `negative_penalty` reflects hidden artworks and disliked tags
- `repetition_penalty` suppresses creator and tag repetition inside one result page
### Trending contribution inside V2
V2 combines the artwork's stored trending columns like this:
```text
trendingBoost
= (trending_score_1h * weight_1h)
+ (trending_score_24h * weight_24h)
+ (trending_score_7d * weight_7d)
```
Then divides by 100 before merging into the final score.
### Negative signals
V2 reads `user_negative_signals` and applies:
- hidden artwork exclusion
- disliked tag penalty
### Supporting data sources
V2 reads from:
- `user_recommendation_cache`
- session/profile signal builders
- `artworks`
- `artwork_stats`
- `artwork_similarities`
- `artwork_embeddings` and vector service, when enabled
- `user_followers`
- `artwork_favourites`
- `user_negative_signals`
## V1 pipeline
V1 is simpler and category-affinity driven.
It reads `user_interest_profiles`, then scores candidates with a weighted blend:
```text
score = (w1 * affinity)
+ (w2 * recency)
+ (w3 * popularity)
+ (w4 * novelty)
```
Cold start falls back to a blend of popular artworks and `artwork_similarities` seeds.
## Background jobs and schedules
### Directly relevant
- `RegenerateUserRecommendationCacheJob`
- dispatched on demand when cache is missing or stale
### Support jobs for candidate quality
- `RecBuildItemPairsFromFavouritesJob` every 4 hours
- `RecComputeSimilarByTagsJob` daily at 02:00
- `RecComputeSimilarByBehaviorJob` daily at 02:15
- `RecComputeSimilarHybridJob` daily at 02:30
- `analytics:aggregate-discovery-feedback` daily at 03:25
These jobs do not directly render the page, but they improve the offline inputs and behavioral data used by the recommender.
## Cache behavior
- V1 cache TTL default: 60 minutes
- V2 cache TTL default: 15 minutes
Cursor pagination is offset-based under the hood.
## Notes
- `For You` is the most configuration-sensitive page in this set.
- What a given user sees can differ by rollout bucket, `algo_version`, cache state, and whether V2/V3 features are enabled.
- If you are debugging a single user's page, inspect `RecommendationFeedResolver::inspectDecision()` first.