// ======================================== // 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 };