seogeneratorserver/lib/adversarial-generation/AdversarialPromptEngine.js
StillHammer dbf1a3de8c Add technical plan for multi-format export system
Added plan.md with complete architecture for format-agnostic content generation:
- Support for Markdown, HTML, Plain Text, JSON formats
- New FormatExporter module with neutral data structure
- Integration strategy with existing ContentAssembly and ArticleStorage
- Bonus features: SEO metadata generation, readability scoring, WordPress Gutenberg format
- Implementation roadmap with 4 phases (6h total estimated)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 16:14:29 +08:00

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
};