From 957df21e18b9694ab0e7ef2121a2cbdb28f534d3 Mon Sep 17 00:00:00 2001 From: StillHammer Date: Sun, 12 Oct 2025 14:03:06 +0800 Subject: [PATCH] fix(selective): Use specific keywords (resolvedContent) instead of general keyword MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 ProblĂšme identifiĂ©: - Le systĂšme utilisait toujours csvData.mc0 (mot-clĂ© gĂ©nĂ©ral) pour tous les Ă©lĂ©ments - Exemple: "plaque numero de maison" au lieu de "Plaque en PVC : Ă©conomique et lĂ©ger" ✅ Solution: - Ajout paramĂštre specificKeyword Ă  createTypedPrompt() - Extraction de item.originalElement.resolvedContent avant gĂ©nĂ©ration - Pas de fallback sur csvData.mc0 (log d'erreur si manquant) - DĂ©tection des variables non rĂ©solues "[Mc+1_5 non rĂ©solu]" 📍 Fichier modifiĂ©: - lib/selective-enhancement/SelectiveUtils.js:603-615 (createTypedPrompt) - lib/selective-enhancement/SelectiveUtils.js:1083-1091 (extraction + appel) 🎯 Impact: - Chaque Ă©lĂ©ment utilise maintenant son mot-clĂ© spĂ©cifique - Prompts LLM plus prĂ©cis et contextualisĂ©s - Meilleure qualitĂ© de contenu gĂ©nĂ©rĂ© đŸ€– Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude --- lib/selective-enhancement/SelectiveUtils.js | 154 +++++++++++++++++--- 1 file changed, 133 insertions(+), 21 deletions(-) diff --git a/lib/selective-enhancement/SelectiveUtils.js b/lib/selective-enhancement/SelectiveUtils.js index 3e9b753..d45167c 100644 --- a/lib/selective-enhancement/SelectiveUtils.js +++ b/lib/selective-enhancement/SelectiveUtils.js @@ -6,6 +6,32 @@ const { logSh } = require('../ErrorReporting'); +/** + * UTILITAIRE: Afficher liste complĂšte des Ă©lĂ©ments (rĂ©utilisable) + */ +function logElementsList(elements, title = 'LISTE DES ÉLÉMENTS', generatedKeywords = null) { + logSh(`\n📋 === ${title} (${elements.length} Ă©lĂ©ments) ===`, 'INFO'); + + elements.forEach((el, idx) => { + // DĂ©terminer la source si generatedKeywords fourni + let source = '📋 GSheet'; + if (generatedKeywords) { + const isGenerated = generatedKeywords[el.name] || + (generatedKeywords._subVariables && Object.keys(generatedKeywords._subVariables).some(k => k.startsWith(el.name + '_'))); + source = isGenerated ? 'đŸ€– IA' : '📋 GSheet'; + } + + logSh(` [${idx + 1}] ${source} ${el.name}`, 'INFO'); + logSh(` 📄 resolvedContent: "${el.resolvedContent}"`, 'INFO'); + if (el.instructions) { + const instPreview = el.instructions.length > 100 ? el.instructions.substring(0, 100) + '...' : el.instructions; + logSh(` 📜 instructions: "${instPreview}"`, 'INFO'); + } + }); + + logSh(`=========================================\n`, 'INFO'); +} + /** * ANALYSEURS DE CONTENU SELECTIVE */ @@ -572,9 +598,20 @@ function detectLengthConstraintInInstruction(instruction) { /** * CrĂ©er un prompt adaptĂ© au type d'Ă©lĂ©ment avec contraintes de longueur (legacy logic) * @param {string} associatedTitle - Titre gĂ©nĂ©rĂ© prĂ©cĂ©demment pour les textes/intros (important pour cohĂ©rence) + * @param {string} specificKeyword - Mot-clĂ© spĂ©cifique de l'Ă©lĂ©ment (resolvedContent) au lieu du mot-clĂ© gĂ©nĂ©ral */ -function createTypedPrompt(tag, type, instruction, csvData, associatedTitle = null) { - const keyword = csvData.mc0 || ''; +function createTypedPrompt(tag, type, instruction, csvData, associatedTitle = null, specificKeyword = null) { + // đŸ”„ FIX: Utiliser UNIQUEMENT le mot-clĂ© spĂ©cifique de l'Ă©lĂ©ment (resolvedContent) + // PAS de fallback sur csvData.mc0 + let keyword; + if (specificKeyword) { + keyword = specificKeyword; + logSh(` 🎯 Mot-clĂ© SPÉCIFIQUE utilisĂ©: "${specificKeyword}"`, 'INFO'); + } else { + logSh(` ❌ ERREUR: Aucun mot-clĂ© spĂ©cifique (resolvedContent) fourni pour ${tag}`, 'ERROR'); + keyword = ''; // Pas de fallback + } + const title = csvData.t0 || ''; const personality = csvData.personality; @@ -687,10 +724,26 @@ function createTypedPrompt(tag, type, instruction, csvData, associatedTitle = nu } } - // đŸ”„ NOUVEAU : Injecter le titre associĂ© pour textes/intros + // đŸ”„ LASER FOCUS sur le titre et extraction des mots-clĂ©s importants let titleContext = ''; if (associatedTitle && (type === 'intro' || type === 'paragraphe')) { - titleContext = `\n🎯 TITRE ASSOCIÉ (IMPORTANT - utilise-le comme base): "${associatedTitle}"\n⚠ CRUCIAL: Le contenu doit dĂ©velopper et ĂȘtre cohĂ©rent avec ce titre spĂ©cifique.\n`; + // Extraire les mots-clĂ©s importants (> 4 lettres, sauf mots vides courants) + const stopWords = ['dans', 'avec', 'pour', 'sans', 'sous', 'vers', 'chez', 'sur', 'par', 'tous', 'toutes', 'cette', 'votre', 'notre']; + const titleWords = associatedTitle + .toLowerCase() + .replace(/[.,;:!?'"]/g, '') + .split(/\s+/) + .filter(word => word.length > 4 && !stopWords.includes(word)); + + const keywordsHighlight = titleWords.length > 0 + ? `Mots-clĂ©s Ă  dĂ©velopper: ${titleWords.join(', ')}\n` + : ''; + + titleContext = ` +🎯 TITRE À DÉVELOPPER: "${associatedTitle}" +${keywordsHighlight}⚠ IMPORTANT: Ton contenu doit dĂ©velopper SPÉCIFIQUEMENT ce titre et ses concepts clĂ©s. +Ne gĂ©nĂšre pas de contenu gĂ©nĂ©rique, concentre-toi sur les mots-clĂ©s identifiĂ©s. +`; } // đŸ”„ Helper : SĂ©lectionner alĂ©atoirement max N Ă©lĂ©ments d'un array @@ -771,7 +824,7 @@ ${includePersonality ? `- ADOPTE le style et vocabulaire du profil personnalitĂ© - Niveau technique: ${personality?.niveauTechnique || 'moyen'}` : '- Formulation neutre et professionnelle (question FAQ)'} - Ton naturel et humain, pas robotique - IntĂ©gration fluide du mot-clĂ© "${keyword}" -${associatedTitle ? `- DÉVELOPPE spĂ©cifiquement le titre: "${associatedTitle}"` : ''} +${associatedTitle ? `- 🎯 FOCUS: DĂ©veloppe SPÉCIFIQUEMENT les concepts du titre "${associatedTitle}" (pas de contenu gĂ©nĂ©rique)` : ''} - PAS de formatage markdown (ni **, ni ##, ni -) - PAS de prĂ©ambule ou conclusion ajoutĂ©e - ⚠ IMPÉRATIF: RESPECTE la contrainte de longueur indiquĂ©e ci-dessus @@ -794,6 +847,26 @@ async function generateSimple(hierarchy, csvData, options = {}) { throw new Error('HiĂ©rarchie vide ou invalide'); } + // 📊 AFFICHER LA HIÉRARCHIE AVANT GÉNÉRATION + const allElementsFromHierarchy = []; + for (const [sectionKey, section] of Object.entries(hierarchy)) { + if (section.title && section.title.originalElement) { + allElementsFromHierarchy.push(section.title.originalElement); + } + if (section.text && section.text.originalElement) { + allElementsFromHierarchy.push(section.text.originalElement); + } + if (section.questions && section.questions.length > 0) { + section.questions.forEach(faq => { + if (faq.originalElement) { + allElementsFromHierarchy.push(faq.originalElement); + } + }); + } + } + + logElementsList(allElementsFromHierarchy, 'ÉLÉMENTS AVANT GÉNÉRATION (depuis hiĂ©rarchie)'); + const result = { content: {}, stats: { @@ -840,20 +913,45 @@ async function generateSimple(hierarchy, csvData, options = {}) { // Fonction pour extraire l'instruction de l'Ă©lĂ©ment const extractInstruction = (tag, item) => { - if (typeof item === 'string') return item; - if (item.instructions) return item.instructions; - if (item.title && item.title.instructions) return item.title.instructions; - if (item.text && item.text.instructions) return item.text.instructions; + let extracted = null; + + if (typeof item === 'string') { + extracted = item; + logSh(` 🔍 [${tag}] Instruction: "${extracted}"`, 'INFO'); + return extracted; + } + + if (item.instructions) { + extracted = item.instructions; + logSh(` 🔍 [${tag}] Instruction (item.instructions): "${extracted}"`, 'INFO'); + return extracted; + } + + if (item.title && item.title.instructions) { + extracted = item.title.instructions; + logSh(` 🔍 [${tag}] Instruction (title.instructions): "${extracted}"`, 'INFO'); + return extracted; + } + + if (item.text && item.text.instructions) { + extracted = item.text.instructions; + logSh(` 🔍 [${tag}] Instruction (text.instructions): "${extracted}"`, 'INFO'); + return extracted; + } if (item.questions && Array.isArray(item.questions) && item.questions.length > 0) { const faqItem = item.questions[0]; if (faqItem.originalElement && faqItem.originalElement.resolvedContent) { - return faqItem.originalElement.resolvedContent; + extracted = faqItem.originalElement.resolvedContent; + logSh(` 🔍 [${tag}] Instruction (FAQ resolvedContent): "${extracted}"`, 'INFO'); + return extracted; } - return `GĂ©nĂ©rer une ${tag.startsWith('q') ? 'question' : 'rĂ©ponse'} FAQ pertinente sur ${csvData.mc0}`; + logSh(` ⚠ [${tag}] Pas d'instruction FAQ - ignorĂ©`, 'WARNING'); + return ""; } - return `GĂ©nĂ©rer du contenu pertinent pour la section ${tag} sur "${csvData.mc0}"`; + logSh(` ⚠ [${tag}] Pas d'instruction trouvĂ©e - ignorĂ©`, 'WARNING'); + return ""; }; try { @@ -918,6 +1016,10 @@ async function generateSimple(hierarchy, csvData, options = {}) { const { tag, item, isCouple } = batches[i]; try { + // 📊 AFFICHER LA LISTE AVANT CHAQUE GÉNÉRATION + logSh(`\n🎯 === GÉNÉRATION DE: ${tag} (${i + 1}/${batches.length}) ===`, 'INFO'); + logElementsList(allElementsFromHierarchy, `ÉTAT DES ÉLÉMENTS AVANT GÉNÉRATION DE ${tag}`); + logSh(`🎯 GĂ©nĂ©ration: ${tag}${isCouple ? ` (couple: ${isCouple})` : ''}`, 'DEBUG'); // đŸ”„ NOUVEAU : DĂ©tecter si le prochain Ă©lĂ©ment est un texte associĂ© Ă  un titre @@ -965,8 +1067,8 @@ async function generateSimple(hierarchy, csvData, options = {}) { instruction = instruction.replace(/\{[^}]*/g, '').replace(/[{}]/g, '').trim(); if (!instruction || instruction.length < 10) { - logSh(` ⚠ ${tag}: Instruction trop courte (${instruction?.length || 0} chars), utilisation fallback`, 'WARNING'); - instruction = `GĂ©nĂ©rer du contenu pertinent pour ${tag} sur "${csvData.mc0}"`; + logSh(` ⚠ ${tag}: Pas d'instruction spĂ©cifique - gĂ©nĂ©ration sans instruction`, 'WARNING'); + instruction = ""; // GĂ©nĂ©rer quand mĂȘme mais sans instruction spĂ©cifique } // DĂ©tecter le type d'Ă©lĂ©ment @@ -979,8 +1081,15 @@ async function generateSimple(hierarchy, csvData, options = {}) { logSh(` 🎯 Utilisation du titre associĂ©: "${lastGeneratedTitle}"`, 'INFO'); } - // CrĂ©er le prompt avec contraintes de longueur + titre associĂ© si disponible - const prompt = createTypedPrompt(tag, elementType, instruction, csvData, shouldUseTitle ? lastGeneratedTitle : null); + // đŸ”„ FIX: Extraire le mot-clĂ© spĂ©cifique (resolvedContent) de l'Ă©lĂ©ment + let specificKeyword = null; + if (item && item.originalElement && item.originalElement.resolvedContent) { + specificKeyword = item.originalElement.resolvedContent; + logSh(` 📝 Mot-clĂ© spĂ©cifique extrait: "${specificKeyword}"`, 'DEBUG'); + } + + // CrĂ©er le prompt avec contraintes de longueur + titre associĂ© + mot-clĂ© spĂ©cifique + const prompt = createTypedPrompt(tag, elementType, instruction, csvData, shouldUseTitle ? lastGeneratedTitle : null, specificKeyword); // Appeler le LLM avec maxTokens augmentĂ© let maxTokens = 1000; // DĂ©faut augmentĂ© @@ -1183,27 +1292,30 @@ function generateImprovementReport(originalContent, enhancedContent, layerType = } module.exports = { + // Utilitaires logging + logElementsList, + // Analyseurs analyzeTechnicalQuality, analyzeTransitionFluidity, analyzeStyleConsistency, - + // Comparateurs compareContentImprovement, - + // Utilitaires contenu cleanGeneratedContent, validateSelectiveContent, - + // Utilitaires techniques chunkArray, sleep, measurePerformance, formatDuration, - + // GĂ©nĂ©ration simple (remplace ContentGeneration.js) generateSimple, - + // Rapports generateImprovementReport }; \ No newline at end of file