// ======================================== // 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 multi-critères */ 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 du prompt de validation intelligent */ 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; } /** * Parse de la réponse IA avec validation */ 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 des scores agrégés */ 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 }; } /** * Validation de fallback en cas d'erreur */ 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 avec critères par défaut */ static async quickValidate(content, context = {}) { return this.validateContent( content, [this.CRITERIA.QUALITY, this.CRITERIA.ANTI_DETECTION], context ); } /** * Validation complète tous critères */ static async fullValidate(content, context = {}) { return this.validateContent( content, Object.values(this.CRITERIA), context ); } /** * Comparaison entre deux contenus */ 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 };