seo-generator-server/lib/selective-smart-touch/SmartStyleLayer.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

216 lines
7.3 KiB
JavaScript

// ========================================
// SMART STYLE LAYER - Améliorations style CIBLÉES
// Responsabilité: Appliquer UNIQUEMENT les améliorations style identifiées par analyse
// LLM: Mistral (excellence style et personnalité)
// Architecture: Phase 2 de SelectiveSmartTouch (post-analyse)
// ========================================
const { callLLM } = require('../LLMManager');
const { logSh } = require('../ErrorReporting');
const { tracer } = require('../trace');
/**
* SMART STYLE LAYER
* Applique améliorations style précises identifiées par SmartAnalysisLayer
*/
class SmartStyleLayer {
constructor() {
this.name = 'SmartStyle';
this.defaultLLM = 'mistral-small';
}
/**
* APPLIQUER AMÉLIORATIONS STYLE CIBLÉES
*/
async applyTargeted(content, analysis, context = {}) {
return await tracer.run('SmartStyle.applyTargeted()', async () => {
const { mc0, personality, intensity = 1.0 } = context;
// Si aucune amélioration style nécessaire, skip
if (!analysis.style.needed) {
logSh(`⏭️ SMART STYLE: Aucune amélioration nécessaire (score: ${analysis.style.score.toFixed(2)})`, 'DEBUG');
return {
content,
modifications: 0,
skipped: true,
reason: 'No style improvements needed'
};
}
await tracer.annotate({
smartStyle: true,
contentLength: content.length,
hasPersonality: !!personality,
intensity
});
// ✅ Utiliser LLM fourni dans context, sinon fallback sur defaultLLM
const llmToUse = context.llmProvider || this.defaultLLM;
const startTime = Date.now();
logSh(`🎨 SMART STYLE: Application améliorations style ciblées avec ${llmToUse}`, 'DEBUG');
try {
const prompt = this.createTargetedPrompt(content, analysis, context);
const response = await callLLM(llmToUse, prompt, {
temperature: 0.7, // Créativité modérée pour style
maxTokens: 2500
}, personality);
const improvedContent = this.cleanResponse(response);
const modifications = this.countModifications(content, improvedContent);
const duration = Date.now() - startTime;
logSh(`✅ SMART STYLE terminé: ${modifications} modifications appliquées (${duration}ms)`, 'DEBUG');
await tracer.event('Smart Style appliqué', {
duration,
modifications,
personalityApplied: personality?.nom || 'generic'
});
return {
content: improvedContent,
modifications,
duration,
personalityApplied: personality?.nom
};
} catch (error) {
const duration = Date.now() - startTime;
logSh(`❌ SMART STYLE ÉCHOUÉ (${duration}ms): ${error.message}`, 'ERROR');
return {
content,
modifications: 0,
error: error.message,
fallback: true
};
}
}, { contentLength: content.length, analysis });
}
/**
* CRÉER PROMPT CIBLÉ
*/
createTargetedPrompt(content, analysis, context) {
const { mc0, personality, intensity = 1.0 } = context;
// Extraire améliorations style
const styleImprovements = analysis.improvements.filter(imp =>
imp.toLowerCase().includes('style') ||
imp.toLowerCase().includes('ton') ||
imp.toLowerCase().includes('personnalis') ||
imp.toLowerCase().includes('expression') ||
imp.toLowerCase().includes('vocabulaire')
);
return `MISSION: Améliore UNIQUEMENT les aspects STYLE listés ci-dessous.
CONTENU ORIGINAL:
"${content}"
${mc0 ? `CONTEXTE SUJET: ${mc0}` : ''}
${personality ? `PERSONNALITÉ CIBLE: ${personality.nom} (${personality.style})
VOCABULAIRE PRÉFÉRÉ: ${personality.vocabulairePref || 'professionnel'}` : 'STYLE: Professionnel standard'}
INTENSITÉ: ${intensity.toFixed(1)}
AMÉLIORATIONS STYLE À APPLIQUER:
${styleImprovements.map((imp, i) => `${i + 1}. ${imp}`).join('\n')}
${analysis.style.genericPhrases && analysis.style.genericPhrases.length > 0 ? `
EXPRESSIONS GÉNÉRIQUES À PERSONNALISER:
${analysis.style.genericPhrases.map(phrase => `- "${phrase}"`).join('\n')}
` : ''}
${analysis.style.toneIssues && analysis.style.toneIssues.length > 0 ? `
PROBLÈMES DE TON IDENTIFIÉS:
${analysis.style.toneIssues.map(issue => `- ${issue}`).join('\n')}
` : ''}
CONSIGNES STRICTES:
- Applique UNIQUEMENT les améliorations style listées ci-dessus
- NE CHANGE PAS le fond du message ni les informations factuelles
- GARDE la même structure et longueur (±15%)
${personality ? `- Applique style "${personality.style}" de façon MESURÉE (pas d'exagération)` : '- Style professionnel web standard'}
- ⚠️ MAINTIENS un TON PROFESSIONNEL (limite connecteurs oraux à 1-2 MAX)
- ⚠️ ÉVITE bombardement de marqueurs de personnalité
EXEMPLES AMÉLIORATION STYLE (génériques multi-secteurs):
**E-commerce mode:**
- ✅ BON: "Cette robe allie élégance et confort" → style commercial mesuré
- ❌ MAUVAIS: "Écoutez, du coup cette robe, voilà, elle est vraiment top" → trop oral
**Services professionnels:**
- ✅ BON: "Notre expertise comptable garantit votre conformité" → professionnel et spécifique
- ❌ MAUVAIS: "Nos solutions de qualité" → générique et vague
**SaaS/Tech:**
- ✅ BON: "Automatisez vos workflows en 3 clics" → action concrète
- ❌ MAUVAIS: "Notre plateforme innovante optimise vos processus" → buzzwords creux
**Contenu informatif:**
- ✅ BON: "Le réchauffement climatique atteint +1.2°C depuis 1850" → factuel et précis
- ❌ MAUVAIS: "Le réchauffement climatique est un problème important" → vague
RÈGLES VOCABULAIRE & TON:
- Remplace expressions génériques par spécificités
- 1-2 touches de personnalité par paragraphe MAXIMUM
- Pas de saturation de connecteurs familiers ("du coup", "voilà", "écoutez")
- Privilégie authenticité sur artifice
FORMAT RÉPONSE:
Retourne UNIQUEMENT le contenu stylisé, SANS balises, SANS métadonnées, SANS explications.`;
}
/**
* NETTOYER RÉPONSE
*/
cleanResponse(response) {
if (!response) return response;
let cleaned = response.trim();
// Supprimer balises
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 STYLISÉ:\s*/gi, '');
cleaned = cleaned.replace(/^(voici\s+)?le\s+contenu\s+(stylisé|avec\s+style)\s*[:.]?\s*/gi, '');
cleaned = cleaned.replace(/^(dans\s+le\s+style\s+de\s+)[^:]*[:.]?\s*/gi, '');
// Nettoyer formatage
cleaned = cleaned.replace(/\*\*([^*]+)\*\*/g, '$1');
cleaned = cleaned.replace(/\s{2,}/g, ' ');
cleaned = cleaned.trim();
return cleaned;
}
/**
* COMPTER MODIFICATIONS
*/
countModifications(original, improved) {
if (original === improved) return 0;
const originalWords = original.toLowerCase().split(/\s+/);
const improvedWords = improved.toLowerCase().split(/\s+/);
let differences = 0;
differences += Math.abs(originalWords.length - improvedWords.length);
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 = { SmartStyleLayer };