// ======================================== // DETECTOR STRATEGIES - NIVEAU 3 // Responsabilité: Stratégies spécialisées par détecteur IA // Anti-détection: Techniques ciblées contre chaque analyseur // ======================================== const { logSh } = require('../ErrorReporting'); const { tracer } = require('../trace'); /** * STRATÉGIES DÉTECTEUR PAR DÉTECTEUR * Chaque classe implémente une approche spécialisée */ class BaseDetectorStrategy { constructor(name) { this.name = name; this.effectiveness = 0.8; this.targetMetrics = []; } /** * Générer instructions spécifiques pour ce détecteur */ generateInstructions(elementType, personality, csvData) { throw new Error('generateInstructions must be implemented by subclass'); } /** * Obtenir instructions anti-détection (NOUVEAU pour modularité) */ getInstructions(intensity = 1.0) { throw new Error('getInstructions must be implemented by subclass'); } /** * Obtenir conseils d'amélioration (NOUVEAU pour modularité) */ getEnhancementTips(intensity = 1.0) { throw new Error('getEnhancementTips must be implemented by subclass'); } /** * Analyser efficacité contre ce détecteur */ analyzeEffectiveness(content) { return { detector: this.name, effectiveness: this.effectiveness, metrics: this.analyzeContent(content) }; } /** * Analyser contenu selon métriques de ce détecteur */ analyzeContent(content) { return { wordCount: content.split(/\s+/).length, sentenceCount: content.split(/[.!?]+/).length }; } } /** * STRATÉGIE ANTI-GPTZERO * Focus: Imprévisibilité et variation syntaxique */ class GPTZeroStrategy extends BaseDetectorStrategy { constructor() { super('GPTZero'); this.effectiveness = 0.9; this.targetMetrics = ['perplexity', 'burstiness', 'unpredictability']; this.techniques = { syntaxVariation: { name: 'Variation syntaxique drastique', rules: [ 'Alterne phrases simples (5-8 mots) et complexes (25-35 mots)', 'Utilise structures atypiques : inversion, anacoluthe, ellipse', 'Intercale incises, parenthèses, tirets pour briser linéarité', 'Varie types : déclarative → interrogative → exclamative' ] }, lexicalUnpredictability: { name: 'Imprévisibilité lexicale', rules: [ 'Synonymes rares ou détournés : "performant" → "qui tient la route"', 'Néologismes justifiés : "sur-mesurable" pour "très mesurable"', 'Registres mélangés : soutenu puis familier dans même paragraphe', 'Évite collocations attendues : "haute qualité" → "qualité qui dépote"' ] }, narrativeDisruption: { name: 'Disruption narrative', rules: [ 'Apartés personnels : "(j\'ai testé, c\'est bluffant)"', 'Questions rhétoriques inattendues au milieu des faits', 'Changements de perspective soudains : technique → humain → commercial', 'Digressions courtes puis retour au sujet' ] } }; } generateInstructions(elementType, personality, csvData) { const instructions = []; instructions.push(`ANTI-GPTZERO - MAXIMUM IMPRÉVISIBILITÉ:`); // Techniques syntaxiques instructions.push(`\nSYNTAXE VARIABLE:`); this.techniques.syntaxVariation.rules.forEach(rule => { instructions.push(`• ${rule}`); }); // Imprévisibilité lexicale instructions.push(`\nLEXIQUE IMPRÉVISIBLE:`); this.techniques.lexicalUnpredictability.rules.forEach(rule => { instructions.push(`• ${rule}`); }); // Adaptation selon type élément if (elementType === 'titre_h1' || elementType === 'titre_h2') { instructions.push(`\nTITRES ANTI-GPTZERO:`); instructions.push(`• Évite formules attendues : "Guide complet" → "Le vrai topo sur"`); instructions.push(`• Structure atypique : question, exclamation, fragment`); } else if (elementType === 'texte') { instructions.push(`\nTEXTES ANTI-GPTZERO:`); this.techniques.narrativeDisruption.rules.forEach(rule => { instructions.push(`• ${rule}`); }); } // Adaptation personnalité if (personality) { instructions.push(`\nAVEC STYLE ${personality.nom.toUpperCase()}:`); instructions.push(`• Pousse ${personality.style} vers extrêmes : plus marqué, moins lisse`); instructions.push(`• Accentue tics de langage : répétitions, expressions favorites`); } return instructions.join('\n'); } /** * Instructions modulaires pour AdversarialCore */ getInstructions(intensity = 1.0) { const baseRules = [ 'Surprends avec tournures inattendues et constructions atypiques', 'Évite phrases trop prévisibles ou patterns répétitifs', 'Utilise synonymes moins évidents et vocabulaire moins commun', 'Varie drastiquement la complexité syntaxique entre phrases' ]; const intensiveRules = [ 'Intercale observations personnelles ou détours narratifs', 'Alterne registres de langue (soutenu → familier → technique)', 'Brise la logique linéaire avec des parenthèses, incises, apartés', 'Créé ruptures narratives et changements de perspective soudains' ]; return intensity >= 1.0 ? [...baseRules, ...intensiveRules] : baseRules; } /** * Conseils d'amélioration pour enhancement method */ getEnhancementTips(intensity = 1.0) { return [ 'Remplace connecteurs prévisibles par variations inattendues', 'Ajoute incises courtes pour briser la linéarité', 'Varie longueurs phrases dans même paragraphe', 'Utilise synonymes moins courants mais naturels', ...(intensity > 0.8 ? [ 'Insère questions rhétoriques ponctuelles', 'Ajoute nuances et hésitations authentiques' ] : []) ]; } analyzeContent(content) { const baseMetrics = super.analyzeContent(content); // Analyse perplexité approximative const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 5); const sentenceLengths = sentences.map(s => s.split(/\s+/).length); // Variance longueur (proxy pour burstiness) const avgLength = sentenceLengths.reduce((a, b) => a + b, 0) / sentenceLengths.length; const variance = sentenceLengths.reduce((acc, len) => acc + Math.pow(len - avgLength, 2), 0) / sentenceLengths.length; const burstiness = Math.sqrt(variance) / avgLength; // Diversité lexicale (proxy pour imprévisibilité) const words = content.toLowerCase().split(/\s+/).filter(w => w.length > 2); const uniqueWords = [...new Set(words)]; const lexicalDiversity = uniqueWords.length / words.length; return { ...baseMetrics, burstiness: Math.round(burstiness * 100) / 100, lexicalDiversity: Math.round(lexicalDiversity * 100) / 100, avgSentenceLength: Math.round(avgLength), gptZeroRiskLevel: this.calculateGPTZeroRisk(burstiness, lexicalDiversity) }; } calculateGPTZeroRisk(burstiness, lexicalDiversity) { // Heuristique : GPTZero détecte uniformité faible + diversité faible const uniformityScore = Math.min(burstiness, 1) * 100; const diversityScore = lexicalDiversity * 100; const combinedScore = (uniformityScore + diversityScore) / 2; if (combinedScore > 70) return 'low'; if (combinedScore > 40) return 'medium'; return 'high'; } } /** * STRATÉGIE ANTI-ORIGINALITY * Focus: Diversité sémantique et originalité */ class OriginalityStrategy extends BaseDetectorStrategy { constructor() { super('Originality'); this.effectiveness = 0.85; this.targetMetrics = ['semantic_diversity', 'originality_score', 'vocabulary_range']; this.techniques = { semanticCreativity: { name: 'Créativité sémantique', rules: [ 'Métaphores inattendues : "cette plaque, c\'est le passeport de votre façade"', 'Comparaisons originales : évite clichés, invente analogies', 'Reformulations créatives : "résistant aux intempéries" → "qui brave les saisons"', 'Néologismes justifiés et expressifs' ] }, perspectiveShifting: { name: 'Changements de perspective', rules: [ 'Angles multiples sur même info : technique → esthétique → pratique', 'Points de vue variés : fabricant, utilisateur, installateur, voisin', 'Temporalités mélangées : présent, futur proche, retour d\'expérience', 'Niveaux d\'abstraction : détail précis puis vue d\'ensemble' ] }, linguisticInventiveness: { name: 'Inventivité linguistique', rules: [ 'Jeux de mots subtils et expressions détournées', 'Régionalismes et références culturelles précises', 'Vocabulaire technique humanisé avec créativité', 'Rythmes et sonorités travaillés : allitérations, assonances' ] } }; } generateInstructions(elementType, personality, csvData) { const instructions = []; instructions.push(`ANTI-ORIGINALITY - MAXIMUM CRÉATIVITÉ SÉMANTIQUE:`); // Créativité sémantique instructions.push(`\nCRÉATIVITÉ SÉMANTIQUE:`); this.techniques.semanticCreativity.rules.forEach(rule => { instructions.push(`• ${rule}`); }); // Changements de perspective instructions.push(`\nPERSPECTIVES MULTIPLES:`); this.techniques.perspectiveShifting.rules.forEach(rule => { instructions.push(`• ${rule}`); }); // Spécialisation par élément if (elementType === 'intro') { instructions.push(`\nINTROS ANTI-ORIGINALITY:`); instructions.push(`• Commence par angle totalement inattendu pour le sujet`); instructions.push(`• Évite intro-types, réinvente présentation du sujet`); instructions.push(`• Crée surprise puis retour naturel au cœur du sujet`); } else if (elementType.includes('faq')) { instructions.push(`\nFAQ ANTI-ORIGINALITY:`); instructions.push(`• Questions vraiment originales, pas standard secteur`); instructions.push(`• Réponses avec angles créatifs et exemples inédits`); } // Contexte métier créatif if (csvData && csvData.mc0) { instructions.push(`\nCRÉATIVITÉ CONTEXTUELLE ${csvData.mc0.toUpperCase()}:`); instructions.push(`• Réinvente façon de parler de ${csvData.mc0}`); instructions.push(`• Évite vocabulaire convenu du secteur, invente expressions`); instructions.push(`• Trouve analogies originales spécifiques à ${csvData.mc0}`); } // Inventivité linguistique instructions.push(`\nINVENTIVITÉ LINGUISTIQUE:`); this.techniques.linguisticInventiveness.rules.forEach(rule => { instructions.push(`• ${rule}`); }); return instructions.join('\n'); } /** * Instructions modulaires pour AdversarialCore */ getInstructions(intensity = 1.0) { const baseRules = [ 'Vocabulaire TRÈS varié : évite répétitions même de synonymes', 'Structures phrases délibérément irrégulières et asymétriques', 'Changements angles fréquents : technique → personnel → général', 'Créativité sémantique : métaphores, comparaisons inattendues' ]; const intensiveRules = [ 'Évite formulations académiques ou trop structurées', 'Intègre références culturelles, expressions régionales', 'Subvertis les attentes : commence par la fin, questionne l\'évidence', 'Réinvente façon de présenter informations basiques' ]; return intensity >= 1.0 ? [...baseRules, ...intensiveRules] : baseRules; } /** * Conseils d'amélioration pour enhancement method */ getEnhancementTips(intensity = 1.0) { return [ 'Trouve synonymes créatifs et expressions détournées', 'Ajoute métaphores subtiles et comparaisons originales', 'Varie angles d\'approche dans même contenu', 'Utilise vocabulaire technique humanisé', ...(intensity > 0.8 ? [ 'Insère références culturelles ou régionalismes', 'Crée néologismes justifiés et expressifs' ] : []) ]; } analyzeContent(content) { const baseMetrics = super.analyzeContent(content); // Analyse diversité sémantique const words = content.toLowerCase().split(/\s+/).filter(w => w.length > 3); const uniqueWords = [...new Set(words)]; const semanticDiversity = uniqueWords.length / words.length; // Détection créativité (heuristique) const creativityIndicators = [ 'comme', 'tel', 'sorte de', 'façon de', 'manière de', // métaphores '(', ')', '"', // originalité structure '?', '!', // variation tonale ]; const creativityCount = creativityIndicators.reduce((count, indicator) => { return count + (content.match(new RegExp(indicator.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length; }, 0); const creativityScore = Math.min(100, (creativityCount / words.length) * 1000); return { ...baseMetrics, semanticDiversity: Math.round(semanticDiversity * 100) / 100, creativityScore: Math.round(creativityScore), uniqueWordRatio: Math.round((uniqueWords.length / words.length) * 100), originalityRiskLevel: this.calculateOriginalityRisk(semanticDiversity, creativityScore) }; } calculateOriginalityRisk(semanticDiversity, creativityScore) { const diversityScore = semanticDiversity * 100; const combinedScore = (diversityScore + creativityScore) / 2; if (combinedScore > 60) return 'low'; if (combinedScore > 35) return 'medium'; return 'high'; } } /** * STRATÉGIE GÉNÉRALE * Équilibre entre toutes les techniques */ class GeneralStrategy extends BaseDetectorStrategy { constructor() { super('General'); this.effectiveness = 0.75; this.targetMetrics = ['overall_naturalness', 'human_likeness']; } generateInstructions(elementType, personality, csvData) { const instructions = []; instructions.push(`ANTI-DÉTECTION GÉNÉRALE - ÉQUILIBRE OPTIMAL:`); // Instructions générales équilibrées instructions.push(`\nNATUREL ET VARIÉ:`); instructions.push(`• Évite mots typiques IA : "optimal", "comprehensive", "seamless", "robust"`); instructions.push(`• Mélange phrases courtes (5-10 mots) et longues (15-25 mots) naturellement`); instructions.push(`• Utilise expressions idiomatiques françaises authentiques`); instructions.push(`• Intègre imperfections humaines : répétitions légères, hésitations`); instructions.push(`• Change angle ou ton 1-2 fois selon longueur du contenu`); // Adaptation personnalité équilibrée if (personality) { instructions.push(`\nSTYLE ${personality.nom} NATUREL:`); instructions.push(`• Applique style ${personality.style} de façon authentique, pas forcée`); instructions.push(`• Respecte vocabulaire naturel : ${personality.vocabulairePref || 'varié'}`); instructions.push(`• Utilise connecteurs variés : ${personality.connecteursPref || 'naturels'}`); } return instructions.join('\n'); } /** * Instructions modulaires pour AdversarialCore */ getInstructions(intensity = 1.0) { const baseRules = [ 'Évite ces mots typiques IA : "optimal", "comprehensive", "seamless", "robust", "leverage"', 'Varie longueurs phrases : mélange phrases courtes (5-10 mots) ET longues (20-30 mots)', 'Utilise expressions françaises familières et tournures idiomatiques', 'Ajoute imperfections naturelles : répétitions légères, hésitations, reformulations' ]; const intensiveRules = [ 'Change angle ou perspective 1-2 fois dans le texte', 'Préfère synonymes moins évidents : "bon" plutôt qu\'excellent", "solide" plutôt qu\'optimisé"', 'Intègre connecteurs variés et naturels selon le contexte', 'Simule variation naturelle d\'humeur et d\'énergie rédactionnelle' ]; return intensity >= 0.8 ? [...baseRules, ...intensiveRules] : baseRules; } /** * Conseils d'amélioration pour enhancement method */ getEnhancementTips(intensity = 1.0) { return [ 'Remplace mots typiques IA par synonymes plus naturels', 'Ajoute nuances et hésitations : "peut-être", "généralement", "souvent"', 'Varie connecteurs pour éviter répétitions mécaniques', 'Personnalise avec observations subjectives légères', ...(intensity > 0.7 ? [ 'Intègre "erreurs" humaines : corrections, précisions', 'Simule changement léger de ton ou d\'énergie' ] : []) ]; } analyzeContent(content) { const baseMetrics = super.analyzeContent(content); // Métrique naturalité générale const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 5); const avgWordsPerSentence = baseMetrics.wordCount / baseMetrics.sentenceCount; // Détection mots typiques IA const aiWords = ['optimal', 'comprehensive', 'seamless', 'robust', 'leverage']; const aiWordCount = aiWords.reduce((count, word) => { return count + (content.toLowerCase().match(new RegExp(`\\b${word}\\b`, 'g')) || []).length; }, 0); const aiWordDensity = aiWordCount / baseMetrics.wordCount * 100; const naturalness = Math.max(0, 100 - (aiWordDensity * 10) - Math.abs(avgWordsPerSentence - 15)); return { ...baseMetrics, avgWordsPerSentence: Math.round(avgWordsPerSentence), aiWordCount, aiWordDensity: Math.round(aiWordDensity * 100) / 100, naturalnessScore: Math.round(naturalness), generalRiskLevel: naturalness > 70 ? 'low' : naturalness > 40 ? 'medium' : 'high' }; } } /** * FACTORY POUR CRÉER STRATÉGIES */ class DetectorStrategyFactory { static strategies = { 'general': GeneralStrategy, 'gptZero': GPTZeroStrategy, 'originality': OriginalityStrategy }; static createStrategy(detectorName) { const StrategyClass = this.strategies[detectorName]; if (!StrategyClass) { logSh(`⚠️ Stratégie inconnue: ${detectorName}, fallback vers général`, 'WARNING'); return new GeneralStrategy(); } return new StrategyClass(); } static getSupportedDetectors() { return Object.keys(this.strategies).map(name => { const strategy = this.createStrategy(name); return { name, displayName: strategy.name, effectiveness: strategy.effectiveness, targetMetrics: strategy.targetMetrics }; }); } static analyzeContentAgainstAllDetectors(content) { const results = {}; Object.keys(this.strategies).forEach(detectorName => { const strategy = this.createStrategy(detectorName); results[detectorName] = strategy.analyzeEffectiveness(content); }); return results; } } /** * FONCTION UTILITAIRE - SÉLECTION STRATÉGIE OPTIMALE */ function selectOptimalStrategy(elementType, personality, previousResults = {}) { // Logique de sélection intelligente // Si résultats précédents disponibles, adapter if (previousResults.gptZero && previousResults.gptZero.effectiveness < 0.6) { return 'gptZero'; // Renforcer anti-GPTZero } if (previousResults.originality && previousResults.originality.effectiveness < 0.6) { return 'originality'; // Renforcer anti-Originality } // Sélection par type d'élément if (elementType === 'titre_h1' || elementType === 'titre_h2') { return 'gptZero'; // Titres bénéficient imprévisibilité } if (elementType === 'intro' || elementType === 'texte') { return 'originality'; // Corps bénéficie créativité sémantique } if (elementType.includes('faq')) { return 'general'; // FAQ équilibre naturalité } // Par personnalité if (personality) { if (personality.style === 'créatif' || personality.style === 'original') { return 'originality'; } if (personality.style === 'technique' || personality.style === 'expert') { return 'gptZero'; } } return 'general'; // Fallback } module.exports = { DetectorStrategyFactory, GPTZeroStrategy, OriginalityStrategy, GeneralStrategy, selectOptimalStrategy, BaseDetectorStrategy };