408 lines
14 KiB
JavaScript
408 lines
14 KiB
JavaScript
// ========================================
|
|
// ADVERSARIAL PROMPT ENGINE - NIVEAU 3
|
|
// Responsabilité: Générer instructions anti-détection pour prompts
|
|
// Anti-détection: Prévention par modification des prompts LLM
|
|
// ========================================
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
const { tracer } = require('../trace');
|
|
|
|
/**
|
|
* INSTRUCTIONS ADVERSARIALES PAR DÉTECTEUR
|
|
* Stratégies spécialisées pour contrer chaque détecteur IA
|
|
*/
|
|
const ADVERSARIAL_INSTRUCTIONS = {
|
|
general: {
|
|
title: 'CONTRAINTES ANTI-DÉTECTION GÉNÉRALES',
|
|
rules: [
|
|
'É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',
|
|
'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'
|
|
],
|
|
intensity: 1.0,
|
|
weight: 0.8
|
|
},
|
|
|
|
gptZero: {
|
|
title: 'SPÉCIAL ANTI-GPTZERO',
|
|
rules: [
|
|
'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',
|
|
'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'
|
|
],
|
|
intensity: 1.2,
|
|
weight: 0.9,
|
|
targetMetric: 'unpredictability'
|
|
},
|
|
|
|
originality: {
|
|
title: 'SPÉCIAL ANTI-ORIGINALITY',
|
|
rules: [
|
|
'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',
|
|
'É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'
|
|
],
|
|
intensity: 1.3,
|
|
weight: 0.95,
|
|
targetMetric: 'semantic_diversity'
|
|
},
|
|
|
|
copyLeaks: {
|
|
title: 'SPÉCIAL ANTI-COPYLEAKS',
|
|
rules: [
|
|
'Reformule idées communes avec angles totalement originaux',
|
|
'Évite phrases-types et formulations standard du secteur',
|
|
'Personnalise chaque assertion avec exemples spécifiques',
|
|
'Réinvente la façon de présenter informations basiques',
|
|
'Utilise analogies et métaphores plutôt que descriptions directes',
|
|
'Fragmente informations techniques en observations pratiques',
|
|
'Transforme données factuelles en récits ou témoignages'
|
|
],
|
|
intensity: 1.1,
|
|
weight: 0.85,
|
|
targetMetric: 'originality_score'
|
|
},
|
|
|
|
winston: {
|
|
title: 'SPÉCIAL ANTI-WINSTON',
|
|
rules: [
|
|
'Évite cohérence stylistique trop parfaite entre paragraphes',
|
|
'Simule variation naturelle d\'humeur et d\'énergie rédactionnelle',
|
|
'Intègre "erreurs" humaines : répétitions, corrections, précisions',
|
|
'Varie niveau de détail : parfois précis, parfois elliptique',
|
|
'Alterne registres émotionnels : enthousiaste → neutre → critique',
|
|
'Inclus hésitations et nuances : "peut-être", "généralement", "souvent"',
|
|
'Personnalise avec opinions subjectives et préférences'
|
|
],
|
|
intensity: 1.0,
|
|
weight: 0.9,
|
|
targetMetric: 'human_variation'
|
|
}
|
|
};
|
|
|
|
/**
|
|
* INSTRUCTIONS PERSONNALISÉES PAR TYPE D'ÉLÉMENT
|
|
*/
|
|
const ELEMENT_SPECIFIC_INSTRUCTIONS = {
|
|
titre_h1: {
|
|
base: 'Crée un titre percutant mais naturel',
|
|
adversarial: 'Évite formules marketing lisses, préfère authentique et direct'
|
|
},
|
|
titre_h2: {
|
|
base: 'Génère un sous-titre informatif',
|
|
adversarial: 'Varie structure : question, affirmation, exclamation selon contexte'
|
|
},
|
|
intro: {
|
|
base: 'Rédige introduction engageante',
|
|
adversarial: 'Commence par angle inattendu : anecdote, constat, question rhétorique'
|
|
},
|
|
texte: {
|
|
base: 'Développe paragraphe informatif',
|
|
adversarial: 'Mélange informations factuelles et observations personnelles'
|
|
},
|
|
faq_question: {
|
|
base: 'Formule question client naturelle',
|
|
adversarial: 'Utilise formulations vraiment utilisées par clients, pas académiques'
|
|
},
|
|
faq_reponse: {
|
|
base: 'Réponds de façon experte et rassurante',
|
|
adversarial: 'Ajoute nuances, "ça dépend", précisions contextuelles comme humain'
|
|
}
|
|
};
|
|
|
|
/**
|
|
* MAIN ENTRY POINT - GÉNÉRATEUR DE PROMPTS ADVERSARIAUX
|
|
* @param {string} basePrompt - Prompt de base
|
|
* @param {Object} config - Configuration adversariale
|
|
* @returns {string} - Prompt enrichi d'instructions anti-détection
|
|
*/
|
|
function createAdversarialPrompt(basePrompt, config = {}) {
|
|
return tracer.run('AdversarialPromptEngine.createAdversarialPrompt()', () => {
|
|
const {
|
|
detectorTarget = 'general',
|
|
intensity = 1.0,
|
|
elementType = 'generic',
|
|
personality = null,
|
|
contextualMode = true,
|
|
csvData = null,
|
|
debugMode = false
|
|
} = config;
|
|
|
|
tracer.annotate({
|
|
detectorTarget,
|
|
intensity,
|
|
elementType,
|
|
personalityStyle: personality?.style
|
|
});
|
|
|
|
try {
|
|
// 1. Sélectionner stratégie détecteur
|
|
const strategy = ADVERSARIAL_INSTRUCTIONS[detectorTarget] || ADVERSARIAL_INSTRUCTIONS.general;
|
|
|
|
// 2. Adapter intensité
|
|
const effectiveIntensity = intensity * (strategy.intensity || 1.0);
|
|
const shouldApplyStrategy = Math.random() < (strategy.weight || 0.8);
|
|
|
|
if (!shouldApplyStrategy && detectorTarget !== 'general') {
|
|
// Fallback sur stratégie générale
|
|
return createAdversarialPrompt(basePrompt, { ...config, detectorTarget: 'general' });
|
|
}
|
|
|
|
// 3. Construire instructions adversariales
|
|
const adversarialSection = buildAdversarialInstructions(strategy, {
|
|
elementType,
|
|
personality,
|
|
effectiveIntensity,
|
|
contextualMode,
|
|
csvData
|
|
});
|
|
|
|
// 4. Assembler prompt final
|
|
const enhancedPrompt = assembleEnhancedPrompt(basePrompt, adversarialSection, {
|
|
strategy,
|
|
elementType,
|
|
debugMode
|
|
});
|
|
|
|
if (debugMode) {
|
|
logSh(`🎯 Prompt adversarial généré: ${detectorTarget} (intensité: ${effectiveIntensity.toFixed(2)})`, 'DEBUG');
|
|
logSh(` Instructions: ${strategy.rules.length} règles appliquées`, 'DEBUG');
|
|
}
|
|
|
|
tracer.event('Prompt adversarial créé', {
|
|
detectorTarget,
|
|
rulesCount: strategy.rules.length,
|
|
promptLength: enhancedPrompt.length
|
|
});
|
|
|
|
return enhancedPrompt;
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Erreur génération prompt adversarial: ${error.message}`, 'ERROR');
|
|
// Fallback: retourner prompt original
|
|
return basePrompt;
|
|
}
|
|
}, config);
|
|
}
|
|
|
|
/**
|
|
* Construire section instructions adversariales
|
|
*/
|
|
function buildAdversarialInstructions(strategy, config) {
|
|
const { elementType, personality, effectiveIntensity, contextualMode, csvData } = config;
|
|
|
|
let instructions = `\n\n=== ${strategy.title} ===\n`;
|
|
|
|
// Règles de base de la stratégie
|
|
const activeRules = selectActiveRules(strategy.rules, effectiveIntensity);
|
|
activeRules.forEach(rule => {
|
|
instructions += `• ${rule}\n`;
|
|
});
|
|
|
|
// Instructions spécifiques au type d'élément
|
|
if (ELEMENT_SPECIFIC_INSTRUCTIONS[elementType]) {
|
|
const elementInstructions = ELEMENT_SPECIFIC_INSTRUCTIONS[elementType];
|
|
instructions += `\nSPÉCIFIQUE ${elementType.toUpperCase()}:\n`;
|
|
instructions += `• ${elementInstructions.adversarial}\n`;
|
|
}
|
|
|
|
// Adaptations personnalité
|
|
if (personality && contextualMode) {
|
|
const personalityAdaptations = generatePersonalityAdaptations(personality, strategy);
|
|
if (personalityAdaptations) {
|
|
instructions += `\nADAPTATION PERSONNALITÉ ${personality.nom.toUpperCase()}:\n`;
|
|
instructions += personalityAdaptations;
|
|
}
|
|
}
|
|
|
|
// Contexte métier si disponible
|
|
if (csvData && contextualMode) {
|
|
const contextualInstructions = generateContextualInstructions(csvData, strategy);
|
|
if (contextualInstructions) {
|
|
instructions += `\nCONTEXTE MÉTIER:\n`;
|
|
instructions += contextualInstructions;
|
|
}
|
|
}
|
|
|
|
instructions += `\nIMPORTANT: Ces contraintes doivent sembler naturelles, pas forcées.\n`;
|
|
|
|
return instructions;
|
|
}
|
|
|
|
/**
|
|
* Sélectionner règles actives selon intensité
|
|
*/
|
|
function selectActiveRules(allRules, intensity) {
|
|
if (intensity >= 1.0) {
|
|
return allRules; // Toutes les règles
|
|
}
|
|
|
|
// Sélection proportionnelle à l'intensité
|
|
const ruleCount = Math.ceil(allRules.length * intensity);
|
|
return allRules.slice(0, ruleCount);
|
|
}
|
|
|
|
/**
|
|
* Générer adaptations personnalité
|
|
*/
|
|
function generatePersonalityAdaptations(personality, strategy) {
|
|
if (!personality) return null;
|
|
|
|
const adaptations = [];
|
|
|
|
// Style de la personnalité
|
|
if (personality.style) {
|
|
adaptations.push(`• Respecte le style ${personality.style} de ${personality.nom} tout en appliquant les contraintes`);
|
|
}
|
|
|
|
// Vocabulaire préféré
|
|
if (personality.vocabulairePref) {
|
|
adaptations.push(`• Intègre vocabulaire naturel: ${personality.vocabulairePref}`);
|
|
}
|
|
|
|
// Connecteurs préférés
|
|
if (personality.connecteursPref) {
|
|
adaptations.push(`• Utilise connecteurs variés: ${personality.connecteursPref}`);
|
|
}
|
|
|
|
// Longueur phrases selon personnalité
|
|
if (personality.longueurPhrases) {
|
|
adaptations.push(`• Longueur phrases: ${personality.longueurPhrases} mais avec variation anti-détection`);
|
|
}
|
|
|
|
return adaptations.length > 0 ? adaptations.join('\n') + '\n' : null;
|
|
}
|
|
|
|
/**
|
|
* Générer instructions contextuelles métier
|
|
*/
|
|
function generateContextualInstructions(csvData, strategy) {
|
|
if (!csvData.mc0) return null;
|
|
|
|
const instructions = [];
|
|
|
|
// Contexte sujet
|
|
instructions.push(`• Sujet: ${csvData.mc0} - utilise terminologie naturelle du domaine`);
|
|
|
|
// Éviter jargon selon détecteur
|
|
if (strategy.targetMetric === 'unpredictability') {
|
|
instructions.push(`• Évite jargon technique trop prévisible, privilégie explications accessibles`);
|
|
} else if (strategy.targetMetric === 'semantic_diversity') {
|
|
instructions.push(`• Varie façons de nommer/décrire ${csvData.mc0} - synonymes créatifs`);
|
|
}
|
|
|
|
return instructions.join('\n') + '\n';
|
|
}
|
|
|
|
/**
|
|
* Assembler prompt final
|
|
*/
|
|
function assembleEnhancedPrompt(basePrompt, adversarialSection, config) {
|
|
const { strategy, elementType, debugMode } = config;
|
|
|
|
// Structure du prompt amélioré
|
|
let enhancedPrompt = basePrompt;
|
|
|
|
// Injecter instructions adversariales
|
|
enhancedPrompt += adversarialSection;
|
|
|
|
// Rappel final selon stratégie
|
|
if (strategy.targetMetric) {
|
|
enhancedPrompt += `\nOBJECTIF PRIORITAIRE: Maximiser ${strategy.targetMetric} tout en conservant qualité.\n`;
|
|
}
|
|
|
|
// Instructions de réponse
|
|
enhancedPrompt += `\nRÉPONDS DIRECTEMENT par le contenu demandé, en appliquant naturellement ces contraintes.`;
|
|
|
|
return enhancedPrompt;
|
|
}
|
|
|
|
/**
|
|
* Analyser efficacité d'un prompt adversarial
|
|
*/
|
|
function analyzePromptEffectiveness(originalPrompt, adversarialPrompt, generatedContent) {
|
|
const analysis = {
|
|
promptEnhancement: {
|
|
originalLength: originalPrompt.length,
|
|
adversarialLength: adversarialPrompt.length,
|
|
enhancementRatio: adversarialPrompt.length / originalPrompt.length,
|
|
instructionsAdded: (adversarialPrompt.match(/•/g) || []).length
|
|
},
|
|
contentMetrics: analyzeGeneratedContent(generatedContent),
|
|
effectiveness: 0
|
|
};
|
|
|
|
// Score d'efficacité simple
|
|
analysis.effectiveness = Math.min(100,
|
|
(analysis.promptEnhancement.enhancementRatio - 1) * 50 +
|
|
analysis.contentMetrics.diversityScore
|
|
);
|
|
|
|
return analysis;
|
|
}
|
|
|
|
/**
|
|
* Analyser contenu généré
|
|
*/
|
|
function analyzeGeneratedContent(content) {
|
|
if (!content || typeof content !== 'string') {
|
|
return { diversityScore: 0, wordCount: 0, sentenceVariation: 0 };
|
|
}
|
|
|
|
const words = content.split(/\s+/).filter(w => w.length > 2);
|
|
const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 5);
|
|
|
|
// Diversité vocabulaire
|
|
const uniqueWords = [...new Set(words.map(w => w.toLowerCase()))];
|
|
const diversityScore = uniqueWords.length / Math.max(1, words.length) * 100;
|
|
|
|
// Variation longueurs phrases
|
|
const sentenceLengths = sentences.map(s => s.split(/\s+/).length);
|
|
const avgLength = sentenceLengths.reduce((a, b) => a + b, 0) / Math.max(1, sentenceLengths.length);
|
|
const variance = sentenceLengths.reduce((acc, len) => acc + Math.pow(len - avgLength, 2), 0) / Math.max(1, sentenceLengths.length);
|
|
const sentenceVariation = Math.sqrt(variance) / Math.max(1, avgLength) * 100;
|
|
|
|
return {
|
|
diversityScore: Math.round(diversityScore),
|
|
wordCount: words.length,
|
|
sentenceCount: sentences.length,
|
|
sentenceVariation: Math.round(sentenceVariation),
|
|
avgSentenceLength: Math.round(avgLength)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Obtenir liste des détecteurs supportés
|
|
*/
|
|
function getSupportedDetectors() {
|
|
return Object.keys(ADVERSARIAL_INSTRUCTIONS).map(key => ({
|
|
id: key,
|
|
name: ADVERSARIAL_INSTRUCTIONS[key].title,
|
|
intensity: ADVERSARIAL_INSTRUCTIONS[key].intensity,
|
|
weight: ADVERSARIAL_INSTRUCTIONS[key].weight,
|
|
rulesCount: ADVERSARIAL_INSTRUCTIONS[key].rules.length,
|
|
targetMetric: ADVERSARIAL_INSTRUCTIONS[key].targetMetric || 'general'
|
|
}));
|
|
}
|
|
|
|
module.exports = {
|
|
createAdversarialPrompt, // ← MAIN ENTRY POINT
|
|
buildAdversarialInstructions,
|
|
analyzePromptEffectiveness,
|
|
analyzeGeneratedContent,
|
|
getSupportedDetectors,
|
|
ADVERSARIAL_INSTRUCTIONS,
|
|
ELEMENT_SPECIFIC_INSTRUCTIONS
|
|
}; |