// ======================================== // PATTERN BREAKING - TECHNIQUE 2: LLM FINGERPRINT REMOVAL // Responsabilité: Remplacer mots/expressions typiques des LLMs // Anti-détection: Éviter vocabulaire détectable par les analyseurs IA // ======================================== const { logSh } = require('../ErrorReporting'); const { tracer } = require('../trace'); /** * DICTIONNAIRE ANTI-DÉTECTION * Mots/expressions LLM → Alternatives humaines naturelles */ const LLM_FINGERPRINTS = { // Mots techniques/corporate typiques IA 'optimal': ['idéal', 'parfait', 'adapté', 'approprié', 'convenable'], 'optimale': ['idéale', 'parfaite', 'adaptée', 'appropriée', 'convenable'], 'comprehensive': ['complet', 'détaillé', 'exhaustif', 'approfondi', 'global'], 'seamless': ['fluide', 'naturel', 'sans accroc', 'harmonieux', 'lisse'], 'robust': ['solide', 'fiable', 'résistant', 'costaud', 'stable'], 'robuste': ['solide', 'fiable', 'résistant', 'costaud', 'stable'], // Expressions trop formelles/IA 'il convient de noter': ['on remarque', 'il faut savoir', 'à noter', 'important'], 'il convient de': ['il faut', 'on doit', 'mieux vaut', 'il est bon de'], 'par conséquent': ['du coup', 'donc', 'résultat', 'ainsi'], 'néanmoins': ['cependant', 'mais', 'pourtant', 'malgré tout'], 'toutefois': ['cependant', 'mais', 'pourtant', 'quand même'], 'de surcroît': ['de plus', 'en plus', 'aussi', 'également'], // Superlatifs excessifs typiques IA 'extrêmement': ['très', 'super', 'vraiment', 'particulièrement'], 'particulièrement': ['très', 'vraiment', 'spécialement', 'surtout'], 'remarquablement': ['très', 'vraiment', 'sacrément', 'fichement'], 'exceptionnellement': ['très', 'vraiment', 'super', 'incroyablement'], // Mots de liaison trop mécaniques 'en définitive': ['au final', 'finalement', 'bref', 'en gros'], 'il s\'avère que': ['on voit que', 'il se trouve que', 'en fait'], 'force est de constater': ['on constate', 'on voit bien', 'c\'est clair'], // Expressions commerciales robotiques 'solution innovante': ['nouveauté', 'innovation', 'solution moderne', 'nouvelle approche'], 'approche holistique': ['approche globale', 'vision d\'ensemble', 'approche complète'], 'expérience utilisateur': ['confort d\'utilisation', 'facilité d\'usage', 'ergonomie'], 'retour sur investissement': ['rentabilité', 'bénéfices', 'profits'], // Adjectifs surutilisés par IA 'révolutionnaire': ['nouveau', 'moderne', 'innovant', 'original'], 'game-changer': ['nouveauté', 'innovation', 'changement', 'révolution'], 'cutting-edge': ['moderne', 'récent', 'nouveau', 'avancé'], 'state-of-the-art': ['moderne', 'récent', 'performant', 'haut de gamme'] }; /** * EXPRESSIONS CONTEXTUELLES SECTEUR SIGNALÉTIQUE * Adaptées au domaine métier pour plus de naturel */ const CONTEXTUAL_REPLACEMENTS = { 'solution': { 'signalétique': ['plaque', 'panneau', 'support', 'réalisation'], 'impression': ['tirage', 'print', 'production', 'fabrication'], 'default': ['option', 'possibilité', 'choix', 'alternative'] }, 'produit': { 'signalétique': ['plaque', 'panneau', 'enseigne', 'support'], 'default': ['article', 'réalisation', 'création'] }, 'service': { 'signalétique': ['prestation', 'réalisation', 'travail', 'création'], 'default': ['prestation', 'travail', 'aide'] } }; /** * MAIN ENTRY POINT - SUPPRESSION EMPREINTES LLM * @param {Object} input - { content: {}, config: {}, context: {} } * @returns {Object} - { content: {}, stats: {}, debug: {} } */ async function removeLLMFingerprints(input) { return await tracer.run('LLMFingerprintRemoval.removeLLMFingerprints()', async () => { const { content, config = {}, context = {} } = input; const { intensity = 1.0, // Probabilité de remplacement (100%) preserveKeywords = true, // Préserver mots-clés SEO contextualMode = true, // Mode contextuel métier csvData = null // Pour contexte métier } = config; await tracer.annotate({ technique: 'fingerprint_removal', intensity, elementsCount: Object.keys(content).length, contextualMode }); const startTime = Date.now(); logSh(`🔍 TECHNIQUE 2/3: Suppression empreintes LLM (intensité: ${intensity})`, 'INFO'); logSh(` 📊 ${Object.keys(content).length} éléments à nettoyer`, 'DEBUG'); try { const results = {}; let totalProcessed = 0; let totalReplacements = 0; let replacementDetails = []; // Préparer contexte métier const businessContext = extractBusinessContext(csvData); // Traiter chaque élément de contenu for (const [tag, text] of Object.entries(content)) { totalProcessed++; if (text.length < 20) { results[tag] = text; continue; } // Appliquer suppression des empreintes const cleaningResult = cleanTextFingerprints(text, { intensity, preserveKeywords, contextualMode, businessContext, tag }); results[tag] = cleaningResult.text; if (cleaningResult.replacements.length > 0) { totalReplacements += cleaningResult.replacements.length; replacementDetails.push({ tag, replacements: cleaningResult.replacements, fingerprintsFound: cleaningResult.fingerprintsDetected }); logSh(` 🧹 [${tag}]: ${cleaningResult.replacements.length} remplacements`, 'DEBUG'); } else { logSh(` ✅ [${tag}]: Aucune empreinte détectée`, 'DEBUG'); } } const duration = Date.now() - startTime; const stats = { processed: totalProcessed, totalReplacements, avgReplacementsPerElement: Math.round(totalReplacements / totalProcessed * 100) / 100, elementsWithFingerprints: replacementDetails.length, duration, technique: 'fingerprint_removal' }; logSh(`✅ NETTOYAGE EMPREINTES: ${stats.totalReplacements} remplacements sur ${stats.elementsWithFingerprints}/${stats.processed} éléments en ${duration}ms`, 'INFO'); await tracer.event('Fingerprint removal terminée', stats); return { content: results, stats, debug: { technique: 'fingerprint_removal', config: { intensity, preserveKeywords, contextualMode }, replacements: replacementDetails, businessContext } }; } catch (error) { const duration = Date.now() - startTime; logSh(`❌ NETTOYAGE EMPREINTES échoué après ${duration}ms: ${error.message}`, 'ERROR'); throw new Error(`LLMFingerprintRemoval failed: ${error.message}`); } }, input); } /** * Nettoyer les empreintes LLM d'un texte */ function cleanTextFingerprints(text, config) { const { intensity, preserveKeywords, contextualMode, businessContext, tag } = config; let cleanedText = text; const replacements = []; const fingerprintsDetected = []; // PHASE 1: Remplacements directs du dictionnaire for (const [fingerprint, alternatives] of Object.entries(LLM_FINGERPRINTS)) { const regex = new RegExp(`\\b${escapeRegex(fingerprint)}\\b`, 'gi'); const matches = text.match(regex); if (matches) { fingerprintsDetected.push(fingerprint); // Appliquer remplacement selon intensité if (Math.random() <= intensity) { const alternative = selectBestAlternative(alternatives, businessContext, contextualMode); cleanedText = cleanedText.replace(regex, (match) => { // Préserver la casse originale return preserveCase(match, alternative); }); replacements.push({ type: 'direct', original: fingerprint, replacement: alternative, occurrences: matches.length }); } } } // PHASE 2: Remplacements contextuels if (contextualMode && businessContext) { const contextualReplacements = applyContextualReplacements(cleanedText, businessContext); cleanedText = contextualReplacements.text; replacements.push(...contextualReplacements.replacements); } // PHASE 3: Détection patterns récurrents const patternReplacements = replaceRecurringPatterns(cleanedText, intensity); cleanedText = patternReplacements.text; replacements.push(...patternReplacements.replacements); return { text: cleanedText, replacements, fingerprintsDetected }; } /** * Sélectionner la meilleure alternative selon le contexte */ function selectBestAlternative(alternatives, businessContext, contextualMode) { if (!contextualMode || !businessContext) { // Mode aléatoire simple return alternatives[Math.floor(Math.random() * alternatives.length)]; } // Mode contextuel : privilégier alternatives adaptées au métier const contextualAlternatives = alternatives.filter(alt => isContextuallyAppropriate(alt, businessContext) ); const finalAlternatives = contextualAlternatives.length > 0 ? contextualAlternatives : alternatives; return finalAlternatives[Math.floor(Math.random() * finalAlternatives.length)]; } /** * Vérifier si une alternative est contextuelle appropriée */ function isContextuallyAppropriate(alternative, businessContext) { const { sector, vocabulary } = businessContext; // Signalétique : privilégier vocabulaire technique/artisanal if (sector === 'signalétique') { const technicalWords = ['solide', 'fiable', 'costaud', 'résistant', 'adapté']; return technicalWords.includes(alternative); } return true; // Par défaut accepter } /** * Appliquer remplacements contextuels */ function applyContextualReplacements(text, businessContext) { let processedText = text; const replacements = []; for (const [word, contexts] of Object.entries(CONTEXTUAL_REPLACEMENTS)) { const regex = new RegExp(`\\b${word}\\b`, 'gi'); const matches = processedText.match(regex); if (matches) { const contextAlternatives = contexts[businessContext.sector] || contexts.default; const replacement = contextAlternatives[Math.floor(Math.random() * contextAlternatives.length)]; processedText = processedText.replace(regex, (match) => { return preserveCase(match, replacement); }); replacements.push({ type: 'contextual', original: word, replacement, occurrences: matches.length, context: businessContext.sector }); } } return { text: processedText, replacements }; } /** * Remplacer patterns récurrents */ function replaceRecurringPatterns(text, intensity) { let processedText = text; const replacements = []; // Pattern 1: "très + adjectif" → variantes const veryPattern = /\btrès\s+(\w+)/gi; const veryMatches = [...text.matchAll(veryPattern)]; if (veryMatches.length > 2 && Math.random() < intensity) { // Remplacer certains "très" par des alternatives const alternatives = ['super', 'vraiment', 'particulièrement', 'assez']; veryMatches.slice(1).forEach((match, index) => { if (Math.random() < 0.5) { const alternative = alternatives[Math.floor(Math.random() * alternatives.length)]; const fullMatch = match[0]; const adjective = match[1]; const replacement = `${alternative} ${adjective}`; processedText = processedText.replace(fullMatch, replacement); replacements.push({ type: 'pattern', pattern: '"très + adjectif"', original: fullMatch, replacement }); } }); } return { text: processedText, replacements }; } /** * Extraire contexte métier des données CSV */ function extractBusinessContext(csvData) { if (!csvData) { return { sector: 'general', vocabulary: [] }; } const mc0 = csvData.mc0?.toLowerCase() || ''; // Détection secteur let sector = 'general'; if (mc0.includes('plaque') || mc0.includes('panneau') || mc0.includes('enseigne')) { sector = 'signalétique'; } else if (mc0.includes('impression') || mc0.includes('print')) { sector = 'impression'; } // Extraction vocabulaire clé const vocabulary = [csvData.mc0, csvData.t0, csvData.tMinus1].filter(Boolean); return { sector, vocabulary }; } /** * Préserver la casse originale */ function preserveCase(original, replacement) { if (original === original.toUpperCase()) { return replacement.toUpperCase(); } else if (original[0] === original[0].toUpperCase()) { return replacement.charAt(0).toUpperCase() + replacement.slice(1).toLowerCase(); } else { return replacement.toLowerCase(); } } /** * Échapper caractères regex */ function escapeRegex(text) { return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } /** * Analyser les empreintes LLM dans un texte */ function analyzeLLMFingerprints(text) { const detectedFingerprints = []; let totalMatches = 0; for (const fingerprint of Object.keys(LLM_FINGERPRINTS)) { const regex = new RegExp(`\\b${escapeRegex(fingerprint)}\\b`, 'gi'); const matches = text.match(regex); if (matches) { detectedFingerprints.push({ fingerprint, occurrences: matches.length, category: categorizefingerprint(fingerprint) }); totalMatches += matches.length; } } return { hasFingerprints: detectedFingerprints.length > 0, fingerprints: detectedFingerprints, totalMatches, riskLevel: calculateRiskLevel(detectedFingerprints, text.length) }; } /** * Catégoriser une empreinte LLM */ function categorizefingerprint(fingerprint) { const categories = { 'technical': ['optimal', 'comprehensive', 'robust', 'seamless'], 'formal': ['il convient de', 'néanmoins', 'par conséquent'], 'superlative': ['extrêmement', 'particulièrement', 'remarquablement'], 'commercial': ['solution innovante', 'game-changer', 'révolutionnaire'] }; for (const [category, words] of Object.entries(categories)) { if (words.some(word => fingerprint.includes(word))) { return category; } } return 'other'; } /** * Calculer niveau de risque de détection */ function calculateRiskLevel(fingerprints, textLength) { if (fingerprints.length === 0) return 'low'; const fingerprintDensity = fingerprints.reduce((sum, fp) => sum + fp.occurrences, 0) / (textLength / 100); if (fingerprintDensity > 3) return 'high'; if (fingerprintDensity > 1.5) return 'medium'; return 'low'; } module.exports = { removeLLMFingerprints, // ← MAIN ENTRY POINT cleanTextFingerprints, analyzeLLMFingerprints, LLM_FINGERPRINTS, CONTEXTUAL_REPLACEMENTS, extractBusinessContext };