From 448b4b6ca77da4ba89c27a6b47ec64646fdf2425 Mon Sep 17 00:00:00 2001 From: jpgiannetti Date: Thu, 5 Feb 2026 13:40:33 +0100 Subject: [PATCH] =?UTF-8?q?doc(regles-metier):=20neutraliser=20p=C3=A9nali?= =?UTF-8?q?t=C3=A9s=20skip=20pour=20abonn=C3=A9s=20+=20tracking=20source?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../03-centres-interet-jauges.md | 11 +++- .../04-algorithme-recommandation.md | 59 +++++++++++++++---- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/docs/regles-metier/03-centres-interet-jauges.md b/docs/regles-metier/03-centres-interet-jauges.md index 3c8c726..2f2e98b 100644 --- a/docs/regles-metier/03-centres-interet-jauges.md +++ b/docs/regles-metier/03-centres-interet-jauges.md @@ -10,7 +10,8 @@ | **Like automatique standard (30-79% écoute)** | **+1%** | Signal modéré d'intérêt | | **Like explicite (manuel)** | **+2%** | Signal fort, cumulable avec auto | | **Abonnement créateur** | **+5%** sur tous ses tags | Signal très fort d'affinité | -| **Skip rapide (<10s)** | **-0.5%** | Désintérêt marqué | +| **Skip rapide (<10s), NON abonné** | **-0.5%** | Désintérêt marqué (signal négatif légitime) | +| **Skip rapide (<10s), ABONNÉ au créateur** | **0%** (neutre) | Abonnement = affinité forte, skip contextuel (pas ce contenu spécifique) | | **Skip tardif (≥30%)** | **0%** | Neutre (contenu essayé suffisamment) | **Note importante** : Les pourcentages indiqués sont des **points de pourcentage absolus**, **PAS des pourcentages relatifs**. @@ -47,10 +48,16 @@ Scénario 3 : Écoute 2min30 (50%) + Like manuel → Jauge Automobile : 45% → 48% → Jauge Voyage : 60% → 63% -Scénario 4 : Skip après 5s +Scénario 4 : Skip après 5s (NON abonné au créateur) → Signal négatif (-0.5%) → Jauge Automobile : 45% → 44.5% → Jauge Voyage : 60% → 59.5% + +Scénario 5 : Skip après 5s (ABONNÉ au créateur) +→ Neutre (0%, pas de pénalité) +→ Jauge Automobile : 45% → 45% +→ Jauge Voyage : 60% → 60% +→ Raison : Abonnement signale affinité globale, skip ponctuel = pas intéressé par CE contenu spécifique ``` **Justification** : diff --git a/docs/regles-metier/04-algorithme-recommandation.md b/docs/regles-metier/04-algorithme-recommandation.md index f4a193f..0f89f18 100644 --- a/docs/regles-metier/04-algorithme-recommandation.md +++ b/docs/regles-metier/04-algorithme-recommandation.md @@ -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)