seo-generator-server/lib/selective-smart-touch/SmartTechnicalLayer.js
StillHammer 0244521f5c feat(selective-smart-touch): Add intelligent analysis-driven enhancement system + validation spec
## SelectiveSmartTouch (NEW)
- Architecture révolutionnaire: Analyse intelligente → Améliorations ciblées précises
- 5 modules: SmartAnalysisLayer, SmartTechnicalLayer, SmartStyleLayer, SmartReadabilityLayer, SmartTouchCore
- Système 10% segments: amélioration uniquement des segments les plus faibles (intensity-based)
- Détection contexte globale pour prompts adaptatifs multi-secteurs
- Intégration complète dans PipelineExecutor et PipelineDefinition

## Pipeline Validator Spec (NEW)
- Spécification complète système validation qualité par LLM
- 5 critères universels: Qualité, Verbosité, SEO, Répétitions, Naturalité
- Échantillonnage intelligent par filtrage balises (pas XML)
- Évaluation multi-versions avec justifications détaillées
- Coût estimé: ~$1/validation (260 appels LLM)

## Optimizations
- Réduction intensités fullEnhancement (technical 1.0→0.7, style 0.8→0.5)
- Ajout gardes-fous anti-familiarité excessive dans StyleLayer
- Sauvegarde étapes intermédiaires activée par défaut (pipeline-runner)

## Fixes
- Fix typo critique SmartTouchCore.js:110 (determineLayers ToApply → determineLayersToApply)
- Prompts généralisés multi-secteurs (e-commerce, SaaS, services, informatif)

🚀 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 15:01:02 +08:00

279 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:
- Applique UNIQUEMENT les améliorations listées ci-dessus
- 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%)
- Intègre les éléments manquants de façon NATURELLE
- ${isB2C ? 'PRIORITÉ ABSOLUE: Reste SIMPLE et ACCESSIBLE' : 'Reste ACCESSIBLE - pas de jargon excessif'}
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 };