The Reinforcement Loop Machine

Step through the algorithmic feedback loop that causes your interests to drift, even when you don't change your behavior.

What Is The Reinforcement Loop?

The algorithm creates a self-reinforcing feedback loop: Your profile determines what you see. What you see determines what you engage with. What you engage with updates your profile. Round and round, amplifying imbalances week after week.

The Six Steps of the Loop

1. Your Profile: 60% AI, 40% Cooking

2. Fetch Candidates: Algorithm fetches more AI tweets (60%) than Cooking (40%)

3. Score Tweets: AI tweets score higher (0.9 × 0.60 vs 0.9 × 0.40)

4. Build Feed: You see 60% AI, 40% Cooking (matches your profile)

5. You Engage: You engage with what you see (60% AI, 40% Cooking)

6. Update Profile: Next week, your profile becomes 62% AI, 38% Cooking

→ Return to Step 1 with NEW profile (loop repeats)

Why This Matters

The Shape of the Drift

The drift follows a logistic curve: Fast growth initially (60 → 70% in 12 weeks), then slowing as you approach saturation (~80% is typical plateau). This isn't about your behavior changing—it's pure mathematics from multiplicative scoring + L2 normalization + weekly batch updates.


Step Through The Loop

Configure Your Starting Profile

Set your initial cluster interests. Watch how even a small imbalance (60/40) compounds over time.

Total: 100%


The Technical Details

Why This Loop Creates Drift

1. Multiplicative Scoring Amplifies Advantages

At the scoring stage, tweets get multiplied by your cluster interest:

AI tweet (quality 0.9): 0.9 × 0.60 = 0.54
Cooking tweet (quality 0.9): 0.9 × 0.40 = 0.36

50% score advantage for AI despite equal quality!

2. You Engage With What You See

Because AI content ranks higher, you see more of it. Your engagement naturally matches what's visible:

Feed composition: 60% AI, 40% Cooking
Your engagement: 60% AI, 40% Cooking

You didn't change your preferences - you engaged with what was shown!

3. L2 Normalization Creates Zero-Sum Dynamics

Cluster interests must sum to 1.0 (100%). When AI increases, Cooking MUST decrease:

Before: 60% AI, 40% Cooking (sum = 100%)
After:  62% AI, 38% Cooking (sum = 100%)

AI gained 2%, Cooking lost 2% - it's zero-sum!

4. Weekly Batch Updates Lock In Changes

InterestedIn updates weekly via batch jobs. Each week's drift becomes the new baseline:

Week 0:  60% AI, 40% Cooking
Week 1:  62% AI, 38% Cooking  ← New baseline
Week 4:  64% AI, 36% Cooking  ← Compounds
Week 12: 70% AI, 30% Cooking  ← Accelerates
Week 24: 76% AI, 24% Cooking  ← Lock-in

5. The Loop Feeds Itself

The output becomes the input:


Mathematical Inevitability

This isn't about user behavior. It's pure math:

The Drift Formula

New_AI_Interest = Old_AI_Interest + (drift_rate × advantage × slowdown)

Where:
- drift_rate = engagement intensity (0.008 to 0.025)
- advantage = AI_interest / Cooking_interest (e.g., 0.60 / 0.40 = 1.5)
- slowdown = 1 - (imbalance × 0.5) (slows as approaching extremes)

Example (Week 0 → Week 1):
New_AI = 0.60 + (0.015 × 1.5 × 0.9) = 0.60 + 0.02025 ≈ 0.62

Key insight: As long as there's ANY imbalance (not perfect 50/50), drift will occur. The stronger interest always wins.

The Only Ways to Prevent Drift

  1. Start perfectly balanced (50/50) - But even 51/49 will drift over time
  2. Use additive scoring - Instead of multiplication, use addition (but this eliminates personalization)
  3. Disable cluster scoring - Don't multiply by interest (but then why have clusters?)
  4. Active counterbalancing - Manually over-engage with minority interest (exhausting)

In the current design, drift is inevitable for any user with unequal interests.


Code References

Multiplicative scoring: ApproximateCosineSimilarity.scala:84-94

Weekly batch updates: InterestedInFromKnownFor.scala:59 - val batchIncrement: Duration = Days(7)

L2 normalization: SimClustersEmbedding.scala:59-72

InterestedIn calculation: InterestedInFromKnownFor.scala:88-95 - Follows who you follow, what you engage with