488 lines
14 KiB
JavaScript
488 lines
14 KiB
JavaScript
// ========================================
|
|
// ADVERSARIAL CORE - MOTEUR MODULAIRE
|
|
// Responsabilité: Moteur adversarial réutilisable sur tout contenu
|
|
// Architecture: Couches applicables à la demande
|
|
// ========================================
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
const { tracer } = require('../trace');
|
|
const { callLLM } = require('../LLMManager');
|
|
|
|
// Import stratégies et utilitaires
|
|
const { DetectorStrategyFactory, selectOptimalStrategy } = require('./DetectorStrategies');
|
|
|
|
/**
|
|
* MAIN ENTRY POINT - APPLICATION COUCHE ADVERSARIALE
|
|
* Input: contenu existant + configuration adversariale
|
|
* Output: contenu avec couche adversariale appliquée
|
|
*/
|
|
async function applyAdversarialLayer(existingContent, config = {}) {
|
|
return await tracer.run('AdversarialCore.applyAdversarialLayer()', async () => {
|
|
const {
|
|
detectorTarget = 'general',
|
|
intensity = 1.0,
|
|
method = 'regeneration', // 'regeneration' | 'enhancement' | 'hybrid'
|
|
preserveStructure = true,
|
|
csvData = null,
|
|
context = {}
|
|
} = config;
|
|
|
|
await tracer.annotate({
|
|
adversarialLayer: true,
|
|
detectorTarget,
|
|
intensity,
|
|
method,
|
|
elementsCount: Object.keys(existingContent).length
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
logSh(`🎯 APPLICATION COUCHE ADVERSARIALE: ${detectorTarget} (${method})`, 'INFO');
|
|
logSh(` 📊 ${Object.keys(existingContent).length} éléments | Intensité: ${intensity}`, 'INFO');
|
|
|
|
try {
|
|
// Initialiser stratégie détecteur
|
|
const strategy = DetectorStrategyFactory.createStrategy(detectorTarget);
|
|
|
|
// Appliquer méthode adversariale choisie
|
|
let adversarialContent = {};
|
|
|
|
switch (method) {
|
|
case 'regeneration':
|
|
adversarialContent = await applyRegenerationMethod(existingContent, config, strategy);
|
|
break;
|
|
case 'enhancement':
|
|
adversarialContent = await applyEnhancementMethod(existingContent, config, strategy);
|
|
break;
|
|
case 'hybrid':
|
|
adversarialContent = await applyHybridMethod(existingContent, config, strategy);
|
|
break;
|
|
default:
|
|
throw new Error(`Méthode adversariale inconnue: ${method}`);
|
|
}
|
|
|
|
const duration = Date.now() - startTime;
|
|
const stats = {
|
|
elementsProcessed: Object.keys(existingContent).length,
|
|
elementsModified: countModifiedElements(existingContent, adversarialContent),
|
|
detectorTarget,
|
|
intensity,
|
|
method,
|
|
duration
|
|
};
|
|
|
|
logSh(`✅ COUCHE ADVERSARIALE APPLIQUÉE: ${stats.elementsModified}/${stats.elementsProcessed} modifiés (${duration}ms)`, 'INFO');
|
|
|
|
await tracer.event('Couche adversariale appliquée', stats);
|
|
|
|
return {
|
|
content: adversarialContent,
|
|
stats,
|
|
original: existingContent,
|
|
config
|
|
};
|
|
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
logSh(`❌ COUCHE ADVERSARIALE ÉCHOUÉE après ${duration}ms: ${error.message}`, 'ERROR');
|
|
|
|
// Fallback: retourner contenu original
|
|
logSh(`🔄 Fallback: contenu original conservé`, 'WARNING');
|
|
return {
|
|
content: existingContent,
|
|
stats: { fallback: true, duration },
|
|
original: existingContent,
|
|
config,
|
|
error: error.message
|
|
};
|
|
}
|
|
}, { existingContent: Object.keys(existingContent), config });
|
|
}
|
|
|
|
/**
|
|
* MÉTHODE RÉGÉNÉRATION - Réécrire complètement avec prompts adversariaux
|
|
*/
|
|
async function applyRegenerationMethod(existingContent, config, strategy) {
|
|
logSh(`🔄 Méthode régénération adversariale`, 'DEBUG');
|
|
|
|
const results = {};
|
|
const contentEntries = Object.entries(existingContent);
|
|
|
|
// Traiter en chunks pour éviter timeouts
|
|
const chunks = chunkArray(contentEntries, 4);
|
|
|
|
for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
|
|
const chunk = chunks[chunkIndex];
|
|
logSh(` 📦 Régénération chunk ${chunkIndex + 1}/${chunks.length}: ${chunk.length} éléments`, 'DEBUG');
|
|
|
|
try {
|
|
const regenerationPrompt = createRegenerationPrompt(chunk, config, strategy);
|
|
|
|
const response = await callLLM('claude', regenerationPrompt, {
|
|
temperature: 0.7 + (config.intensity * 0.2), // Température variable selon intensité
|
|
maxTokens: 2000 * chunk.length
|
|
}, config.csvData?.personality);
|
|
|
|
const chunkResults = parseRegenerationResponse(response, chunk);
|
|
Object.assign(results, chunkResults);
|
|
|
|
logSh(` ✅ Chunk ${chunkIndex + 1}: ${Object.keys(chunkResults).length} éléments régénérés`, 'DEBUG');
|
|
|
|
// Délai entre chunks
|
|
if (chunkIndex < chunks.length - 1) {
|
|
await sleep(1500);
|
|
}
|
|
|
|
} catch (error) {
|
|
logSh(` ❌ Chunk ${chunkIndex + 1} échoué: ${error.message}`, 'ERROR');
|
|
|
|
// Fallback: garder contenu original pour ce chunk
|
|
chunk.forEach(([tag, content]) => {
|
|
results[tag] = content;
|
|
});
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* MÉTHODE ENHANCEMENT - Améliorer sans réécrire complètement
|
|
*/
|
|
async function applyEnhancementMethod(existingContent, config, strategy) {
|
|
logSh(`🔧 Méthode enhancement adversarial`, 'DEBUG');
|
|
|
|
const results = { ...existingContent }; // Base: contenu original
|
|
const elementsToEnhance = selectElementsForEnhancement(existingContent, config);
|
|
|
|
if (elementsToEnhance.length === 0) {
|
|
logSh(` ⏭️ Aucun élément nécessite enhancement`, 'DEBUG');
|
|
return results;
|
|
}
|
|
|
|
logSh(` 📋 ${elementsToEnhance.length} éléments sélectionnés pour enhancement`, 'DEBUG');
|
|
|
|
const enhancementPrompt = createEnhancementPrompt(elementsToEnhance, config, strategy);
|
|
|
|
try {
|
|
const response = await callLLM('gpt4', enhancementPrompt, {
|
|
temperature: 0.5 + (config.intensity * 0.3),
|
|
maxTokens: 3000
|
|
}, config.csvData?.personality);
|
|
|
|
const enhancedResults = parseEnhancementResponse(response, elementsToEnhance);
|
|
|
|
// Appliquer améliorations
|
|
Object.keys(enhancedResults).forEach(tag => {
|
|
if (enhancedResults[tag] !== existingContent[tag]) {
|
|
results[tag] = enhancedResults[tag];
|
|
}
|
|
});
|
|
|
|
return results;
|
|
|
|
} catch (error) {
|
|
logSh(`❌ Enhancement échoué: ${error.message}`, 'ERROR');
|
|
return results; // Fallback: contenu original
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MÉTHODE HYBRIDE - Combinaison régénération + enhancement
|
|
*/
|
|
async function applyHybridMethod(existingContent, config, strategy) {
|
|
logSh(`⚡ Méthode hybride adversariale`, 'DEBUG');
|
|
|
|
// 1. Enhancement léger sur tout le contenu
|
|
const enhancedContent = await applyEnhancementMethod(existingContent, {
|
|
...config,
|
|
intensity: config.intensity * 0.6 // Intensité réduite pour enhancement
|
|
}, strategy);
|
|
|
|
// 2. Régénération ciblée sur éléments clés
|
|
const keyElements = selectKeyElementsForRegeneration(enhancedContent, config);
|
|
|
|
if (keyElements.length === 0) {
|
|
return enhancedContent;
|
|
}
|
|
|
|
const keyElementsContent = {};
|
|
keyElements.forEach(tag => {
|
|
keyElementsContent[tag] = enhancedContent[tag];
|
|
});
|
|
|
|
const regeneratedElements = await applyRegenerationMethod(keyElementsContent, {
|
|
...config,
|
|
intensity: config.intensity * 1.2 // Intensité augmentée pour régénération
|
|
}, strategy);
|
|
|
|
// 3. Merger résultats
|
|
const hybridContent = { ...enhancedContent };
|
|
Object.keys(regeneratedElements).forEach(tag => {
|
|
hybridContent[tag] = regeneratedElements[tag];
|
|
});
|
|
|
|
return hybridContent;
|
|
}
|
|
|
|
// ============= HELPER FUNCTIONS =============
|
|
|
|
/**
|
|
* Créer prompt de régénération adversariale
|
|
*/
|
|
function createRegenerationPrompt(chunk, config, strategy) {
|
|
const { detectorTarget, intensity, csvData } = config;
|
|
|
|
let prompt = `MISSION: Réécris ces contenus pour éviter détection par ${detectorTarget}.
|
|
|
|
TECHNIQUE ANTI-${detectorTarget.toUpperCase()}:
|
|
${strategy.getInstructions(intensity).join('\n')}
|
|
|
|
CONTENUS À RÉÉCRIRE:
|
|
|
|
${chunk.map(([tag, content], i) => `[${i + 1}] TAG: ${tag}
|
|
ORIGINAL: "${content}"`).join('\n\n')}
|
|
|
|
CONSIGNES:
|
|
- GARDE exactement le même message et informations factuelles
|
|
- CHANGE structure, vocabulaire, style pour éviter détection ${detectorTarget}
|
|
- Intensité adversariale: ${intensity.toFixed(2)}
|
|
${csvData?.personality ? `- Style: ${csvData.personality.nom} (${csvData.personality.style})` : ''}
|
|
|
|
IMPORTANT: Réponse DIRECTE par les contenus réécrits, pas d'explication.
|
|
|
|
FORMAT:
|
|
[1] Contenu réécrit anti-${detectorTarget}
|
|
[2] Contenu réécrit anti-${detectorTarget}
|
|
etc...`;
|
|
|
|
return prompt;
|
|
}
|
|
|
|
/**
|
|
* Créer prompt d'enhancement adversarial
|
|
*/
|
|
function createEnhancementPrompt(elementsToEnhance, config, strategy) {
|
|
const { detectorTarget, intensity } = config;
|
|
|
|
let prompt = `MISSION: Améliore subtilement ces contenus pour réduire détection ${detectorTarget}.
|
|
|
|
AMÉLIORATIONS CIBLÉES:
|
|
${strategy.getEnhancementTips(intensity).join('\n')}
|
|
|
|
ÉLÉMENTS À AMÉLIORER:
|
|
|
|
${elementsToEnhance.map((element, i) => `[${i + 1}] TAG: ${element.tag}
|
|
CONTENU: "${element.content}"
|
|
PROBLÈME: ${element.detectionRisk}`).join('\n\n')}
|
|
|
|
CONSIGNES:
|
|
- Modifications LÉGÈRES et naturelles
|
|
- GARDE le fond du message intact
|
|
- Focus sur réduction détection ${detectorTarget}
|
|
- Intensité: ${intensity.toFixed(2)}
|
|
|
|
FORMAT:
|
|
[1] Contenu légèrement amélioré
|
|
[2] Contenu légèrement amélioré
|
|
etc...`;
|
|
|
|
return prompt;
|
|
}
|
|
|
|
/**
|
|
* Parser réponse régénération
|
|
*/
|
|
function parseRegenerationResponse(response, chunk) {
|
|
const results = {};
|
|
const regex = /\[(\d+)\]\s*([^[]*?)(?=\n\[\d+\]|$)/gs;
|
|
let match;
|
|
const parsedItems = {};
|
|
|
|
while ((match = regex.exec(response)) !== null) {
|
|
const index = parseInt(match[1]) - 1;
|
|
const content = cleanAdversarialContent(match[2].trim());
|
|
if (index >= 0 && index < chunk.length) {
|
|
parsedItems[index] = content;
|
|
}
|
|
}
|
|
|
|
// Mapper aux vrais tags
|
|
chunk.forEach(([tag, originalContent], index) => {
|
|
if (parsedItems[index] && parsedItems[index].length > 10) {
|
|
results[tag] = parsedItems[index];
|
|
} else {
|
|
results[tag] = originalContent; // Fallback
|
|
logSh(`⚠️ Fallback régénération pour [${tag}]`, 'WARNING');
|
|
}
|
|
});
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Parser réponse enhancement
|
|
*/
|
|
function parseEnhancementResponse(response, elementsToEnhance) {
|
|
const results = {};
|
|
const regex = /\[(\d+)\]\s*([^[]*?)(?=\n\[\d+\]|$)/gs;
|
|
let match;
|
|
let index = 0;
|
|
|
|
while ((match = regex.exec(response)) && index < elementsToEnhance.length) {
|
|
let enhancedContent = cleanAdversarialContent(match[2].trim());
|
|
const element = elementsToEnhance[index];
|
|
|
|
if (enhancedContent && enhancedContent.length > 10) {
|
|
results[element.tag] = enhancedContent;
|
|
} else {
|
|
results[element.tag] = element.content; // Fallback
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Sélectionner éléments pour enhancement
|
|
*/
|
|
function selectElementsForEnhancement(existingContent, config) {
|
|
const elements = [];
|
|
|
|
Object.entries(existingContent).forEach(([tag, content]) => {
|
|
const detectionRisk = assessDetectionRisk(content, config.detectorTarget);
|
|
|
|
if (detectionRisk.score > 0.6) { // Risque élevé
|
|
elements.push({
|
|
tag,
|
|
content,
|
|
detectionRisk: detectionRisk.reasons.join(', '),
|
|
priority: detectionRisk.score
|
|
});
|
|
}
|
|
});
|
|
|
|
// Trier par priorité (risque élevé en premier)
|
|
elements.sort((a, b) => b.priority - a.priority);
|
|
|
|
return elements;
|
|
}
|
|
|
|
/**
|
|
* Sélectionner éléments clés pour régénération (hybride)
|
|
*/
|
|
function selectKeyElementsForRegeneration(content, config) {
|
|
const keyTags = [];
|
|
|
|
Object.keys(content).forEach(tag => {
|
|
// Éléments clés: titres, intro, premiers paragraphes
|
|
if (tag.includes('Titre') || tag.includes('H1') || tag.includes('intro') ||
|
|
tag.includes('Introduction') || tag.includes('1')) {
|
|
keyTags.push(tag);
|
|
}
|
|
});
|
|
|
|
return keyTags.slice(0, 3); // Maximum 3 éléments clés
|
|
}
|
|
|
|
/**
|
|
* Évaluer risque de détection
|
|
*/
|
|
function assessDetectionRisk(content, detectorTarget) {
|
|
let score = 0;
|
|
const reasons = [];
|
|
|
|
// Indicateurs génériques de contenu IA
|
|
const aiWords = ['optimal', 'comprehensive', 'seamless', 'robust', 'leverage', 'cutting-edge'];
|
|
const aiCount = aiWords.reduce((count, word) => {
|
|
return count + (content.toLowerCase().includes(word) ? 1 : 0);
|
|
}, 0);
|
|
|
|
if (aiCount > 2) {
|
|
score += 0.4;
|
|
reasons.push('mots_typiques_ia');
|
|
}
|
|
|
|
// Structure trop parfaite
|
|
const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 10);
|
|
if (sentences.length > 2) {
|
|
const avgLength = sentences.reduce((sum, s) => sum + s.length, 0) / sentences.length;
|
|
const variance = sentences.reduce((sum, s) => sum + Math.pow(s.length - avgLength, 2), 0) / sentences.length;
|
|
const uniformity = 1 - (Math.sqrt(variance) / avgLength);
|
|
|
|
if (uniformity > 0.8) {
|
|
score += 0.3;
|
|
reasons.push('structure_uniforme');
|
|
}
|
|
}
|
|
|
|
// Spécifique selon détecteur
|
|
if (detectorTarget === 'gptZero') {
|
|
// GPTZero détecte la prévisibilité
|
|
if (content.includes('par ailleurs') && content.includes('en effet')) {
|
|
score += 0.3;
|
|
reasons.push('connecteurs_prévisibles');
|
|
}
|
|
}
|
|
|
|
return { score: Math.min(1, score), reasons };
|
|
}
|
|
|
|
/**
|
|
* Nettoyer contenu adversarial généré
|
|
*/
|
|
function cleanAdversarialContent(content) {
|
|
if (!content) return content;
|
|
|
|
// Supprimer préfixes indésirables
|
|
content = content.replace(/^(voici\s+)?le\s+contenu\s+(réécrit|amélioré)[:\s]*/gi, '');
|
|
content = content.replace(/^(bon,?\s*)?(alors,?\s*)?/gi, '');
|
|
content = content.replace(/\*\*[^*]+\*\*/g, '');
|
|
content = content.replace(/\s{2,}/g, ' ');
|
|
content = content.trim();
|
|
|
|
return content;
|
|
}
|
|
|
|
/**
|
|
* Compter éléments modifiés
|
|
*/
|
|
function countModifiedElements(original, modified) {
|
|
let count = 0;
|
|
|
|
Object.keys(original).forEach(tag => {
|
|
if (modified[tag] && modified[tag] !== original[tag]) {
|
|
count++;
|
|
}
|
|
});
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Chunk array utility
|
|
*/
|
|
function chunkArray(array, size) {
|
|
const chunks = [];
|
|
for (let i = 0; i < array.length; i += size) {
|
|
chunks.push(array.slice(i, i + size));
|
|
}
|
|
return chunks;
|
|
}
|
|
|
|
/**
|
|
* Sleep utility
|
|
*/
|
|
function sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
module.exports = {
|
|
applyAdversarialLayer, // ← MAIN ENTRY POINT MODULAIRE
|
|
applyRegenerationMethod,
|
|
applyEnhancementMethod,
|
|
applyHybridMethod,
|
|
assessDetectionRisk,
|
|
selectElementsForEnhancement
|
|
}; |