seo-generator-server/backup/sequential-system/lib/generation/TechnicalEnhancement.js

277 lines
8.7 KiB
JavaScript

// ========================================
// ÉTAPE 2: ENHANCEMENT TECHNIQUE
// Responsabilité: Améliorer la précision technique avec GPT-4
// LLM: GPT-4o-mini (température 0.4)
// ========================================
const { callLLM } = require('../LLMManager');
const { logSh } = require('../ErrorReporting');
const { tracer } = require('../trace');
/**
* MAIN ENTRY POINT - ENHANCEMENT TECHNIQUE
* Input: { content: {}, csvData: {}, context: {} }
* Output: { content: {}, stats: {}, debug: {} }
*/
async function enhanceTechnicalTerms(input) {
return await tracer.run('TechnicalEnhancement.enhanceTechnicalTerms()', async () => {
const { content, csvData, context = {} } = input;
await tracer.annotate({
step: '2/4',
llmProvider: 'gpt4',
elementsCount: Object.keys(content).length,
mc0: csvData.mc0
});
const startTime = Date.now();
logSh(`🔧 ÉTAPE 2/4: Enhancement technique (GPT-4)`, 'INFO');
logSh(` 📊 ${Object.keys(content).length} éléments à analyser`, 'INFO');
try {
// 1. Analyser tous les éléments pour détecter termes techniques
const technicalAnalysis = await analyzeTechnicalTerms(content, csvData);
// 2. Filter les éléments qui ont besoin d'enhancement
const elementsNeedingEnhancement = technicalAnalysis.filter(item => item.needsEnhancement);
logSh(` 📋 Analyse: ${elementsNeedingEnhancement.length}/${Object.keys(content).length} éléments nécessitent enhancement`, 'INFO');
if (elementsNeedingEnhancement.length === 0) {
logSh(`✅ ÉTAPE 2/4: Aucun enhancement nécessaire`, 'INFO');
return {
content,
stats: { processed: Object.keys(content).length, enhanced: 0, duration: Date.now() - startTime },
debug: { llmProvider: 'gpt4', step: 2, enhancementsApplied: [] }
};
}
// 3. Améliorer les éléments sélectionnés
const enhancedResults = await enhanceSelectedElements(elementsNeedingEnhancement, csvData);
// 4. Merger avec contenu original
const finalContent = { ...content };
let actuallyEnhanced = 0;
Object.keys(enhancedResults).forEach(tag => {
if (enhancedResults[tag] !== content[tag]) {
finalContent[tag] = enhancedResults[tag];
actuallyEnhanced++;
}
});
const duration = Date.now() - startTime;
const stats = {
processed: Object.keys(content).length,
enhanced: actuallyEnhanced,
candidate: elementsNeedingEnhancement.length,
duration
};
logSh(`✅ ÉTAPE 2/4 TERMINÉE: ${stats.enhanced} éléments améliorés (${duration}ms)`, 'INFO');
await tracer.event(`Enhancement technique terminé`, stats);
return {
content: finalContent,
stats,
debug: {
llmProvider: 'gpt4',
step: 2,
enhancementsApplied: Object.keys(enhancedResults),
technicalTermsFound: elementsNeedingEnhancement.map(e => e.technicalTerms)
}
};
} catch (error) {
const duration = Date.now() - startTime;
logSh(`❌ ÉTAPE 2/4 ÉCHOUÉE après ${duration}ms: ${error.message}`, 'ERROR');
throw new Error(`TechnicalEnhancement failed: ${error.message}`);
}
}, input);
}
/**
* Analyser tous les éléments pour détecter termes techniques
*/
async function analyzeTechnicalTerms(content, csvData) {
logSh(`🔍 Analyse termes techniques batch`, 'DEBUG');
const contentEntries = Object.keys(content);
const analysisPrompt = `MISSION: Analyser ces ${contentEntries.length} contenus et identifier leurs termes techniques.
CONTEXTE: ${csvData.mc0} - Secteur: signalétique/impression
CONTENUS À ANALYSER:
${contentEntries.map((tag, i) => `[${i + 1}] TAG: ${tag}
CONTENU: "${content[tag]}"`).join('\n\n')}
CONSIGNES:
- Identifie UNIQUEMENT les vrais termes techniques métier/industrie
- Évite mots génériques (qualité, service, pratique, personnalisé)
- Focus: matériaux, procédés, normes, dimensions, technologies
- Si aucun terme technique → "AUCUN"
EXEMPLES VALIDES: dibond, impression UV, fraisage CNC, épaisseur 3mm
EXEMPLES INVALIDES: durable, pratique, personnalisé, moderne
FORMAT RÉPONSE:
[1] dibond, impression UV OU AUCUN
[2] AUCUN
[3] aluminium, fraisage CNC OU AUCUN
etc...`;
try {
const analysisResponse = await callLLM('gpt4', analysisPrompt, {
temperature: 0.3,
maxTokens: 2000
}, csvData.personality);
return parseAnalysisResponse(analysisResponse, content, contentEntries);
} catch (error) {
logSh(`❌ Analyse termes techniques échouée: ${error.message}`, 'ERROR');
throw error;
}
}
/**
* Améliorer les éléments sélectionnés
*/
async function enhanceSelectedElements(elementsNeedingEnhancement, csvData) {
logSh(`🛠️ Enhancement ${elementsNeedingEnhancement.length} éléments`, 'DEBUG');
const enhancementPrompt = `MISSION: Améliore UNIQUEMENT la précision technique de ces contenus.
CONTEXTE: ${csvData.mc0} - Secteur signalétique/impression
PERSONNALITÉ: ${csvData.personality?.nom} (${csvData.personality?.style})
CONTENUS À AMÉLIORER:
${elementsNeedingEnhancement.map((item, i) => `[${i + 1}] TAG: ${item.tag}
CONTENU: "${item.content}"
TERMES TECHNIQUES: ${item.technicalTerms.join(', ')}`).join('\n\n')}
CONSIGNES:
- GARDE même longueur, structure et ton ${csvData.personality?.style}
- Intègre naturellement les termes techniques listés
- NE CHANGE PAS le fond du message
- Vocabulaire expert mais accessible
- Termes secteur: dibond, aluminium, impression UV, fraisage, PMMA
FORMAT RÉPONSE:
[1] Contenu avec amélioration technique
[2] Contenu avec amélioration technique
etc...`;
try {
const enhancedResponse = await callLLM('gpt4', enhancementPrompt, {
temperature: 0.4,
maxTokens: 5000
}, csvData.personality);
return parseEnhancementResponse(enhancedResponse, elementsNeedingEnhancement);
} catch (error) {
logSh(`❌ Enhancement éléments échoué: ${error.message}`, 'ERROR');
throw error;
}
}
/**
* Parser réponse analyse
*/
function parseAnalysisResponse(response, content, contentEntries) {
const results = [];
const regex = /\[(\d+)\]\s*([^[]*?)(?=\[\d+\]|$)/gs;
let match;
const parsedItems = {};
while ((match = regex.exec(response)) !== null) {
const index = parseInt(match[1]) - 1;
const termsText = match[2].trim();
parsedItems[index] = termsText;
}
contentEntries.forEach((tag, index) => {
const termsText = parsedItems[index] || 'AUCUN';
const hasTerms = !termsText.toUpperCase().includes('AUCUN');
const technicalTerms = hasTerms ?
termsText.split(',').map(t => t.trim()).filter(t => t.length > 0) :
[];
results.push({
tag,
content: content[tag],
technicalTerms,
needsEnhancement: hasTerms && technicalTerms.length > 0
});
logSh(`🔍 [${tag}]: ${hasTerms ? technicalTerms.join(', ') : 'aucun terme technique'}`, 'DEBUG');
});
return results;
}
/**
* Parser réponse enhancement
*/
function parseEnhancementResponse(response, elementsNeedingEnhancement) {
const results = {};
const regex = /\[(\d+)\]\s*([^[]*?)(?=\[\d+\]|$)/gs;
let match;
let index = 0;
while ((match = regex.exec(response)) && index < elementsNeedingEnhancement.length) {
let enhancedContent = match[2].trim();
const element = elementsNeedingEnhancement[index];
// Nettoyer le contenu généré
enhancedContent = cleanEnhancedContent(enhancedContent);
if (enhancedContent && enhancedContent.length > 10) {
results[element.tag] = enhancedContent;
logSh(`✅ Enhanced [${element.tag}]: "${enhancedContent.substring(0, 100)}..."`, 'DEBUG');
} else {
results[element.tag] = element.content;
logSh(`⚠️ Fallback [${element.tag}]: contenu invalide`, 'WARNING');
}
index++;
}
// Compléter les manquants
while (index < elementsNeedingEnhancement.length) {
const element = elementsNeedingEnhancement[index];
results[element.tag] = element.content;
index++;
}
return results;
}
/**
* Nettoyer contenu amélioré
*/
function cleanEnhancedContent(content) {
if (!content) return content;
// Supprimer préfixes indésirables
content = content.replace(/^(Bon,?\s*)?(alors,?\s*)?pour\s+/gi, '');
content = content.replace(/\*\*[^*]+\*\*/g, '');
content = content.replace(/\s{2,}/g, ' ');
content = content.trim();
return content;
}
module.exports = {
enhanceTechnicalTerms, // ← MAIN ENTRY POINT
analyzeTechnicalTerms,
enhanceSelectedElements,
parseAnalysisResponse,
parseEnhancementResponse
};