// ======================================== // VALIDATEUR ANTI-DÉTECTION IA // Détection des patterns typiques d'IA et évaluation de naturalité // ======================================== // 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 AntiDetectionValidator:', error.message); } /** * Validateur spécialisé dans la détection d'empreintes IA * Score de naturalité et détection de patterns suspects */ class AntiDetectionValidator { // Base de données des empreintes IA suspectes static AI_FINGERPRINTS = { // Mots/expressions typiquement IA (niveau critique) critical: { english: [ 'comprehensive', 'robust', 'seamless', 'innovative', 'cutting-edge', 'state-of-the-art', 'furthermore', 'moreover', 'in conclusion', 'it is important to note', 'it\'s worth noting', 'additionally' ], french: [ 'complet et exhaustif', 'robuste et fiable', 'innovant et révolutionnaire', 'de pointe', 'il est important de souligner', 'il convient de noter', 'en outre', 'par ailleurs', 'en définitive', 'en fin de compte' ] }, // Patterns de structure suspects (niveau élevé) high: { patterns: [ /^(Premièrement|Deuxièmement|Troisièmement).+(Deuxièmement|Troisièmement|Enfin)/s, /En conclusion.+(il est|nous pouvons|il convient)/i, /(Il est important|Il faut souligner|Il convient de noter).+(que|de)/gi, /^\d+\.\s.+\n\d+\.\s.+\n\d+\.\s/m // Listes numérotées systématiques ], phrases: [ 'permet de', 'contribue à', 'favorise le développement', 'optimise les performances', 'améliore significativement' ] }, // Indicateurs moyens (niveau modéré) moderate: { overused: [ 'cependant', 'néanmoins', 'toutefois', 'ainsi', 'donc', 'par conséquent', 'en effet', 'effectivement', 'naturellement' ], formal: [ 'il s\'avère que', 'force est de constater', 'il ressort que', 'on peut considérer', 'il est possible de' ] } }; // Patterns de transitions trop parfaites static PERFECT_TRANSITIONS = [ /\.\s+(Cependant|Néanmoins|Toutefois|Par ailleurs),/g, /\.\s+(En outre|De plus|Par ailleurs),/g, /\.\s+(Ainsi|Donc|Par conséquent),/g, /\.\s+(En effet|Effectivement|Naturellement),/g ]; // Structures de phrases trop régulières static REGULAR_PATTERNS = [ /^[A-Z][^.]{50,80}\./gm, // Phrases de longueur trop uniforme /^[A-Z][^.]+, [^.]+, [^.]+\./gm, // Structure triple répétitive ]; /** * 🚫 VALIDATION ANTI-DÉTECTION PRINCIPALE - ORCHESTRATEUR GLOBAL * * CE QUI EST TESTÉ : * ✅ Analyse statique 4 métriques : empreintes IA, variabilité, naturalité, erreurs humaines * ✅ Score pondéré : empreintes(30%) + variabilité(25%) + naturalité(25%) + erreurs(20%) * ✅ Classification risque détection (LOW/MODERATE/HIGH/CRITICAL) * ✅ Validation LLM complémentaire optionnelle (fusion 70% statique + 30% IA) * * ALGORITHMES EXÉCUTÉS : * - 4 analyses parallèles : patterns suspects, diversité syntaxique, markers humains * - Protection NaN avec fallback scores à 50 * - Pondération sophistiquée privilégiant détection patterns critiques * - Fusion intelligente avec validation IA si disponible */ static async validateAntiDetection(content, options = {}) { // Analyse statique (patterns) const staticAnalysis = { fingerprintScore: this.analyzeFingerprintScore(content), variabilityScore: this.analyzeVariabilityScore(content), naturalness: this.analyzeNaturalness(content), humanErrors: this.detectHumanLikeErrors(content), overallScore: 0, details: {} }; // Calcul score global pondéré avec vérification NaN const fp = isNaN(staticAnalysis.fingerprintScore?.score) ? 50 : staticAnalysis.fingerprintScore.score; const vs = isNaN(staticAnalysis.variabilityScore) ? 50 : staticAnalysis.variabilityScore; const nat = isNaN(staticAnalysis.naturalness) ? 50 : staticAnalysis.naturalness; const he = isNaN(staticAnalysis.humanErrors) ? 50 : staticAnalysis.humanErrors; staticAnalysis.overallScore = Math.round( fp * 0.3 + vs * 0.25 + nat * 0.25 + he * 0.2 ); // Classification du niveau de détection staticAnalysis.detectionRisk = this.calculateDetectionRisk(staticAnalysis.overallScore); // Validation IA complémentaire (si LLM disponible) let aiAnalysis = null; if (LLMManager && options.enableLLM !== false) { try { aiAnalysis = await this.validateWithLLM(content); // Fusion des scores (moyenne pondérée) const combinedScore = Math.round(staticAnalysis.overallScore * 0.7 + aiAnalysis.score * 0.3); return { ...staticAnalysis, overallScore: combinedScore, aiAnalysis: aiAnalysis, detectionRisk: this.calculateDetectionRisk(combinedScore) }; } catch (error) { console.warn('Erreur validation LLM anti-détection:', error.message); } } return staticAnalysis; } /** * 🤖 VALIDATION LLM QUALITATIVE - ANALYSE IA vs HUMAIN * * CE QUI EST TESTÉ : * ✅ Appel OpenAI pour détection qualitative empreintes IA * ✅ Évaluation 4 critères : empreintes, variabilité, naturalité, fluidité * ✅ Score probabilité humain (0=clairement IA, 100=clairement humain) * * ALGORITHMES EXÉCUTÉS : * - Prompt spécialisé détection IA avec exemples concrets * - Demande JSON : score, likelihood, fingerprints détectées, feedback * - Parse robuste avec extraction regex JSON * - Clamp score [0,100] et confidence [0,1] */ static async validateWithLLM(content) { const prompt = ` MISSION: Analyse si ce texte a été généré par une IA ou écrit par un humain. TEXTE À ANALYSER: --- ${content.substring(0, 1500)}${content.length > 1500 ? '...[TRONQUÉ]' : ''} --- CRITÈRES D'ANALYSE: 1. EMPREINTES IA: Expressions typiques ("comprehensive", "robuste", "il convient de noter", transitions parfaites) 2. VARIABILITÉ: Longueur des phrases, structures variées, vocabulaire diversifié 3. NATURALITÉ: Hésitations, nuances, imperfections subtiles, ton spontané 4. FLUIDITÉ: Transitions naturelles vs mécaniques RÉPONSE JSON STRICTE: { "score": 85, "humanLikelihood": "high", "aiFingerprints": ["expressions trouvées"], "naturalElements": ["éléments humains détectés"], "feedback": "Analyse détaillée en 2-3 phrases", "confidence": 0.8 } SCORE: 0-100 (0=clairement IA, 100=clairement humain)`; const response = await LLMManager.callLLM('openai', prompt, { temperature: 0.1, max_tokens: 500 }); 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)), likelihood: parsed.humanLikelihood || 'medium', fingerprints: parsed.aiFingerprints || [], naturalElements: parsed.naturalElements || [], 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 anti-détection:', error.message); return { score: 50, feedback: `Erreur parsing: ${error.message}`, confidence: 0.1, source: 'fallback' }; } } /** * 🕵️ ANALYSE EMPREINTES IA - DÉTECTEUR PATTERNS SUSPECTS * * CE QUI EST TESTÉ : * ✅ Détection expressions critiques anglais/français (comprehensive, robuste, etc.) * ✅ Patterns structurels suspects (listes numérotées, transitions parfaites) * ✅ Suremploi mots de liaison formels (cependant, néanmoins, etc.) * * ALGORITHMES EXÉCUTÉS : * - Score base = 100, décrément par occurrence suspecte * - Critique : -15pts par occurrence ("comprehensive", "il convient de noter") * - High patterns : -10pts par regex match (enumérations systématiques) * - Modéré : -5pts par suremploi connecteur (>2 occurrences) * - Return score + détail complet par catégorie */ static analyzeFingerprintScore(content) { let score = 100; const details = { critical: [], high: [], moderate: [] }; const lowerContent = content.toLowerCase(); // Vérification empreintes critiques this.AI_FINGERPRINTS.critical.english.forEach(phrase => { const count = (lowerContent.match(new RegExp(phrase.toLowerCase(), 'g')) || []).length; if (count > 0) { score -= count * 15; // -15 points par occurrence critique details.critical.push({ phrase, count }); } }); this.AI_FINGERPRINTS.critical.french.forEach(phrase => { const count = (lowerContent.match(new RegExp(phrase.toLowerCase(), 'g')) || []).length; if (count > 0) { score -= count * 15; details.critical.push({ phrase, count }); } }); // Vérification patterns suspects this.AI_FINGERPRINTS.high.patterns.forEach(pattern => { const matches = content.match(pattern); if (matches) { score -= matches.length * 10; // -10 points par pattern details.high.push({ pattern: pattern.toString(), matches: matches.length }); } }); // Vérification phrases surutilisées this.AI_FINGERPRINTS.moderate.overused.forEach(phrase => { const count = (lowerContent.split(phrase.toLowerCase()).length - 1); if (count > 2) { // Plus de 2 occurrences = suspect score -= (count - 2) * 5; details.moderate.push({ phrase, count }); } }); return { score: Math.max(0, score), details: details }; } /** * 🌈 ANALYSE VARIABILITÉ LINGUISTIQUE - MESURE DIVERSITÉ SYNTAXIQUE * * CE QUI EST TESTÉ : * ✅ Variabilité longueur phrases (coefficient de variation statistique) * ✅ Diversité débuts de phrases (ratio mots uniques / total) * ✅ Variété ponctuation (nombre types différents utilisés) * ✅ Pénalité transitions trop parfaites ("Cependant,", "Par ailleurs,") * * ALGORITHMES EXÉCUTÉS : * - Coefficient variation longueurs = σ/μ (max 30pts) * - Diversité starters = Set(premiers10chars).size / total (40pts) * - Ponctuation = count(['.', ',', ';', ':', '!', '?', '...', '—']) (10pts) * - Pénalité = -5pts par transition parfaite détectée */ static analyzeVariabilityScore(content) { const sentences = this.getSentences(content); if (sentences.length < 3) return 50; // Variabilité longueur des phrases const lengths = sentences.map(s => s.trim().length); const lengthVariation = this.calculateCoeffVariation(lengths); // Variabilité structure (début de phrase) const starters = sentences.map(s => s.trim().substring(0, 10).toLowerCase()); const uniqueStarters = new Set(starters).size; const starterVariation = uniqueStarters / sentences.length; // Variabilité ponctuation const punctuationTypes = this.analyzePunctuationVariety(content); // Score combiné let variabilityScore = 0; variabilityScore += Math.min(lengthVariation * 30, 30); variabilityScore += starterVariation * 40; variabilityScore += punctuationTypes * 10; // Pénalité pour transitions trop parfaites const perfectTransitions = this.countPerfectTransitions(content); variabilityScore -= perfectTransitions * 5; return Math.max(0, Math.min(100, Math.round(variabilityScore))); } /** * 🌱 ANALYSE NATURALITÉ - DÉTECTEUR MARKERS HUMAINS * * CE QUI EST TESTÉ : * ✅ Détection hésitations humaines ("peut-être", "j'ai l'impression") * ✅ Expressions familiales ("du coup", "en fait", "franchement") * ✅ Variété connecteurs évitant répétition mécanique * ✅ Parenthèses/commentaires spontanés (marques d'oralité) * * ALGORITHMES EXÉCUTÉS : * - Score base = 50, bonus par marker détecté * - Hésitations : +8pts par marker (7 patterns surveillés) * - Familiarité : +5pts par expression (7 expressions) * - Connecteur variety : +25pts max selon diversité * - Parenthèses/tirets : +3pts chacune (max 15pts) */ static analyzeNaturalness(content) { let naturalness = 50; // Score de base // Présence d'hésitations ou nuances humaines const hesitationMarkers = [ 'peut-être', 'probablement', 'il semble que', 'on dirait que', 'j\'ai l\'impression', 'si je ne me trompe', 'en quelque sorte' ]; hesitationMarkers.forEach(marker => { if (content.toLowerCase().includes(marker)) { naturalness += 8; } }); // Présence d'expressions familières const colloquialisms = [ 'du coup', 'en fait', 'franchement', 'carrément', 'pas mal de', 'un tas de', 'histoire de' ]; colloquialisms.forEach(expr => { if (content.toLowerCase().includes(expr)) { naturalness += 5; } }); // Variation dans les connecteurs (évite la répétition) const connectorVariety = this.analyzeConnectorVariety(content); naturalness += connectorVariety; // Présence de parenthèses ou commentaires entre tirets const asides = (content.match(/\([^)]+\)/g) || []).length + (content.match(/—[^—]+—/g) || []).length; naturalness += Math.min(asides * 3, 15); return Math.max(0, Math.min(100, Math.round(naturalness))); } /** * ✨ DÉTECTION IMPERFECTIONS HUMAINES POSITIVES - BONUS NATURALITÉ * * CE QUI EST TESTÉ : * ✅ Phrases incomplètes (...) comme marque spontanéité * ✅ Questions rhétoriques (engagement naturel) * ✅ Exclamations modérées (enthousiasme sans exagération) * ✅ Répétitions légères positives (2-3x = humain, >3x = suspect) * ✅ Irrégularité longueurs phrases (variation naturelle) * * ALGORITHMES EXÉCUTÉS : * - Score base = 20, bonus par élément naturel * - Points suspension : +10pts chacun (max 30pts) * - Questions : +8pts chacune (max 25pts) * - Exclamations : +5pts si 1-4, 0pts si >5 * - Répétitions : +3pts si 2x, +2pts si 3x, -2pts si >3x * - Irrégularité : +20pts si coeff 0.2-0.5, +10pts si >0.1 */ static detectHumanLikeErrors(content) { let humanScore = 20; // Score de base faible // Phrases incomplètes (positif pour naturalité) const incompleteMarkers = content.match(/\.\.\./g) || []; humanScore += Math.min(incompleteMarkers.length * 10, 30); // Questions rhétoriques const questions = (content.match(/\?/g) || []).length; humanScore += Math.min(questions * 8, 25); // Exclamations (modération) const exclamations = (content.match(/!/g) || []).length; if (exclamations > 0 && exclamations < 5) { humanScore += exclamations * 5; } // Répétitions légères (positives si modérées) const repetitionScore = this.analyzePositiveRepetition(content); humanScore += repetitionScore; // Longueurs de phrases irrégulières (bon signe) const sentences = this.getSentences(content); const lengthIrregularity = this.calculateLengthIrregularity(sentences); humanScore += lengthIrregularity; return Math.max(0, Math.min(100, Math.round(humanScore))); } /** * ⚠️ CALCUL RISQUE DÉTECTION - CLASSIFICATEUR NIVEAUX ALERTE * * CE QUI EST TESTÉ : * ✅ Classification score global en 4 niveaux risque * ✅ Seuils optimisés selon tests empiriques détection IA * ✅ Retour niveau + texte explicatif pour UI * * ALGORITHME EXÉCUTÉ : * - Seuils : ≥85=LOW, ≥70=MODERATE, ≥50=HIGH, <50=CRITICAL * - Return {level, text} pour affichage utilisateur * - LOW = "Risque faible" (contenu très humain) * - CRITICAL = "Risque critique" (clairement IA) */ static calculateDetectionRisk(overallScore) { if (overallScore >= 85) return { level: 'LOW', text: 'Risque faible' }; if (overallScore >= 70) return { level: 'MODERATE', text: 'Risque modéré' }; if (overallScore >= 50) return { level: 'HIGH', text: 'Risque élevé' }; return { level: 'CRITICAL', text: 'Risque critique' }; } /** * 🔢 UTILITAIRE EXTRACTION PHRASES - PARSER TEXTUEL * * CE QUI EST TESTÉ : * ✅ Split sur ponctuations finales [.!?] avec filtre longueur * ✅ Filtrage phrases trop courtes (<10 chars) pour éviter artef acts * ✅ Extraction propre pour calculs statistiques * * ALGORITHME EXÉCUTÉ : * - Split regex : /[.!?]+/ pour gérer ponctuations multiples * - Filter : s => s.trim().length > 10 pour phrases significatives * - Usage dans variabilité et calculs statistiques */ static getSentences(content) { return content.split(/[.!?]+/).filter(s => s.trim().length > 10); } /** * 📊 COEFFICIENT VARIATION - MESURE STATISTIQUE DISPERSION * * CE QUI EST TESTÉ : * ✅ Calcul coefficient variation CV = σ/μ (dispersion relative) * ✅ Mesure homogénéité vs diversité des longueurs * ✅ Protection division par zéro avec vérification mean > 0 * * ALGORITHMES EXÉCUTÉS : * - Moyenne : μ = Σ(numbers) / length * - Variance : σ² = Σ((n-μ)²) / length * - Écart-type : σ = sqrt(σ²) * - Coefficient : CV = σ/μ (0=uniforme, >0=varié) */ static calculateCoeffVariation(numbers) { if (numbers.length < 2) return 0; const mean = numbers.reduce((a, b) => a + b) / numbers.length; const variance = numbers.reduce((sum, n) => sum + Math.pow(n - mean, 2), 0) / numbers.length; const stdDev = Math.sqrt(variance); return mean > 0 ? stdDev / mean : 0; } /** * 📋 ANALYSE VARIÉTÉ PONCTUATION - COMPTEUR DIVERSITÉ * * CE QUI EST TESTÉ : * ✅ Détection 8 types ponctuation : . , ; : ! ? ... — * ✅ Comptage nombre types utilisés (richesse expression) * ✅ Indicateur sophistication rédactionnelle * * ALGORITHME EXÉCUTÉ : * - Liste types = ['.', ',', ';', ':', '!', '?', '...', '—'] * - Filter : types présents dans content avec .includes() * - Return count (0-8) pour score variabilité */ static analyzePunctuationVariety(content) { const punctTypes = ['.', ',', ';', ':', '!', '?', '...', '—'].filter(p => content.includes(p) ); return punctTypes.length; } /** * 🎯 COMPTAGE TRANSITIONS PARFAITES - DÉTECTEUR MÉCANISME IA * * CE QUI EST TESTÉ : * ✅ Détection patterns transitions trop formelles/mécaniques * ✅ 4 regex PERFECT_TRANSITIONS : "Cependant,", "En outre,", "Ainsi,", "En effet," * ✅ Comptage occurrences suspectes pour pénalité variabilité * * ALGORITHME EXÉCUTÉ : * - Boucle sur PERFECT_TRANSITIONS regex patterns * - Match count += pattern.match(content).length pour chaque * - Return total pour pénalité -5pts par transition parfaite */ static countPerfectTransitions(content) { let count = 0; this.PERFECT_TRANSITIONS.forEach(pattern => { const matches = content.match(pattern); if (matches) count += matches.length; }); return count; } /** * 🔗 ANALYSE VARIÉTÉ CONNECTEURS - MESURE DIVERSITÉ LIAISON * * CE QUI EST TESTÉ : * ✅ Détection 10 connecteurs logiques variés * ✅ Comptage nombre types utilisés (vs répétition mécanique) * ✅ Bonus naturalité selon richesse connecteurs * * ALGORITHME EXÉCUTÉ : * - Liste = ['mais', 'cependant', 'toutefois', ...] (10 connecteurs) * - Filter : connecteurs présents avec .toLowerCase().includes() * - Score = used.length * 5 (max 25pts) pour diversité */ static analyzeConnectorVariety(content) { const connectors = [ 'mais', 'cependant', 'toutefois', 'néanmoins', 'ainsi', 'donc', 'par conséquent', 'en effet', 'par ailleurs', 'en outre' ]; const used = connectors.filter(c => content.toLowerCase().includes(c)); return Math.min(used.length * 5, 25); // Max 25 points } /** * ♾️ ANALYSE RÉPÉTITIONS POSITIVES - DÉTECTEUR NATURALITÉ MODÉRÉE * * CE QUI EST TESTÉ : * ✅ Comptage répétitions mots significatifs (longueur>5) * ✅ Scoring positif pour répétitions modérées humaines (2-3x) * ✅ Pénalité répétitions excessives (>3x = suspect IA) * * ALGORITHMES EXÉCUTÉS : * - Filter mots : word.length > 5 pour significatifs * - Map fréquences : repetitions[word] = count * - Scoring : 2x=+3pts, 3x=+2pts, >3x=-2pts * - Clamp [0,15] pour éviter scores excessifs */ static analyzePositiveRepetition(content) { const words = content.toLowerCase().split(/\s+/); const importantWords = words.filter(w => w.length > 5); const repetitions = {}; importantWords.forEach(word => { repetitions[word] = (repetitions[word] || 0) + 1; }); // Répétitions modérées = humain (2-3 fois max) let positiveReps = 0; Object.values(repetitions).forEach(count => { if (count === 2) positiveReps += 3; if (count === 3) positiveReps += 2; if (count > 3) positiveReps -= 2; // Pénalité excessive }); return Math.max(0, Math.min(positiveReps, 15)); } /** * 📏 CALCUL IRRÉGULARITÉ LONGUEURS - BONUS VARIATION NATURELLE * * CE QUI EST TESTÉ : * ✅ Mesure irrégularité longueurs phrases (signe humanité) * ✅ Optimum à coefficient 0.2-0.5 (variation naturelle) * ✅ Bonus pour éviter uniformité suspecte IA * * ALGORITHME EXÉCUTÉ : * - Calcul coefficient variation longueurs phrases * - If [0.2,0.5] = +20pts (variation idéale humaine) * - If >0.1 = +10pts (variation acceptable) * - Else = 0pts (trop uniforme = suspect) */ static calculateLengthIrregularity(sentences) { if (sentences.length < 3) return 0; const lengths = sentences.map(s => s.trim().length); const coeffVar = this.calculateCoeffVariation(lengths); // Irrégularité modérée = bon signe humain if (coeffVar >= 0.2 && coeffVar <= 0.5) { return 20; } else if (coeffVar > 0.1) { return 10; } return 0; } /** * 📋 RAPPORT DÉTAILLÉ DEBUG - DIAGNOSTIC COMPLET * * CE QUI EST TESTÉ : * ✅ Exécution validation complète + structuration résultats * ✅ Breakdown détaillé par métrique pour debugging * ✅ Génération recommandations amélioration automatiques * * ALGORITHME EXÉCUTÉ : * - Appel validateAntiDetection() pour scores complets * - Structuration : score global + risque + breakdown 4 métriques * - Génération recommandations selon seuils faibles * - Timestamp pour traçabilité */ static generateDetailedReport(content) { const validation = this.validateAntiDetection(content); return { score: validation.overallScore, risk: validation.detectionRisk, breakdown: { fingerprints: validation.fingerprintScore, variability: validation.variabilityScore, naturalness: validation.naturalness, humanErrors: validation.humanErrors }, recommendations: this.generateRecommendations(validation), timestamp: new Date().toISOString() }; } /** * 💡 GÉNÉRATION RECOMMANDATIONS - CONSEILS AMÉLIORATION AUTO * * CE QUI EST TESTÉ : * ✅ Analyse scores faibles (<60-70) pour cibler défauts * ✅ Recommandations spécifiques par métrique défaillante * ✅ Conseils actionables pour réduction risque détection * * ALGORITHMES EXÉCUTÉS : * - If fingerprints<70 = "Réduire expressions IA" * - If variability<60 = "Augmenter diversité syntaxique" * - If naturalness<60 = "Ajouter nuances humaines" * - If humanErrors<40 = "Introduire imperfections subtiles" * - Return array recommandations pour UI */ static generateRecommendations(validation) { const recommendations = []; if (validation.fingerprintScore.score < 70) { recommendations.push('Réduire les expressions typiquement IA identifiées'); } if (validation.variabilityScore < 60) { recommendations.push('Augmenter la variabilité des structures de phrases'); } if (validation.naturalness < 60) { recommendations.push('Ajouter des nuances et hésitations humaines'); } if (validation.humanErrors < 40) { recommendations.push('Introduire des imperfections subtiles pour plus de naturalité'); } return recommendations; } } module.exports = { AntiDetectionValidator };