seo-generator-server/tests/validators/AIContentValidator.js
Trouve Alexis 870cfb0340 [200~add step-by-step versioning system with Google Sheets integration
- Add intermediate saves (v1.0-v1.4) to Generated_Articles_Versioned
  - Fix compiled_text pipeline (generatedTexts object structure)
  - Add /api/workflow-modulaire endpoint with version tracking
  - Create test-modulaire.html interface with real-time logs
  - Support parent-child linking via Parent_Article_ID
2025-09-06 16:38:20 +08:00

435 lines
15 KiB
JavaScript

// ========================================
// VALIDATEUR IA MULTI-CRITÈRES
// Validation intelligente du contenu généré avec scores objectifs
// ========================================
// Import correct du LLMManager
let LLMManager;
try {
const llmModule = require('../../lib/LLMManager');
// Essai direct avec callLLM
if (llmModule && typeof llmModule.callLLM === 'function') {
LLMManager = llmModule;
}
// Essai avec propriété
else if (llmModule && llmModule.LLMManager && typeof llmModule.LLMManager.callLLM === 'function') {
LLMManager = llmModule.LLMManager;
}
// Import par défaut module.exports
else if (llmModule && llmModule.default && typeof llmModule.default.callLLM === 'function') {
LLMManager = llmModule.default;
}
} catch (error) {
console.warn('LLMManager non disponible:', error.message);
}
const { logSh } = require('../../lib/ErrorReporting');
/**
* Validateur de contenu utilisant l'IA pour évaluer la qualité
* Scores objectifs sur 4 critères principaux
*/
class AIContentValidator {
static CRITERIA = {
QUALITY: 'quality', // Cohérence, fluidité, pertinence
ANTI_DETECTION: 'antiDetection', // Variabilité, naturalité
PERSONALITY: 'personality', // Respect du profil sélectionné
TECHNICAL: 'technical' // SEO, structure, mots-clés
};
static THRESHOLDS = {
EXCELLENT: 90,
GOOD: 75,
ACCEPTABLE: 60,
POOR: 40
};
/**
* 🧪 VALIDATION PRINCIPALE - CŒUR DU VALIDATEUR IA
*
* CE QUI EST TESTÉ :
* ✅ Appel LLM OpenAI avec prompt structuré pour évaluation objective
* ✅ Parse JSON de la réponse IA avec validation des scores (0-100)
* ✅ Calcul score agrégé pondéré selon importance des critères
*
* ALGORITHMES EXÉCUTÉS :
* - Construction prompt intelligent selon critères demandés
* - Appel OpenAI avec temperature=0.1 (cohérence évaluations)
* - Parsing JSON robuste avec fallback sur erreurs
* - Calcul moyenne pondérée : Quality(30%) + Anti-detection(30%) + Personality(20%) + Technical(20%)
*/
static async validateContent(content, criteria = [], context = {}) {
try {
logSh(`🧪 Validation IA démarrée - Critères: ${criteria.join(', ')}`, 'DEBUG');
// Préparation du prompt de validation
const prompt = this.buildValidationPrompt(content, criteria, context);
// Vérification et appel LLM pour validation
if (!LLMManager || typeof LLMManager.callLLM !== 'function') {
throw new Error('LLMManager not available or callLLM method missing');
}
const response = await LLMManager.callLLM(
'openai',
prompt,
{
temperature: 0.1, // Faible pour cohérence des évaluations
max_tokens: 1500
}
);
// Parse de la réponse
const validationResult = this.parseValidationResponse(response, criteria);
// Calcul scores agrégés
const aggregated = this.calculateAggregatedScores(validationResult);
logSh(`✅ Validation IA terminée - Score global: ${aggregated.overall}/100`, 'INFO');
return {
...validationResult,
...aggregated,
timestamp: new Date().toISOString(),
criteria: criteria,
context: context
};
} catch (error) {
logSh(`❌ Erreur validation IA: ${error.message}`, 'ERROR');
// Fallback avec scores neutres
return this.getFallbackValidation(criteria, error);
}
}
/**
* 🔧 CONSTRUCTION PROMPT - GÉNÉRATEUR DE PROMPTS ADAPTATIFS
*
* CE QUI EST TESTÉ :
* ✅ Construction dynamique prompt selon critères sélectionnés
* ✅ Intégration contexte (personnalité, mots-clés, longueur attendue)
* ✅ Format JSON strict demandé à l'IA pour parsing automatique
*
* ALGORITHMES EXÉCUTÉS :
* - Template de prompt avec placeholders dynamiques
* - Ajout conditionnel sections selon critères (Quality, Anti-detection, etc.)
* - Troncature contenu à 2000 chars pour éviter timeout LLM
* - Format réponse JSON strict avec scores 0-100 + feedback + confidence
*/
static buildValidationPrompt(content, criteria, context) {
const basePrompt = `
VALIDATION CONTENU IA - ANALYSE MULTI-CRITÈRES OBJECTIVE
Tu es un expert en évaluation de contenu généré par IA. Évalue ce contenu selon des critères objectifs et mesurables.
CONTENU À ANALYSER :
---
${content.substring(0, 2000)}${content.length > 2000 ? '...[TRONQUÉ]' : ''}
---
CONTEXTE :
${context.personality ? `- Personnalité cible : ${context.personality.nom || context.personality} (${context.personality.style || 'style non défini'})` : ''}
${context.keywords ? `- Mots-clés attendus : ${Array.isArray(context.keywords) ? context.keywords.join(', ') : context.keywords}` : ''}
${context.expectedLength ? `- Longueur attendue : ${context.expectedLength} caractères` : ''}
${context.targetAudience ? `- Audience cible : ${context.targetAudience}` : ''}
CRITÈRES D'ÉVALUATION (0-100) :`;
let criteriaDetails = '';
if (criteria.includes(this.CRITERIA.QUALITY)) {
criteriaDetails += `
1. QUALITÉ (0-100) :
- Cohérence du propos et logique argumentaire
- Fluidité de lecture et transitions naturelles
- Pertinence par rapport au sujet
- Richesse du vocabulaire et variété syntaxique`;
}
if (criteria.includes(this.CRITERIA.ANTI_DETECTION)) {
criteriaDetails += `
2. ANTI-DÉTECTION (0-100) :
- Absence de formulations typiquement IA ("comprehensive", "robust", etc.)
- Variabilité dans les structures de phrases
- Naturalité du ton et spontanéité
- Présence d'imperfections humaines subtiles`;
}
if (criteria.includes(this.CRITERIA.PERSONALITY)) {
criteriaDetails += `
3. PERSONNALITÉ (0-100) :
- Respect du style de la personnalité cible
- Cohérence avec le ton attendu
- Vocabulaire approprié au profil
- Authenticité de la voix narrative`;
}
if (criteria.includes(this.CRITERIA.TECHNICAL)) {
criteriaDetails += `
4. TECHNIQUE/SEO (0-100) :
- Intégration naturelle des mots-clés
- Structure optimisée pour le référencement
- Respect des bonnes pratiques de rédaction web
- Équilibre lisibilité/optimisation`;
}
const responseFormat = `
RÉPONSE REQUISE - FORMAT JSON STRICT :
{
${criteria.map(c => `"${c}": 85`).join(',\n ')},
"feedback": "Analyse détaillée : points forts, points faibles, recommandations d'amélioration (max 200 mots)",
"confidence": 0.9
}
IMPORTANT : Réponds UNIQUEMENT avec du JSON valide, sans texte avant ou après.`;
return basePrompt + criteriaDetails + responseFormat;
}
/**
* 🔍 PARSING RÉPONSE IA - EXTRACTEUR JSON ROBUSTE
*
* CE QUI EST TESTÉ :
* ✅ Extraction JSON depuis réponse LLM (même avec texte parasite)
* ✅ Validation scores numériques dans range 0-100
* ✅ Fallback automatique avec scores neutres sur erreurs parsing
*
* ALGORITHMES EXÉCUTÉS :
* - Regex extraction JSON : /{[\s\S]*}/ pour isoler contenu JSON
* - Validation type number + range [0,100] pour chaque score
* - Arrondi Math.round() des scores décimaux
* - Clamp confidence dans [0,1] avec Math.min/Math.max
* - Score fallback = 50 (neutre) sur erreurs
*/
static parseValidationResponse(response, criteria) {
try {
// Nettoyage de la réponse
let jsonStr = response.trim();
// Extraction du JSON si emballé dans du texte
const jsonMatch = jsonStr.match(/\{[\s\S]*\}/);
if (jsonMatch) {
jsonStr = jsonMatch[0];
}
const parsed = JSON.parse(jsonStr);
// Validation des scores
const validated = {};
criteria.forEach(criterion => {
const score = parsed[criterion];
if (typeof score === 'number' && score >= 0 && score <= 100) {
validated[criterion] = Math.round(score);
} else {
logSh(`⚠️ Score invalide pour ${criterion}: ${score}`, 'WARNING');
validated[criterion] = 50; // Score neutre par défaut
}
});
return {
scores: validated,
feedback: parsed.feedback || 'Aucun feedback fourni',
confidence: Math.min(Math.max(parsed.confidence || 0.5, 0), 1),
rawResponse: response
};
} catch (error) {
logSh(`⚠️ Erreur parsing réponse validation: ${error.message}`, 'WARNING');
// Scores de fallback
const fallbackScores = {};
criteria.forEach(c => fallbackScores[c] = 50);
return {
scores: fallbackScores,
feedback: `Erreur parsing: ${error.message}`,
confidence: 0.1,
rawResponse: response
};
}
}
/**
* 📊 CALCUL SCORES AGRÉGÉS - ALGORITHME DE PONDÉRATION
*
* CE QUI EST TESTÉ :
* ✅ Moyenne pondérée selon importance relative des critères
* ✅ Classification automatique en niveaux (EXCELLENT/GOOD/ACCEPTABLE/POOR)
* ✅ Calcul breakdown détaillé par critère
*
* ALGORITHMES EXÉCUTÉS :
* - Pondération : Quality(30%) + Anti-detection(30%) + Personality(20%) + Technical(20%)
* - Formula : weightedSum = Σ(score[i] * weight[i]) / totalWeight
* - Classification par seuils : ≥90=EXCELLENT, ≥75=GOOD, ≥60=ACCEPTABLE, <60=POOR
* - Math.round() pour score entier final
*/
static calculateAggregatedScores(validationResult) {
const scores = validationResult.scores;
const scoreValues = Object.values(scores);
if (scoreValues.length === 0) {
return { overall: 0, level: 'UNKNOWN' };
}
// Moyenne pondérée (qualité et anti-détection plus importants)
const weights = {
[this.CRITERIA.QUALITY]: 0.3,
[this.CRITERIA.ANTI_DETECTION]: 0.3,
[this.CRITERIA.PERSONALITY]: 0.2,
[this.CRITERIA.TECHNICAL]: 0.2
};
let weightedSum = 0;
let totalWeight = 0;
Object.keys(scores).forEach(criterion => {
const weight = weights[criterion] || 0.25;
weightedSum += scores[criterion] * weight;
totalWeight += weight;
});
const overall = Math.round(weightedSum / totalWeight);
// Détermination du niveau
let level;
if (overall >= this.THRESHOLDS.EXCELLENT) level = 'EXCELLENT';
else if (overall >= this.THRESHOLDS.GOOD) level = 'GOOD';
else if (overall >= this.THRESHOLDS.ACCEPTABLE) level = 'ACCEPTABLE';
else level = 'POOR';
return {
overall,
level,
breakdown: scores,
confidence: validationResult.confidence
};
}
/**
* 🚨 FALLBACK VALIDATION - GESTION ROBUSTE DES ERREURS
*
* CE QUI EST TESTÉ :
* ✅ Génération scores neutres quand LLM indisponible/échec
* ✅ Structure réponse identique pour compatibilité code appelant
* ✅ Logging erreur pour debug + confidence=0 pour signaler échec
*
* ALGORITHMES EXÉCUTÉS :
* - Attribution score=50 (neutre) pour tous les critères demandés
* - overall=50, level='UNKNOWN' pour indiquer validation échouée
* - confidence=0.0 pour signaler résultat non fiable
* - Timestamp ISO pour traçabilité
*/
static getFallbackValidation(criteria, error) {
const fallbackScores = {};
criteria.forEach(c => fallbackScores[c] = 50);
return {
scores: fallbackScores,
overall: 50,
level: 'UNKNOWN',
breakdown: fallbackScores,
feedback: `Validation échouée: ${error.message}`,
confidence: 0.0,
error: error.message,
timestamp: new Date().toISOString(),
criteria: criteria
};
}
/**
* ⚡ VALIDATION RAPIDE - PRESET OPTIMISÉ PERFORMANCE
*
* CE QUI EST TESTÉ :
* ✅ Évaluation accélérée sur 2 critères essentiels seulement
* ✅ Quality + Anti-detection (critères les plus discriminants)
* ✅ Délai <3 secondes vs ~8 secondes pour validation complète
*
* ALGORITHMES EXÉCUTÉS :
* - Appel validateContent() avec criteria=[QUALITY, ANTI_DETECTION]
* - Prompt réduit (moins de sections = moins de tokens)
* - Même pondération mais seulement 2 critères
* - Idéal pour validation temps réel ou batch processing
*/
static async quickValidate(content, context = {}) {
return this.validateContent(
content,
[this.CRITERIA.QUALITY, this.CRITERIA.ANTI_DETECTION],
context
);
}
/**
* 🔬 VALIDATION COMPLÈTE - ANALYSE EXHAUSTIVE 4 CRITÈRES
*
* CE QUI EST TESTÉ :
* ✅ Évaluation approfondie sur TOUS les critères disponibles
* ✅ Quality + Anti-detection + Personality + Technical/SEO
* ✅ Maximum de précision pour validation finale avant publication
*
* ALGORITHMES EXÉCUTÉS :
* - Appel validateContent() avec Object.values(CRITERIA) = tous critères
* - Prompt complet avec 4 sections détaillées
* - Pondération complète 30%+30%+20%+20%
* - Délai ~8-12 secondes pour analyse exhaustive
*/
static async fullValidate(content, context = {}) {
return this.validateContent(
content,
Object.values(this.CRITERIA),
context
);
}
/**
* 🔄 COMPARAISON CONTENUS - ALGORITHME DE DIFFÉRENTIEL
*
* CE QUI EST TESTÉ :
* ✅ Validation parallèle des 2 contenus (Promise.all pour performance)
* ✅ Calcul différences score par score pour chaque critère
* ✅ Détermination gagnant automatique + score d'amélioration
*
* ALGORITHMES EXÉCUTÉS :
* - Double appel validateContent() en parallèle (versionA, versionB)
* - Delta calculation : diff[criterion] = scoreB - scoreA pour chaque critère
* - overallDiff = validation2.overall - validation1.overall
* - Winner logic : diff>0='B', diff<0='A', diff=0='TIE'
* - Structure comparison complète pour analyse détaillée
*/
static async compareContent(content1, content2, criteria = [], context = {}) {
logSh('🔄 Comparaison de deux contenus', 'DEBUG');
const [validation1, validation2] = await Promise.all([
this.validateContent(content1, criteria, { ...context, label: 'Version A' }),
this.validateContent(content2, criteria, { ...context, label: 'Version B' })
]);
// Calcul des différences
const comparison = {
versionA: validation1,
versionB: validation2,
differences: {},
winner: null,
improvement: 0
};
criteria.forEach(criterion => {
const diff = validation2.scores[criterion] - validation1.scores[criterion];
comparison.differences[criterion] = diff;
});
const overallDiff = validation2.overall - validation1.overall;
comparison.improvement = overallDiff;
comparison.winner = overallDiff > 0 ? 'B' : (overallDiff < 0 ? 'A' : 'TIE');
return comparison;
}
}
module.exports = { AIContentValidator };