## 🎯 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>
326 lines
12 KiB
JavaScript
326 lines
12 KiB
JavaScript
// ========================================
|
|
// FICHIER: HumanSimulationCore.js
|
|
// RESPONSABILITÉ: Orchestrateur principal Human Simulation
|
|
// Niveau 5: Temporal & Personality Injection
|
|
// ========================================
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
const { tracer } = require('../trace');
|
|
const { calculateFatigue, injectFatigueMarkers, getFatigueProfile } = require('./FatiguePatterns');
|
|
const { injectPersonalityErrors, getPersonalityErrorPatterns } = require('./PersonalityErrors');
|
|
const { applyTemporalStyle, getTemporalStyle } = require('./TemporalStyles');
|
|
const { HumanSimulationTracker } = require('./HumanSimulationTracker');
|
|
const { selectAndApplyErrors } = require('./error-profiles/ErrorSelector'); // ✅ NOUVEAU: Système erreurs graduées
|
|
const {
|
|
analyzeContentComplexity,
|
|
calculateReadabilityScore,
|
|
preserveKeywords,
|
|
validateSimulationQuality
|
|
} = require('./HumanSimulationUtils');
|
|
|
|
/**
|
|
* CONFIGURATION PAR DÉFAUT
|
|
* ⚠️ VALIDATION DÉSACTIVÉE - Imperfections volontaires acceptées
|
|
*/
|
|
const DEFAULT_CONFIG = {
|
|
fatigueEnabled: true,
|
|
personalityErrorsEnabled: true,
|
|
temporalStyleEnabled: true,
|
|
graduatedErrorsEnabled: true, // ✅ NOUVEAU: Système erreurs graduées (grave/moyenne/légère)
|
|
imperfectionIntensity: 0.5,
|
|
naturalRepetitions: true,
|
|
qualityThreshold: 0, // ✅ VALIDATION DÉSACTIVÉE (threshold=0)
|
|
maxModificationsPerElement: 3
|
|
};
|
|
|
|
/**
|
|
* ORCHESTRATEUR PRINCIPAL - Human Simulation Layer
|
|
* @param {object} content - Contenu généré à simuler
|
|
* @param {object} options - Options de simulation
|
|
* @returns {object} - { content, stats, fallback }
|
|
*/
|
|
async function applyHumanSimulationLayer(content, options = {}) {
|
|
return await tracer.run('HumanSimulationCore.applyHumanSimulationLayer()', async () => {
|
|
const startTime = Date.now();
|
|
|
|
await tracer.annotate({
|
|
contentKeys: Object.keys(content).length,
|
|
elementIndex: options.elementIndex,
|
|
totalElements: options.totalElements,
|
|
currentHour: options.currentHour,
|
|
personality: options.csvData?.personality?.nom
|
|
});
|
|
|
|
logSh(`🧠 HUMAN SIMULATION - Début traitement`, 'INFO');
|
|
logSh(` 📊 ${Object.keys(content).length} éléments | Position: ${options.elementIndex}/${options.totalElements}`, 'DEBUG');
|
|
|
|
try {
|
|
// Configuration fusionnée
|
|
const config = { ...DEFAULT_CONFIG, ...options };
|
|
|
|
// ✅ INITIALISATION TRACKER ANTI-RÉPÉTITION
|
|
const tracker = new HumanSimulationTracker();
|
|
logSh(`🧠 Tracker anti-répétition initialisé`, 'DEBUG');
|
|
|
|
// Stats de simulation
|
|
const simulationStats = {
|
|
elementsProcessed: 0,
|
|
fatigueModifications: 0,
|
|
personalityModifications: 0,
|
|
temporalModifications: 0,
|
|
spellingModifications: 0,
|
|
totalModifications: 0,
|
|
qualityScore: 0,
|
|
fallbackUsed: false
|
|
};
|
|
|
|
// Contenu simulé
|
|
let simulatedContent = { ...content };
|
|
|
|
// ========================================
|
|
// 1. ANALYSE CONTEXTE GLOBAL
|
|
// ========================================
|
|
const globalContext = await analyzeGlobalContext(content, config);
|
|
logSh(` 🔍 Contexte: fatigue=${globalContext.fatigueLevel.toFixed(2)}, heure=${globalContext.currentHour}h, personnalité=${globalContext.personalityName}`, 'DEBUG');
|
|
|
|
// ========================================
|
|
// 2. TRAITEMENT PAR ÉLÉMENT
|
|
// ========================================
|
|
for (const [elementKey, elementContent] of Object.entries(content)) {
|
|
await tracer.run(`HumanSimulation.processElement(${elementKey})`, async () => {
|
|
|
|
logSh(` 🎯 Traitement élément: ${elementKey}`, 'DEBUG');
|
|
|
|
let processedContent = elementContent;
|
|
let elementModifications = 0;
|
|
|
|
try {
|
|
// 2a. Simulation Fatigue Cognitive
|
|
if (config.fatigueEnabled && globalContext.fatigueLevel > 0.1) { // FIXÉ: Seuil plus bas (était 0.3)
|
|
const fatigueResult = await applyFatigueSimulation(processedContent, globalContext, config);
|
|
processedContent = fatigueResult.content;
|
|
elementModifications += fatigueResult.modifications;
|
|
simulationStats.fatigueModifications += fatigueResult.modifications;
|
|
|
|
logSh(` 💤 Fatigue: ${fatigueResult.modifications} modifications (niveau: ${globalContext.fatigueLevel.toFixed(2)})`, 'DEBUG');
|
|
}
|
|
|
|
// 2b. Erreurs Personnalité
|
|
if (config.personalityErrorsEnabled && globalContext.personalityProfile) {
|
|
const personalityResult = await applyPersonalitySimulation(processedContent, globalContext, config, tracker);
|
|
processedContent = personalityResult.content;
|
|
elementModifications += personalityResult.modifications;
|
|
simulationStats.personalityModifications += personalityResult.modifications;
|
|
|
|
logSh(` 🎭 Personnalité: ${personalityResult.modifications} erreurs injectées`, 'DEBUG');
|
|
}
|
|
|
|
// 2c. Style Temporel
|
|
if (config.temporalStyleEnabled && globalContext.temporalStyle) {
|
|
const temporalResult = await applyTemporalSimulation(processedContent, globalContext, config, tracker);
|
|
processedContent = temporalResult.content;
|
|
elementModifications += temporalResult.modifications;
|
|
simulationStats.temporalModifications += temporalResult.modifications;
|
|
|
|
logSh(` ⏰ Temporel: ${temporalResult.modifications} ajustements (${globalContext.temporalStyle.period})`, 'DEBUG');
|
|
}
|
|
|
|
// 2d. Erreurs Graduées Procédurales (NOUVEAU - grave 10% / moyenne 30% / légère 50%)
|
|
if (config.graduatedErrorsEnabled) {
|
|
const errorResult = selectAndApplyErrors(processedContent, {
|
|
currentHour: globalContext.currentHour,
|
|
tracker
|
|
});
|
|
processedContent = errorResult.content;
|
|
elementModifications += errorResult.errorsApplied;
|
|
simulationStats.graduatedErrors = (simulationStats.graduatedErrors || 0) + errorResult.errorsApplied;
|
|
|
|
if (errorResult.errorsApplied > 0) {
|
|
logSh(` 🎲 Erreurs graduées: ${errorResult.errorsApplied} (${errorResult.errorDetails.severity})`, 'DEBUG');
|
|
}
|
|
}
|
|
|
|
// 2e. Validation Qualité
|
|
const qualityCheck = validateSimulationQuality(elementContent, processedContent, config.qualityThreshold);
|
|
|
|
if (qualityCheck.acceptable) {
|
|
simulatedContent[elementKey] = processedContent;
|
|
simulationStats.elementsProcessed++;
|
|
simulationStats.totalModifications += elementModifications;
|
|
|
|
logSh(` ✅ Élément simulé: ${elementModifications} modifications totales`, 'DEBUG');
|
|
} else {
|
|
// Fallback: garder contenu original
|
|
simulatedContent[elementKey] = elementContent;
|
|
simulationStats.fallbackUsed = true;
|
|
|
|
logSh(` ⚠️ Qualité insuffisante, fallback vers contenu original`, 'WARNING');
|
|
}
|
|
|
|
} catch (elementError) {
|
|
logSh(` ❌ Erreur simulation élément ${elementKey}: ${elementError.message}`, 'WARNING');
|
|
simulatedContent[elementKey] = elementContent; // Fallback
|
|
simulationStats.fallbackUsed = true;
|
|
}
|
|
|
|
}, { elementKey, originalLength: elementContent?.length });
|
|
}
|
|
|
|
// ========================================
|
|
// 3. CALCUL SCORE QUALITÉ GLOBAL
|
|
// ========================================
|
|
simulationStats.qualityScore = calculateGlobalQualityScore(content, simulatedContent);
|
|
|
|
// ========================================
|
|
// 4. RÉSULTATS FINAUX
|
|
// ========================================
|
|
const duration = Date.now() - startTime;
|
|
const success = simulationStats.elementsProcessed > 0 && !simulationStats.fallbackUsed;
|
|
|
|
logSh(`🧠 HUMAN SIMULATION - Terminé (${duration}ms)`, 'INFO');
|
|
logSh(` ✅ ${simulationStats.elementsProcessed}/${Object.keys(content).length} éléments simulés`, 'INFO');
|
|
logSh(` 📊 ${simulationStats.fatigueModifications} fatigue | ${simulationStats.personalityModifications} personnalité | ${simulationStats.temporalModifications} temporel | ${simulationStats.spellingModifications || 0} fautes`, 'INFO');
|
|
logSh(` 🎯 Score qualité: ${simulationStats.qualityScore.toFixed(2)} | Fallback: ${simulationStats.fallbackUsed ? 'OUI' : 'NON'}`, 'INFO');
|
|
|
|
await tracer.event('Human Simulation terminée', {
|
|
success,
|
|
duration,
|
|
stats: simulationStats
|
|
});
|
|
|
|
return {
|
|
content: simulatedContent,
|
|
stats: simulationStats,
|
|
modifications: simulationStats.totalModifications, // ✅ AJOUTÉ: Mapping pour PipelineExecutor
|
|
fallback: simulationStats.fallbackUsed,
|
|
qualityScore: simulationStats.qualityScore,
|
|
duration
|
|
};
|
|
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
logSh(`❌ HUMAN SIMULATION ÉCHOUÉE (${duration}ms): ${error.message}`, 'ERROR');
|
|
|
|
await tracer.event('Human Simulation échouée', {
|
|
error: error.message,
|
|
duration,
|
|
contentKeys: Object.keys(content).length
|
|
});
|
|
|
|
// Fallback complet
|
|
return {
|
|
content,
|
|
stats: { fallbackUsed: true, error: error.message },
|
|
fallback: true,
|
|
qualityScore: 0,
|
|
duration
|
|
};
|
|
}
|
|
|
|
}, {
|
|
contentElements: Object.keys(content).length,
|
|
elementIndex: options.elementIndex,
|
|
personality: options.csvData?.personality?.nom
|
|
});
|
|
}
|
|
|
|
/**
|
|
* ANALYSE CONTEXTE GLOBAL
|
|
*/
|
|
async function analyzeGlobalContext(content, config) {
|
|
const elementIndex = config.elementIndex || 0;
|
|
const totalElements = config.totalElements || Object.keys(content).length;
|
|
const currentHour = config.currentHour || new Date().getHours();
|
|
const personality = config.csvData?.personality;
|
|
|
|
return {
|
|
fatigueLevel: calculateFatigue(elementIndex, totalElements),
|
|
fatigueProfile: personality ? getFatigueProfile(personality.nom) : null,
|
|
personalityName: personality?.nom || 'unknown',
|
|
personalityProfile: personality ? getPersonalityErrorPatterns(personality.nom) : null,
|
|
temporalStyle: getTemporalStyle(currentHour),
|
|
currentHour,
|
|
elementIndex,
|
|
totalElements,
|
|
contentComplexity: analyzeContentComplexity(content)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* APPLICATION SIMULATION FATIGUE
|
|
*/
|
|
async function applyFatigueSimulation(content, globalContext, config) {
|
|
const fatigueResult = injectFatigueMarkers(content, globalContext.fatigueLevel, {
|
|
profile: globalContext.fatigueProfile,
|
|
intensity: config.imperfectionIntensity
|
|
});
|
|
|
|
return {
|
|
content: fatigueResult.content,
|
|
modifications: fatigueResult.modifications || 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* APPLICATION SIMULATION PERSONNALITÉ
|
|
*/
|
|
async function applyPersonalitySimulation(content, globalContext, config, tracker) {
|
|
const personalityResult = injectPersonalityErrors(
|
|
content,
|
|
globalContext.personalityProfile,
|
|
config.imperfectionIntensity,
|
|
tracker
|
|
);
|
|
|
|
return {
|
|
content: personalityResult.content,
|
|
modifications: personalityResult.modifications || 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* APPLICATION SIMULATION TEMPORELLE
|
|
*/
|
|
async function applyTemporalSimulation(content, globalContext, config, tracker) {
|
|
const temporalResult = applyTemporalStyle(content, globalContext.temporalStyle, {
|
|
intensity: config.imperfectionIntensity,
|
|
tracker
|
|
});
|
|
|
|
return {
|
|
content: temporalResult.content,
|
|
modifications: temporalResult.modifications || 0
|
|
};
|
|
}
|
|
|
|
|
|
/**
|
|
* CALCUL SCORE QUALITÉ GLOBAL
|
|
*/
|
|
function calculateGlobalQualityScore(originalContent, simulatedContent) {
|
|
let totalScore = 0;
|
|
let elementCount = 0;
|
|
|
|
for (const [key, original] of Object.entries(originalContent)) {
|
|
const simulated = simulatedContent[key];
|
|
if (simulated) {
|
|
const elementScore = calculateReadabilityScore(simulated) * 0.7 +
|
|
preserveKeywords(original, simulated) * 0.3;
|
|
totalScore += elementScore;
|
|
elementCount++;
|
|
}
|
|
}
|
|
|
|
return elementCount > 0 ? totalScore / elementCount : 0;
|
|
}
|
|
|
|
// ============= EXPORTS =============
|
|
module.exports = {
|
|
applyHumanSimulationLayer,
|
|
analyzeGlobalContext,
|
|
applyFatigueSimulation,
|
|
applyPersonalitySimulation,
|
|
applyTemporalSimulation,
|
|
calculateGlobalQualityScore,
|
|
DEFAULT_CONFIG
|
|
}; |