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
- 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>
818 lines
23 KiB
Markdown
818 lines
23 KiB
Markdown
# 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 :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
// 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é
|
||
|
||
```javascript
|
||
// 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
|
||
|
||
```javascript
|
||
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)
|
||
|
||
```javascript
|
||
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)
|
||
|
||
```javascript
|
||
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
|
||
|
||
```javascript
|
||
// 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 :
|
||
|
||
```javascript
|
||
// 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 :
|
||
|
||
```javascript
|
||
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)
|
||
|
||
```javascript
|
||
// 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é.
|
||
|
||
```javascript
|
||
// 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 :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
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
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
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é :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
// ✅ 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 :
|
||
|
||
```javascript
|
||
{
|
||
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é
|
||
|
||
```json
|
||
{
|
||
"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
|
||
|
||
```json
|
||
{
|
||
"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é
|
||
|
||
```json
|
||
{
|
||
"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 :
|
||
|
||
```javascript
|
||
// 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 :
|
||
|
||
```javascript
|
||
// 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 :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
// 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 :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
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 :
|
||
|
||
```javascript
|
||
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. |