## 🎯 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>
292 lines
11 KiB
JavaScript
292 lines
11 KiB
JavaScript
// ========================================
|
|
// SMART TECHNICAL LAYER - Améliorations techniques CIBLÉES
|
|
// Responsabilité: Appliquer UNIQUEMENT les améliorations techniques identifiées par analyse
|
|
// LLM: GPT-4o-mini (précision technique)
|
|
// Architecture: Phase 2 de SelectiveSmartTouch (post-analyse)
|
|
// ========================================
|
|
|
|
const { callLLM } = require('../LLMManager');
|
|
const { logSh } = require('../ErrorReporting');
|
|
const { tracer } = require('../trace');
|
|
|
|
/**
|
|
* SMART TECHNICAL LAYER
|
|
* Applique améliorations techniques précises identifiées par SmartAnalysisLayer
|
|
*/
|
|
class SmartTechnicalLayer {
|
|
constructor() {
|
|
this.name = 'SmartTechnical';
|
|
this.defaultLLM = 'gpt-4o-mini';
|
|
}
|
|
|
|
/**
|
|
* APPLIQUER AMÉLIORATIONS TECHNIQUES CIBLÉES
|
|
* @param {string} content - Contenu original
|
|
* @param {object} analysis - Analyse de SmartAnalysisLayer
|
|
* @param {object} context - Contexte (mc0, personality, intensity)
|
|
* @returns {object} - { content, modifications }
|
|
*/
|
|
async applyTargeted(content, analysis, context = {}) {
|
|
return await tracer.run('SmartTechnical.applyTargeted()', async () => {
|
|
const { mc0, personality, intensity = 1.0, contentContext } = context;
|
|
|
|
// Si aucune amélioration technique nécessaire, skip
|
|
if (!analysis.technical.needed || analysis.improvements.length === 0) {
|
|
logSh(`⏭️ SMART TECHNICAL: Aucune amélioration nécessaire (score: ${analysis.technical.score.toFixed(2)})`, 'DEBUG');
|
|
return {
|
|
content,
|
|
modifications: 0,
|
|
skipped: true,
|
|
reason: 'No technical improvements needed'
|
|
};
|
|
}
|
|
|
|
// === GARDE-FOU 1: Détection contenu déjà trop technique ===
|
|
if (contentContext?.techLevel === 'too_high') {
|
|
logSh(`🛡️ GARDE-FOU: Contenu déjà trop technique (level: ${contentContext.techLevel}), SKIP amélioration`, 'WARN');
|
|
return {
|
|
content,
|
|
modifications: 0,
|
|
skipped: true,
|
|
reason: 'Content already too technical - avoided over-engineering'
|
|
};
|
|
}
|
|
|
|
// === GARDE-FOU 2: Comptage specs techniques existantes ===
|
|
const existingSpecs = (content.match(/\d+\s*(mm|cm|kg|°C|%|watt|lumen|J\/cm²|K⁻¹)/g) || []).length;
|
|
const existingNorms = (content.match(/(ISO|ASTM|EN\s|DIN|norme)/gi) || []).length;
|
|
|
|
if (existingSpecs > 6 || existingNorms > 3) {
|
|
logSh(`🛡️ GARDE-FOU: Trop de specs existantes (${existingSpecs} specs, ${existingNorms} normes), SKIP pour éviter surcharge`, 'WARN');
|
|
return {
|
|
content,
|
|
modifications: 0,
|
|
skipped: true,
|
|
reason: `Specs overload (${existingSpecs} specs, ${existingNorms} norms) - avoided adding more`
|
|
};
|
|
}
|
|
|
|
// === GARDE-FOU 3: Si B2C + niveau high, limiter portée ===
|
|
if (contentContext?.audience === 'B2C' && contentContext.techLevel === 'high') {
|
|
logSh(`🛡️ GARDE-FOU: B2C + niveau technique déjà high, limitation de la portée`, 'INFO');
|
|
// Réduire nombre d'améliorations à appliquer
|
|
analysis.improvements = analysis.improvements.slice(0, 2); // Max 2 améliorations
|
|
}
|
|
|
|
await tracer.annotate({
|
|
smartTechnical: true,
|
|
contentLength: content.length,
|
|
improvementsCount: analysis.improvements.length,
|
|
intensity,
|
|
guardrailsApplied: true,
|
|
existingSpecs,
|
|
existingNorms
|
|
});
|
|
|
|
// ✅ Utiliser LLM fourni dans context, sinon fallback sur defaultLLM
|
|
const llmToUse = context.llmProvider || this.defaultLLM;
|
|
|
|
const startTime = Date.now();
|
|
logSh(`🔧 SMART TECHNICAL: Application de ${analysis.improvements.length} améliorations ciblées avec ${llmToUse}`, 'DEBUG');
|
|
|
|
try {
|
|
const prompt = this.createTargetedPrompt(content, analysis, context);
|
|
|
|
const response = await callLLM(llmToUse, prompt, {
|
|
temperature: 0.4, // Précision technique
|
|
maxTokens: 2500
|
|
}, personality);
|
|
|
|
const improvedContent = this.cleanResponse(response);
|
|
|
|
// Calculer nombre de modifications
|
|
const modifications = this.countModifications(content, improvedContent);
|
|
|
|
const duration = Date.now() - startTime;
|
|
logSh(`✅ SMART TECHNICAL terminé: ${modifications} modifications appliquées (${duration}ms)`, 'DEBUG');
|
|
|
|
await tracer.event('Smart Technical appliqué', {
|
|
duration,
|
|
modifications,
|
|
improvementsRequested: analysis.improvements.length
|
|
});
|
|
|
|
return {
|
|
content: improvedContent,
|
|
modifications,
|
|
duration,
|
|
improvementsApplied: analysis.improvements
|
|
};
|
|
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
logSh(`❌ SMART TECHNICAL ÉCHOUÉ (${duration}ms): ${error.message}`, 'ERROR');
|
|
|
|
return {
|
|
content, // Fallback: contenu original
|
|
modifications: 0,
|
|
error: error.message,
|
|
fallback: true
|
|
};
|
|
}
|
|
}, { contentLength: content.length, analysis });
|
|
}
|
|
|
|
/**
|
|
* CRÉER PROMPT CIBLÉ (instructions précises, exemples génériques)
|
|
*/
|
|
createTargetedPrompt(content, analysis, context) {
|
|
const { mc0, personality, intensity = 1.0, contentContext } = context;
|
|
|
|
// Extraire uniquement les améliorations techniques de la liste globale
|
|
const technicalImprovements = analysis.improvements.filter(imp =>
|
|
imp.toLowerCase().includes('technique') ||
|
|
imp.toLowerCase().includes('données') ||
|
|
imp.toLowerCase().includes('chiffr') ||
|
|
imp.toLowerCase().includes('précision') ||
|
|
imp.toLowerCase().includes('spécif') ||
|
|
analysis.technical.missing.some(missing => imp.includes(missing))
|
|
);
|
|
|
|
// === ADAPTER PROMPT SELON CONTEXTE (B2C vs B2B) ===
|
|
const isB2C = contentContext?.audience === 'B2C';
|
|
const isTechnicalContent = contentContext?.contentType === 'technical';
|
|
|
|
let technicalGuidelines = '';
|
|
if (isB2C && !isTechnicalContent) {
|
|
technicalGuidelines = `
|
|
⚠️ CONTEXTE: Contenu B2C grand public - SIMPLICITÉ MAXIMALE
|
|
- Ajoute UNIQUEMENT 2-3 spécifications SIMPLES et UTILES pour un client
|
|
- ÉVITE absolument: normes ISO/ASTM/EN, coefficients techniques, jargon industriel
|
|
- PRIVILÉGIE: dimensions pratiques, matériaux compréhensibles, bénéfices concrets
|
|
- INTERDICTION: termes comme "coefficient", "résistance à la corrosion", "norme", "conformité"
|
|
- MAX 1-2 données chiffrées pertinentes (ex: taille, poids, durée)`;
|
|
} else if (isTechnicalContent) {
|
|
technicalGuidelines = `
|
|
📋 CONTEXTE: Contenu technique - Précision acceptable
|
|
- Ajoute spécifications techniques précises si nécessaire
|
|
- Normes et standards acceptables si pertinents
|
|
- Garde équilibre entre précision et lisibilité`;
|
|
} else {
|
|
technicalGuidelines = `
|
|
🎯 CONTEXTE: Contenu standard - Équilibre
|
|
- Ajoute 2-4 spécifications pertinentes
|
|
- Évite jargon technique excessif
|
|
- Privilégie clarté et accessibilité`;
|
|
}
|
|
|
|
return `MISSION: Améliore UNIQUEMENT les aspects techniques PRÉCIS listés ci-dessous.
|
|
|
|
CONTENU ORIGINAL:
|
|
"${content}"
|
|
|
|
${mc0 ? `CONTEXTE SUJET: ${mc0}` : ''}
|
|
${personality ? `PERSONNALITÉ: ${personality.nom} (${personality.style})` : ''}
|
|
INTENSITÉ: ${intensity.toFixed(1)} (0.5=léger, 1.0=standard, 1.5=intensif)
|
|
${technicalGuidelines}
|
|
|
|
AMÉLIORATIONS TECHNIQUES À APPLIQUER:
|
|
${technicalImprovements.map((imp, i) => `${i + 1}. ${imp}`).join('\n')}
|
|
|
|
${analysis.technical.missing.length > 0 ? `
|
|
ÉLÉMENTS MANQUANTS IDENTIFIÉS:
|
|
${analysis.technical.missing.map((item, i) => `- ${item}`).join('\n')}
|
|
` : ''}
|
|
|
|
CONSIGNES STRICTES:
|
|
⚠️ RÈGLE ABSOLUE: REMPLACE la phrase originale, N'AJOUTE PAS de texte après
|
|
- Reformule la phrase en intégrant les améliorations techniques
|
|
- NE CHANGE PAS le ton, style ou structure générale
|
|
- NE TOUCHE PAS aux aspects non mentionnés
|
|
- Garde la même longueur approximative (±20%, PAS +100%)
|
|
- ${isB2C ? 'PRIORITÉ ABSOLUE: Reste SIMPLE et ACCESSIBLE' : 'Reste ACCESSIBLE - pas de jargon excessif'}
|
|
- ⚠️ Si la phrase est une question, garde-la sous forme de question (réponds DANS la question)
|
|
|
|
EXEMPLES REMPLACER vs AJOUTER:
|
|
|
|
✅ BON (REMPLACER):
|
|
AVANT: "Est-ce que les plaques sont résistantes aux intempéries ?"
|
|
APRÈS: "Les plaques en aluminium résistent-elles aux intempéries (-20°C à 50°C) ?"
|
|
→ Phrase REMPLACÉE, même longueur, garde format question
|
|
|
|
❌ MAUVAIS (AJOUTER):
|
|
AVANT: "Est-ce que les plaques sont résistantes aux intempéries ?"
|
|
APRÈS: "Est-ce que les plaques sont résistantes aux intempéries ? Les plaques sont en aluminium..."
|
|
→ Texte AJOUTÉ après = INTERDIT
|
|
|
|
EXEMPLES D'AMÉLIORATION TECHNIQUE (génériques):
|
|
${isB2C ? `
|
|
- ✅ BON (B2C): "Dimensions: 30x20cm, épaisseur 3mm" → clair et utile
|
|
- ❌ MAUVAIS (B2C): "Dimensions: 30x20cm, épaisseur 3mm, résistance 1,5 J/cm² (norme EN 12354-2)" → trop technique
|
|
- ✅ BON (B2C): "Délai de livraison: 3-5 jours" → simple
|
|
- ❌ MAUVAIS (B2C): "Conformité ISO 9001, délai d'expédition optimisé selon norme" → jargon inutile` : `
|
|
- ✅ BON: "Dimensions: 30x20cm, épaisseur 3mm" → données concrètes
|
|
- ❌ MAUVAIS: "Produit de qualité aux dimensions optimales" → vague
|
|
- ✅ BON: "Délai de livraison: 3-5 jours ouvrés" → précis
|
|
- ❌ MAUVAIS: "Livraison rapide" → imprécis`}
|
|
- ✅ BON: "Compatible avec 95% des systèmes" → chiffre concret
|
|
- ❌ MAUVAIS: "Très compatible" → vague
|
|
|
|
RÈGLES VOCABULAIRE TECHNIQUE:
|
|
- Privilégie clarté sur technicité excessive
|
|
- ${isB2C ? 'MAX 1-2 détails techniques SIMPLES' : '1-2 détails techniques pertinents par paragraphe MAX'}
|
|
- Évite le jargon pompeux inutile
|
|
- Vocabulaire accessible ${isB2C ? 'au GRAND PUBLIC' : 'à un public large'}
|
|
|
|
FORMAT RÉPONSE:
|
|
Retourne UNIQUEMENT le contenu amélioré, SANS balises, SANS métadonnées, SANS explications.`;
|
|
}
|
|
|
|
/**
|
|
* NETTOYER RÉPONSE LLM
|
|
*/
|
|
cleanResponse(response) {
|
|
if (!response) return response;
|
|
|
|
let cleaned = response.trim();
|
|
|
|
// Supprimer balises et préfixes indésirables
|
|
cleaned = cleaned.replace(/^TAG:\s*[^\s]+\s+/gi, '');
|
|
cleaned = cleaned.replace(/\bTAG:\s*[^\s]+\s+/gi, '');
|
|
cleaned = cleaned.replace(/^CONTENU:\s*/gi, '');
|
|
cleaned = cleaned.replace(/^CONTENU AMÉLIORÉ:\s*/gi, '');
|
|
cleaned = cleaned.replace(/^(voici\s+)?le\s+contenu\s+amélioré\s*[:.]?\s*/gi, '');
|
|
cleaned = cleaned.replace(/^(avec\s+)?amélioration[s]?\s+technique[s]?\s*[:.]?\s*/gi, '');
|
|
|
|
// Nettoyer formatage markdown
|
|
cleaned = cleaned.replace(/\*\*([^*]+)\*\*/g, '$1'); // **texte** → texte
|
|
cleaned = cleaned.replace(/\s{2,}/g, ' '); // Espaces multiples
|
|
cleaned = cleaned.trim();
|
|
|
|
return cleaned;
|
|
}
|
|
|
|
/**
|
|
* COMPTER MODIFICATIONS (comparaison contenu original vs amélioré)
|
|
*/
|
|
countModifications(original, improved) {
|
|
if (original === improved) return 0;
|
|
|
|
// Méthode simple: compter mots différents
|
|
const originalWords = original.toLowerCase().split(/\s+/);
|
|
const improvedWords = improved.toLowerCase().split(/\s+/);
|
|
|
|
let differences = 0;
|
|
|
|
// Compter ajouts/suppressions
|
|
differences += Math.abs(originalWords.length - improvedWords.length);
|
|
|
|
// Compter modifications (mots communs)
|
|
const minLength = Math.min(originalWords.length, improvedWords.length);
|
|
for (let i = 0; i < minLength; i++) {
|
|
if (originalWords[i] !== improvedWords[i]) {
|
|
differences++;
|
|
}
|
|
}
|
|
|
|
return differences;
|
|
}
|
|
}
|
|
|
|
module.exports = { SmartTechnicalLayer };
|