sourcefinder/docs/SYSTEME_SCORING.md
Alexis Trouvé a7bd6115b7
Some checks failed
SourceFinder CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
SourceFinder CI/CD Pipeline / Unit Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Security Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Integration Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Performance Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Code Coverage Report (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (16.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (18.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (20.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Regression Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Security Audit (push) Has been cancelled
SourceFinder CI/CD Pipeline / Notify Results (push) Has been cancelled
feat: Implémentation complète du système SourceFinder avec tests
- Architecture modulaire avec injection de dépendances
- Système de scoring intelligent multi-facteurs (spécificité, fraîcheur, qualité, réutilisation)
- Moteur anti-injection 4 couches (preprocessing, patterns, sémantique, pénalités)
- API REST complète avec validation et rate limiting
- Repository JSON avec index mémoire et backup automatique
- Provider LLM modulaire pour génération de contenu
- Suite de tests complète (Jest) :
  * Tests unitaires pour sécurité et scoring
  * Tests d'intégration API end-to-end
  * Tests de sécurité avec simulation d'attaques
  * Tests de performance et charge
- Pipeline CI/CD avec GitHub Actions
- Logging structuré et monitoring
- Configuration ESLint et environnement de test

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 23:06:10 +08:00

23 KiB
Raw Permalink Blame History

Système de Scoring Intelligent SourceFinder

Vue d'Ensemble

Le système de scoring de SourceFinder évalue intelligemment la pertinence des articles d'actualités canines selon quatre critères pondérés, conformément aux spécifications du CDC (Cahier des Charges). Chaque article reçoit un score final de 0 à 100 points selon la formule :

Score Final = (Spécificité × 0.4) + (Fraîcheur × 0.3) + (Qualité × 0.2) + (Réutilisabilité × 0.1)

Cette approche multi-critères garantit une sélection équilibrée entre pertinence thématique, actualité, fiabilité des sources et optimisation de la réutilisation du contenu.

Architecture Modulaire

Organisation des Composants

BasicScoringEngine (Orchestrateur principal)
├── SpecificityCalculator (40% du score)
├── FreshnessCalculator (30% du score)
├── QualityCalculator (20% du score)
└── ReuseCalculator (10% du score)

Chaque calculateur est indépendant et interchangeable, respectant le principe d'architecture modulaire du système.

Interface IScoringEngine

Tous les moteurs de scoring implémentent cette interface standardisée :

interface IScoringEngine {
  async scoreArticle(newsItem, context): Promise<ScoringResult>
  async batchScore(newsItems, context): Promise<Array<ScoredArticle>>
  explainScore(scoredArticle): Object
}

1. Calculateur de Spécificité (40% du Score)

Principe

La spécificité évalue la pertinence du contenu par rapport à la race de chien recherchée. C'est le critère le plus important car il détermine directement l'utilité de l'article pour le client final.

Hiérarchie de Scoring

Niveau Score Critère Exemple
Mention Exacte 100 pts Nom exact de la race trouvé "Berger Allemand", "Golden Retriever"
Groupe/Famille 70 pts Famille de race mentionnée "Chiens de berger", "Retrievers"
Taille Similaire 50 pts Catégorie de taille "Grands chiens", "Petite race"
Usage Similaire 40 pts Usage/fonction similaire "Chien de garde", "Chien de famille"
Générique Chiens 25 pts Mention générale canine "Chiens", "Compagnons"
Animaux Domestiques 10 pts Contexte animal général "Animaux de compagnie"

Base de Données des Races

Le système intègre une base de données des races FCI avec :

// Exemple : Berger Allemand (352-1)
{
  name: 'berger allemand',
  variants: ['german shepherd', 'berger d\'allemagne'],
  group: 'chiens de berger',
  families: ['bergers', 'chiens de troupeau'],
  size: 'grands chiens',
  usages: ['chien de garde', 'chien de travail', 'chien policier']
}

Algorithme de Détection

  1. Normalisation du contenu : Conversion en minuscules, suppression de la ponctuation
  2. Recherche par regex : Détection des mots-clés avec délimiteurs (\b)
  3. Scoring hiérarchique : Attribution du score le plus élevé trouvé
  4. Traçabilité : Enregistrement des termes correspondants pour audit

Cas Spéciaux

  • Races composées : "Berger Allemand à poil long" → Détection du nom principal
  • Synonymes multiples : "Labrador" → "Labrador Retriever"
  • Variantes linguistiques : "German Shepherd" → "Berger Allemand"

2. Calculateur de Fraîcheur (30% du Score)

Principe

La fraîcheur évalue la récence de l'article. Plus un article est récent, plus il est susceptible d'être pertinent pour la génération de contenu actualisé.

Seuils d'Évaluation

Catégorie Âge Score Usage Recommandé
Excellent < 7 jours 100 pts Actualités urgentes
Bon 7-30 jours 70 pts Contenu récent
Correct 30-90 jours 40 pts Informations générales
Ancien 90-180 jours 20 pts Contenu de référence
Obsolète > 180 jours 5 pts Archives uniquement

Gestion des Dates

Formats Supportés

  • ISO 8601 : 2024-01-15T10:30:00Z
  • Français : 15/01/2024, 15-01-2024, 15.01.2024
  • Timestamps : Unix timestamp (secondes ou millisecondes)
  • Objets Date : Instances JavaScript Date

Validation et Sécurité

// Plage de dates valides : 1990 à (année actuelle + 5)
isValidDate(date) {
  const year = date.getFullYear();
  const currentYear = new Date().getFullYear();
  return year >= 1990 && year <= currentYear + 5;
}

Ajustements Contextuels

Bonus Contenu "Evergreen" (+20 pts max)

Articles à valeur permanente identifiés par mots-clés :

  • Guides : "guide", "comment", "conseils"
  • Éducation : "dressage", "formation", "méthode"
  • Santé générale : "prévention", "bien-être"

Malus Actualités Périmées (-30% du score)

Articles d'actualité urgente devenus obsolètes :

  • Mots-clés : "actualité", "urgent", "breaking", "annonce"
  • Appliqué si score de base < 40 points

Bonus Recherche d'Archives (+15 pts max)

Si context.allowOldContent = true, améliore la valorisation du contenu ancien.

Calcul de l'Âge

calculateAgeInDays(publishDate, searchDate) {
  const diffMs = searchDate.getTime() - publishDate.getTime();
  return Math.floor(diffMs / (1000 * 60 * 60 * 24));
}

Gestion des Cas d'Erreur

  • Date future : Score = 0 (erreur de publication)
  • Date manquante : Score = 0 (non fiable)
  • Date invalide : Score = 0 (format incorrect)

3. Calculateur de Qualité (20% du Score)

Principe

La qualité évalue la fiabilité et l'autorité de la source de publication. Ce critère garantit la crédibilité du contenu généré.

Classification des Sources

Sources Premium (90-100 pts)

Organismes Officiels et Institutions

  • centrale-canine.fr (Société Centrale Canine) : 100 pts
  • fci.be (Fédération Cynologique Internationale) : 100 pts
  • veterinaire.fr (Ordre des Vétérinaires) : 95 pts
  • Sites universitaires vétérinaires : 95 pts

Critères d'identification :

  • Extension .edu ou .fr officielle
  • Mentions légales complètes
  • Références scientifiques
  • Autorité reconnue dans le domaine

Sources Spécialisées (70-85 pts)

Médias Spécialisés Canins

  • 30millionsdamis.fr : 85 pts
  • wamiz.com : 80 pts
  • woopets.fr : 80 pts
  • Clubs de race officiels : 85 pts

Caractéristiques :

  • Spécialisation exclusive dans le domaine canin
  • Équipe éditoriale identifiée
  • Historique de publication
  • Partenariats avec organismes officiels

Sources Standard (50-70 pts)

Médias Généralistes de Qualité

  • lefigaro.fr/animaux : 65 pts
  • ouest-france.fr/animaux : 60 pts
  • Magazines lifestyle avec section animaux : 55 pts

Évaluation :

  • Réputation générale du média
  • Qualité éditoriale
  • Processus de vérification
  • Expertise occasionnelle sur les animaux

Sources Fallback (20-50 pts)

Contenu Généraliste ou Non-Vérifié

  • Blogs personnels : 30 pts
  • Forums : 25 pts
  • Réseaux sociaux : 20 pts
  • Sources inconnues : 25 pts

Indicateurs de Qualité

Indicateurs Positifs (+5 à +15 pts)

qualityIndicators = {
  hasAuthor: +10,           // Auteur identifié
  hasPublishDate: +10,      // Date de publication
  hasReferences: +15,       // Références citées
  hasVetReview: +15,        // Validation vétérinaire
  hasCitations: +10,        // Citations scientifiques
  isRecent: +5,            // Publication récente
  hasImages: +5,           // Illustrations présentes
  hasStructure: +5         // Contenu bien structuré
}

Indicateurs Négatifs (-5 à -20 pts)

qualityPenalties = {
  hasAds: -5,              // Publicités excessives
  poorWriting: -10,        // Qualité rédactionnelle
  noContact: -10,          // Pas de contact
  noLegal: -15,           // Pas de mentions légales
  anonymousContent: -10,   // Contenu anonyme
  clickbait: -15,         // Titre aguicheur
  outdatedInfo: -20       // Informations obsolètes
}

Détection Automatique

Analyse du Contenu

// Détection de qualité par analyse textuelle
analyzeContentQuality(content) {
  const wordCount = content.split(/\s+/).length;
  const sentenceCount = content.split(/[.!?]+/).length;
  const avgSentenceLength = wordCount / sentenceCount;

  return {
    isSubstantial: wordCount > 200,
    isWellStructured: avgSentenceLength > 8 && avgSentenceLength < 25,
    hasVariety: this.calculateLexicalDiversity(content) > 0.6
  };
}

Analyse des Métadonnées

  • Présence d'auteur et date
  • Structure HTML appropriée
  • Balises meta descriptions
  • Schema.org markup

Pondération Contextuelle

Le score de qualité peut être ajusté selon le contexte :

// Bonus pour recherche spécialisée
if (context.requireHighQuality) {
  // Réduction des scores sources non-premium
  if (baseScore < 70) baseScore *= 0.8;
}

// Malus cumul sources faibles
if (context.lowQualityCount > 3) {
  baseScore *= 0.9;
}

4. Calculateur de Réutilisabilité (10% du Score)

Principe

La réutilisabilité optimise l'usage du stock d'articles en évitant la sur-utilisation et en respectant les périodes de rotation. Ce critère assure la diversité du contenu généré.

Scoring par Usage

Catégorie Utilisations Score Statut
Neuf 0 100 pts Priorité maximale
Peu utilisé 1-2 80 pts Recommandé
Modérément utilisé 3-5 60 pts Acceptable
Très utilisé 6-10 40 pts Limité
Saturé > 10 20 pts À éviter

Périodes de Rotation

Le système respecte des périodes de rotation selon le type de source :

rotationPeriods = {
  premium: 90,    // 3 mois - Sources premium (coût élevé, qualité maximale)
  standard: 60,   // 2 mois - Sources standard (équilibre qualité/coût)
  fallback: 30    // 1 mois - Sources fallback (renouvellement rapide)
}

Ajustements Temporels

Bonus Période de Rotation Respectée (+10 à +20 pts)

// Calcul du bonus temporel
if (daysSinceLastUse >= rotationPeriod) {
  const bonus = Math.min(20, daysSinceLastUse - rotationPeriod + 10);
  return bonus;
}

Malus Utilisation Récente (-10 à -20 pts)

Articles utilisés dans les 7 derniers jours subissent une pénalité pour favoriser la diversité.

// Malus utilisation récente
if (daysSinceLastUse < 7) {
  const penalty = -Math.max(10, 20 - daysSinceLastUse * 2);
  return penalty;
}

Ajustements Contextuels

Bonus Client Différent (+10 pts)

Si l'article est utilisé par un client différent du précédent :

if (context.clientId && article.lastClientId &&
    context.clientId !== article.lastClientId) {
  adjustment += 10;
}

Bonus Contexte Différent (+15 pts max)

Évaluation de la similarité avec le contexte précédent :

calculateContextSimilarity(context1, context2) {
  const ctx1Words = context1.toLowerCase().split(/\s+/);
  const ctx2Words = context2.toLowerCase().split(/\s+/);

  const intersection = ctx1Words.filter(word => ctx2Words.includes(word));
  const union = [...new Set([...ctx1Words, ...ctx2Words])];

  return intersection.length / union.length;
}

Bonus Contenu Evergreen (+5 pts)

Articles à valeur permanente (guides, conseils) bénéficient d'un bonus de réutilisabilité.

Malus Sur-utilisation Race (-10 pts)

Pénalité si l'article a été trop utilisé pour la même race (≥ 5 utilisations).

Statuts de Rotation

getRotationStatus(lastUsed, sourceType, now) {
  const daysSinceLastUse = calculateDaysDifference(lastUsed, now);
  const rotationPeriod = this.rotationPeriods[sourceType];

  if (daysSinceLastUse >= rotationPeriod) return 'available';
  if (daysSinceLastUse >= rotationPeriod * 0.7) return 'soon_available';
  return 'in_rotation';
}

Statistiques de Collection

Le calculateur fournit des statistiques globales sur l'état de réutilisation du stock :

getCollectionReuseStats(articles) {
  return {
    totalArticles: articles.length,
    byUsageCategory: { fresh: X, low: Y, ... },
    byRotationStatus: { available: A, in_rotation: B, ... },
    averageUsage: averageUsageCount,
    reuseEfficiency: percentageAvailable,
    recommendations: ['action1', 'action2', ...]
  };
}

Orchestration par BasicScoringEngine

Calcul Principal

Le BasicScoringEngine coordonne les quatre calculateurs :

async scoreArticle(newsItem, context) {
  // Exécution en parallèle pour optimiser les performances
  const [specificityResult, freshnessResult, qualityResult, reuseResult] =
    await Promise.all([
      this.specificityCalculator.calculateSpecificity(newsItem, context),
      this.freshnessCalculator.calculateFreshness(newsItem, context),
      this.qualityCalculator.calculateQuality(newsItem, context),
      this.reuseCalculator.calculateReuse(newsItem, context)
    ]);

  // Application de la formule CDC
  const finalScore = Math.round(
    (specificityResult.score * 0.4) +    // 40%
    (freshnessResult.score * 0.3) +      // 30%
    (qualityResult.score * 0.2) +        // 20%
    (reuseResult.score * 0.1)            // 10%
  );

  return {
    finalScore,
    specificityScore: specificityResult.score,
    freshnessScore: freshnessResult.score,
    qualityScore: qualityResult.score,
    reuseScore: reuseResult.score,
    scoringDetails: { /* détails complets */ },
    scoreCategory: this.categorizeScore(finalScore),
    usageRecommendation: this.generateUsageRecommendation(...)
  };
}

Catégorisation des Scores

Catégorie Plage Recommandation Usage
Excellent 80-100 priority_use Utilisation prioritaire
Bon 65-79 recommended Recommandé
Correct 50-64 conditional_use Usage conditionnel
Faible 30-49 limited_use Usage limité
Rejeté 0-29 avoid À éviter

Scoring par Lot

Pour optimiser les performances, le système support le scoring en lot avec limitation de concurrence :

async batchScore(newsItems, context) {
  const batchSize = 10; // Limitation pour éviter la surcharge
  const results = [];

  for (let i = 0; i < newsItems.length; i += batchSize) {
    const batch = newsItems.slice(i, i + batchSize);
    const batchResults = await Promise.all(
      batch.map(item => this.scoreArticle(item, context))
    );
    results.push(...batchResults);
  }

  // Tri par score décroissant
  return results.sort((a, b) => (b.finalScore || 0) - (a.finalScore || 0));
}

Explication des Scores

Le moteur peut expliquer en détail comment un score a été calculé :

explainScore(scoredArticle) {
  return {
    scoreBreakdown: {
      finalScore: scoredArticle.finalScore,
      components: {
        specificity: {
          score: scoredArticle.specificityScore,
          weight: 0.4,
          contribution: Math.round(scoredArticle.specificityScore * 0.4),
          reason: scoredArticle.scoringDetails.specificity.reason,
          details: scoredArticle.scoringDetails.specificity.details
        },
        // ... autres composants
      }
    },
    strengths: this.identifyStrengths(scoredArticle),
    weaknesses: this.identifyWeaknesses(scoredArticle),
    improvementSuggestions: this.generateImprovementSuggestions(scoredArticle),
    usageGuideline: {
      category: scoredArticle.scoreCategory,
      recommendation: scoredArticle.usageRecommendation,
      confidence: this.calculateConfidence(scoredArticle)
    }
  };
}

Performance et Optimisation

Exécution Parallèle

Les quatre calculateurs s'exécutent en parallèle pour minimiser la latence :

// ✅ Optimal : 4 calculs en parallèle
const results = await Promise.all([calc1, calc2, calc3, calc4]);

// ❌ Suboptimal : 4 calculs séquentiels
const result1 = await calc1;
const result2 = await calc2;
const result3 = await calc3;
const result4 = await calc4;

Cache et Mémorisation

  • Base de données des races : Chargée en mémoire au démarrage
  • Sources quality : Index en mémoire pour accès O(1)
  • Calculs récents : Cache des scores pour éviter les recalculs

Métriques de Performance

Le système collecte des métriques de performance :

{
  totalScored: 1250,
  averageScore: 67.3,
  scoreDistribution: {
    excellent: 156,
    good: 234,
    fair: 345,
    poor: 289,
    reject: 226
  },
  calculationTime: {
    total: 45678,      // ms
    average: 36.5      // ms par article
  }
}

Cas d'Usage et Exemples

Exemple 1 : Article Premium Spécialisé

{
  "title": "Nouvelle étude génétique sur la dysplasie chez les Bergers Allemands",
  "content": "Une équipe de chercheurs de l'École Vétérinaire de Maisons-Alfort...",
  "url": "https://centrale-canine.fr/etudes/dysplasie-berger-allemand-2024",
  "publishDate": "2024-01-10T08:00:00Z",
  "sourceType": "premium",
  "sourceDomain": "centrale-canine.fr"
}

// Contexte
{
  "raceCode": "352-1", // Berger Allemand
  "clientId": "client-123",
  "searchDate": "2024-01-12T10:00:00Z"
}

// Résultat de scoring
{
  "finalScore": 91,
  "specificityScore": 100,  // Mention exacte "Bergers Allemands"
  "freshnessScore": 95,     // 2 jours, très récent
  "qualityScore": 100,      // centrale-canine.fr = source premium
  "reuseScore": 80,         // Article neuf, jamais utilisé
  "scoreCategory": "excellent",
  "usageRecommendation": "priority_use"
}

Exemple 2 : Article Standard Généraliste

{
  "title": "5 conseils pour l'alimentation des grands chiens",
  "content": "Les chiens de grande taille ont des besoins nutritionnels spécifiques...",
  "url": "https://wamiz.com/conseils-alimentation-grands-chiens",
  "publishDate": "2023-12-15T14:30:00Z",
  "sourceType": "standard",
  "sourceDomain": "wamiz.com",
  "usageCount": 3,
  "lastUsed": "2024-01-05T10:00:00Z"
}

// Contexte
{
  "raceCode": "352-1", // Berger Allemand (grand chien)
  "clientId": "client-456",
  "searchDate": "2024-01-12T10:00:00Z"
}

// Résultat de scoring
{
  "finalScore": 64,
  "specificityScore": 50,   // "grands chiens" = taille similaire
  "freshnessScore": 40,     // 28 jours, dans la catégorie "fair"
  "qualityScore": 80,       // wamiz.com = source spécialisée
  "reuseScore": 60,         // 3 utilisations = modérément utilisé
  "scoreCategory": "fair",
  "usageRecommendation": "conditional_use"
}

Exemple 3 : Article Fallback Sur-utilisé

{
  "title": "Les animaux de compagnie et la famille",
  "content": "Avoir un animal de compagnie apporte de nombreux bénéfices...",
  "url": "https://blog-perso.com/animaux-famille",
  "publishDate": "2023-10-20T16:00:00Z",
  "sourceType": "fallback",
  "sourceDomain": "blog-perso.com",
  "usageCount": 12,
  "lastUsed": "2024-01-10T08:00:00Z"
}

// Résultat de scoring
{
  "finalScore": 23,
  "specificityScore": 10,   // "animaux de compagnie" = très généraliste
  "freshnessScore": 20,     // 84 jours = ancien
  "qualityScore": 30,       // Blog personnel = faible qualité
  "reuseScore": 20,         // > 10 utilisations = saturé
  "scoreCategory": "reject",
  "usageRecommendation": "avoid"
}

Extensibilité et Personnalisation

Ajout de Nouveaux Calculateurs

L'architecture modulaire permet d'ajouter facilement de nouveaux critères :

// Exemple : Calculateur de sentiment
class SentimentCalculator {
  async calculateSentiment(article, context) {
    // Logique d'analyse de sentiment
    return {
      score: sentimentScore,
      reason: 'positive_sentiment',
      details: 'Contenu majoritairement positif'
    };
  }
}

// Intégration dans BasicScoringEngine
constructor() {
  this.sentimentCalculator = new SentimentCalculator();
  this.weights = {
    specificity: 0.35,   // Réduction pour faire place au sentiment
    freshness: 0.25,
    quality: 0.2,
    reuse: 0.1,
    sentiment: 0.1       // Nouveau critère
  };
}

Personnalisation des Poids

Les poids peuvent être ajustés selon le contexte d'usage :

// Profil "News" : Privilégier fraîcheur et spécificité
const newsWeights = {
  specificity: 0.5,
  freshness: 0.4,
  quality: 0.1,
  reuse: 0.0
};

// Profil "Evergreen" : Équilibrer qualité et réutilisabilité
const evergreenWeights = {
  specificity: 0.3,
  freshness: 0.1,
  quality: 0.4,
  reuse: 0.2
};

Configuration Dynamique

Le système support la configuration dynamique via le contexte :

const context = {
  raceCode: "352-1",
  scoringProfile: "premium", // news, evergreen, premium, balanced
  qualityThreshold: 70,
  freshnessBonus: 1.2,
  customWeights: { /* poids spécifiques */ }
};

Monitoring et Observabilité

Logs Structurés

Chaque opération de scoring génère des logs détaillés :

logger.info('Article scored successfully', {
  articleId: 'art-123',
  finalScore: 85,
  breakdown: {
    specificity: 90,
    freshness: 95,
    quality: 80,
    reuse: 70
  },
  calculationTime: 45,
  raceCode: '352-1',
  category: 'excellent'
});

Métriques Business

  • Distribution des scores : Répartition par catégorie
  • Performance moyenne : Score moyen par race/source
  • Efficacité de réutilisation : Taux d'articles disponibles
  • Qualité des sources : Évolution de la qualité du stock

Alertes Automatiques

Le système peut déclencher des alertes :

// Alerte qualité dégradée
if (averageQualityScore < threshold) {
  alerting.trigger('quality_degradation', {
    currentScore: averageQualityScore,
    threshold: threshold,
    recommendation: 'Renouveler sources premium'
  });
}

Évolutions Futures

Machine Learning

Integration future d'un modèle ML pour affiner les scores :

class MLScoringEngine extends BasicScoringEngine {
  constructor() {
    super();
    this.mlModel = new ContentQualityModel();
  }

  async scoreArticle(newsItem, context) {
    const baseScore = await super.scoreArticle(newsItem, context);
    const mlAdjustment = await this.mlModel.predict(newsItem, context);

    return {
      ...baseScore,
      finalScore: this.adjustWithML(baseScore.finalScore, mlAdjustment),
      mlConfidence: mlAdjustment.confidence
    };
  }
}

Scoring Adaptatif

Ajustement automatique des poids selon les performances :

class AdaptiveScoringEngine extends BasicScoringEngine {
  updateWeights(feedbackData) {
    // Apprentissage des poids optimaux selon feedback utilisateur
    this.weights = this.optimizeWeights(feedbackData);
  }
}

Intégration Multi-langues

Support de scoring multi-langues avec détection automatique :

const languageSpecificCalculators = {
  'fr': new FrenchSpecificityCalculator(),
  'en': new EnglishSpecificityCalculator(),
  'de': new GermanSpecificityCalculator()
};

Conclusion

Le système de scoring SourceFinder offre une évaluation sophistiquée et équilibrée du contenu canin, combinant pertinence thématique, actualité, qualité des sources et optimisation de la réutilisation.

Son architecture modulaire garantit :

  • Flexibilité : Ajout facile de nouveaux critères
  • Performance : Calculs parallèles et optimisations
  • Transparence : Explication détaillée des scores
  • Fiabilité : Gestion d'erreurs et logging complet
  • Évolutivité : Support de personnalisations avancées

Cette approche multi-critères assure une sélection de contenu optimale pour tous les cas d'usage, de la génération d'actualités urgentes aux guides permanents de référence.