seo-generator-server/lib/adversarial-generation/AdversarialLayers.js
StillHammer 9a2ef7da2b feat(human-simulation): Système d'erreurs graduées procédurales + anti-répétition complet
## 🎯 Nouveau système d'erreurs graduées (architecture SmartTouch)

### Architecture procédurale intelligente :
- **3 niveaux de gravité** : Légère (50%) → Moyenne (30%) → Grave (10%)
- **14 types d'erreurs** réalistes et subtiles
- **Sélection procédurale** selon contexte (longueur, technique, heure)
- **Distribution contrôlée** : max 1 grave, 2 moyennes, 3 légères par article

### 1. Erreurs GRAVES (10% articles max) :
- Accord sujet-verbe : "ils sont" → "ils est"
- Mot manquant : "pour garantir la qualité" → "pour garantir qualité"
- Double mot : "pour garantir" → "pour pour garantir"
- Négation oubliée : "n'est pas" → "est pas"

### 2. Erreurs MOYENNES (30% articles) :
- Accord pluriel : "plaques résistantes" → "plaques résistant"
- Virgule manquante : "Ainsi, il" → "Ainsi il"
- Registre inapproprié : "Par conséquent" → "Du coup"
- Préposition incorrecte : "résistant aux" → "résistant des"
- Connecteur illogique : "cependant" → "donc"

### 3. Erreurs LÉGÈRES (50% articles) :
- Double espace : "de votre" → "de  votre"
- Trait d'union : "c'est-à-dire" → "c'est à dire"
- Espace ponctuation : "qualité ?" → "qualité?"
- Majuscule : "Toutenplaque" → "toutenplaque"
- Apostrophe droite : "l'article" → "l'article"

##  Système anti-répétition complet :

### Corrections critiques :
- **HumanSimulationTracker.js** : Tracker centralisé global
- **Word boundaries (\b)** sur TOUS les regex → FIX "maison" → "néanmoinson"
- **Protection 30+ expressions idiomatiques** françaises
- **Anti-répétition** : max 2× même mot, jamais 2× même développement
- **Diversification** : 48 variantes (hésitations, développements, connecteurs)

### Nouvelle structure (comme SmartTouch) :
```
lib/human-simulation/
├── error-profiles/                (NOUVEAU)
│   ├── ErrorProfiles.js          (définitions + probabilités)
│   ├── ErrorGrave.js             (10% articles)
│   ├── ErrorMoyenne.js           (30% articles)
│   ├── ErrorLegere.js            (50% articles)
│   └── ErrorSelector.js          (sélection procédurale)
├── HumanSimulationCore.js         (orchestrateur)
├── HumanSimulationTracker.js      (anti-répétition)
└── [autres modules]
```

## 🔄 Remplace ancien système :
-  SpellingErrors.js (basique, répétitif, "et" → "." × 8)
-  error-profiles/ (gradué, procédural, intelligent, diversifié)

## 🎲 Fonctionnalités procédurales :
- Analyse contexte : longueur texte, complexité technique, heure rédaction
- Multiplicateurs adaptatifs selon contexte
- Conditions application intelligentes
- Tracking global par batch (respecte limites 10%/30%/50%)

## 📊 Résultats validation :
Sur 100 articles → ~40-50 avec erreurs subtiles et diverses (plus de spam répétitif)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 01:06:28 +08:00

429 lines
14 KiB
JavaScript

// ========================================
// ADVERSARIAL LAYERS - COUCHES MODULAIRES
// Responsabilité: Couches adversariales composables et réutilisables
// Architecture: Fonction pipeline |> layer1 |> layer2 |> layer3
// ========================================
const { logSh } = require('../ErrorReporting');
const { tracer } = require('../trace');
const { applyAdversarialLayer } = require('./AdversarialCore');
/**
* COUCHE ANTI-GPTZEERO - Spécialisée contre GPTZero
*/
async function applyAntiGPTZeroLayer(content, options = {}) {
return await applyAdversarialLayer(content, {
detectorTarget: 'gptZero',
intensity: options.intensity || 1.0,
method: options.method || 'regeneration',
...options
});
}
/**
* COUCHE ANTI-ORIGINALITY - Spécialisée contre Originality.ai
*/
async function applyAntiOriginalityLayer(content, options = {}) {
return await applyAdversarialLayer(content, {
detectorTarget: 'originality',
intensity: options.intensity || 1.1,
method: options.method || 'hybrid',
...options
});
}
/**
* COUCHE ANTI-WINSTON - Spécialisée contre Winston AI
*/
async function applyAntiWinstonLayer(content, options = {}) {
return await applyAdversarialLayer(content, {
detectorTarget: 'winston',
intensity: options.intensity || 0.9,
method: options.method || 'enhancement',
...options
});
}
/**
* COUCHE GÉNÉRALE - Protection généraliste multi-détecteurs
*/
async function applyGeneralAdversarialLayer(content, options = {}) {
return await applyAdversarialLayer(content, {
detectorTarget: 'general',
intensity: options.intensity || 0.8,
method: options.method || 'hybrid',
...options
});
}
/**
* COUCHE LÉGÈRE - Modifications subtiles pour préserver qualité
*/
async function applyLightAdversarialLayer(content, options = {}) {
return await applyAdversarialLayer(content, {
detectorTarget: options.detectorTarget || 'general',
intensity: 0.5,
method: 'enhancement',
preserveStructure: true,
...options
});
}
/**
* COUCHE INTENSIVE - Maximum anti-détection
*/
async function applyIntensiveAdversarialLayer(content, options = {}) {
return await applyAdversarialLayer(content, {
detectorTarget: options.detectorTarget || 'gptZero',
intensity: 1.5,
method: 'regeneration',
preserveStructure: false,
...options
});
}
/**
* PIPELINE COMPOSABLE - Application séquentielle de couches
*/
async function applyLayerPipeline(content, layers = [], globalOptions = {}) {
return await tracer.run('AdversarialLayers.applyLayerPipeline()', async () => {
await tracer.annotate({
layersPipeline: true,
layersCount: layers.length,
elementsCount: Object.keys(content).length
});
const startTime = Date.now();
logSh(`🔄 PIPELINE COUCHES ADVERSARIALES: ${layers.length} couches`, 'INFO');
let currentContent = content;
const pipelineStats = {
layers: [],
totalDuration: 0,
totalModifications: 0,
success: true
};
try {
for (let i = 0; i < layers.length; i++) {
const layer = layers[i];
const layerStartTime = Date.now();
logSh(` 🎯 Couche ${i + 1}/${layers.length}: ${layer.name || layer.type || 'anonyme'}`, 'DEBUG');
try {
const layerResult = await applyLayerByConfig(currentContent, layer, globalOptions);
currentContent = layerResult.content;
const layerStats = {
name: layer.name || `layer_${i + 1}`,
type: layer.type,
duration: Date.now() - layerStartTime,
modificationsCount: layerResult.stats?.elementsModified || 0,
success: true
};
pipelineStats.layers.push(layerStats);
pipelineStats.totalModifications += layerStats.modificationsCount;
logSh(`${layerStats.name}: ${layerStats.modificationsCount} modifs (${layerStats.duration}ms)`, 'DEBUG');
} catch (error) {
logSh(` ❌ Couche ${i + 1} échouée: ${error.message}`, 'ERROR');
pipelineStats.layers.push({
name: layer.name || `layer_${i + 1}`,
type: layer.type,
duration: Date.now() - layerStartTime,
success: false,
error: error.message
});
// Continuer avec le contenu précédent si une couche échoue
if (!globalOptions.stopOnError) {
continue;
} else {
throw error;
}
}
}
pipelineStats.totalDuration = Date.now() - startTime;
pipelineStats.success = pipelineStats.layers.every(layer => layer.success);
logSh(`🔄 PIPELINE TERMINÉ: ${pipelineStats.totalModifications} modifs totales (${pipelineStats.totalDuration}ms)`, 'INFO');
await tracer.event('Pipeline couches terminé', pipelineStats);
return {
content: currentContent,
stats: pipelineStats,
modifications: pipelineStats.totalModifications, // ✅ AJOUTÉ: Mapping pour PipelineExecutor
original: content
};
} catch (error) {
pipelineStats.totalDuration = Date.now() - startTime;
pipelineStats.success = false;
logSh(`❌ PIPELINE COUCHES ÉCHOUÉ après ${pipelineStats.totalDuration}ms: ${error.message}`, 'ERROR');
throw error;
}
}, { layers: layers.map(l => l.name || l.type), content: Object.keys(content) });
}
/**
* COUCHES PRÉDÉFINIES - Configurations courantes
*/
const PREDEFINED_LAYERS = {
// Stack défensif léger
lightDefense: [
{ type: 'general', name: 'General Light', intensity: 0.6, method: 'enhancement' },
{ type: 'anti-gptZero', name: 'GPTZero Light', intensity: 0.5, method: 'enhancement' }
],
// Stack défensif standard
standardDefense: [
{ type: 'general', name: 'General Standard', intensity: 0.8, method: 'hybrid' },
{ type: 'anti-gptZero', name: 'GPTZero Standard', intensity: 0.9, method: 'enhancement' },
{ type: 'anti-originality', name: 'Originality Standard', intensity: 0.8, method: 'enhancement' }
],
// Stack défensif intensif
heavyDefense: [
{ type: 'general', name: 'General Heavy', intensity: 1.0, method: 'regeneration' },
{ type: 'anti-gptZero', name: 'GPTZero Heavy', intensity: 1.2, method: 'regeneration' },
{ type: 'anti-originality', name: 'Originality Heavy', intensity: 1.1, method: 'hybrid' },
{ type: 'anti-winston', name: 'Winston Heavy', intensity: 1.0, method: 'enhancement' }
],
// Stack ciblé GPTZero
gptZeroFocused: [
{ type: 'anti-gptZero', name: 'GPTZero Primary', intensity: 1.3, method: 'regeneration' },
{ type: 'general', name: 'General Support', intensity: 0.7, method: 'enhancement' }
],
// Stack ciblé Originality
originalityFocused: [
{ type: 'anti-originality', name: 'Originality Primary', intensity: 1.4, method: 'hybrid' },
{ type: 'general', name: 'General Support', intensity: 0.8, method: 'enhancement' }
]
};
/**
* APPLIQUER STACK PRÉDÉFINI
*/
async function applyPredefinedStack(content, stackName, options = {}) {
const stack = PREDEFINED_LAYERS[stackName];
if (!stack) {
throw new Error(`Stack prédéfini inconnu: ${stackName}. Disponibles: ${Object.keys(PREDEFINED_LAYERS).join(', ')}`);
}
logSh(`📦 APPLICATION STACK PRÉDÉFINI: ${stackName}`, 'INFO');
return await applyLayerPipeline(content, stack, options);
}
/**
* COUCHES ADAPTATIVES - S'adaptent selon le contenu
*/
async function applyAdaptiveLayers(content, options = {}) {
const {
targetDetectors = ['gptZero', 'originality'],
maxIntensity = 1.0,
analysisMode = true
} = options;
logSh(`🧠 COUCHES ADAPTATIVES: Analyse + adaptation auto`, 'INFO');
// 1. Analyser le contenu pour détecter les risques
const contentAnalysis = analyzeContentRisks(content);
// 2. Construire pipeline adaptatif selon l'analyse
const adaptiveLayers = [];
// Niveau de base selon risque global
const baseIntensity = Math.min(maxIntensity, contentAnalysis.globalRisk * 1.2);
if (baseIntensity > 0.3) {
adaptiveLayers.push({
type: 'general',
name: 'Adaptive Base',
intensity: baseIntensity,
method: baseIntensity > 0.7 ? 'hybrid' : 'enhancement'
});
}
// Couches spécifiques selon détecteurs ciblés
targetDetectors.forEach(detector => {
const detectorRisk = contentAnalysis.detectorRisks[detector] || 0;
if (detectorRisk > 0.4) {
const intensity = Math.min(maxIntensity * 1.1, detectorRisk * 1.5);
adaptiveLayers.push({
type: `anti-${detector}`,
name: `Adaptive ${detector}`,
intensity,
method: intensity > 0.8 ? 'regeneration' : 'enhancement'
});
}
});
logSh(` 🎯 ${adaptiveLayers.length} couches adaptatives générées`, 'DEBUG');
if (adaptiveLayers.length === 0) {
logSh(` ✅ Contenu déjà optimal, aucune couche nécessaire`, 'INFO');
return { content, stats: { adaptive: true, layersApplied: 0 }, original: content };
}
return await applyLayerPipeline(content, adaptiveLayers, options);
}
// ============= HELPER FUNCTIONS =============
/**
* Appliquer couche selon configuration
*/
async function applyLayerByConfig(content, layerConfig, globalOptions = {}) {
const { type, intensity, method, ...layerOptions } = layerConfig;
// ✅ FIX: Ne override que si la valeur est explicitement définie (pas undefined/null)
const options = {
...globalOptions,
...layerOptions
};
// Override intensity seulement si défini dans layerConfig
if (intensity !== undefined && intensity !== null) {
options.intensity = intensity;
}
// Override method seulement si défini dans layerConfig
if (method !== undefined && method !== null) {
options.method = method;
}
switch (type) {
case 'general':
return await applyGeneralAdversarialLayer(content, options);
case 'anti-gptZero':
return await applyAntiGPTZeroLayer(content, options);
case 'anti-originality':
return await applyAntiOriginalityLayer(content, options);
case 'anti-winston':
return await applyAntiWinstonLayer(content, options);
case 'light':
return await applyLightAdversarialLayer(content, options);
case 'intensive':
return await applyIntensiveAdversarialLayer(content, options);
default:
throw new Error(`Type de couche inconnu: ${type}`);
}
}
/**
* Analyser risques du contenu pour adaptation
*/
function analyzeContentRisks(content) {
const analysis = {
globalRisk: 0,
detectorRisks: {},
riskFactors: []
};
const allContent = Object.values(content).join(' ');
// Risques génériques
let riskScore = 0;
// 1. Mots typiques IA
const aiWords = ['optimal', 'comprehensive', 'seamless', 'robust', 'leverage', 'cutting-edge', 'furthermore', 'moreover'];
const aiWordCount = aiWords.filter(word => allContent.toLowerCase().includes(word)).length;
if (aiWordCount > 2) {
riskScore += 0.3;
analysis.riskFactors.push(`mots_ia: ${aiWordCount}`);
}
// 2. Structure uniforme
const contentLengths = Object.values(content).map(c => c.length);
const avgLength = contentLengths.reduce((a, b) => a + b, 0) / contentLengths.length;
const variance = contentLengths.reduce((sum, len) => sum + Math.pow(len - avgLength, 2), 0) / contentLengths.length;
const uniformity = 1 - (Math.sqrt(variance) / Math.max(avgLength, 1));
if (uniformity > 0.8) {
riskScore += 0.2;
analysis.riskFactors.push(`uniformité: ${uniformity.toFixed(2)}`);
}
// 3. Connecteurs répétitifs
const repetitiveConnectors = ['par ailleurs', 'en effet', 'de plus', 'cependant'];
const connectorCount = repetitiveConnectors.filter(conn =>
(allContent.match(new RegExp(conn, 'gi')) || []).length > 1
).length;
if (connectorCount > 2) {
riskScore += 0.2;
analysis.riskFactors.push(`connecteurs_répétitifs: ${connectorCount}`);
}
analysis.globalRisk = Math.min(1, riskScore);
// Risques spécifiques par détecteur
analysis.detectorRisks = {
gptZero: analysis.globalRisk + (uniformity > 0.7 ? 0.3 : 0),
originality: analysis.globalRisk + (aiWordCount > 3 ? 0.4 : 0),
winston: analysis.globalRisk + (connectorCount > 2 ? 0.2 : 0)
};
return analysis;
}
/**
* Obtenir informations sur les stacks disponibles
*/
function getAvailableStacks() {
return Object.keys(PREDEFINED_LAYERS).map(stackName => ({
name: stackName,
layersCount: PREDEFINED_LAYERS[stackName].length,
description: getStackDescription(stackName),
layers: PREDEFINED_LAYERS[stackName]
}));
}
/**
* Description des stacks prédéfinis
*/
function getStackDescription(stackName) {
const descriptions = {
lightDefense: 'Protection légère préservant la qualité',
standardDefense: 'Protection équilibrée multi-détecteurs',
heavyDefense: 'Protection maximale tous détecteurs',
gptZeroFocused: 'Optimisation spécifique anti-GPTZero',
originalityFocused: 'Optimisation spécifique anti-Originality.ai'
};
return descriptions[stackName] || 'Stack personnalisé';
}
module.exports = {
// Couches individuelles
applyAntiGPTZeroLayer,
applyAntiOriginalityLayer,
applyAntiWinstonLayer,
applyGeneralAdversarialLayer,
applyLightAdversarialLayer,
applyIntensiveAdversarialLayer,
// Pipeline et stacks
applyLayerPipeline, // ← MAIN ENTRY POINT PIPELINE
applyPredefinedStack, // ← MAIN ENTRY POINT STACKS
applyAdaptiveLayers, // ← MAIN ENTRY POINT ADAPTATIF
// Utilitaires
getAvailableStacks,
analyzeContentRisks,
PREDEFINED_LAYERS
};