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

211 lines
7.0 KiB
JavaScript

// ========================================
// SMART READABILITY LAYER - Améliorations lisibilité CIBLÉES
// Responsabilité: Appliquer UNIQUEMENT les améliorations lisibilité identifiées par analyse
// LLM: GPT-4o-mini (clarté et structure)
// Architecture: Phase 2 de SelectiveSmartTouch (post-analyse)
// ========================================
const { callLLM } = require('../LLMManager');
const { logSh } = require('../ErrorReporting');
const { tracer } = require('../trace');
/**
* SMART READABILITY LAYER
* Applique améliorations lisibilité précises identifiées par SmartAnalysisLayer
*/
class SmartReadabilityLayer {
constructor() {
this.name = 'SmartReadability';
this.defaultLLM = 'gpt-4o-mini';
}
/**
* APPLIQUER AMÉLIORATIONS LISIBILITÉ CIBLÉES
*/
async applyTargeted(content, analysis, context = {}) {
return await tracer.run('SmartReadability.applyTargeted()', async () => {
const { mc0, personality, intensity = 1.0 } = context;
// Si aucune amélioration lisibilité nécessaire, skip
if (!analysis.readability.needed) {
logSh(`⏭️ SMART READABILITY: Aucune amélioration nécessaire (score: ${analysis.readability.score.toFixed(2)})`, 'DEBUG');
return {
content,
modifications: 0,
skipped: true,
reason: 'No readability improvements needed'
};
}
await tracer.annotate({
smartReadability: true,
contentLength: content.length,
intensity
});
// ✅ Utiliser LLM fourni dans context, sinon fallback sur defaultLLM
const llmToUse = context.llmProvider || this.defaultLLM;
const startTime = Date.now();
logSh(`📖 SMART READABILITY: Application améliorations lisibilité ciblées avec ${llmToUse}`, 'DEBUG');
try {
const prompt = this.createTargetedPrompt(content, analysis, context);
const response = await callLLM(llmToUse, prompt, {
temperature: 0.4, // Précision pour structure
maxTokens: 2500
}, personality);
const improvedContent = this.cleanResponse(response);
const modifications = this.countModifications(content, improvedContent);
const duration = Date.now() - startTime;
logSh(`✅ SMART READABILITY terminé: ${modifications} modifications appliquées (${duration}ms)`, 'DEBUG');
await tracer.event('Smart Readability appliqué', {
duration,
modifications
});
return {
content: improvedContent,
modifications,
duration
};
} catch (error) {
const duration = Date.now() - startTime;
logSh(`❌ SMART READABILITY É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 lisibilité
const readabilityImprovements = analysis.improvements.filter(imp =>
imp.toLowerCase().includes('lisib') ||
imp.toLowerCase().includes('phrase') ||
imp.toLowerCase().includes('connecteur') ||
imp.toLowerCase().includes('structure') ||
imp.toLowerCase().includes('fluidité')
);
return `MISSION: Améliore UNIQUEMENT la lisibilité selon les points listés.
CONTENU ORIGINAL:
"${content}"
${mc0 ? `CONTEXTE: ${mc0}` : ''}
INTENSITÉ: ${intensity.toFixed(1)}
AMÉLIORATIONS LISIBILITÉ À APPLIQUER:
${readabilityImprovements.map((imp, i) => `${i + 1}. ${imp}`).join('\n')}
${analysis.readability.complexSentences && analysis.readability.complexSentences.length > 0 ? `
PHRASES TROP COMPLEXES (à simplifier):
${analysis.readability.complexSentences.map(line => `- Ligne ${line}`).join('\n')}
` : ''}
${analysis.readability.repetitiveConnectors && analysis.readability.repetitiveConnectors.length > 0 ? `
CONNECTEURS RÉPÉTITIFS (à varier):
${analysis.readability.repetitiveConnectors.map(conn => `- "${conn}"`).join('\n')}
` : ''}
CONSIGNES STRICTES:
- Applique UNIQUEMENT les améliorations lisibilité listées
- NE CHANGE PAS le sens, ton ou style général
- GARDE le même contenu informatif
- Phrases: 15-25 mots idéalement (simplifier si > 30 mots)
- Connecteurs: variés et naturels
- Structure: fluide et logique
TECHNIQUES LISIBILITÉ:
**Simplifier phrases longues:**
- ✅ AVANT: "Ce produit, qui a été conçu par nos experts après plusieurs années de recherche, permet d'obtenir des résultats exceptionnels."
- ✅ APRÈS: "Ce produit a été conçu par nos experts après plusieurs années de recherche. Il permet d'obtenir des résultats exceptionnels."
**Varier connecteurs:**
- ✅ AVANT: "Par ailleurs... Par ailleurs... Par ailleurs..."
- ✅ APRÈS: "Par ailleurs... De plus... En outre..."
**Fluidifier structure:**
- ✅ AVANT: "Produit X. Produit Y. Produit Z." (juxtaposition sèche)
- ✅ APRÈS: "Produit X offre... Quant au produit Y, il propose... Enfin, produit Z permet..."
**Clarifier relations:**
- ✅ AVANT: "Ce service existe. Il est pratique." (lien flou)
- ✅ APRÈS: "Ce service existe. Grâce à lui, vous gagnez du temps." (lien clair)
RÈGLES LISIBILITÉ:
- Privilégie clarté immédiate
- Évite subordonnées multiples imbriquées
- Structure logique: contexte → explication → bénéfice
- Connecteurs variés: évite répétitions sur 3 phrases consécutives
FORMAT RÉPONSE:
Retourne UNIQUEMENT le contenu fluidifié, 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 FLUIDIFIÉ:\s*/gi, '');
cleaned = cleaned.replace(/^(voici\s+)?le\s+contenu\s+(fluidifié|lisible)\s*[:.]?\s*/gi, '');
cleaned = cleaned.replace(/^(avec\s+)?amélioration[s]?\s+lisibilité\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 = { SmartReadabilityLayer };