Save workspace changes
This commit is contained in:
174
.deploy/artwork-evolution-release/docs/Discover/for-you.md
Normal file
174
.deploy/artwork-evolution-release/docs/Discover/for-you.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user