feat(keywords): Add hierarchical context to missing keywords prompt and fix LLM response format
This commit improves keyword generation by providing hierarchical context for each element and fixing the LLM response format parsing. Changes: 1. lib/MissingKeywords.js: - Add buildHierarchicalContext() to generate compact contextual info for each element - Display hierarchy in prompt (e.g., "H2 existants: 'Titre1', 'Titre2'") - For Txt elements: show associated MC keyword + parent title - For FAQ elements: count existing FAQs - Fix LLM response format by providing 3 concrete examples from actual list - Add explicit warning to use exact tag names [Titre_H2_3], [Txt_H2_6] - Improve getElementContext() to better retrieve hierarchical elements 2. lib/selective-enhancement/SelectiveUtils.js: - Fix createTypedPrompt() to use specific keyword from resolvedContent - Remove fallback to csvData.mc0 (log error if no specific keyword) 3. lib/pipeline/PipelineExecutor.js: - Integrate generateMissingSheetVariables() as "Étape 0" before extraction Prompt format now: 1. [Titre_H2_3] (titre) — H2 existants: "Titre1", "Titre2" 2. [Txt_H2_6] (texte) — MC: "Plaque dibond" | Parent: "Guide dibond" 3. [Faq_q_1] (question) — 3 FAQ existantes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
957df21e18
commit
3751ab047b
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,9 @@ const { tracer } = require('../trace');
|
|||||||
const { PipelineDefinition } = require('./PipelineDefinition');
|
const { PipelineDefinition } = require('./PipelineDefinition');
|
||||||
const { getPersonalities, readInstructionsData, selectPersonalityWithAI } = require('../BrainConfig');
|
const { getPersonalities, readInstructionsData, selectPersonalityWithAI } = require('../BrainConfig');
|
||||||
const { extractElements, buildSmartHierarchy } = require('../ElementExtraction');
|
const { extractElements, buildSmartHierarchy } = require('../ElementExtraction');
|
||||||
const { generateMissingKeywords } = require('../MissingKeywords');
|
const { generateMissingKeywords, generateMissingSheetVariables } = require('../MissingKeywords');
|
||||||
|
const { injectGeneratedContent } = require('../ContentAssembly');
|
||||||
|
const { saveGeneratedArticleOrganic } = require('../ArticleStorage');
|
||||||
|
|
||||||
// Modules d'exécution
|
// Modules d'exécution
|
||||||
const { generateSimple } = require('../selective-enhancement/SelectiveUtils');
|
const { generateSimple } = require('../selective-enhancement/SelectiveUtils');
|
||||||
@ -31,6 +33,10 @@ class PipelineExecutor {
|
|||||||
this.currentContent = null;
|
this.currentContent = null;
|
||||||
this.executionLog = [];
|
this.executionLog = [];
|
||||||
this.checkpoints = [];
|
this.checkpoints = [];
|
||||||
|
this.versionHistory = []; // ✅ Historique des versions sauvegardées
|
||||||
|
this.parentArticleId = null; // ✅ ID parent pour versioning
|
||||||
|
this.csvData = null; // ✅ Données CSV pour sauvegarde
|
||||||
|
this.finalElements = null; // ✅ Éléments extraits pour assemblage
|
||||||
this.metadata = {
|
this.metadata = {
|
||||||
startTime: null,
|
startTime: null,
|
||||||
endTime: null,
|
endTime: null,
|
||||||
@ -55,9 +61,12 @@ class PipelineExecutor {
|
|||||||
this.metadata.startTime = Date.now();
|
this.metadata.startTime = Date.now();
|
||||||
this.executionLog = [];
|
this.executionLog = [];
|
||||||
this.checkpoints = [];
|
this.checkpoints = [];
|
||||||
|
this.versionHistory = []; // ✅ Reset version history
|
||||||
|
this.parentArticleId = null; // ✅ Reset parent ID
|
||||||
|
|
||||||
// Charger les données
|
// Charger les données
|
||||||
const csvData = await this.loadData(rowNumber);
|
const csvData = await this.loadData(rowNumber);
|
||||||
|
this.csvData = csvData; // ✅ Stocker pour sauvegarde
|
||||||
|
|
||||||
// Exécuter les étapes
|
// Exécuter les étapes
|
||||||
const enabledSteps = pipelineConfig.pipeline.filter(s => s.enabled !== false);
|
const enabledSteps = pipelineConfig.pipeline.filter(s => s.enabled !== false);
|
||||||
@ -99,6 +108,11 @@ class PipelineExecutor {
|
|||||||
logSh(`💾 Checkpoint sauvegardé (étape ${step.step})`, 'DEBUG');
|
logSh(`💾 Checkpoint sauvegardé (étape ${step.step})`, 'DEBUG');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ Sauvegarde Google Sheets si activée
|
||||||
|
if (options.saveIntermediateSteps && this.currentContent) {
|
||||||
|
await this.saveStepVersion(step, result.modifications || 0, pipelineConfig.name);
|
||||||
|
}
|
||||||
|
|
||||||
logSh(`✔ Étape ${step.step} terminée (${stepDuration}ms, ${result.modifications || 0} modifs)`, 'INFO');
|
logSh(`✔ Étape ${step.step} terminée (${stepDuration}ms, ${result.modifications || 0} modifs)`, 'INFO');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -130,6 +144,7 @@ class PipelineExecutor {
|
|||||||
finalContent: this.currentContent,
|
finalContent: this.currentContent,
|
||||||
executionLog: this.executionLog,
|
executionLog: this.executionLog,
|
||||||
checkpoints: this.checkpoints,
|
checkpoints: this.checkpoints,
|
||||||
|
versionHistory: this.versionHistory, // ✅ Inclure version history
|
||||||
metadata: {
|
metadata: {
|
||||||
...this.metadata,
|
...this.metadata,
|
||||||
pipelineName: pipelineConfig.name,
|
pipelineName: pipelineConfig.name,
|
||||||
@ -204,12 +219,19 @@ class PipelineExecutor {
|
|||||||
return { content: this.currentContent, modifications: 0 };
|
return { content: this.currentContent, modifications: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Étape 1: Extraire les éléments depuis le template XML
|
// 🆕 Étape 0: Générer les variables Google Sheets manquantes (MC+1_5, T+1_6, etc.)
|
||||||
|
logSh('🔄 Vérification variables Google Sheets...', 'DEBUG');
|
||||||
|
const updatedCsvData = await generateMissingSheetVariables(csvData.xmlTemplate, csvData);
|
||||||
|
// Mettre à jour csvData pour les étapes suivantes
|
||||||
|
Object.assign(csvData, updatedCsvData);
|
||||||
|
|
||||||
|
// Étape 1: Extraire les éléments depuis le template XML (avec csvData complet)
|
||||||
const elements = await extractElements(csvData.xmlTemplate, csvData);
|
const elements = await extractElements(csvData.xmlTemplate, csvData);
|
||||||
logSh(`✓ Extraction: ${elements.length} éléments extraits`, 'DEBUG');
|
logSh(`✓ Extraction: ${elements.length} éléments extraits`, 'DEBUG');
|
||||||
|
|
||||||
// Étape 2: Générer les mots-clés manquants
|
// Étape 2: Générer les mots-clés manquants (titres, textes, FAQ)
|
||||||
const finalElements = await generateMissingKeywords(elements, csvData);
|
const finalElements = await generateMissingKeywords(elements, csvData);
|
||||||
|
this.finalElements = finalElements; // ✅ Stocker pour sauvegarde
|
||||||
|
|
||||||
// Étape 3: Construire la hiérarchie
|
// Étape 3: Construire la hiérarchie
|
||||||
const elementsArray = Array.isArray(finalElements) ? finalElements :
|
const elementsArray = Array.isArray(finalElements) ? finalElements :
|
||||||
@ -218,7 +240,7 @@ class PipelineExecutor {
|
|||||||
logSh(`✓ Hiérarchie: ${Object.keys(hierarchy).length} sections`, 'DEBUG');
|
logSh(`✓ Hiérarchie: ${Object.keys(hierarchy).length} sections`, 'DEBUG');
|
||||||
|
|
||||||
// Étape 4: Génération simple avec LLM configurable
|
// Étape 4: Génération simple avec LLM configurable
|
||||||
const llmProvider = step.parameters?.llmProvider || 'claude';
|
const llmProvider = step.parameters?.llmProvider || 'claude-sonnet-4-5';
|
||||||
const result = await generateSimple(hierarchy, csvData, { llmProvider });
|
const result = await generateSimple(hierarchy, csvData, { llmProvider });
|
||||||
|
|
||||||
logSh(`✓ Génération: ${Object.keys(result.content || {}).length} éléments créés avec ${llmProvider}`, 'DEBUG');
|
logSh(`✓ Génération: ${Object.keys(result.content || {}).length} éléments créés avec ${llmProvider}`, 'DEBUG');
|
||||||
@ -242,7 +264,7 @@ class PipelineExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configuration de la couche
|
// Configuration de la couche
|
||||||
const llmProvider = step.parameters?.llmProvider || 'openai';
|
const llmProvider = step.parameters?.llmProvider || 'gpt-4o-mini';
|
||||||
const config = {
|
const config = {
|
||||||
csvData,
|
csvData,
|
||||||
personality: csvData.personality,
|
personality: csvData.personality,
|
||||||
@ -267,7 +289,7 @@ class PipelineExecutor {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
content: result.content || result,
|
content: result.content || result,
|
||||||
modifications: result.modificationsCount || 0
|
modifications: result.modifications || 0 // ✅ CORRIGÉ: modifications au lieu de modificationsCount
|
||||||
};
|
};
|
||||||
|
|
||||||
}, { mode: step.mode, intensity: step.intensity });
|
}, { mode: step.mode, intensity: step.intensity });
|
||||||
@ -288,7 +310,7 @@ class PipelineExecutor {
|
|||||||
return { content: this.currentContent, modifications: 0 };
|
return { content: this.currentContent, modifications: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const llmProvider = step.parameters?.llmProvider || 'gemini';
|
const llmProvider = step.parameters?.llmProvider || 'gemini-pro';
|
||||||
const config = {
|
const config = {
|
||||||
csvData,
|
csvData,
|
||||||
detectorTarget: step.parameters?.detector || 'general',
|
detectorTarget: step.parameters?.detector || 'general',
|
||||||
@ -326,7 +348,7 @@ class PipelineExecutor {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
content: result.content || result,
|
content: result.content || result,
|
||||||
modifications: result.modificationsCount || 0
|
modifications: result.modifications || 0 // ✅ CORRIGÉ: modifications au lieu de modificationsCount
|
||||||
};
|
};
|
||||||
|
|
||||||
}, { mode: step.mode, detector: step.parameters?.detector });
|
}, { mode: step.mode, detector: step.parameters?.detector });
|
||||||
@ -347,7 +369,7 @@ class PipelineExecutor {
|
|||||||
return { content: this.currentContent, modifications: 0 };
|
return { content: this.currentContent, modifications: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const llmProvider = step.parameters?.llmProvider || 'mistral';
|
const llmProvider = step.parameters?.llmProvider || 'mistral-small';
|
||||||
const config = {
|
const config = {
|
||||||
csvData,
|
csvData,
|
||||||
personality: csvData.personality,
|
personality: csvData.personality,
|
||||||
@ -373,7 +395,7 @@ class PipelineExecutor {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
content: result.content || result,
|
content: result.content || result,
|
||||||
modifications: result.modificationsCount || 0
|
modifications: result.modifications || 0 // ✅ CORRIGÉ: modifications au lieu de modificationsCount
|
||||||
};
|
};
|
||||||
|
|
||||||
}, { mode: step.mode, intensity: step.intensity });
|
}, { mode: step.mode, intensity: step.intensity });
|
||||||
@ -394,7 +416,7 @@ class PipelineExecutor {
|
|||||||
return { content: this.currentContent, modifications: 0 };
|
return { content: this.currentContent, modifications: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const llmProvider = step.parameters?.llmProvider || 'deepseek';
|
const llmProvider = step.parameters?.llmProvider || 'deepseek-chat';
|
||||||
const config = {
|
const config = {
|
||||||
csvData,
|
csvData,
|
||||||
personality: csvData.personality,
|
personality: csvData.personality,
|
||||||
@ -419,7 +441,7 @@ class PipelineExecutor {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
content: result.content || result,
|
content: result.content || result,
|
||||||
modifications: result.modificationsCount || 0
|
modifications: result.modifications || 0 // ✅ CORRIGÉ: modifications au lieu de modificationsCount
|
||||||
};
|
};
|
||||||
|
|
||||||
}, { mode: step.mode, intensity: step.intensity });
|
}, { mode: step.mode, intensity: step.intensity });
|
||||||
@ -460,6 +482,10 @@ class PipelineExecutor {
|
|||||||
this.currentContent = null;
|
this.currentContent = null;
|
||||||
this.executionLog = [];
|
this.executionLog = [];
|
||||||
this.checkpoints = [];
|
this.checkpoints = [];
|
||||||
|
this.versionHistory = [];
|
||||||
|
this.parentArticleId = null;
|
||||||
|
this.csvData = null;
|
||||||
|
this.finalElements = null;
|
||||||
this.metadata = {
|
this.metadata = {
|
||||||
startTime: null,
|
startTime: null,
|
||||||
endTime: null,
|
endTime: null,
|
||||||
@ -467,6 +493,67 @@ class PipelineExecutor {
|
|||||||
personality: null
|
personality: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ✅ Sauvegarde une version intermédiaire dans Google Sheets
|
||||||
|
*/
|
||||||
|
async saveStepVersion(step, modifications, pipelineName) {
|
||||||
|
try {
|
||||||
|
if (!this.csvData || !this.finalElements) {
|
||||||
|
logSh('⚠️ Données manquantes pour sauvegarde, ignorée', 'WARN');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Déterminer la version basée sur le module et le nombre d'étapes
|
||||||
|
const versionNumber = `v1.${step.step}`;
|
||||||
|
const stageName = `${step.module}_${step.mode}`;
|
||||||
|
|
||||||
|
logSh(`💾 Sauvegarde ${versionNumber}: ${stageName}`, 'INFO');
|
||||||
|
|
||||||
|
// Assemblage du contenu
|
||||||
|
const xmlString = this.csvData.xmlTemplate.startsWith('<?xml')
|
||||||
|
? this.csvData.xmlTemplate
|
||||||
|
: Buffer.from(this.csvData.xmlTemplate, 'base64').toString('utf8');
|
||||||
|
|
||||||
|
await injectGeneratedContent(xmlString, this.currentContent, this.finalElements);
|
||||||
|
|
||||||
|
// Sauvegarde dans Google Sheets
|
||||||
|
const storage = await saveGeneratedArticleOrganic(
|
||||||
|
{ generatedTexts: this.currentContent },
|
||||||
|
this.csvData,
|
||||||
|
{
|
||||||
|
version: versionNumber,
|
||||||
|
stage: stageName,
|
||||||
|
source: `pipeline_${pipelineName}`,
|
||||||
|
adversarialMode: step.mode === 'adversarial' ? step.mode : 'none',
|
||||||
|
stageDescription: `${step.module} (${step.mode}) - ${modifications} modifications`,
|
||||||
|
parentArticleId: this.parentArticleId,
|
||||||
|
useVersionedSheet: true // ✅ Sauvegarder dans Generated_Articles_Versioned
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stocker l'ID parent si c'est la première version
|
||||||
|
if (!this.parentArticleId) {
|
||||||
|
this.parentArticleId = storage.articleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajouter à l'historique
|
||||||
|
this.versionHistory.push({
|
||||||
|
version: versionNumber,
|
||||||
|
stage: stageName,
|
||||||
|
articleId: storage.articleId,
|
||||||
|
length: storage.textLength,
|
||||||
|
wordCount: storage.wordCount,
|
||||||
|
modifications: modifications
|
||||||
|
});
|
||||||
|
|
||||||
|
logSh(` ✅ Sauvé ${versionNumber} - ID: ${storage.articleId}`, 'INFO');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logSh(`❌ Erreur sauvegarde version: ${error.message}`, 'ERROR');
|
||||||
|
// Ne pas propager l'erreur pour ne pas bloquer l'exécution
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { PipelineExecutor };
|
module.exports = { PipelineExecutor };
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user