Files
SkinbaseNova/.deploy/artwork-evolution-release/docs/Discover/for-you.md
2026-04-18 17:02:56 +02:00

4.6 KiB

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:

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

V2 combines the artwork's stored trending columns like this:

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:

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.