33800 Docs

← Retour

Proposition : Correction du scoring NeedFinder

Date : 23/12/2025 Status : IMPLEMENTEE Fichier concerné : /app/src/analyzers/need-extractor.ts Problème : Le scoring est basé uniquement sur la confidence Ollama (souvent 1.0 → score 100)


Situation actuelle

opportunity_score = Math.round((result.confidence || 0.5) * 100)

Résultat : 90% des besoins ont un score de 100, aucune différenciation.


Proposition : Scoring multi-facteurs

Formule proposée (0-100)

Facteur Points max Description
Mentions 25 pts +5 pts par mention (cap 25)
Score posts 20 pts Moyenne upvotes normalisée
Engagement 15 pts Total commentaires normalisé
Sources 15 pts +5 pts par source unique (cap 15)
Type besoin 15 pts wish=15, frustration=12, feature=10, question=5
Willingness to pay 10 pts Bonus si détecté

Total max : 100 pts


Code proposé

1. Nouvelle fonction calculateOpportunityScore

// /app/src/analyzers/scoring.ts

interface ScoringInput {
  mentionCount: number;
  uniqueSources: number;
  avgPostScore: number;
  totalComments: number;
  needType: string;
  willingToPay: boolean;
}

export function calculateOpportunityScore(input: ScoringInput): number {
  let score = 0;

  // Mentions (0-25 pts) - +5 par mention, cap 25
  score += Math.min(25, input.mentionCount * 5);

  // Score posts (0-20 pts) - normalisé sur 500 upvotes max
  score += Math.min(20, Math.round((input.avgPostScore / 500) * 20));

  // Engagement (0-15 pts) - normalisé sur 100 commentaires
  score += Math.min(15, Math.round((input.totalComments / 100) * 15));

  // Sources uniques (0-15 pts) - +5 par source
  score += Math.min(15, input.uniqueSources * 5);

  // Type de besoin (0-15 pts)
  const typeScores: Record<string, number> = {
    wish: 15,
    frustration: 12,
    feature_request: 10,
    question: 5,
  };
  score += typeScores[input.needType] || 5;

  // Willingness to pay (0-10 pts)
  if (input.willingToPay) score += 10;

  return Math.min(100, score);
}

2. Modification de extractNeed pour détecter willingness to pay

// Dans le prompt Ollama, ajouter :
{
  // ... existant ...
  "willingToPay": true/false  // "would pay", "shut up and take my money", etc.
}

3. Modification de l'insertion avec score initial bas

// Lors de l'insertion d'un nouveau besoin
opportunity_score = calculateOpportunityScore({
  mentionCount: 1,
  uniqueSources: 1,
  avgPostScore: post.score || 0,
  totalComments: post.comment_count || 0,
  needType: result.needType || "question",
  willingToPay: result.willingToPay || false,
});

4. Job de recalcul périodique (optionnel)

// Recalcule les scores tous les jours pour tenir compte des nouvelles mentions
export async function recalculateAllScores(): Promise<void> {
  await sql`SET search_path TO needfinder`;

  await sql`
    UPDATE needs n SET
      opportunity_score = (
        -- Mentions (0-25)
        LEAST(25, n.mention_count * 5) +
        -- Score posts moyen (0-20)
        LEAST(20, ROUND(COALESCE(
          (SELECT AVG(p.score) FROM mentions m
           JOIN posts p ON p.id = m.post_id
           WHERE m.need_id = n.id), 0
        ) / 500 * 20)) +
        -- Engagement (0-15)
        LEAST(15, ROUND(COALESCE(
          (SELECT SUM(p.comment_count) FROM mentions m
           JOIN posts p ON p.id = m.post_id
           WHERE m.need_id = n.id), 0
        ) / 100 * 15)) +
        -- Sources uniques (0-15)
        LEAST(15, COALESCE(
          (SELECT COUNT(DISTINCT p.source_id) FROM mentions m
           JOIN posts p ON p.id = m.post_id
           WHERE m.need_id = n.id), 1
        ) * 5) +
        -- Type (0-15)
        CASE n.need_type
          WHEN 'wish' THEN 15
          WHEN 'frustration' THEN 12
          WHEN 'feature_request' THEN 10
          ELSE 5
        END +
        -- Willing to pay (0-10)
        CASE WHEN n.willing_to_pay THEN 10 ELSE 0 END
      )
  `;
}

Impact attendu

Avant Après
90% des besoins à 100 Distribution 5-100
Aucune différenciation Top besoins = vrais signaux forts
Score statique Score évolutif avec mentions

Étapes d'implémentation

  1. Ajouter colonne willing_to_pay BOOLEAN DEFAULT false si absente
  2. Créer fichier scoring.ts avec la nouvelle fonction
  3. Modifier need-extractor.ts pour utiliser le nouveau scoring
  4. Exécuter une requête SQL pour recalculer les scores existants
  5. Rebuild et redéployer le container

Recalcul des scores existants (SQL one-shot)

SET search_path TO needfinder;

-- Recalcule tous les scores
UPDATE needs n SET
  opportunity_score = (
    LEAST(25, n.mention_count * 5) +
    LEAST(20, ROUND(COALESCE(
      (SELECT AVG(p.score)::numeric FROM mentions m
       JOIN posts p ON p.id = m.post_id
       WHERE m.need_id = n.id), 0
    ) / 500 * 20)) +
    LEAST(15, ROUND(COALESCE(
      (SELECT SUM(p.comment_count)::numeric FROM mentions m
       JOIN posts p ON p.id = m.post_id
       WHERE m.need_id = n.id), 0
    ) / 100 * 15)) +
    LEAST(15, COALESCE(
      (SELECT COUNT(DISTINCT p.source_id) FROM mentions m
       JOIN posts p ON p.id = m.post_id
       WHERE m.need_id = n.id), 1
    ) * 5) +
    CASE n.need_type
      WHEN 'wish' THEN 15
      WHEN 'frustration' THEN 12
      WHEN 'feature_request' THEN 10
      ELSE 5
    END +
    CASE WHEN n.willing_to_pay THEN 10 ELSE 0 END
  );

Validation demandée

Attente de ton GO pour implémenter.