Added plan.md with complete architecture for format-agnostic content generation: - Support for Markdown, HTML, Plain Text, JSON formats - New FormatExporter module with neutral data structure - Integration strategy with existing ContentAssembly and ArticleStorage - Bonus features: SEO metadata generation, readability scoring, WordPress Gutenberg format - Implementation roadmap with 4 phases (6h total estimated) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
555 lines
28 KiB
Plaintext
555 lines
28 KiB
Plaintext
[1mdiff --git a/.gitignore b/.gitignore[m
|
||
[1mindex cff1b08..7217f01 100644[m
|
||
[1m--- a/.gitignore[m
|
||
[1m+++ b/.gitignore[m
|
||
[36m@@ -53,8 +53,7 @@[m [mtest_*.js[m
|
||
*_debug.js[m
|
||
test-*.js[m
|
||
[m
|
||
[31m-# HTML généré (logs viewer)[m
|
||
[31m-logs-viewer.html[m
|
||
[32m+[m[32m# HTML généré était ici mais maintenant on le garde dans tools/[m
|
||
[m
|
||
# Unit test reports[m
|
||
TEST*.xml[m
|
||
[1mdiff --git a/code.js b/code.js[m
|
||
[1mindex a1dcd72..a2bdb22 100644[m
|
||
[1m--- a/code.js[m
|
||
[1m+++ b/code.js[m
|
||
[36m@@ -1,6 +1,6 @@[m
|
||
/*[m
|
||
code.js — bundle concaténé[m
|
||
[31m- Généré: 2025-09-03T04:21:57.159Z[m
|
||
[32m+[m[32m Généré: 2025-09-04T01:10:08.540Z[m
|
||
Source: lib[m
|
||
Fichiers: 16[m
|
||
Ordre: topo[m
|
||
[36m@@ -57,12 +57,13 @@[m [mconst fileDest = pino.destination({[m
|
||
});[m
|
||
tee.pipe(fileDest);[m
|
||
[m
|
||
[31m-// Custom levels for Pino to include TRACE and PROMPT[m
|
||
[32m+[m[32m// Custom levels for Pino to include TRACE, PROMPT, and LLM[m
|
||
const customLevels = {[m
|
||
trace: 5, // Below debug (10)[m
|
||
debug: 10,[m
|
||
info: 20,[m
|
||
prompt: 25, // New level for prompts (between info and warn)[m
|
||
[32m+[m[32m llm: 26, // New level for LLM interactions (between prompt and warn)[m
|
||
warn: 30,[m
|
||
error: 40,[m
|
||
fatal: 50[m
|
||
[36m@@ -178,6 +179,9 @@[m [masync function logSh(message, level = 'INFO') {[m
|
||
case 'prompt':[m
|
||
logger.prompt(traceData, message);[m
|
||
break;[m
|
||
[32m+[m[32m case 'llm':[m
|
||
[32m+[m[32m logger.llm(traceData, message);[m
|
||
[32m+[m[32m break;[m
|
||
default:[m
|
||
logger.info(traceData, message);[m
|
||
}[m
|
||
[36m@@ -1282,7 +1286,10 @@[m [masync function callLLM(llmProvider, prompt, options = {}, personality = null) {[m
|
||
// 📢 AFFICHAGE PROMPT COMPLET POUR DEBUG AVEC INFO IA[m
|
||
logSh(`\n🔍 ===== PROMPT ENVOYÉ À ${llmProvider.toUpperCase()} (${config.model}) | PERSONNALITÉ: ${personality?.nom || 'AUCUNE'} =====`, 'PROMPT');[m
|
||
logSh(prompt, 'PROMPT');[m
|
||
[31m- logSh(`===== FIN PROMPT ${llmProvider.toUpperCase()} (${personality?.nom || 'AUCUNE'}) =====\n`, 'PROMPT');[m
|
||
[32m+[m[41m [m
|
||
[32m+[m[32m // 📤 LOG LLM REQUEST COMPLET[m
|
||
[32m+[m[32m logSh(`📤 LLM REQUEST [${llmProvider.toUpperCase()}] (${config.model}) | Personnalité: ${personality?.nom || 'AUCUNE'}`, 'LLM');[m
|
||
[32m+[m[32m logSh(prompt, 'LLM');[m
|
||
[m
|
||
// Préparer la requête selon le provider[m
|
||
const requestData = buildRequestData(llmProvider, prompt, options, personality);[m
|
||
[36m@@ -1293,8 +1300,12 @@[m [masync function callLLM(llmProvider, prompt, options = {}, personality = null) {[m
|
||
// Parser la réponse selon le format du provider[m
|
||
const content = parseResponse(llmProvider, response);[m
|
||
[m
|
||
[32m+[m[32m // 📥 LOG LLM RESPONSE COMPLET[m
|
||
[32m+[m[32m logSh(`📥 LLM RESPONSE [${llmProvider.toUpperCase()}] (${config.model}) | Durée: ${Date.now() - startTime}ms`, 'LLM');[m
|
||
[32m+[m[32m logSh(content, 'LLM');[m
|
||
[32m+[m[41m [m
|
||
const duration = Date.now() - startTime;[m
|
||
[31m- logSh(`✅ ${llmProvider.toUpperCase()} (${personality?.nom || 'sans personnalité'}) réponse en ${duration}ms: "${content.substring(0, 150)}${content.length > 150 ? '...' : ''}"`, 'INFO');[m
|
||
[32m+[m[32m logSh(`✅ ${llmProvider.toUpperCase()} (${personality?.nom || 'sans personnalité'}) réponse en ${duration}ms`, 'INFO');[m
|
||
[m
|
||
// Enregistrer les stats d'usage[m
|
||
await recordUsageStats(llmProvider, prompt.length, content.length, duration);[m
|
||
[36m@@ -1727,6 +1738,8 @@[m [mmodule.exports = {[m
|
||
LLM_CONFIG[m
|
||
};[m
|
||
[m
|
||
[32m+[m
|
||
[32m+[m
|
||
/*[m
|
||
┌────────────────────────────────────────────────────────────────────┐[m
|
||
│ File: lib/ElementExtraction.js │[m
|
||
[36m@@ -2309,38 +2322,21 @@[m [mCONTEXTE:[m
|
||
`;[m
|
||
[m
|
||
missingElements.forEach((missing, index) => {[m
|
||
[31m- prompt += `${index + 1}. [${missing.name}] `;[m
|
||
[31m- [m
|
||
[31m- // INSTRUCTIONS SPÉCIFIQUES PAR TYPE[m
|
||
[31m- if (missing.type.includes('titre_h1')) {[m
|
||
[31m- prompt += `→ Titre H1 principal (8-10 mots) pour ${contextAnalysis.mainKeyword}\n`;[m
|
||
[31m- } else if (missing.type.includes('titre_h2')) {[m
|
||
[31m- prompt += `→ Titre H2 section (6-8 mots) lié à ${contextAnalysis.mainKeyword}\n`;[m
|
||
[31m- } else if (missing.type.includes('titre_h3')) {[m
|
||
[31m- prompt += `→ Sous-titre H3 (4-6 mots) spécialisé ${contextAnalysis.mainKeyword}\n`;[m
|
||
[31m- } else if (missing.type.includes('texte') || missing.type.includes('txt')) {[m
|
||
[31m- prompt += `→ Thème/sujet pour paragraphe 150 mots sur ${contextAnalysis.mainKeyword}\n`;[m
|
||
[31m- } else if (missing.type.includes('faq_question')) {[m
|
||
[31m- prompt += `→ Question client directe sur ${contextAnalysis.mainKeyword} (8-12 mots)\n`;[m
|
||
[31m- } else if (missing.type.includes('faq_reponse')) {[m
|
||
[31m- prompt += `→ Thème réponse experte ${contextAnalysis.mainKeyword} (2-4 mots)\n`;[m
|
||
[31m- } else {[m
|
||
[31m- prompt += `→ Expression/mot-clé pertinent ${contextAnalysis.mainKeyword}\n`;[m
|
||
[31m- }[m
|
||
[32m+[m[32m prompt += `${index + 1}. [${missing.name}] → Mot-clé SEO\n`;[m
|
||
});[m
|
||
[m
|
||
prompt += `\nCONSIGNES:[m
|
||
[31m-- Reste dans le thème ${contextAnalysis.mainKeyword}[m
|
||
[31m-- Varie les angles et expressions[m
|
||
[31m-- Évite répétitions avec mots-clés existants[m
|
||
[31m-- Précis et pertinents[m
|
||
[32m+[m[32m- Thème: ${contextAnalysis.mainKeyword}[m
|
||
[32m+[m[32m- Mots-clés SEO naturels[m
|
||
[32m+[m[32m- Varie les termes[m
|
||
[32m+[m[32m- Évite répétitions[m
|
||
[m
|
||
FORMAT:[m
|
||
[${missingElements[0].name}][m
|
||
[31m-Expression/mot-clé généré 1[m
|
||
[32m+[m[32mmot-clé[m
|
||
[m
|
||
[${missingElements[1] ? missingElements[1].name : 'exemple'}][m
|
||
[31m-Expression/mot-clé généré 2[m
|
||
[32m+[m[32mmot-clé[m
|
||
[m
|
||
etc...`;[m
|
||
[m
|
||
[36m@@ -2596,6 +2592,11 @@[m [mconst { logSh } = require('./ErrorReporting');[m
|
||
const { tracer } = require('./trace.js');[m
|
||
const { selectMultiplePersonalitiesWithAI, getPersonalities } = require('./BrainConfig');[m
|
||
[m
|
||
[32m+[m[32m// Utilitaire pour les délais[m
|
||
[32m+[m[32mfunction sleep(ms) {[m
|
||
[32m+[m[32m return new Promise(resolve => setTimeout(resolve, ms));[m
|
||
[32m+[m[32m}[m
|
||
[32m+[m
|
||
/**[m
|
||
* NOUVELLE APPROCHE - Multi-Personnalités Batch Enhancement[m
|
||
* 4 personnalités différentes utilisées dans le pipeline pour maximum d'anti-détection[m
|
||
[36m@@ -2804,8 +2805,8 @@[m [masync function generateAllContentBase(hierarchy, csvData, aiProvider) {[m
|
||
}[m
|
||
[m
|
||
/**[m
|
||
[31m- * ÉTAPE 2 - Enhancement technique BATCH OPTIMISÉ avec IA configurable[m
|
||
[31m- * OPTIMISATION : 1 appel extraction + 1 appel enhancement au lieu de 20+[m
|
||
[32m+[m[32m * ÉTAPE 2 - Enhancement technique ÉLÉMENT PAR ÉLÉMENT avec IA configurable[m
|
||
[32m+[m[32m * NOUVEAU : Traitement individuel pour fiabilité maximale et debug précis[m
|
||
*/[m
|
||
async function enhanceAllTechnicalTerms(baseContents, csvData, aiProvider) {[m
|
||
logSh('🔧 === DÉBUT ENHANCEMENT TECHNIQUE ===', 'INFO');[m
|
||
[36m@@ -2872,96 +2873,96 @@[m [masync function enhanceAllTechnicalTerms(baseContents, csvData, aiProvider) {[m
|
||
}[m
|
||
[m
|
||
/**[m
|
||
[31m- * NOUVELLE FONCTION : Extraction batch TOUS les termes techniques[m
|
||
[32m+[m[32m * Analyser un seul élément pour détecter les termes techniques[m
|
||
*/[m
|
||
[31m-async function extractAllTechnicalTermsBatch(baseContents, csvData, aiProvider) {[m
|
||
[31m- const contentEntries = Object.keys(baseContents);[m
|
||
[31m- [m
|
||
[31m- const batchAnalysisPrompt = `MISSION: Analyser ces ${contentEntries.length} contenus et identifier leurs termes techniques.[m
|
||
[32m+[m[32masync function analyzeSingleElementTechnicalTerms(tag, content, csvData, aiProvider) {[m
|
||
[32m+[m[32m const prompt = `MISSION: Analyser ce contenu et déterminer s'il contient des termes techniques.[m
|
||
[m
|
||
CONTEXTE: ${csvData.mc0} - Secteur: signalétique/impression[m
|
||
[m
|
||
[31m-CONTENUS À ANALYSER:[m
|
||
[31m-[m
|
||
[31m-${contentEntries.map((tag, i) => `[${i + 1}] TAG: ${tag}[m
|
||
[31m-CONTENU: "${baseContents[tag]}"`).join('\n\n')}[m
|
||
[32m+[m[32mCONTENU À ANALYSER:[m
|
||
[32m+[m[32mTAG: ${tag}[m
|
||
[32m+[m[32mCONTENU: "${content}"[m
|
||
[m
|
||
CONSIGNES:[m
|
||
[31m-- Identifie UNIQUEMENT les vrais termes techniques métier/industrie[m
|
||
[32m+[m[32m- Cherche UNIQUEMENT des vrais termes techniques métier/industrie[m
|
||
- Évite mots génériques (qualité, service, pratique, personnalisé, etc.)[m
|
||
[31m-- Focus: matériaux, procédés, normes, dimensions, technologies[m
|
||
[31m-- Si aucun terme technique → "AUCUN"[m
|
||
[32m+[m[32m- Focus: matériaux, procédés, normes, dimensions, technologies spécifiques[m
|
||
[m
|
||
[31m-EXEMPLES VALIDES: dibond, impression UV, fraisage CNC, épaisseur 3mm, aluminium brossé[m
|
||
[31m-EXEMPLES INVALIDES: durable, pratique, personnalisé, moderne, esthétique[m
|
||
[32m+[m[32mEXEMPLES VALIDES: dibond, impression UV, fraisage CNC, épaisseur 3mm, aluminium brossé, anodisation[m
|
||
[32m+[m[32mEXEMPLES INVALIDES: durable, pratique, personnalisé, moderne, esthétique, haute performance[m
|
||
[m
|
||
[31m-FORMAT RÉPONSE EXACT:[m
|
||
[31m-[1] dibond, impression UV, 3mm OU AUCUN[m
|
||
[31m-[2] aluminium, fraisage CNC OU AUCUN[m
|
||
[31m-[3] AUCUN[m
|
||
[31m-etc... (${contentEntries.length} lignes total)`;[m
|
||
[32m+[m[32mRÉPONSE REQUISE:[m
|
||
[32m+[m[32m- Si termes techniques trouvés: "OUI - termes: [liste des termes séparés par virgules]"[m
|
||
[32m+[m[32m- Si aucun terme technique: "NON"[m
|
||
[32m+[m
|
||
[32m+[m[32mEXEMPLE:[m
|
||
[32m+[m[32mOUI - termes: aluminium composite, impression numérique, gravure laser`;[m
|
||
[m
|
||
try {[m
|
||
[31m- const analysisResponse = await callLLM(aiProvider, batchAnalysisPrompt, {[m
|
||
[31m- temperature: 0.3,[m
|
||
[31m- maxTokens: 2000[m
|
||
[31m- }, csvData.personality);[m
|
||
[31m- [m
|
||
[31m- return parseAllTechnicalTermsResponse(analysisResponse, baseContents, contentEntries);[m
|
||
[31m- [m
|
||
[32m+[m[32m const response = await callLLM(aiProvider, prompt, { temperature: 0.3 });[m
|
||
[32m+[m[41m [m
|
||
[32m+[m[32m if (response.toUpperCase().startsWith('OUI')) {[m
|
||
[32m+[m[32m // Extraire les termes de la réponse[m
|
||
[32m+[m[32m const termsMatch = response.match(/termes:\s*(.+)/i);[m
|
||
[32m+[m[32m const terms = termsMatch ? termsMatch[1].trim() : '';[m
|
||
[32m+[m[32m logSh(`✅ [${tag}] Termes techniques détectés: ${terms}`, 'DEBUG');[m
|
||
[32m+[m[32m return true;[m
|
||
[32m+[m[32m } else {[m
|
||
[32m+[m[32m logSh(`⏭️ [${tag}] Pas de termes techniques`, 'DEBUG');[m[41m [m
|
||
[32m+[m[32m return false;[m
|
||
[32m+[m[32m }[m
|
||
} catch (error) {[m
|
||
[31m- logSh(`❌ FATAL: Extraction termes techniques batch échouée: ${error.message}`, 'ERROR');[m
|
||
[31m- throw new Error(`FATAL: Analyse termes techniques impossible - arrêt du workflow: ${error.message}`);[m
|
||
[32m+[m[32m logSh(`❌ ERREUR analyse ${tag}: ${error.message}`, 'ERROR');[m
|
||
[32m+[m[32m return false; // En cas d'erreur, on skip l'enhancement[m
|
||
}[m
|
||
}[m
|
||
[m
|
||
/**[m
|
||
[31m- * NOUVELLE FONCTION : Enhancement batch TOUS les éléments[m
|
||
[32m+[m[32m * Enhancer un seul élément techniquement[m
|
||
*/[m
|
||
[31m-async function enhanceAllElementsTechnicalBatch(elementsNeedingEnhancement, csvData, aiProvider) {[m
|
||
[31m- if (elementsNeedingEnhancement.length === 0) return {};[m
|
||
[31m- [m
|
||
[31m- const batchEnhancementPrompt = `MISSION: Améliore UNIQUEMENT la précision technique de ces ${elementsNeedingEnhancement.length} contenus.[m
|
||
[32m+[m[32masync function enhanceSingleElementTechnical(tag, content, csvData, aiProvider) {[m
|
||
[32m+[m[32m const prompt = `MISSION: Améliore ce contenu en intégrant des termes techniques précis.[m
|
||
[m
|
||
[31m-PERSONNALITÉ: ${csvData.personality?.nom} (${csvData.personality?.style})[m
|
||
[31m-CONTEXTE: ${csvData.mc0} - Secteur: Signalétique/impression[m
|
||
[31m-VOCABULAIRE PRÉFÉRÉ: ${csvData.personality?.vocabulairePref}[m
|
||
[31m-[m
|
||
[31m-CONTENUS + TERMES À AMÉLIORER:[m
|
||
[31m-[m
|
||
[31m-${elementsNeedingEnhancement.map((item, i) => `[${i + 1}] TAG: ${item.tag}[m
|
||
[31m-CONTENU ACTUEL: "${item.content}"[m
|
||
[31m-TERMES TECHNIQUES À INTÉGRER: ${item.technicalTerms.join(', ')}`).join('\n\n')}[m
|
||
[32m+[m[32mCONTEXTE: ${csvData.mc0} - Secteur: signalétique/impression[m
|
||
[m
|
||
[31m-CONSIGNES STRICTES:[m
|
||
[31m-- Améliore UNIQUEMENT la précision technique, garde le style ${csvData.personality?.nom}[m
|
||
[31m-- GARDE la même longueur, structure et ton[m
|
||
[31m-- Intègre naturellement les termes techniques listés[m
|
||
[31m-- NE CHANGE PAS le fond du message ni le style personnel[m
|
||
[31m-- Utilise un vocabulaire expert mais accessible[m
|
||
[31m-- ÉVITE les répétitions excessives[m
|
||
[31m-- RESPECTE le niveau technique: ${csvData.personality?.niveauTechnique}[m
|
||
[31m-- Termes techniques secteur: dibond, aluminium, impression UV, fraisage, épaisseur, PMMA[m
|
||
[32m+[m[32mCONTENU À AMÉLIORER:[m
|
||
[32m+[m[32mTAG: ${tag}[m
|
||
[32m+[m[32mCONTENU: "${content}"[m
|
||
[m
|
||
[31m-FORMAT RÉPONSE:[m
|
||
[31m-[1] Contenu avec amélioration technique selon ${csvData.personality?.nom}[m
|
||
[31m-[2] Contenu avec amélioration technique selon ${csvData.personality?.nom}[m
|
||
[31m-etc... (${elementsNeedingEnhancement.length} éléments total)`;[m
|
||
[32m+[m[32mOBJECTIFS:[m
|
||
[32m+[m[32m- Remplace les termes génériques par des termes techniques précis[m
|
||
[32m+[m[32m- Ajoute des spécifications techniques réalistes[m
|
||
[32m+[m[32m- Maintient le même style et longueur[m
|
||
[32m+[m[32m- Intègre naturellement: matériaux (dibond, aluminium composite), procédés (impression UV, gravure laser), dimensions, normes[m
|
||
[32m+[m
|
||
[32m+[m[32mEXEMPLE DE TRANSFORMATION:[m
|
||
[32m+[m[32m"matériaux haute performance" → "dibond 3mm ou aluminium composite"[m
|
||
[32m+[m[32m"impression moderne" → "impression UV haute définition"[m
|
||
[32m+[m[32m"fixation solide" → "fixation par chevilles inox Ø6mm"[m
|
||
[32m+[m
|
||
[32m+[m[32mCONTRAINTES:[m
|
||
[32m+[m[32m- GARDE la même structure[m
|
||
[32m+[m[32m- MÊME longueur approximative[m
|
||
[32m+[m[32m- Style cohérent avec l'original[m
|
||
[32m+[m[32m- RÉPONDS DIRECTEMENT par le contenu amélioré, sans préfixe`;[m
|
||
[m
|
||
try {[m
|
||
[31m- const enhanced = await callLLM(aiProvider, batchEnhancementPrompt, {[m
|
||
[31m- temperature: 0.4,[m
|
||
[31m- maxTokens: 5000 // Plus large pour batch total[m
|
||
[31m- }, csvData.personality);[m
|
||
[31m- [m
|
||
[31m- return parseTechnicalEnhancementBatchResponse(enhanced, elementsNeedingEnhancement);[m
|
||
[31m- [m
|
||
[32m+[m[32m const enhancedContent = await callLLM(aiProvider, prompt, { temperature: 0.7 });[m
|
||
[32m+[m[32m return enhancedContent.trim();[m
|
||
} catch (error) {[m
|
||
[31m- logSh(`❌ FATAL: Enhancement technique batch échoué: ${error.message}`, 'ERROR');[m
|
||
[31m- throw new Error(`FATAL: Enhancement technique batch impossible - arrêt du workflow: ${error.message}`);[m
|
||
[32m+[m[32m logSh(`❌ ERREUR enhancement ${tag}: ${error.message}`, 'ERROR');[m
|
||
[32m+[m[32m return content; // En cas d'erreur, on retourne le contenu original[m
|
||
}[m
|
||
}[m
|
||
[m
|
||
[32m+[m[32m// ANCIENNES FONCTIONS BATCH SUPPRIMÉES - REMPLACÉES PAR TRAITEMENT INDIVIDUEL[m
|
||
[32m+[m
|
||
[32m+[m[32m/**[m
|
||
[32m+[m[32m * NOUVELLE FONCTION : Enhancement batch TOUS les éléments[m
|
||
[32m+[m[32m */[m
|
||
[32m+[m[32m// FONCTION SUPPRIMÉE : enhanceAllElementsTechnicalBatch() - Remplacée par traitement individuel[m
|
||
[32m+[m
|
||
/**[m
|
||
* ÉTAPE 3 - Enhancement transitions BATCH avec IA configurable[m
|
||
*/[m
|
||
[36m@@ -3015,7 +3016,8 @@[m [masync function enhanceAllTransitions(baseContents, csvData, aiProvider) {[m
|
||
[m
|
||
const batchTransitionsPrompt = `MISSION: Améliore UNIQUEMENT les transitions et fluidité de ces contenus.[m
|
||
[m
|
||
[31m-PERSONNALITÉ: ${csvData.personality?.nom} (${csvData.personality?.style})[m
|
||
[32m+[m[32mCONTEXTE: Article SEO professionnel pour site web commercial[m
|
||
[32m+[m[32mPERSONNALITÉ: ${csvData.personality?.nom} (${csvData.personality?.style} adapté web)[m
|
||
CONNECTEURS PRÉFÉRÉS: ${csvData.personality?.connecteursPref}[m
|
||
[m
|
||
CONTENUS:[m
|
||
[36m@@ -3093,9 +3095,10 @@[m [masync function enhanceAllPersonalityStyle(baseContents, csvData, aiProvider) {[m
|
||
[m
|
||
const batchStylePrompt = `MISSION: Adapte UNIQUEMENT le style de ces contenus selon ${personality.nom}.[m
|
||
[m
|
||
[32m+[m[32mCONTEXTE: Finalisation article SEO pour site e-commerce professionnel[m
|
||
PERSONNALITÉ: ${personality.nom}[m
|
||
DESCRIPTION: ${personality.description}[m
|
||
[31m-STYLE CIBLE: ${personality.style}[m
|
||
[32m+[m[32mSTYLE CIBLE: ${personality.style} adapté au web professionnel[m
|
||
VOCABULAIRE: ${personality.vocabulairePref}[m
|
||
CONNECTEURS: ${personality.connecteursPref}[m
|
||
NIVEAU TECHNIQUE: ${personality.niveauTechnique}[m
|
||
[36m@@ -3149,9 +3152,8 @@[m [metc...`;[m
|
||
/**[m
|
||
* Sleep function replacement for Utilities.sleep[m
|
||
*/[m
|
||
[31m-function sleep(ms) {[m
|
||
[31m- return new Promise(resolve => setTimeout(resolve, ms));[m
|
||
[31m-}[m
|
||
[32m+[m
|
||
[32m+[m[32m// FONCTION SUPPRIMÉE : sleep() dupliquée - déjà définie ligne 12[m
|
||
[m
|
||
/**[m
|
||
* RESTAURÉ DEPUIS .GS : Génération des paires FAQ cohérentes[m
|
||
[36m@@ -3187,32 +3189,33 @@[m [masync function generateFAQPairsRestored(faqPairs, csvData, aiProvider) {[m
|
||
function createBatchFAQPairsPrompt(faqPairs, csvData) {[m
|
||
const personality = csvData.personality;[m
|
||
[m
|
||
[31m- let prompt = `PERSONNALITÉ: ${personality.nom} | ${personality.description}[m
|
||
[31m-STYLE: ${personality.style}[m
|
||
[31m-VOCABULAIRE: ${personality.vocabulairePref}[m
|
||
[31m-CONNECTEURS: ${personality.connecteursPref}[m
|
||
[31m-NIVEAU TECHNIQUE: ${personality.niveauTechnique}[m
|
||
[32m+[m[32m let prompt = `=== 1. CONTEXTE ===[m
|
||
[32m+[m[32mEntreprise: Autocollant.fr - signalétique personnalisée[m
|
||
[32m+[m[32mSujet: ${csvData.mc0}[m
|
||
[32m+[m[32mSection: FAQ pour article SEO commercial[m
|
||
[m
|
||
[31m-GÉNÈRE ${faqPairs.length} PAIRES FAQ COHÉRENTES pour ${csvData.mc0}:[m
|
||
[32m+[m[32m=== 2. PERSONNALITÉ ===[m
|
||
[32m+[m[32mRédacteur: ${personality.nom}[m
|
||
[32m+[m[32mStyle: ${personality.style}[m
|
||
[32m+[m[32mTon: ${personality.description || 'professionnel'}[m
|
||
[m
|
||
[31m-RÈGLES STRICTES:[m
|
||
[31m-- QUESTIONS: Neutres, directes, langage client naturel (8-15 mots)[m
|
||
[31m-- RÉPONSES: Style ${personality.style}, vocabulaire ${personality.vocabulairePref} (50-80 mots)[m
|
||
[31m-- Sujets à couvrir: prix, livraison, personnalisation, installation, durabilité[m
|
||
[31m-- ÉVITE répétitions excessives et expressions trop familières[m
|
||
[31m-- Style ${personality.nom} reconnaissable mais PROFESSIONNEL[m
|
||
[31m-- PAS de messages d'excuse ("je n'ai pas l'information")[m
|
||
[31m-- RÉPONDS DIRECTEMENT par questions et réponses, sans préfixe[m
|
||
[32m+[m[32m=== 3. RÈGLES GÉNÉRALES ===[m
|
||
[32m+[m[32m- Questions naturelles de clients[m
|
||
[32m+[m[32m- Réponses expertes et rassurantes[m[41m [m
|
||
[32m+[m[32m- Langage professionnel mais accessible[m
|
||
[32m+[m[32m- Textes rédigés humainement et de façon authentique[m
|
||
[32m+[m[32m- Couvrir: prix, livraison, personnalisation, installation, durabilité[m
|
||
[32m+[m[32m- IMPÉRATIF: Respecter strictement les contraintes XML[m
|
||
[32m+[m
|
||
[32m+[m[32m=== 4. PAIRES FAQ À GÉNÉRER ===[m
|
||
[m
|
||
[31m-PAIRES À GÉNÉRER:[m
|
||
`;[m
|
||
[m
|
||
faqPairs.forEach((pair, index) => {[m
|
||
const questionTag = pair.question.tag.replace(/\|/g, '').replace(/[{}]/g, '').replace(/<\/?strong>/g, '');[m
|
||
const answerTag = pair.answer.tag.replace(/\|/g, '').replace(/[{}]/g, '').replace(/<\/?strong>/g, '');[m
|
||
[m
|
||
[31m- prompt += `${index + 1}. [${questionTag}] + [${answerTag}][m
|
||
[31m- Question client sur ${csvData.mc0} → Réponse ${personality.style}[m
|
||
[32m+[m[32m prompt += `${index + 1}. [${questionTag}] + [${answerTag}] - Paire FAQ naturelle[m
|
||
`;[m
|
||
});[m
|
||
[m
|
||
[36m@@ -3533,10 +3536,25 @@[m [mfunction findAssociatedTitle(textElement, existingResults) {[m
|
||
function createBatchBasePrompt(elements, type, csvData, existingResults = {}) {[m
|
||
const personality = csvData.personality;[m
|
||
[m
|
||
[31m- let prompt = `RÉDACTEUR: ${personality.nom} | Style: ${personality.style}[m
|
||
[31m-SUJET: ${csvData.mc0}[m
|
||
[31m-[m
|
||
[31m-${type === 'titre' ? 'GÉNÈRE DES TITRES COURTS ET IMPACTANTS' : `GÉNÈRE ${elements.length} ${type.toUpperCase()}S PROFESSIONNELS`}:[m
|
||
[32m+[m[32m let prompt = `=== 1. CONTEXTE ===[m
|
||
[32m+[m[32mEntreprise: Autocollant.fr - signalétique personnalisée[m
|
||
[32m+[m[32mSujet: ${csvData.mc0}[m
|
||
[32m+[m[32mType d'article: SEO professionnel pour site commercial[m
|
||
[32m+[m
|
||
[32m+[m[32m=== 2. PERSONNALITÉ ===[m
|
||
[32m+[m[32mRédacteur: ${personality.nom}[m
|
||
[32m+[m[32mStyle: ${personality.style}[m
|
||
[32m+[m[32mTon: ${personality.description || 'professionnel'}[m
|
||
[32m+[m
|
||
[32m+[m[32m=== 3. RÈGLES GÉNÉRALES ===[m
|
||
[32m+[m[32m- Contenu SEO optimisé[m
|
||
[32m+[m[32m- Langage naturel et fluide[m
|
||
[32m+[m[32m- Éviter répétitions[m
|
||
[32m+[m[32m- Pas de références techniques dans le contenu[m
|
||
[32m+[m[32m- Textes rédigés humainement et de façon authentique[m
|
||
[32m+[m[32m- IMPÉRATIF: Respecter strictement les contraintes XML (nombre de mots, etc.)[m
|
||
[32m+[m
|
||
[32m+[m[32m=== 4. ÉLÉMENTS À GÉNÉRER ===[m
|
||
`;[m
|
||
[m
|
||
// AJOUTER CONTEXTE DES TITRES POUR LES TEXTES[m
|
||
[36m@@ -3548,7 +3566,7 @@[m [m${type === 'titre' ? 'GÉNÈRE DES TITRES COURTS ET IMPACTANTS' : `GÉNÈRE ${el[m
|
||
[m
|
||
if (generatedTitles.length > 0) {[m
|
||
prompt += `[m
|
||
[31m-CONTEXTE - TITRES GÉNÉRÉS:[m
|
||
[32m+[m[32mTitres existants pour contexte:[m
|
||
${generatedTitles.join('\n')}[m
|
||
[m
|
||
`;[m
|
||
[36m@@ -3560,34 +3578,33 @@[m [m${generatedTitles.join('\n')}[m
|
||
[m
|
||
prompt += `${index + 1}. [${cleanTag}] `;[m
|
||
[m
|
||
[31m- // INSTRUCTIONS SPÉCIFIQUES ET COURTES PAR TYPE[m
|
||
[32m+[m[32m // INSTRUCTIONS PROPRES PAR ÉLÉMENT[m
|
||
if (type === 'titre') {[m
|
||
if (elementInfo.element.type === 'titre_h1') {[m
|
||
[31m- prompt += `CRÉER UN TITRE H1 PRINCIPAL (8-12 mots) sur "${csvData.t0}" - NE PAS écrire "Titre_H1_1"\n`;[m
|
||
[32m+[m[32m prompt += `Titre principal accrocheur\n`;[m
|
||
} else if (elementInfo.element.type === 'titre_h2') {[m
|
||
[31m- prompt += `CRÉER UN TITRE H2 SECTION (6-10 mots) sur "${csvData.mc0}" - NE PAS écrire "Titre_H2_X"\n`;[m
|
||
[32m+[m[32m prompt += `Titre de section engageant\n`;[m
|
||
} else if (elementInfo.element.type === 'titre_h3') {[m
|
||
[31m- prompt += `CRÉER UN TITRE H3 SOUS-SECTION (4-8 mots) - NE PAS écrire "Titre_H3_X"\n`;[m
|
||
[32m+[m[32m prompt += `Sous-titre spécialisé\n`;[m
|
||
} else {[m
|
||
[31m- prompt += `CRÉER UN TITRE ACCROCHEUR (4-10 mots) sur "${csvData.mc0}" - NE PAS écrire "Titre_"\n`;[m
|
||
[32m+[m[32m prompt += `Titre pertinent\n`;[m
|
||
}[m
|
||
} else if (type === 'texte') {[m
|
||
[31m- const wordCount = elementInfo.element.name && elementInfo.element.name.includes('H2') ? '150' : '100';[m
|
||
[31m- prompt += `Paragraphe ${wordCount} mots, style ${personality.style}\n`;[m
|
||
[32m+[m[32m prompt += `Paragraphe informatif\n`;[m
|
||
[m
|
||
// ASSOCIER LE TITRE CORRESPONDANT AUTOMATIQUEMENT[m
|
||
const associatedTitle = findAssociatedTitle(elementInfo, existingResults);[m
|
||
if (associatedTitle) {[m
|
||
[31m- prompt += ` Développe le titre: "${associatedTitle}"\n`;[m
|
||
[32m+[m[32m prompt += ` Contexte: "${associatedTitle}"\n`;[m
|
||
}[m
|
||
[m
|
||
if (elementInfo.element.resolvedContent) {[m
|
||
[31m- prompt += ` Thème: "${elementInfo.element.resolvedContent}"\n`;[m
|
||
[32m+[m[32m prompt += ` Angle: "${elementInfo.element.resolvedContent}"\n`;[m
|
||
}[m
|
||
} else if (type === 'intro') {[m
|
||
[31m- prompt += `Introduction 80-100 mots, ton accueillant\n`;[m
|
||
[32m+[m[32m prompt += `Introduction engageante\n`;[m
|
||
} else {[m
|
||
[31m- prompt += `Contenu pertinent pour ${csvData.mc0}\n`;[m
|
||
[32m+[m[32m prompt += `Contenu pertinent\n`;[m
|
||
}[m
|
||
});[m
|
||
[m
|
||
[36m@@ -3597,15 +3614,16 @@[m [m${generatedTitles.join('\n')}[m
|
||
- Phrases: ${personality.longueurPhrases}[m
|
||
- Niveau technique: ${personality.niveauTechnique}[m
|
||
[m
|
||
[31m-CONSIGNES STRICTES:[m
|
||
[31m-- RESPECTE le style ${personality.style} de ${personality.nom} mais RESTE PROFESSIONNEL[m
|
||
[31m-- INTERDICTION ABSOLUE: "du coup", "bon", "alors", "franchement", "nickel", "tip-top", "costaud" en excès[m
|
||
[31m-- VARIE les connecteurs: ${personality.connecteursPref}[m
|
||
[31m-- POUR LES TITRES: SEULEMENT le titre réel, JAMAIS de référence "Titre_H1_1" ou "Titre_H2_7"[m
|
||
[31m-- EXEMPLE TITRE: "Plaques personnalisées résistantes aux intempéries" PAS "Titre_H2_1" [m
|
||
[31m-- RÉPONDS DIRECTEMENT par le contenu demandé, SANS introduction ni nom de tag[m
|
||
[31m-- PAS de message d'excuse du type "je n'ai pas l'information"[m
|
||
[31m-- CONTENU cohérent et professionnel, évite la sur-familiarité[m
|
||
[32m+[m[32mCONSIGNES STRICTES POUR ARTICLE SEO:[m
|
||
[32m+[m[32m- CONTEXTE: Article professionnel pour site e-commerce, destiné aux clients potentiels[m
|
||
[32m+[m[32m- STYLE: ${personality.style} de ${personality.nom} mais ADAPTÉ au web professionnel[m
|
||
[32m+[m[32m- INTERDICTION ABSOLUE: expressions trop familières répétées ("du coup", "bon", "franchement", "nickel", "tip-top")[m[41m [m
|
||
[32m+[m[32m- VOCABULAIRE: Mélange expertise technique + accessibilité client[m
|
||
[32m+[m[32m- SEO: Utilise naturellement "${csvData.mc0}" et termes associés[m
|
||
[32m+[m[32m- POUR LES TITRES: Titre SEO attractif UNIQUEMENT, JAMAIS "Titre_H1_1" ou "Titre_H2_7"[m
|
||
[32m+[m[32m- EXEMPLE TITRE: "Plaques personnalisées résistantes : guide complet 2024"[m[41m [m
|
||
[32m+[m[32m- CONTENU: Informatif, rassurant, incite à l'achat SANS être trop commercial[m
|
||
[32m+[m[32m- RÉPONDS DIRECTEMENT par le contenu web demandé, SANS préfixe[m
|
||
[m
|
||
FORMAT DE RÉPONSE ${type === 'titre' ? '(TITRES UNIQUEMENT)' : ''}:[m
|
||
[${elements[0].tag.replace(/\|/g, '').replace(/[{}]/g, '').replace(/<\/?strong>/g, '')}][m
|
||
[36m@@ -3696,107 +3714,11 @@[m [mfunction cleanXMLTagsFromContent(content) {[m
|
||
[m
|
||
// ============= PARSING FUNCTIONS =============[m
|
||
[m
|
||
[31m-/**[m
|
||
[31m- * Parser réponse extraction termes[m
|
||
[31m- */[m
|
||
[31m-function parseAllTechnicalTermsResponse(response, baseContents, contentEntries) {[m
|
||
[31m- const results = [];[m
|
||
[31m- const regex = /\[(\d+)\]\s*([^[]*?)(?=\[\d+\]|$)/gs;[m
|
||
[31m- let match;[m
|
||
[31m- const parsedItems = {};[m
|
||
[31m- [m
|
||
[31m- // Parser la réponse[m
|
||
[31m- while ((match = regex.exec(response)) !== null) {[m
|
||
[31m- const index = parseInt(match[1]) - 1; // Convertir en 0-indexé[m
|
||
[31m- const termsText = match[2].trim();[m
|
||
[31m- parsedItems[index] = termsText;[m
|
||
[31m- }[m
|
||
[31m- [m
|
||
[31m- // Mapper aux éléments[m
|
||
[31m- contentEntries.forEach((tag, index) => {[m
|
||
[31m- const termsText = parsedItems[index] || 'AUCUN';[m
|
||
[31m- const hasTerms = !termsText.toUpperCase().includes('AUCUN');[m
|
||
[31m- [m
|
||
[31m- const technicalTerms = hasTerms ? [m
|
||
[31m- termsText.split(',').map(t => t.trim()).filter(t => t.length > 0) : [m
|
||
[31m- [];[m
|
||
[31m- [m
|
||
[31m- results.push({[m
|
||
[31m- tag: tag,[m
|
||
[31m- content: baseContents[tag],[m
|
||
[31m- technicalTerms: technicalTerms,[m
|
||
[31m- needsEnhancement: hasTerms && technicalTerms.length > 0[m
|
||
[31m- });[m
|
||
[31m- [m
|
||
[31m- logSh(`🔍 [${tag}]: ${hasTerms ? technicalTerms.join(', ') : 'pas de termes techniques'}`, 'DEBUG');[m
|
||
[31m- });[m
|
||
[31m- [m
|
||
[31m- const enhancementCount = results.filter(r => r.needsEnhancement).length;[m
|
||
[31m- logSh(`📊 Analyse terminée: ${enhancementCount}/${contentEntries.length} éléments ont besoin d'enhancement`, 'INFO');[m
|
||
[31m- [m
|
||
[31m- return results;[m
|
||
[31 |