// ======================================== // 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 };