Skip to content

Recommendation Model [WIP]

Status: Planned. This spec is a work-in-progress, and the shipped API does not currently expose GET /recommendations. The custom model architecture, scoring pipeline, training cycle, and embedding-based approach will be designed in a later phase. The interim ranking below describes the intended first shipped recommendation behavior.

The recommendation model provides personalized ranking of events and gigs for each user. It operates independently from the LLM chatbot. The final model will be a purpose-built system trained on platform-specific signals.

Until the custom model is designed, the GET /recommendations endpoint uses a weighted scoring model:

  1. Candidate selection: Events with status OPEN or IN_PROGRESS and startAt in the future.
  2. Dismiss exclusion: Events the user has dismissed are excluded before scoring.
  3. Scoring: Each candidate receives a score combining:
    • Interest alignment (dominant weight): events whose category matches any stated user interest receive a large fixed bonus that cannot be overcome by popularity alone.
    • Popularity (secondary, capped): log-normalized interaction count, capped so even very popular events cannot outrank an interest-matched one.
  4. Tiebreakers: Equal-score events are ordered by recency (sooner start first), then by stable id.
  5. Fallback: Users with no interests and no interactions receive popularity-then-recency ranking (POPULARITY_FALLBACK mode).

This is a deterministic query-time computation — no model training, no vectors, no caching layer.

SignalSourceUsed in interim?
User interestsUser.interestsYes — category match boost
Interaction historyInteraction tablePartial — dismiss exclusion only
Event recencyEvent.startAtYes
Event popularityAggregate interaction countYes
Category affinityFrequency of interactions per categoryNo — deferred to custom model
Geographic proximityUser location vs. event lat/lngNo — deferred to custom model
Embeddings / semantic similarityEvent embeddings via pgvectorNo — deferred to custom model
Collaborative filteringUser-event matrixNo — deferred to custom model

The custom model phase will evaluate:

  • Embedding-based content scoring: Represent events as embeddings (via the AI module’s embedding task) and compute semantic similarity against user profile embeddings. Replaces manual feature vectors.
  • Collaborative filtering: User-event interaction matrix with matrix factorization.
  • Hybrid approach: Content-based as primary ranker, collaborative as boost signal.
  • Training/update cycle: Nightly retraining for collaborative; real-time embedding updates for content.
  • Caching: Per-user cache with TTL and invalidation on interaction/interest change.

These details will be spec’d when the model is designed.

GIVEN user A has interests ["music", "tech"]
WHEN user A sends GET /recommendations
THEN events with category "music" or "tech" appear higher in the list
GIVEN event E has 50 total interactions and event F has 5
AND both match user A's interests equally
WHEN user A sends GET /recommendations
THEN event E ranks higher than event F
GIVEN event A starts tomorrow and event B starts in 30 days
AND both have equal popularity and interest match
WHEN user A sends GET /recommendations
THEN event A ranks higher than event B

S-REC-MODEL-7: Profile alignment outranks high popularity

Section titled “S-REC-MODEL-7: Profile alignment outranks high popularity”
GIVEN user A has interests ["music"]
AND event E has category "music" and 0 interactions
AND event F has category "sports" and 100 interactions
WHEN user A sends GET /recommendations
THEN event E ranks higher than event F
GIVEN user A dismissed event E
WHEN user A sends GET /recommendations
THEN event E does not appear in the results
GIVEN event E has startAt in the past and status COMPLETED
WHEN user A sends GET /recommendations
THEN event E is not in the results

S-REC-MODEL-6: No interests, no interactions — popularity fallback

Section titled “S-REC-MODEL-6: No interests, no interactions — popularity fallback”
GIVEN user A has no interests and no interactions
WHEN user A sends GET /recommendations
THEN events are sorted by popularity (interaction count) then recency

See test-cases/recommendations/model.md for the full test case registry (TC-REC-MODEL-001 through TC-REC-MODEL-006).