doc(regles-metier): neutraliser pénalités skip pour abonnés + tracking source

Modifie les règles de skip et engagement pour distinguer abonnés/non-abonnés :

Jauges d'intérêt (03-centres-interet-jauges.md) :
- Skip <10s NON abonné : -0.5% (signal négatif)
- Skip <10s ABONNÉ : 0% neutre (affinité globale, skip contextuel)

Score d'engagement créateur (04-algorithme-recommandation.md) :
- Ajout colonne "source" pour tracking origine écoute
- Ajout colonne "is_subscribed" pour état abonnement au moment écoute
- Skips d'abonnés ne pénalisent PAS les métriques créateur
- Skips via search/direct_link/profile/history ne comptent PAS
- Seules sources pertinentes (recommendation, live_notification) comptent

Reproposition (04-algorithme-recommandation.md) :
- Skip <10s non-abonné : ne pas reproposer
- Skip <10s abonné : peut reproposer (skip contextuel acceptable)

Avantages :
- Cohérence UX : abonnement = signal affinité fort
- Protection créateur : abonnés fidèles ne nuisent pas aux stats
- Anti-raid naturel : skips malveillants via liens directs inefficaces
- Encourage créateurs à diversifier contenus sans peur de perdre abonnés

Référence: CLARIFICATIONS-REGLES-METIER.md section 4
This commit is contained in:
jpgiannetti
2026-02-05 13:40:33 +01:00
parent 7de686ab33
commit 448b4b6ca7
2 changed files with 58 additions and 12 deletions

View File

@@ -111,12 +111,41 @@ score_final = (score_geo * 0.5) + (score_interets * 0.3) + (score_engagement * 0
**Décision** : Intégration popularité avec poids 0.2
**Métriques** :
- **Taux de complétion** : écoutes >80% / total écoutes (poids 0.5)
- **Taux de complétion** : écoutes >80% / total écoutes pertinentes (poids 0.5)
- **Ratio likes** : likes / écoutes (poids 0.3)
- **Ratio abonnements** : nouveaux abonnés après écoute / écoutes (poids 0.2)
**Distinction sources et abonnements** (neutralisation pénalités) :
Les métriques d'engagement **ne comptent que les écoutes pertinentes** pour éviter de pénaliser injustement les créateurs :
| Source écoute | Abonné au créateur ? | Skip <10s pénalise ? | Compte dans "total écoutes" ? | Justification |
|---------------|---------------------|---------------------|------------------------------|---------------|
| **`recommendation`** | ❌ Non | ✅ Oui | ✅ Oui | Skip = mauvaise recommandation OU mauvais contenu |
| **`recommendation`** | ✅ Oui | ❌ **Non** | ❌ **Non** | Abonné intéressé globalement, skip contextuel |
| **`search`** | Peu importe | ❌ Non | ❌ Non | User cherchait quelque chose de précis, skip = "pas maintenant" |
| **`direct_link`** | Peu importe | ❌ Non | ❌ Non | User curieux, peut skip sans jugement qualité |
| **`profile`** | Peu importe | ❌ Non | ❌ Non | User explore catalogue créateur |
| **`history`** | Peu importe | ❌ Non | ❌ Non | Pas une première écoute |
| **`live_notification`** | ❌ Non | ✅ Oui | ✅ Oui | Abonné normalement intéressé |
| **`live_notification`** | ✅ Oui | ❌ **Non** | ❌ **Non** | Abonné = affinité, skip contextuel |
| **`audio_guide`** | Peu importe | ❌ Non | ❌ Non | Navigation guidée, pas jugement qualité |
**Calcul engagement créateur** (exemple SQL) :
```sql
SELECT
content_id,
AVG(completion_rate) as avg_completion,
COUNT(*) FILTER (WHERE completion_rate > 0.8) as complete_listens,
COUNT(*) FILTER (WHERE completion_rate < 0.1 AND NOT is_subscribed) as penalizing_skips
FROM user_listening_history
WHERE source IN ('recommendation', 'live_notification') -- Sources pertinentes
GROUP BY content_id;
```
**Seuil minimum** :
- Minimum **50 écoutes** avant de considérer l'engagement
- Minimum **50 écoutes pertinentes** avant de considérer l'engagement
- Contenu <50 écoutes : score engagement = 0.5 (neutre)
**Contenu viral** :
@@ -130,8 +159,11 @@ score_final = (score_geo * 0.5) + (score_interets * 0.3) + (score_engagement * 0
**Justification** :
- Équilibre découverte / qualité
- **Protection créateur** : abonnés fidèles ne pénalisent pas les métriques
- **Anti-raid naturel** : skips via search/direct_link ne comptent pas (raid inefficace)
- **Cohérence UX** : abonnement = signal d'affinité fort, skip ponctuel ≠ rejet créateur
- Pas de pénalisation arbitraire des contenus anciens
- Coût : calculs sur métriques existantes
- Coût : calculs sur métriques existantes + colonne `source` + colonne `is_subscribed`
---
@@ -251,24 +283,31 @@ score_final = (score_geo * 0.5) + (score_interets * 0.3) + (score_engagement * 0
### 2.8 Historique et repropositon
**Décision** : Pas de reproposition sauf contenu partiel
**Décision** : Pas de reproposition sauf contenu partiel ou skip d'abonné
**Règles** :
| État écoute | Completion | Action |
|-------------|------------|--------|
| **Écouté complètement** | >80% | ❌ Ne jamais reproposer (sauf flag `replayable = true` pour audio-guides) |
| **Skippé rapidement** | <10s | ❌ Ne pas reproposer |
| **Partiellement écouté** | 10-80% | ✅ Reproposer avec reprise position (`last_position_seconds`) |
| État écoute | Completion | Abonné au créateur ? | Action |
|-------------|------------|---------------------|--------|
| **Écouté complètement** | >80% | Peu importe | ❌ Ne jamais reproposer (sauf flag `replayable = true` pour audio-guides) |
| **Skippé rapidement** | <10s | ❌ Non | ❌ Ne pas reproposer (signal négatif clair) |
| **Skippé rapidement** | <10s | ✅ **Oui** | ✅ **Peut reproposer** (abonnement = affinité, skip contextuel) |
| **Partiellement écouté** | 10-80% | Peu importe | ✅ Reproposer avec reprise position (`last_position_seconds`) |
**Stockage historique** :
- Table `user_content_history` (user_id, content_id, completion_rate, last_position, listened_at)
- Table `user_content_history` (user_id, content_id, creator_id, **is_subscribed**, completion_rate, last_position, listened_at)
- Historique **illimité** (PostgreSQL)
- Algorithme considère les **100 derniers** pour optimisation requêtes
- Export complet disponible (RGPD)
**Colonne `is_subscribed`** :
- Booléen stockant si l'utilisateur était abonné au créateur **au moment de l'écoute**
- Permet de distinguer les skips d'abonnés (contextuels) des skips de non-abonnés (désintérêt)
- Utilisé pour décisions de reproposition et calculs d'engagement
**Justification** :
- Découverte maximale (pas de redites)
- **Cohérence abonnement** : un skip ponctuel d'un abonné ≠ rejet du créateur (peut être contextuel : "pas maintenant", "pas ce sujet", "mauvais timing")
- Respect erreurs de clic (contenu partiel = 2nde chance)
- Coût stockage négligeable (PostgreSQL scalable)