## 🎯 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>
176 lines
5.2 KiB
JavaScript
176 lines
5.2 KiB
JavaScript
/**
|
|
* SamplingEngine.js
|
|
*
|
|
* Moteur d'échantillonnage pour Pipeline Validator
|
|
* Extrait automatiquement des échantillons représentatifs du contenu généré
|
|
*/
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
const { tracer } = require('../trace');
|
|
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
|
|
/**
|
|
* Classe SamplingEngine
|
|
*/
|
|
class SamplingEngine {
|
|
constructor() {
|
|
this.samples = {
|
|
titles: [],
|
|
content: [],
|
|
faqs: []
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Extrait les échantillons depuis les versions sauvegardées
|
|
* @param {Array<string>} versionPaths - Chemins des fichiers JSON versions
|
|
* @returns {Object} - Échantillons avec leurs versions
|
|
*/
|
|
async extractSamples(versionPaths) {
|
|
return tracer.run('SamplingEngine.extractSamples', async () => {
|
|
logSh(`📊 Démarrage échantillonnage: ${versionPaths.length} versions`, 'INFO');
|
|
|
|
// Charger la version finale pour identifier les échantillons
|
|
const finalVersionPath = versionPaths.find(p => p.includes('v2.0.json'));
|
|
if (!finalVersionPath) {
|
|
throw new Error('Version finale v2.0.json introuvable');
|
|
}
|
|
|
|
const finalContent = await this.loadVersion(finalVersionPath);
|
|
const allTags = Object.keys(finalContent);
|
|
|
|
logSh(` 📋 ${allTags.length} balises trouvées dans version finale`, 'DEBUG');
|
|
|
|
// Catégoriser les balises automatiquement
|
|
const titleTags = allTags.filter(tag => tag.includes('T'));
|
|
const contentTags = allTags.filter(tag => tag.includes('MC') || tag.includes('L')).slice(0, 4);
|
|
const faqTags = allTags.filter(tag => tag.includes('FAQ')).slice(0, 4);
|
|
|
|
logSh(` ✓ Catégorisation: ${titleTags.length} titres, ${contentTags.length} contenus, ${faqTags.length} FAQ`, 'INFO');
|
|
|
|
// Extraire versions pour chaque échantillon
|
|
const samplesData = {};
|
|
|
|
// Titres
|
|
for (const tag of titleTags) {
|
|
samplesData[tag] = await this.extractVersionsForTag(tag, versionPaths);
|
|
samplesData[tag].type = 'title';
|
|
this.samples.titles.push(tag);
|
|
}
|
|
|
|
// Contenus
|
|
for (const tag of contentTags) {
|
|
samplesData[tag] = await this.extractVersionsForTag(tag, versionPaths);
|
|
samplesData[tag].type = 'content';
|
|
this.samples.content.push(tag);
|
|
}
|
|
|
|
// FAQ
|
|
for (const tag of faqTags) {
|
|
samplesData[tag] = await this.extractVersionsForTag(tag, versionPaths);
|
|
samplesData[tag].type = 'faq';
|
|
this.samples.faqs.push(tag);
|
|
}
|
|
|
|
const totalSamples = titleTags.length + contentTags.length + faqTags.length;
|
|
logSh(`✅ Échantillonnage terminé: ${totalSamples} échantillons extraits`, 'INFO');
|
|
|
|
return {
|
|
samples: samplesData,
|
|
summary: {
|
|
totalSamples,
|
|
titles: titleTags.length,
|
|
content: contentTags.length,
|
|
faqs: faqTags.length
|
|
}
|
|
};
|
|
|
|
}, { versionsCount: versionPaths.length });
|
|
}
|
|
|
|
/**
|
|
* Extrait les versions d'une balise à travers toutes les étapes
|
|
* @param {string} tag - Balise à extraire
|
|
* @param {Array<string>} versionPaths - Chemins des versions
|
|
* @returns {Object} - Versions de la balise
|
|
*/
|
|
async extractVersionsForTag(tag, versionPaths) {
|
|
const versions = {};
|
|
|
|
for (const versionPath of versionPaths) {
|
|
try {
|
|
const content = await this.loadVersion(versionPath);
|
|
const versionName = path.basename(versionPath, '.json');
|
|
|
|
// Stocker le contenu de cette balise pour cette version
|
|
versions[versionName] = content[tag] || "[Non disponible à cette étape]";
|
|
|
|
} catch (error) {
|
|
logSh(`⚠️ Erreur lecture version ${versionPath}: ${error.message}`, 'WARN');
|
|
versions[path.basename(versionPath, '.json')] = "[Erreur lecture]";
|
|
}
|
|
}
|
|
|
|
return {
|
|
tag,
|
|
versions
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Charge un fichier version JSON
|
|
* @param {string} versionPath - Chemin du fichier
|
|
* @returns {Object} - Contenu JSON
|
|
*/
|
|
async loadVersion(versionPath) {
|
|
try {
|
|
const data = await fs.readFile(versionPath, 'utf8');
|
|
return JSON.parse(data);
|
|
} catch (error) {
|
|
logSh(`❌ Erreur chargement version ${versionPath}: ${error.message}`, 'ERROR');
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sauvegarde les échantillons dans un fichier
|
|
* @param {Object} samplesData - Données échantillons
|
|
* @param {string} outputPath - Chemin de sauvegarde
|
|
*/
|
|
async saveSamples(samplesData, outputPath) {
|
|
try {
|
|
await fs.writeFile(outputPath, JSON.stringify(samplesData, null, 2), 'utf8');
|
|
logSh(`💾 Échantillons sauvegardés: ${outputPath}`, 'DEBUG');
|
|
} catch (error) {
|
|
logSh(`❌ Erreur sauvegarde échantillons: ${error.message}`, 'ERROR');
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtient le résumé des échantillons
|
|
*/
|
|
getSummary() {
|
|
return {
|
|
titles: this.samples.titles,
|
|
content: this.samples.content,
|
|
faqs: this.samples.faqs,
|
|
total: this.samples.titles.length + this.samples.content.length + this.samples.faqs.length
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Reset l'état
|
|
*/
|
|
reset() {
|
|
this.samples = {
|
|
titles: [],
|
|
content: [],
|
|
faqs: []
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = { SamplingEngine };
|