// ======================================== // VALIDATEUR DE PERSONNALITÉ // Validation de la cohérence avec le profil de personnalité sélectionné // ======================================== // Import du LLMManager pour validation IA let LLMManager; try { const llmModule = require('../../lib/LLMManager'); if (llmModule && typeof llmModule.callLLM === 'function') { LLMManager = llmModule; } } catch (error) { console.warn('LLMManager non disponible pour PersonalityValidator:', error.message); } /** * Validateur spécialisé dans l'évaluation de la cohérence de personnalité */ class PersonalityValidator { // Base de données des indicateurs de personnalité static PERSONALITY_INDICATORS = { 'Marc': { style: 'technique', vocabulary: ['technique', 'précision', 'optimisé', 'performance', 'efficace'], tone: 'professionnel', structure: 'logique', markers: ['il est recommandé', 'la solution optimale', 'techniquement'] }, 'Sophie': { style: 'décoratif', vocabulary: ['élégant', 'harmonieux', 'esthétique', 'raffinement', 'style'], tone: 'inspirant', structure: 'descriptive', markers: ['créer une ambiance', 'apporter du charme', 'sublimer'] }, 'Laurent': { style: 'commercial', vocabulary: ['opportunité', 'avantage', 'investissement', 'rentable', 'performant'], tone: 'persuasif', structure: 'argumentative', markers: ['bénéficier de', 'maximiser', 'opportunité unique'] }, 'Julie': { style: 'architectural', vocabulary: ['structure', 'conception', 'aménagement', 'fonctionnel', 'ergonomie'], tone: 'expert', structure: 'méthodique', markers: ['concevoir', 'structurer', 'aménager'] }, 'Kévin': { style: 'terrain', vocabulary: ['pratique', 'solide', 'durable', 'fiable', 'résistant'], tone: 'direct', structure: 'concrète', markers: ['sur le terrain', 'concrètement', 'en pratique'] }, 'Amara': { style: 'ingénierie', vocabulary: ['innovation', 'technologie', 'développement', 'recherche', 'avancée'], tone: 'scientifique', structure: 'analytique', markers: ['développer', 'innover', 'technologies avancées'] }, 'Mamadou': { style: 'artisan', vocabulary: ['artisanal', 'savoir-faire', 'tradition', 'minutieux', 'authentique'], tone: 'passionné', structure: 'narrative', markers: ['avec passion', 'savoir-faire artisanal', 'tradition'] }, 'Émilie': { style: 'digital', vocabulary: ['numérique', 'connecté', 'innovation', 'tendance', 'moderne'], tone: 'dynamique', structure: 'concise', markers: ['solutions digitales', 'innovation numérique', 'connecté'] }, 'Pierre-Henri': { style: 'patrimoine', vocabulary: ['héritage', 'tradition', 'authentique', 'noble', 'raffinement'], tone: 'distingué', structure: 'élaborée', markers: ['héritage culturel', 'tradition séculaire', 'authentique'] }, 'Yasmine': { style: 'éco', vocabulary: ['durable', 'écologique', 'responsable', 'environnement', 'naturel'], tone: 'engagé', structure: 'consciente', markers: ['respectueux de l\'environnement', 'démarche durable', 'écologique'] }, 'Fabrice': { style: 'métallurgie', vocabulary: ['métallique', 'robuste', 'industriel', 'résistance', 'qualité'], tone: 'technique', structure: 'précise', markers: ['qualité métallurgique', 'résistance optimale', 'fabrication'] }, 'Chloé': { style: 'contenu', vocabulary: ['créatif', 'expressif', 'original', 'inspirant', 'captivant'], tone: 'créatif', structure: 'fluide', markers: ['créer du contenu', 'inspiration créative', 'expression'] }, 'Linh': { style: 'fabrication', vocabulary: ['production', 'qualité', 'processus', 'fabrication', 'contrôle'], tone: 'rigoureux', structure: 'processuelle', markers: ['processus de fabrication', 'contrôle qualité', 'production'] }, 'Minh': { style: 'design', vocabulary: ['design', 'esthétique', 'forme', 'beauté', 'élégance'], tone: 'artistique', structure: 'visuelle', markers: ['design innovant', 'esthétique soignée', 'beauté'] }, 'Thierry': { style: 'créole', vocabulary: ['chaleur', 'convivial', 'authentique', 'tradition', 'accueil'], tone: 'chaleureux', structure: 'conviviale', markers: ['accueil chaleureux', 'tradition créole', 'convivialité'] } }; /** * 🎭 VALIDATION PRINCIPALE PERSONNALITÉ - ORCHESTRATEUR GLOBAL * * CE QUI EST TESTÉ : * ✅ Récupération profil cible depuis PERSONALITY_INDICATORS (15 profils) * ✅ Analyse statique 4 métriques : vocabulaire, ton, structure, marqueurs * ✅ Score pondéré : vocabulaire(30%) + ton(25%) + structure(25%) + marqueurs(20%) * ✅ Validation LLM complémentaire (fusion 60% statique + 40% IA) * * ALGORITHMES EXÉCUTÉS : * - Lookup profil par nom dans base 15 personnalités * - 4 analyses parallèles avec indicateurs spécialisés * - Calcul confiance via variance des scores (cohérence interne) * - Génération feedback personnalisé par personnalité */ static async validatePersonality(content, targetPersonality, context = {}) { // Récupération des indicateurs de la personnalité cible const indicators = this.PERSONALITY_INDICATORS[targetPersonality.nom] || this.PERSONALITY_INDICATORS[targetPersonality]; if (!indicators) { return { score: 50, confidence: 0.1, feedback: `Personnalité "${targetPersonality.nom || targetPersonality}" inconnue`, breakdown: {} }; } // Analyse statique const staticAnalysis = { vocabularyMatch: this.analyzeVocabularyMatch(content, indicators.vocabulary), toneConsistency: this.analyzeToneConsistency(content, indicators.tone), structureAlignment: this.analyzeStructureAlignment(content, indicators.structure), markerPresence: this.analyzeMarkerPresence(content, indicators.markers), overallCoherence: 0 }; // Calcul du score statique global staticAnalysis.overallCoherence = Math.round( staticAnalysis.vocabularyMatch.score * 0.3 + staticAnalysis.toneConsistency.score * 0.25 + staticAnalysis.structureAlignment.score * 0.25 + staticAnalysis.markerPresence.score * 0.2 ); // Validation IA complémentaire (si LLM disponible) let aiAnalysis = null; if (LLMManager && context.enableLLM !== false) { try { const personalityName = targetPersonality.nom || targetPersonality; aiAnalysis = await this.validatePersonalityWithLLM(content, personalityName, indicators); // Fusion des scores (moyenne pondérée) const combinedScore = Math.round(staticAnalysis.overallCoherence * 0.6 + aiAnalysis.score * 0.4); return { score: combinedScore, confidence: Math.max(this.calculateConfidence(staticAnalysis), aiAnalysis.confidence), feedback: aiAnalysis.feedback || this.generatePersonalityFeedback(staticAnalysis, indicators), breakdown: staticAnalysis, aiAnalysis: aiAnalysis, targetPersonality: personalityName, indicators: indicators }; } catch (error) { console.warn('Erreur validation LLM personnalité:', error.message); } } return { score: staticAnalysis.overallCoherence, confidence: this.calculateConfidence(staticAnalysis), feedback: this.generatePersonalityFeedback(staticAnalysis, indicators), breakdown: staticAnalysis, targetPersonality: targetPersonality.nom || targetPersonality, indicators: indicators }; } /** * 🤖 VALIDATION LLM QUALITATIVE - ANALYSE STYLE PERSONNALITÉ IA * * CE QUI EST TESTÉ : * ✅ Appel OpenAI avec profil complet personnalié en contexte * ✅ Évaluation 4 critères : vocabulaire, ton, structure, authenticité * ✅ Match qualitatif (good/medium/poor) + points forts/faibles * * ALGORITHMES EXÉCUTÉS : * - Prompt enrichi avec profil complet (style, ton, vocabulaire, marqueurs) * - Demande JSON : score + match + strengths + weaknesses + feedback * - Parse robuste avec extraction regex JSON * - Clamp score [0,100] et confidence [0,1] */ static async validatePersonalityWithLLM(content, personalityName, indicators) { const prompt = ` MISSION: Évalue si ce contenu correspond au style et à la personnalité de "${personalityName}". PROFIL PERSONNALITÉ CIBLE - ${personalityName}: - Style: ${indicators.style} - Ton: ${indicators.tone} - Structure: ${indicators.structure} - Vocabulaire attendu: ${indicators.vocabulary.join(', ')} - Marqueurs spécifiques: ${indicators.markers.join(', ')} CONTENU À ANALYSER: --- ${content.substring(0, 1200)}${content.length > 1200 ? '...[TRONQUÉ]' : ''} --- CRITÈRES D'ÉVALUATION: 1. VOCABULAIRE: Utilisation du vocabulaire spécialisé attendu 2. TON: Cohérence avec le ton caractéristique de la personnalité 3. STRUCTURE: Alignement avec le style de rédaction 4. AUTHENTICITÉ: Le contenu sonne-t-il vraiment comme cette personnalité? RÉPONSE JSON STRICTE: { "score": 75, "personalityMatch": "good", "strengths": ["éléments bien alignés"], "weaknesses": ["éléments à améliorer"], "feedback": "Analyse personnalisée en 2-3 phrases", "confidence": 0.8 } SCORE: 0-100 (0=pas du tout cette personnalité, 100=parfaitement aligné)`; const response = await LLMManager.callLLM('openai', prompt, { temperature: 0.1, max_tokens: 400 }); try { // Parse JSON de la réponse let jsonStr = response.trim(); const jsonMatch = jsonStr.match(/\{[\s\S]*\}/); if (jsonMatch) { jsonStr = jsonMatch[0]; } const parsed = JSON.parse(jsonStr); return { score: Math.max(0, Math.min(100, parsed.score || 50)), match: parsed.personalityMatch || 'medium', strengths: parsed.strengths || [], weaknesses: parsed.weaknesses || [], feedback: parsed.feedback || 'Analyse non disponible', confidence: Math.max(0, Math.min(1, parsed.confidence || 0.5)), source: 'llm' }; } catch (error) { console.warn('Erreur parsing réponse LLM personnalité:', error.message); return { score: 50, feedback: `Erreur parsing: ${error.message}`, confidence: 0.1, source: 'fallback' }; } } /** * 📝 ANALYSE CORRESPONDANCE VOCABULAIRE - MATCHING LEXICAL * * CE QUI EST TESTÉ : * ✅ Recherche mots-clés spécialisés personnalié dans contenu * ✅ Comptage occurrences avec pondération (10pts par occurrence) * ✅ Calcul densité vocabulaire spécialisé (pour 100 mots) * * ALGORITHMES EXÉCUTÉS : * - Regex search : new RegExp(word, 'g') pour chaque mot-clé * - Score = totalRelevance + density * 10 (max 100pts) * - Densité = matchCount / (totalWords / 100) * - Return : score + détail mots trouvés + densité */ static analyzeVocabularyMatch(content, targetVocabulary) { const contentWords = this.extractSignificantWords(content); const contentLower = content.toLowerCase(); let matchCount = 0; let totalRelevance = 0; const foundWords = []; targetVocabulary.forEach(word => { const occurrences = (contentLower.match(new RegExp(word, 'g')) || []).length; if (occurrences > 0) { matchCount += occurrences; foundWords.push({ word, count: occurrences }); totalRelevance += occurrences * 10; // 10 points par occurrence } }); // Score basé sur la densité de vocabulaire spécialisé const vocabularyDensity = matchCount / (contentWords.length / 100); // Pour 100 mots const score = Math.min(totalRelevance + vocabularyDensity * 10, 100); return { score: Math.round(score), matchCount, foundWords, density: vocabularyDensity }; } /** * 🎵 ANALYSE COHÉRENCE TON - DÉTECTEUR REGISTRE LINGUISTIQUE * * CE QUI EST TESTÉ : * ✅ Dictionnaire 6 tons : professionnel, inspirant, persuasif, direct, chaleureux, scientifique * ✅ Mots positifs/négatifs par ton avec pondération différentiée * ✅ Score = base(50) + positifs(+15 chacun) - négatifs(-10 chacun) * * ALGORITHMES EXÉCUTÉS : * - Lookup toneIndicators[targetTone] pour listes positive/negative * - Comptage regex occurrences : (contentLower.match(word, 'g') || []).length * - Scoring : positiveScore = count * 15, negativeScore = count * 10 * - Final = Math.max(0, Math.min(100, 50 + positive - negative)) */ static analyzeToneConsistency(content, targetTone) { const toneIndicators = { 'professionnel': { positive: ['recommandé', 'conseillé', 'expertise', 'spécialisé'], negative: ['super', 'génial', 'carrément', 'franchement'] }, 'inspirant': { positive: ['créer', 'transformer', 'magnifier', 'sublimer', 'imaginer'], negative: ['technique', 'spécification', 'paramètre'] }, 'persuasif': { positive: ['bénéficier', 'avantage', 'opportunité', 'maximiser'], negative: ['peut-être', 'probablement', 'incertain'] }, 'direct': { positive: ['directement', 'simplement', 'clairement', 'concrètement'], negative: ['sophistiqué', 'complexe', 'nuancé'] }, 'chaleureux': { positive: ['accueillant', 'convivial', 'chaleureux', 'bienveillant'], negative: ['froid', 'technique', 'rigide'] }, 'scientifique': { positive: ['recherche', 'analyse', 'étude', 'développement'], negative: ['ressenti', 'intuition', 'approximatif'] } }; const indicators = toneIndicators[targetTone] || { positive: [], negative: [] }; const contentLower = content.toLowerCase(); let positiveScore = 0; let negativeScore = 0; indicators.positive.forEach(word => { const count = (contentLower.match(new RegExp(word, 'g')) || []).length; positiveScore += count * 15; }); indicators.negative.forEach(word => { const count = (contentLower.match(new RegExp(word, 'g')) || []).length; negativeScore += count * 10; }); const toneScore = Math.max(0, Math.min(100, 50 + positiveScore - negativeScore)); return { score: Math.round(toneScore), positiveMatches: positiveScore / 15, negativeMatches: negativeScore / 10 }; } /** * 🏢 ANALYSE ALIGNEMENT STRUCTURE - PATTERNS ORGANISATIONNELS * * CE QUI EST TESTÉ : * ✅ 4 structures : logique, descriptive, argumentative, narrative * ✅ Détection patterns spécifiques par structure (regex + comptages) * ✅ Scoring cumul selon présence éléments structurels * * ALGORITHMES EXÉCUTÉS : * - Logique : listes numérotées(30pts) + connecteurs logiques(40pts) + flux méthodique(30pts) * - Descriptive : mots descriptifs(40pts) + structure fluide(30pts) + adverbes(5pts/mot, max 30) * - Argumentative : arguments(40pts) + contre-points(30pts) + conclusion(30pts) * - Narrative : éléments histoire(40pts) + touch personnelle(30pts) + marqueurs temporels(30pts) */ static analyzeStructureAlignment(content, targetStructure) { const structureFeatures = { 'logique': { check: content => { const hasNumberedList = /\d+\.\s/.test(content); const hasLogicalConnectors = /(donc|ainsi|par conséquent)/gi.test(content); const hasMethodicalFlow = /(premièrement|deuxièmement|enfin)/gi.test(content); return (hasNumberedList ? 30 : 0) + (hasLogicalConnectors ? 40 : 0) + (hasMethodicalFlow ? 30 : 0); } }, 'descriptive': { check: content => { const hasDescriptiveWords = /(élégant|beau|harmonieux|raffinement)/gi.test(content); const hasFlowingStructure = !/(1\.|2\.|3\.)/g.test(content); const hasAdjectives = (content.match(/\b\w+ment\b/g) || []).length; return (hasDescriptiveWords ? 40 : 0) + (hasFlowingStructure ? 30 : 0) + Math.min(hasAdjectives * 5, 30); } }, 'argumentative': { check: content => { const hasArguments = /(avantage|bénéfice|preuve|démontrer)/gi.test(content); const hasCounterpoints = /(cependant|néanmoins|mais)/gi.test(content); const hasConclusion = /(en conclusion|finalement|donc)/gi.test(content); return (hasArguments ? 40 : 0) + (hasCounterpoints ? 30 : 0) + (hasConclusion ? 30 : 0); } }, 'narrative': { check: content => { const hasStoryElements = /(histoire|tradition|expérience|passion)/gi.test(content); const hasPersonalTouch = /(nous|notre|mon|ma)/gi.test(content); const hasTemporalMarkers = /(depuis|aujourd'hui|maintenant)/gi.test(content); return (hasStoryElements ? 40 : 0) + (hasPersonalTouch ? 30 : 0) + (hasTemporalMarkers ? 30 : 0); } } }; const feature = structureFeatures[targetStructure]; const score = feature ? feature.check(content) : 50; return { score: Math.min(100, Math.round(score)), targetStructure }; } /** * 🎯 ANALYSE PRÉSENCE MARQUEURS - DÉTECTEUR SIGNATURES * * CE QUI EST TESTÉ : * ✅ Recherche expressions-signatures spécifiques personnalié * ✅ Comptage exact avec scoring linéaire (20pts par marqueur) * ✅ Bonus multiplicateur si plusieurs marqueurs (effet cumulatif) * * ALGORITHMES EXÉCUTÉS : * - Search : contentLower.includes(marker.toLowerCase()) pour chaque * - Base scoring : foundMarkers * 20pts * - Bonus : if (foundMarkers > 1) totalScore += foundMarkers * 5 * - Return : score + count + marqueurs détectés list */ static analyzeMarkerPresence(content, targetMarkers) { const contentLower = content.toLowerCase(); let foundMarkers = 0; let totalScore = 0; const detectedMarkers = []; targetMarkers.forEach(marker => { if (contentLower.includes(marker.toLowerCase())) { foundMarkers++; totalScore += 20; // 20 points par marqueur trouvé detectedMarkers.push(marker); } }); // Bonus si plusieurs marqueurs présents if (foundMarkers > 1) { totalScore += foundMarkers * 5; } return { score: Math.min(100, totalScore), foundCount: foundMarkers, totalCount: targetMarkers.length, detectedMarkers }; } /** * 📊 CALCUL CONFIANCE ÉVALUATION - MESURE COHÉRENCE INTERNE * * CE QUI EST TESTÉ : * ✅ Variance des 4 scores métriques (vocabulaire, ton, structure, marqueurs) * ✅ Confidence inverse de dispersion (écart-type faible = confiance haute) * ✅ Normalisation [0.1, 1.0] pour éviter confiance zéro * * ALGORITHMES EXÉCUTÉS : * - Calcul moyenne : mean = Σ(scores) / 4 * - Variance : variance = Σ((score-mean)²) / 4 * - Écart-type : stdDev = sqrt(variance) * - Confidence : Math.max(0.1, 1 - (stdDev / 50)) * - Return arrondi à 2 décimales */ static calculateConfidence(analysis) { // Confiance basée sur la cohérence des différents indicateurs const scores = [ analysis.vocabularyMatch.score, analysis.toneConsistency.score, analysis.structureAlignment.score, analysis.markerPresence.score ]; const mean = scores.reduce((a, b) => a + b) / scores.length; const variance = scores.reduce((sum, score) => sum + Math.pow(score - mean, 2), 0) / scores.length; const stdDev = Math.sqrt(variance); // Confiance élevée si peu de variance entre les scores const confidence = Math.max(0.1, 1 - (stdDev / 50)); return Math.round(confidence * 100) / 100; } /** * 💬 GÉNÉRATION FEEDBACK PERSONNALISÉ - RAPPORT ADAPTÉ * * CE QUI EST TESTÉ : * ✅ Évaluation seuil 70pts pour validation/amélioration par métrique * ✅ Messages contextualisés selon personnalité (vocabulaire, ton, etc.) * ✅ Feedback actionnable avec suggestions concrètes * * ALGORITHMES EXÉCUTÉS : * - If score >= 70 : message ✅ validation * - If score < 70 : message ⚠️ avec suggestions * - Join messages avec ' | ' pour format compact * - Intégration indicateurs.vocabulary/tone/style dans conseils */ static generatePersonalityFeedback(analysis, indicators) { const feedback = []; if (analysis.vocabularyMatch.score >= 70) { feedback.push(`✅ Vocabulaire bien aligné avec le style ${indicators.style}`); } else { feedback.push(`⚠️ Vocabulaire à enrichir (mots-clés: ${indicators.vocabulary.join(', ')})`); } if (analysis.toneConsistency.score >= 70) { feedback.push(`✅ Ton ${indicators.tone} respecté`); } else { feedback.push(`⚠️ Ton à ajuster vers plus ${indicators.tone}`); } if (analysis.structureAlignment.score >= 70) { feedback.push(`✅ Structure ${indicators.structure} appropriée`); } else { feedback.push(`⚠️ Structure à adapter au style ${indicators.structure}`); } if (analysis.markerPresence.foundCount > 0) { feedback.push(`✅ ${analysis.markerPresence.foundCount} marqueur(s) spécifique(s) détecté(s)`); } else { feedback.push(`⚠️ Aucun marqueur spécifique détecté`); } return feedback.join(' | '); } /** * 🔢 UTILITAIRE EXTRACTION MOTS SIGNIFICATIFS - PARSER LEXICAL * * CE QUI EST TESTÉ : * ✅ Nettoyage ponctuation et normalisation lowercase * ✅ Split sur espaces avec filtre longueur >3 chars * ✅ Exclusion mots courts (articles, prépositions) pour focus contenu * * ALGORITHME EXÉCUTÉ : * - toLowerCase() pour normalisation * - replace(/[^\w\s]/g, ' ') pour supprimer ponctuation * - split(/\s+/) pour mots individuels * - filter(word => word.length > 3) pour significatifs */ static extractSignificantWords(content) { return content .toLowerCase() .replace(/[^\w\s]/g, ' ') .split(/\s+/) .filter(word => word.length > 3); } /** * ⚡ VALIDATION RAPIDE - PRESET PERFORMANCE OPTIMISÉ * * CE QUI EST TESTÉ : * ✅ Validation complète simplifiée sans LLM (plus rapide) * ✅ Classification 3 niveaux : GOOD(≥75), MODERATE(≥50), POOR(<50) * ✅ Recommandations automatiques selon faiblesses détectées * * ALGORITHMES EXÉCUTÉS : * - Appel validatePersonality() en mode statique only * - Classification par seuils : 75/50 pour GOOD/MODERATE/POOR * - Génération recommandations via generateRecommendations() * - Structure optimisée pour dashboard temps réel */ static quickValidation(content, personality) { const validation = this.validatePersonality(content, personality); return { score: validation.score, level: validation.score >= 75 ? 'GOOD' : validation.score >= 50 ? 'MODERATE' : 'POOR', recommendations: this.generateRecommendations(validation), confidence: validation.confidence }; } /** * 💡 GÉNÉRATION RECOMMANDATIONS AMÉLIORATION - CONSEILS AUTOMATIQUES * * CE QUI EST TESTÉ : * ✅ Analyse scores <60 pour identifier défauts prioritaires * ✅ Recommandations spécifiques avec exemples concrets * ✅ Suggestions actionables adaptées à chaque métrique * * ALGORITHMES EXÉCUTÉS : * - If vocabularyMatch<60 : suggère top-3 mots-clés à intégrer * - If toneConsistency<60 : conseil adaptation ton cible * - If structureAlignment<60 : guidance réorganisation * - If markerPresence=0 : propose top-2 marqueurs à utiliser * - Return array pour affichage UI */ static generateRecommendations(validation) { const recs = []; if (validation.breakdown.vocabularyMatch.score < 60) { recs.push(`Intégrer plus de vocabulaire spécialisé: ${validation.indicators.vocabulary.slice(0, 3).join(', ')}`); } if (validation.breakdown.toneConsistency.score < 60) { recs.push(`Adapter le ton pour qu'il soit plus ${validation.indicators.tone}`); } if (validation.breakdown.structureAlignment.score < 60) { recs.push(`Réorganiser selon une structure plus ${validation.indicators.structure}`); } if (validation.breakdown.markerPresence.foundCount === 0) { recs.push(`Utiliser des marqueurs spécifiques: ${validation.indicators.markers.slice(0, 2).join(', ')}`); } return recs; } } module.exports = { PersonalityValidator };