/**
* FeedbackWorkflow - Complete iterative feedback workflow
*
* This workflow implements the full iterative feedback system that combines:
* 1. WikidataWorkflow for enhanced initial answers
* 2. IterationManager for feedback loop coordination
* 3. Progressive answer improvement through multiple iterations
*
* API: execute(input, resources, options)
*/
import BaseWorkflow from './BaseWorkflow.js';
import WikidataWorkflow from './WikidataWorkflow.js';
import IterationManager from '../feedback/IterationManager.js';
import WikidataResearcher from '../../aux/wikidata/WikidataResearcher.js';
import logger from 'loglevel';
export default class FeedbackWorkflow extends BaseWorkflow {
constructor() {
super('feedback-iterative');
this.wikidataWorkflow = new WikidataWorkflow();
this.iterationManager = new IterationManager();
}
/**
* Execute the complete iterative feedback workflow
*
* @param {Object} input - Workflow input data
* @param {Object} input.question - Question object with text and optional uri
* @param {boolean} input.enableIterativeFeedback - Enable iterative feedback loops (default: true)
* @param {boolean} input.enableWikidataResearch - Enable Wikidata research (default: true)
* @param {Object} resources - External dependencies
* @param {Object} resources.llmHandler - LLM handler for analysis and generation
* @param {Object} resources.sparqlHelper - SPARQL helper for database operations
* @param {Object} resources.config - Configuration object
* @param {Object} resources.wikidataResearch - Wikidata research component (optional)
* @param {Object} options - Configuration options
* @param {number} options.maxIterations - Maximum feedback iterations (default: 3)
* @param {number} options.completenessThreshold - Completeness threshold (default: 0.8)
* @param {number} options.maxFollowUpQuestions - Max follow-up questions per iteration (default: 2)
* @param {string} options.workflowMode - Workflow mode: 'fast', 'standard', 'comprehensive' (default: 'standard')
* @returns {Promise<Object>} Complete workflow results with iterative improvements
*/
async execute(input, resources, options = {}) {
const startTime = Date.now();
try {
this._validateInput(input, ['question']);
this._validateResources(resources, ['llmHandler', 'sparqlHelper', 'config']);
const {
question,
enableIterativeFeedback = true,
enableWikidataResearch = true
} = input;
const { llmHandler, sparqlHelper, config, wikidataResearch } = resources;
const workflowConfig = this._mergeConfig(options, {
maxIterations: 3,
completenessThreshold: 0.8,
maxFollowUpQuestions: 2,
workflowMode: 'standard',
enhancementLevel: 'standard',
fallbackToBeerQA: true
});
// Adjust settings based on workflow mode
this._adjustConfigForMode(workflowConfig);
this._logStep('START', `Processing question with feedback workflow: "${question.text}"`);
// Step 1: Generate initial enhanced answer using WikidataWorkflow
const initialResult = await this._generateInitialAnswer(
question,
{ llmHandler, sparqlHelper, config, wikidataResearch },
workflowConfig
);
if (!initialResult.success) {
return this._handleError(new Error(initialResult.error), 'initial-answer');
}
let finalAnswer = initialResult.data.enhancedAnswer || initialResult.data.standardAnswer;
let iterationResults = null;
// Step 2: Iterative feedback processing if enabled
if (enableIterativeFeedback) {
const feedbackResult = await this._processIterativeFeedback(
question,
finalAnswer,
initialResult.data.beerqaContext || '',
{ llmHandler, sparqlHelper, config, wikidataResearch },
workflowConfig
);
if (feedbackResult.success) {
iterationResults = feedbackResult.data;
finalAnswer = feedbackResult.data.finalAnswer;
}
}
// Step 3: Generate comprehensive final response
const comprehensiveResult = await this._generateComprehensiveResponse(
question,
initialResult.data,
iterationResults,
finalAnswer,
llmHandler,
workflowConfig
);
this._logStep('COMPLETE', 'Feedback workflow completed successfully');
return this._createResult(true, {
question: question,
initialAnswer: initialResult.data.enhancedAnswer || initialResult.data.standardAnswer,
finalAnswer: comprehensiveResult.success ? comprehensiveResult.data.response : finalAnswer,
iterations: iterationResults?.iterations || [],
wikidataResults: initialResult.data.wikidataResults,
totalResearchQuestions: this._countResearchQuestions(iterationResults),
totalEntitiesDiscovered: this._countTotalEntities(initialResult.data, iterationResults),
completenessImprovement: this._calculateCompleteness(iterationResults),
workflow: {
initialWorkflow: 'wikidata-enhanced',
feedbackEnabled: enableIterativeFeedback,
iterationsPerformed: iterationResults?.iterations?.length || 0
}
}, null, this._getWorkflowMetadata(startTime, {
initialAnswerDuration: initialResult.duration,
feedbackDuration: iterationResults?.metadata?.totalDuration || 0,
comprehensiveResponseDuration: comprehensiveResult.duration || 0,
workflowMode: workflowConfig.workflowMode,
totalSteps: enableIterativeFeedback ? 3 : 2,
enhancementLevel: workflowConfig.enhancementLevel
}));
} catch (error) {
return this._handleError(error, 'workflow-execution');
}
}
/**
* Generate initial enhanced answer using WikidataWorkflow
* @private
*/
async _generateInitialAnswer(question, resources, workflowConfig) {
const stepStart = Date.now();
try {
this._logStep('INITIAL', 'Generating initial enhanced answer');
const result = await this.wikidataWorkflow.execute(
{
question,
enableWikidataResearch: true
},
resources,
{
enhancementLevel: workflowConfig.enhancementLevel,
maxWikidataEntities: workflowConfig.maxWikidataEntities || 15,
answerStyle: 'comprehensive'
}
);
return {
...result,
duration: Date.now() - stepStart
};
} catch (error) {
logger.error('Initial answer generation failed:', error.message);
return {
success: false,
error: error.message,
duration: Date.now() - stepStart
};
}
}
/**
* Process iterative feedback to improve answer completeness
* @private
*/
async _processIterativeFeedback(question, initialAnswer, context, resources, workflowConfig) {
const stepStart = Date.now();
try {
this._logStep('FEEDBACK', 'Processing iterative feedback loops');
const result = await this.iterationManager.processIterations(
{
originalQuestion: question,
initialResponse: initialAnswer,
context: context
},
resources,
{
maxIterations: workflowConfig.maxIterations,
completenessThreshold: workflowConfig.completenessThreshold,
maxFollowUpQuestions: workflowConfig.maxFollowUpQuestions
}
);
return {
success: result.success,
data: result,
duration: Date.now() - stepStart
};
} catch (error) {
logger.error('Iterative feedback processing failed:', error.message);
return {
success: false,
error: error.message,
duration: Date.now() - stepStart
};
}
}
/**
* Generate final comprehensive response
* @private
*/
async _generateComprehensiveResponse(question, initialData, iterationResults, finalAnswer, llmHandler, workflowConfig) {
const stepStart = Date.now();
try {
this._logStep('FINAL', 'Generating comprehensive final response');
// If no iterations were performed, return the final answer as-is
if (!iterationResults || iterationResults.iterations.length === 0) {
return {
success: true,
data: { response: finalAnswer },
duration: Date.now() - stepStart
};
}
const prompt = this._buildFinalResponsePrompt(
question,
initialData,
iterationResults,
finalAnswer,
workflowConfig
);
const comprehensiveResponse = await llmHandler.generateResponse(prompt);
return {
success: true,
data: {
response: comprehensiveResponse,
prompt: prompt.substring(0, 300) + '...' // Truncated
},
duration: Date.now() - stepStart
};
} catch (error) {
logger.error('Comprehensive response generation failed:', error.message);
return {
success: false,
error: error.message,
data: { response: finalAnswer }, // Fallback
duration: Date.now() - stepStart
};
}
}
/**
* Adjust configuration based on workflow mode
* @private
*/
_adjustConfigForMode(config) {
switch (config.workflowMode) {
case 'fast':
config.maxIterations = 1;
config.maxFollowUpQuestions = 1;
config.enhancementLevel = 'basic';
break;
case 'comprehensive':
config.maxIterations = 4;
config.maxFollowUpQuestions = 3;
config.enhancementLevel = 'comprehensive';
config.completenessThreshold = 0.9;
break;
default: // 'standard'
// Use default values
break;
}
}
/**
* Count total research questions across iterations
* @private
*/
_countResearchQuestions(iterationResults) {
if (!iterationResults?.iterations) return 0;
return iterationResults.iterations.reduce((total, iteration) => {
return total + (iteration.followUpQuestions?.length || 0);
}, 0);
}
/**
* Count total entities discovered across all research
* @private
*/
_countTotalEntities(initialData, iterationResults) {
let total = initialData.wikidataEntitiesFound || 0;
if (iterationResults?.iterations) {
total += iterationResults.iterations.reduce((sum, iteration) => {
return sum + (iteration.researchResults?.totalEntities || 0);
}, 0);
}
return total;
}
/**
* Calculate completeness improvement across iterations
* @private
*/
_calculateCompleteness(iterationResults) {
if (!iterationResults?.iterations || iterationResults.iterations.length === 0) {
return { initial: 1.0, final: 1.0, improvement: 0 };
}
const initial = iterationResults.iterations[0]?.completenessScore || 0;
const final = iterationResults.metadata?.finalCompleteness || initial;
return {
initial,
final,
improvement: final - initial
};
}
/**
* Build final comprehensive response prompt
* @private
*/
_buildFinalResponsePrompt(question, initialData, iterationResults, finalAnswer, workflowConfig) {
const researchSummary = this._createResearchSummary(initialData, iterationResults);
return `You are providing a final, comprehensive answer after a complete iterative research and feedback process.
ORIGINAL QUESTION: ${question.text}
RESEARCH PROCESS SUMMARY:
${researchSummary}
CURRENT FINAL ANSWER: ${finalAnswer}
Please provide a polished, comprehensive final answer that:
1. Synthesizes all research findings and iterations
2. Provides the most complete response possible
3. Is well-structured and highly informative
4. Addresses all aspects of the original question
5. Demonstrates the value of the iterative research process
COMPREHENSIVE FINAL ANSWER:`;
}
/**
* Create summary of entire research process
* @private
*/
_createResearchSummary(initialData, iterationResults) {
let summary = '## Research Process Overview\n';
// Initial research
if (initialData.wikidataEntitiesFound > 0) {
summary += `- Initial Wikidata research: ${initialData.wikidataEntitiesFound} entities discovered\n`;
}
// Iterative research
if (iterationResults?.iterations) {
summary += `- Iterative feedback: ${iterationResults.iterations.length} iterations performed\n`;
let totalFollowUps = 0;
let totalIterativeEntities = 0;
for (const iteration of iterationResults.iterations) {
totalFollowUps += iteration.followUpQuestions?.length || 0;
totalIterativeEntities += iteration.researchResults?.totalEntities || 0;
}
summary += `- Follow-up research questions: ${totalFollowUps}\n`;
summary += `- Additional entities discovered: ${totalIterativeEntities}\n`;
}
return summary;
}
}