forensic-pathways/src/utils/aiPipeline.ts
2025-08-17 16:30:58 +02:00

1240 lines
42 KiB
TypeScript

// src/utils/aiPipeline.ts - Fixed with accurate audit data and meaningful confidence
import { getCompressedToolsDataForAI, getDataVersion } from './dataService.js';
import { aiService } from './aiService.js';
import { toolSelector, type SelectionContext } from './toolSelector.js';
import { confidenceScoring, type AnalysisContext } from './confidenceScoring.js';
import { embeddingsService } from './embeddings.js';
import { auditService, type AuditEntry } from './auditService.js';
import { JSONParser } from './jsonUtils.js';
import { getPrompt } from '../config/prompts.js';
import 'dotenv/config';
interface PipelineConfig {
microTaskDelay: number;
maxContextTokens: number;
maxPromptTokens: number;
taskRelevanceModeration: {
maxInitialScore: number;
maxWithPhaseBonus: number;
moderationThreshold: number;
};
}
interface MicroTaskResult {
taskType: string;
content: string;
processingTimeMs: number;
success: boolean;
error?: string;
aiUsage?: {
promptTokens?: number;
completionTokens?: number;
totalTokens?: number;
};
}
interface AnalysisResult {
recommendation: any;
processingStats: {
embeddingsUsed: boolean;
candidatesFromEmbeddings: number;
finalSelectedItems: number;
processingTimeMs: number;
microTasksCompleted: number;
microTasksFailed: number;
contextContinuityUsed: boolean;
totalAITokensUsed: number;
auditEntriesGenerated: number;
aiModel: string;
toolsDataHash: string;
temperature: number;
maxTokensUsed: number;
};
}
interface PipelineContext {
userQuery: string;
mode: string;
filteredData: any;
contextHistory: string[];
maxContextLength: number;
currentContextLength: number;
scenarioAnalysis?: string;
problemAnalysis?: string;
investigationApproach?: string;
criticalConsiderations?: string;
selectedTools?: Array<{
tool: any;
phase: string;
priority: string;
justification?: string;
taskRelevance?: number;
limitations?: string[];
}>;
backgroundKnowledge?: Array<{
concept: any;
relevance: string;
}>;
seenToolNames: Set<string>;
embeddingsSimilarities: Map<string, number>;
}
class AIPipeline {
private config: PipelineConfig;
private totalTokensUsed: number = 0;
constructor() {
this.config = {
microTaskDelay: parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10),
maxContextTokens: parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10),
maxPromptTokens: parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10),
taskRelevanceModeration: {
maxInitialScore: 85,
maxWithPhaseBonus: 95,
moderationThreshold: 80
}
};
console.log('[AI-PIPELINE] Initialized with improved audit accuracy');
}
async processQuery(userQuery: string, mode: string): Promise<AnalysisResult> {
const startTime = Date.now();
let completedTasks = 0;
let failedTasks = 0;
this.totalTokensUsed = 0;
console.log('[AI-PIPELINE] Starting', mode, 'analysis pipeline');
auditService.clearAuditTrail();
try {
const toolsData = await getCompressedToolsDataForAI();
const aiConfig = aiService.getConfig();
const toolsDataHash = getDataVersion?.() || 'unknown';
const context: PipelineContext = {
userQuery,
mode,
filteredData: {},
contextHistory: [],
maxContextLength: this.config.maxContextTokens,
currentContextLength: 0,
seenToolNames: new Set<string>(),
embeddingsSimilarities: new Map<string, number>()
};
// Skip initialization audit entry - it doesn't add transparency value
console.log('[AI-PIPELINE] Phase 1: Tool candidate selection');
const candidateSelectionStart = Date.now();
const candidateData = await toolSelector.getIntelligentCandidates(userQuery, toolsData, mode, context);
// Calculate meaningful confidence for tool selection
const selectionConfidence = this.calculateToolSelectionConfidence(
candidateData.tools.length,
toolsData.tools.length,
candidateData.selectionMethod,
candidateData.concepts.length
);
auditService.addToolSelection(
candidateData.tools.map(t => t.name),
toolsData.tools.map(t => t.name),
candidateData.selectionMethod,
selectionConfidence,
candidateSelectionStart,
{
embeddingsUsed: embeddingsService.isEnabled(),
totalCandidatesFound: candidateData.tools.length + candidateData.concepts.length,
selectionMethod: candidateData.selectionMethod,
reductionRatio: candidateData.tools.length / toolsData.tools.length
}
);
context.filteredData = candidateData;
console.log('[AI-PIPELINE] Phase 2: Contextual analysis');
const analysisResult = await this.analyzeScenario(context, startTime);
if (analysisResult.success) completedTasks++; else failedTasks++;
this.trackTokenUsage(analysisResult.aiUsage);
await this.delay(this.config.microTaskDelay);
const approachResult = await this.generateApproach(context, startTime);
if (approachResult.success) completedTasks++; else failedTasks++;
this.trackTokenUsage(approachResult.aiUsage);
await this.delay(this.config.microTaskDelay);
const considerationsResult = await this.generateCriticalConsiderations(context, startTime);
if (considerationsResult.success) completedTasks++; else failedTasks++;
this.trackTokenUsage(considerationsResult.aiUsage);
await this.delay(this.config.microTaskDelay);
console.log('[AI-PIPELINE] Phase 3: Tool-specific analysis');
if (mode === 'workflow') {
const workflowResults = await this.processWorkflowMode(context, toolsData, completedTasks, failedTasks, startTime);
completedTasks = workflowResults.completed;
failedTasks = workflowResults.failed;
} else {
const toolResults = await this.processToolMode(context, completedTasks, failedTasks, startTime);
completedTasks = toolResults.completed;
failedTasks = toolResults.failed;
}
console.log('[AI-PIPELINE] Phase 4: Knowledge synthesis');
const knowledgeResult = await this.selectBackgroundKnowledge(context, startTime);
if (knowledgeResult.success) completedTasks++; else failedTasks++;
this.trackTokenUsage(knowledgeResult.aiUsage);
await this.delay(this.config.microTaskDelay);
const finalResult = await this.generateFinalRecommendations(context, startTime);
if (finalResult.success) completedTasks++; else failedTasks++;
this.trackTokenUsage(finalResult.aiUsage);
const recommendation = this.buildRecommendation(context, mode, finalResult.content);
// Skip completion audit entry - it doesn't add transparency value
const processingStats = {
embeddingsUsed: embeddingsService.isEnabled(),
candidatesFromEmbeddings: candidateData.tools.length,
finalSelectedItems: (context.selectedTools?.length || 0) + (context.backgroundKnowledge?.length || 0),
processingTimeMs: Date.now() - startTime,
microTasksCompleted: completedTasks,
microTasksFailed: failedTasks,
contextContinuityUsed: true,
totalAITokensUsed: this.totalTokensUsed,
auditEntriesGenerated: auditService.getCurrentAuditTrail().length,
aiModel: aiConfig.model,
toolsDataHash,
temperature: 0.3,
maxTokensUsed: 2500
};
console.log('[AI-PIPELINE] Pipeline completed successfully:', {
mode,
processingTimeMs: processingStats.processingTimeMs,
completedTasks,
failedTasks,
finalItems: processingStats.finalSelectedItems,
totalTokensUsed: this.totalTokensUsed,
auditEntries: processingStats.auditEntriesGenerated
});
const finalAuditTrail = auditService.finalizeAuditTrail();
return {
recommendation: {
...recommendation,
auditTrail: auditService.isEnabled() ? finalAuditTrail : undefined,
processingStats
},
processingStats
};
} catch (error) {
console.error('[AI-PIPELINE] Pipeline failed:', error);
throw error;
}
}
private calculateToolSelectionConfidence(
selectedCount: number,
totalCount: number,
method: string,
conceptsCount: number
): number {
let confidence = 50;
const selectionRatio = selectedCount / totalCount;
// Good selection ratio (5-20% is optimal)
if (selectionRatio >= 0.05 && selectionRatio <= 0.20) {
confidence += 25;
} else if (selectionRatio < 0.05) {
confidence += 15; // Very selective
} else if (selectionRatio > 0.30) {
confidence -= 15; // Too inclusive
}
// Embeddings method bonus
if (method.includes('embeddings')) {
confidence += 15;
}
// Concepts also selected
if (conceptsCount > 0) {
confidence += 10;
}
// Reasonable absolute numbers
if (selectedCount >= 8 && selectedCount <= 25) {
confidence += 10;
}
return Math.min(95, Math.max(40, confidence));
}
private async processWorkflowMode(
context: PipelineContext,
toolsData: any,
completedTasks: number,
failedTasks: number,
pipelineStart: number
): Promise<{ completed: number; failed: number }> {
const phases = toolsData.phases || [];
for (const phase of phases) {
const phaseStart = Date.now();
const phaseTools = context.filteredData.tools.filter((tool: any) =>
tool && tool.phases && Array.isArray(tool.phases) && tool.phases.includes(phase.id)
);
if (phaseTools.length === 0) {
console.log(`[AI-PIPELINE] No tools available for phase: ${phase.id}`);
continue;
}
const selections = await toolSelector.selectToolsForPhase(context.userQuery, phase, phaseTools, context);
// Calculate meaningful confidence based on phase selection quality
const phaseConfidence = this.calculatePhaseSelectionConfidence(
selections.length,
phaseTools.length,
phase.id,
selections
);
auditService.addEntry(
'workflow-phase',
'phase-tool-selection',
{
phaseId: phase.id,
phaseName: phase.name,
availableTools: phaseTools.map(t => t.name),
toolCount: phaseTools.length
},
{
selectedTools: selections.map(s => s.toolName),
selectionCount: selections.length,
avgTaskRelevance: selections.length > 0 ?
Math.round(selections.reduce((sum, s) => sum + (s.taskRelevance || 70), 0) / selections.length) : 0
},
phaseConfidence,
phaseStart,
{
phaseId: phase.id,
availableToolsCount: phaseTools.length,
selectedToolsCount: selections.length,
microTaskType: 'phase-tool-selection',
reasoning: `${selections.length} von ${phaseTools.length} verfügbaren Tools für ${phase.name} ausgewählt - KI bewertete Eignung für spezifische Phasenaufgaben`
}
);
selections.forEach((sel: any) => {
const tool = phaseTools.find((t: any) => t && t.name === sel.toolName);
if (tool) {
const moderatedTaskRelevance = this.moderateTaskRelevance(sel.taskRelevance);
const priority = this.derivePriorityFromScore(moderatedTaskRelevance);
this.addToolToSelection(context, tool, phase.id, priority, sel.justification, moderatedTaskRelevance, sel.limitations);
auditService.addEntry(
'tool-reasoning',
'tool-added-to-phase',
{
toolName: tool.name,
phaseId: phase.id,
taskRelevance: moderatedTaskRelevance,
priority: priority
},
{
justification: sel.justification,
limitations: sel.limitations,
addedToPhase: phase.name
},
moderatedTaskRelevance || 70,
phaseStart,
{
toolType: tool.type,
priority,
moderationApplied: sel.taskRelevance !== moderatedTaskRelevance,
reasoning: `${tool.name} als ${priority}-Priorität für ${phase.name} ausgewählt: ${sel.justification?.slice(0, 100)}...`
}
);
}
});
if (selections.length > 0) completedTasks++; else failedTasks++;
await this.delay(this.config.microTaskDelay);
}
const completionResult = await this.completeUnderrepresentedPhases(context, toolsData, pipelineStart);
completedTasks += completionResult.completed;
failedTasks += completionResult.failed;
return { completed: completedTasks, failed: failedTasks };
}
private calculatePhaseSelectionConfidence(
selectedCount: number,
availableCount: number,
phaseId: string,
selections: any[]
): number {
let confidence = 60;
// Phase-specific expectations
const criticalPhases = ['acquisition', 'examination', 'analysis'];
const isCritical = criticalPhases.includes(phaseId);
// Selection made
if (selectedCount > 0) {
confidence += 20;
} else {
return 30; // No selection is concerning
}
// Selection ratio (for phases, 20-50% is reasonable)
const ratio = selectedCount / availableCount;
if (ratio >= 0.2 && ratio <= 0.5) {
confidence += 15;
} else if (ratio < 0.2 && selectedCount >= 1) {
confidence += 10; // Selective is ok
}
// Critical phases should have adequate tools
if (isCritical && selectedCount >= 2) {
confidence += 10;
}
// Quality of selections (based on task relevance)
const avgRelevance = selections.length > 0 ?
selections.reduce((sum, s) => sum + (s.taskRelevance || 70), 0) / selections.length : 0;
if (avgRelevance >= 75) {
confidence += 10;
} else if (avgRelevance >= 65) {
confidence += 5;
}
return Math.min(95, Math.max(30, confidence));
}
private async processToolMode(
context: PipelineContext,
completedTasks: number,
failedTasks: number,
pipelineStart: number
): Promise<{ completed: number; failed: number }> {
const topTools = context.filteredData.tools.slice(0, 3);
for (let i = 0; i < topTools.length; i++) {
const evaluationResult = await this.evaluateSpecificTool(context, topTools[i], i + 1, pipelineStart);
if (evaluationResult.success) completedTasks++; else failedTasks++;
this.trackTokenUsage(evaluationResult.aiUsage);
await this.delay(this.config.microTaskDelay);
}
return { completed: completedTasks, failed: failedTasks };
}
private async completeUnderrepresentedPhases(
context: PipelineContext,
toolsData: any,
pipelineStart: number
): Promise<{ completed: number; failed: number }> {
const phases = toolsData.phases || [];
const selectedPhases = new Map<string, number>();
let completedTasks = 0;
let failedTasks = 0;
context.selectedTools?.forEach((st: any) => {
const count = selectedPhases.get(st.phase) || 0;
selectedPhases.set(st.phase, count + 1);
});
const underrepresentedPhases = phases.filter((phase: any) => {
const count = selectedPhases.get(phase.id) || 0;
return count <= 1;
});
if (underrepresentedPhases.length === 0) {
console.log('[AI-PIPELINE] All phases adequately represented');
return { completed: 0, failed: 0 };
}
console.log('[AI-PIPELINE] Completing underrepresented phases:', underrepresentedPhases.map((p: any) => p.id).join(', '));
for (const phase of underrepresentedPhases) {
const result = await this.completePhaseWithSemanticSearchAndAI(context, phase, toolsData, pipelineStart);
if (result.success) completedTasks++; else failedTasks++;
await this.delay(this.config.microTaskDelay);
}
return { completed: completedTasks, failed: failedTasks };
}
private async completePhaseWithSemanticSearchAndAI(
context: PipelineContext,
phase: any,
toolsData: any,
pipelineStart: number
): Promise<MicroTaskResult> {
const phaseStart = Date.now();
const phaseQuery = `forensic ${phase.name.toLowerCase()} tools methods`;
console.log('[AI-PIPELINE] Phase completion for:', phase.id);
try {
const phaseResults = await embeddingsService.findSimilar(phaseQuery, 20, 0.2);
auditService.addEmbeddingsSearch(
phaseQuery,
phaseResults,
0.2,
phaseStart,
{
phaseId: phase.id,
phaseName: phase.name,
completionPurpose: 'underrepresented-phase-enhancement'
}
);
if (phaseResults.length === 0) {
console.log('[AI-PIPELINE] No semantic results for phase:', phase.id);
return {
taskType: 'phase-completion',
content: '',
processingTimeMs: Date.now() - phaseStart,
success: true
};
}
const toolsMap = new Map(toolsData.tools.map((tool: any) => [tool.name, tool]));
const conceptsMap = new Map(toolsData.concepts.map((concept: any) => [concept.name, concept]));
const phaseTools = phaseResults
.filter((result: any) => result.type === 'tool')
.map((result: any) => toolsMap.get(result.name))
.filter((tool: any): tool is NonNullable<any> =>
tool !== undefined &&
tool !== null &&
tool.phases &&
Array.isArray(tool.phases) &&
tool.phases.includes(phase.id) &&
!context.seenToolNames.has(tool.name)
)
.slice(0, 5);
const phaseConcepts = phaseResults
.filter((result: any) => result.type === 'concept')
.map((result: any) => conceptsMap.get(result.name))
.filter((concept: any): concept is NonNullable<any> => concept !== undefined && concept !== null)
.slice(0, 2);
if (phaseTools.length === 0) {
console.log('[AI-PIPELINE] No suitable tools for phase completion:', phase.id);
return {
taskType: 'phase-completion',
content: '',
processingTimeMs: Date.now() - phaseStart,
success: true
};
}
const selectionPrompt = getPrompt('generatePhaseCompletionPrompt', context.userQuery, phase, phaseTools, phaseConcepts);
const selectionResult = await this.callMicroTaskAI(selectionPrompt, context, 800, 'phase-completion-selection');
if (!selectionResult.success) {
console.error('[AI-PIPELINE] Phase completion selection failed for:', phase.id);
return {
taskType: 'phase-completion',
content: '',
processingTimeMs: Date.now() - phaseStart,
success: false,
error: 'Selection micro-task failed'
};
}
const selection = JSONParser.safeParseJSON(selectionResult.content, {
selectedTools: [],
selectedConcepts: [],
completionReasoning: ''
});
const validTools = selection.selectedTools
.map((name: string) => phaseTools.find((t: any) => t && t.name === name))
.filter((tool: any): tool is NonNullable<any> => tool !== undefined && tool !== null)
.slice(0, 2);
if (validTools.length === 0) {
console.log('[AI-PIPELINE] No valid tools selected for phase completion:', phase.id);
return {
taskType: 'phase-completion',
content: selection.completionReasoning || '',
processingTimeMs: Date.now() - phaseStart,
success: true
};
}
// This is the fix for "0 tools added" - use the actual valid tools
const actualToolsAdded = validTools.map(tool => tool.name);
for (const tool of validTools) {
console.log('[AI-PIPELINE] Generating AI reasoning for phase completion tool:', tool.name);
const reasoningPrompt = getPrompt(
'phaseCompletionReasoning',
context.userQuery,
phase,
tool.name,
tool,
selection.completionReasoning || 'Nachergänzung zur Vervollständigung der Phasenabdeckung'
);
const reasoningResult = await this.callMicroTaskAI(reasoningPrompt, context, 400, 'phase-completion-reasoning');
let detailedJustification: string;
let moderatedTaskRelevance = 75;
if (reasoningResult.success && reasoningResult.content.trim()) {
detailedJustification = reasoningResult.content.trim();
moderatedTaskRelevance = this.moderateTaskRelevance(80);
} else {
detailedJustification = `Nachträglich hinzugefügt zur Vervollständigung der ${phase.name}-Phase. Die ursprüngliche KI-Auswahl war zu spezifisch und hat wichtige Tools für diese Phase übersehen.`;
moderatedTaskRelevance = this.moderateTaskRelevance(75);
}
this.addToolToSelection(
context,
tool,
phase.id,
'medium',
detailedJustification,
moderatedTaskRelevance,
['Nachträgliche Ergänzung via semantische Phasensuche mit KI-Bewertung']
);
console.log('[AI-PIPELINE] Added phase completion tool with AI reasoning:', tool.name);
}
// Use the actual tools added for audit
auditService.addPhaseCompletion(
phase.id,
actualToolsAdded, // This ensures correct count
selection.completionReasoning || `${actualToolsAdded.length} Tools für ${phase.name} hinzugefügt`,
phaseStart,
{
toolsAdded: actualToolsAdded,
toolType: validTools[0]?.type,
semanticSimilarity: phaseResults.find(r => r.name === validTools[0]?.name)?.similarity,
completionReason: 'underrepresented-phase',
originalSelectionMissed: true,
aiReasoningUsed: true,
moderatedTaskRelevance: 75
}
);
return {
taskType: 'phase-completion',
content: selection.completionReasoning || '',
processingTimeMs: Date.now() - phaseStart,
success: true
};
} catch (error) {
console.error('[AI-PIPELINE] Phase completion failed for:', phase.id, error);
return {
taskType: 'phase-completion',
content: '',
processingTimeMs: Date.now() - phaseStart,
success: false,
error: error.message
};
}
}
private moderateTaskRelevance(taskRelevance: number): number {
if (typeof taskRelevance !== 'number') {
return 70;
}
let moderated = Math.min(taskRelevance, this.config.taskRelevanceModeration.maxInitialScore);
if (moderated > this.config.taskRelevanceModeration.moderationThreshold) {
const excess = moderated - this.config.taskRelevanceModeration.moderationThreshold;
moderated = this.config.taskRelevanceModeration.moderationThreshold + (excess * 0.7);
}
return Math.round(Math.min(moderated, this.config.taskRelevanceModeration.maxInitialScore));
}
private async analyzeScenario(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
console.log('[AI-PIPELINE] Micro-task: Scenario analysis');
const taskStart = Date.now();
const isWorkflow = context.mode === 'workflow';
const prompt = getPrompt('scenarioAnalysis', isWorkflow, context.userQuery);
const result = await this.callMicroTaskAI(prompt, context, 400, 'scenario-analysis');
if (result.success) {
if (isWorkflow) {
context.scenarioAnalysis = result.content;
} else {
context.problemAnalysis = result.content;
}
this.addToContextHistory(context, `${isWorkflow ? 'Szenario' : 'Problem'}-Analyse: ${result.content.slice(0, 200)}...`);
const confidence = auditService.calculateAIResponseConfidence(
result.content,
{ min: 50, max: 300 },
'scenario-analysis'
);
auditService.addAIDecision(
'contextual-analysis',
prompt,
result.content,
confidence,
`Analysierte ${isWorkflow ? 'Szenario' : 'Problem'} basierend auf Nutzereingabe: "${context.userQuery.slice(0, 100)}..." - Identifizierte Kernaspekte und Herausforderungen für forensische Untersuchung`,
taskStart,
{
microTaskType: 'scenario-analysis',
analysisType: isWorkflow ? 'scenario' : 'problem',
contentLength: result.content.length,
decisionBasis: 'ai-analysis',
aiModel: aiService.getConfig().model,
...result.aiUsage
}
);
}
return result;
}
private async generateApproach(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
console.log('[AI-PIPELINE] Micro-task: Investigation approach');
const taskStart = Date.now();
const isWorkflow = context.mode === 'workflow';
const prompt = getPrompt('investigationApproach', isWorkflow, context.userQuery);
const result = await this.callMicroTaskAI(prompt, context, 400, 'investigation-approach');
if (result.success) {
context.investigationApproach = result.content;
this.addToContextHistory(context, `${isWorkflow ? 'Untersuchungs' : 'Lösungs'}ansatz: ${result.content.slice(0, 200)}...`);
const confidence = auditService.calculateAIResponseConfidence(
result.content,
{ min: 50, max: 300 },
'investigation-approach'
);
auditService.addAIDecision(
'contextual-analysis',
prompt,
result.content,
confidence,
`Entwickelte ${isWorkflow ? 'Untersuchungs' : 'Lösungs'}ansatz unter Berücksichtigung der Szenario-Analyse - Strukturierte Herangehensweise für forensische Methodik`,
taskStart,
{
microTaskType: 'investigation-approach',
approachType: isWorkflow ? 'investigation' : 'solution',
contentLength: result.content.length,
contextHistoryLength: context.contextHistory.length,
decisionBasis: 'ai-analysis',
aiModel: aiService.getConfig().model,
...result.aiUsage
}
);
}
return result;
}
private async generateCriticalConsiderations(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
console.log('[AI-PIPELINE] Micro-task: Critical considerations');
const taskStart = Date.now();
const isWorkflow = context.mode === 'workflow';
const prompt = getPrompt('criticalConsiderations', isWorkflow, context.userQuery);
const result = await this.callMicroTaskAI(prompt, context, 350, 'critical-considerations');
if (result.success) {
context.criticalConsiderations = result.content;
this.addToContextHistory(context, `Kritische Überlegungen: ${result.content.slice(0, 200)}...`);
const confidence = auditService.calculateAIResponseConfidence(
result.content,
{ min: 40, max: 250 },
'critical-considerations'
);
auditService.addAIDecision(
'contextual-analysis',
prompt,
result.content,
confidence,
'Identifizierte kritische Überlegungen für forensische Untersuchung - Berücksichtigung von Beweissicherung, Chain of Custody und methodischen Herausforderungen',
taskStart,
{
microTaskType: 'critical-considerations',
contentLength: result.content.length,
decisionBasis: 'ai-analysis',
aiModel: aiService.getConfig().model,
...result.aiUsage
}
);
}
return result;
}
private async evaluateSpecificTool(
context: PipelineContext,
tool: any,
rank: number,
pipelineStart: number
): Promise<MicroTaskResult> {
console.log('[AI-PIPELINE] Micro-task: Tool evaluation for:', tool.name);
const taskStart = Date.now();
const existingSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name);
const originalTaskRelevance = existingSelection?.taskRelevance || 70;
const moderatedTaskRelevance = this.moderateTaskRelevance(originalTaskRelevance);
const priority = this.derivePriorityFromScore(moderatedTaskRelevance);
const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank, moderatedTaskRelevance);
const result = await this.callMicroTaskAI(prompt, context, 1000, 'tool-evaluation');
if (result.success) {
const evaluation = JSONParser.safeParseJSON(result.content, {
detailed_explanation: 'Evaluation failed',
implementation_approach: '',
pros: [],
limitations: [],
alternatives: ''
});
this.addToolToSelection(context, {
...tool,
evaluation: {
...evaluation,
rank,
task_relevance: moderatedTaskRelevance
}
}, 'evaluation', priority, evaluation.detailed_explanation, moderatedTaskRelevance, evaluation.limitations);
const responseConfidence = auditService.calculateAIResponseConfidence(
result.content,
{ min: 200, max: 800 },
'tool-evaluation'
);
const finalConfidence = Math.max(responseConfidence, moderatedTaskRelevance);
auditService.addAIDecision(
'tool-evaluation',
prompt,
result.content,
finalConfidence,
`Bewertete Tool "${tool.name}" (Rang ${rank}) - Analysierte Eignung für spezifische Aufgabenstellung mit Fokus auf praktische Anwendbarkeit und methodische Integration`,
taskStart,
{
microTaskType: 'tool-evaluation',
toolName: tool.name,
toolType: tool.type,
rank,
originalTaskRelevance,
moderatedTaskRelevance,
responseConfidence,
finalConfidence,
moderationApplied: originalTaskRelevance !== moderatedTaskRelevance,
evaluationParsed: !!evaluation.detailed_explanation,
prosCount: evaluation.pros?.length || 0,
limitationsCount: evaluation.limitations?.length || 0,
decisionBasis: 'ai-analysis',
aiModel: aiService.getConfig().model,
...result.aiUsage
}
);
}
return result;
}
private async selectBackgroundKnowledge(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
console.log('[AI-PIPELINE] Micro-task: Background knowledge selection');
const taskStart = Date.now();
const availableConcepts = context.filteredData.concepts;
if (availableConcepts.length === 0) {
return {
taskType: 'background-knowledge',
content: JSON.stringify([]),
processingTimeMs: 0,
success: true
};
}
const selectedToolNames = context.selectedTools?.map((st: any) => st.tool && st.tool.name).filter(Boolean) || [];
const prompt = getPrompt('backgroundKnowledgeSelection', context.userQuery, context.mode, selectedToolNames, availableConcepts);
const result = await this.callMicroTaskAI(prompt, context, 700, 'background-knowledge');
if (result.success) {
const selections = JSONParser.safeParseJSON(result.content, []);
if (Array.isArray(selections)) {
context.backgroundKnowledge = selections.filter((sel: any) =>
sel.conceptName && availableConcepts.some((concept: any) => concept.name === sel.conceptName)
).map((sel: any) => ({
concept: availableConcepts.find((c: any) => c.name === sel.conceptName),
relevance: sel.relevance
}));
const responseConfidence = auditService.calculateAIResponseConfidence(
result.content,
{ min: 100, max: 500 },
'background-knowledge'
);
// Calculate confidence based on quality of selections
const selectionQualityBonus = this.calculateKnowledgeSelectionBonus(context.backgroundKnowledge, availableConcepts);
const finalConfidence = Math.min(95, responseConfidence + selectionQualityBonus);
auditService.addEntry(
'knowledge-synthesis',
'concept-selection',
{
availableConcepts: availableConcepts.map(c => c.name),
selectedToolsContext: selectedToolNames,
selectionCriteria: 'methodische Fundierung'
},
{
selectedConcepts: context.backgroundKnowledge.map(bk => bk.concept.name),
selectionReasonings: context.backgroundKnowledge.map(bk => bk.relevance)
},
finalConfidence,
taskStart,
{
microTaskType: 'background-knowledge',
availableConceptsCount: availableConcepts.length,
selectedConceptsCount: context.backgroundKnowledge.length,
selectionRatio: context.backgroundKnowledge.length / availableConcepts.length,
responseConfidence,
selectionQualityBonus,
decisionBasis: 'ai-analysis',
reasoning: `Wählte ${context.backgroundKnowledge.length} von ${availableConcepts.length} verfügbaren Konzepten für methodische Fundierung der Empfehlungen`,
aiModel: aiService.getConfig().model,
...result.aiUsage
}
);
}
}
return result;
}
private calculateKnowledgeSelectionBonus(
selectedKnowledge: Array<{concept: any; relevance: string}>,
availableConcepts: any[]
): number {
let bonus = 0;
if (selectedKnowledge.length > 0) {
bonus += 10;
}
// Good selection ratio (10-30% of available concepts)
const ratio = selectedKnowledge.length / availableConcepts.length;
if (ratio >= 0.1 && ratio <= 0.3) {
bonus += 15;
}
// Quality reasoning provided
const hasGoodReasonings = selectedKnowledge.some(bk =>
bk.relevance && bk.relevance.length > 30
);
if (hasGoodReasonings) {
bonus += 10;
}
return bonus;
}
private async generateFinalRecommendations(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
console.log('[AI-PIPELINE] Micro-task: Final recommendations');
const taskStart = Date.now();
const selectedToolNames = context.selectedTools?.map((st: any) => st.tool && st.tool.name).filter(Boolean) || [];
const prompt = getPrompt('finalRecommendations', context.mode === 'workflow', context.userQuery, selectedToolNames);
const result = await this.callMicroTaskAI(prompt, context, 350, 'final-recommendations');
if (result.success) {
const confidence = auditService.calculateAIResponseConfidence(
result.content,
{ min: 60, max: 250 },
'final-recommendations'
);
// Calculate bonus based on context quality
const contextBonus = this.calculateSynthesisBonus(selectedToolNames, context);
const finalConfidence = Math.min(95, confidence + contextBonus);
auditService.addAIDecision(
'synthesis',
prompt,
result.content,
finalConfidence,
`Generierte abschließende ${context.mode}-Empfehlungen basierend auf ausgewählten ${selectedToolNames.length} Tools - Synthese aller Analyseschritte zu kohärenter Handlungsempfehlung`,
taskStart,
{
microTaskType: 'final-recommendations',
mode: context.mode,
selectedToolsCount: selectedToolNames.length,
contentLength: result.content.length,
responseConfidence: confidence,
contextBonus,
decisionBasis: 'ai-analysis',
aiModel: aiService.getConfig().model,
...result.aiUsage
}
);
}
return result;
}
private calculateSynthesisBonus(selectedToolNames: string[], context: PipelineContext): number {
let bonus = 0;
if (selectedToolNames.length >= 3) {
bonus += 10;
}
if (context.backgroundKnowledge && context.backgroundKnowledge.length > 0) {
bonus += 10;
}
if (context.scenarioAnalysis || context.problemAnalysis) {
bonus += 5;
}
if (context.investigationApproach) {
bonus += 5;
}
return bonus;
}
private buildRecommendation(context: PipelineContext, mode: string, finalContent: string): any {
const isWorkflow = mode === 'workflow';
console.log('[AI-PIPELINE] Building recommendation for', mode, 'mode with', context.selectedTools?.length || 0, 'tools');
const base = {
[isWorkflow ? 'scenario_analysis' : 'problem_analysis']:
isWorkflow ? context.scenarioAnalysis : context.problemAnalysis,
investigation_approach: context.investigationApproach,
critical_considerations: context.criticalConsiderations,
background_knowledge: context.backgroundKnowledge?.map((bk: any) => ({
concept_name: bk.concept.name,
relevance: bk.relevance
})) || []
};
if (isWorkflow) {
const recommendedToolsWithConfidence = context.selectedTools?.map((st: any) => {
const analysisContext: AnalysisContext = {
userQuery: context.userQuery,
mode: context.mode,
embeddingsSimilarities: context.embeddingsSimilarities,
selectedTools: context.selectedTools
};
const confidence = confidenceScoring.calculateRecommendationConfidence(
st.tool,
analysisContext,
st.taskRelevance || 70,
st.limitations || []
);
auditService.addConfidenceCalculation(
st.tool.name,
confidence,
Date.now(),
{
phase: st.phase,
priority: st.priority,
toolType: st.tool.type,
moderatedTaskRelevance: st.taskRelevance
}
);
return {
name: st.tool.name,
type: st.tool.type,
phase: st.phase,
priority: st.priority,
justification: st.justification || `Empfohlen für ${st.phase}`,
confidence: confidence,
recommendationStrength: confidenceScoring.getConfidenceLevel(confidence.overall)
};
}) || [];
return {
...base,
recommended_tools: recommendedToolsWithConfidence,
workflow_suggestion: finalContent
};
} else {
const recommendedToolsWithConfidence = context.selectedTools?.map((st: any) => {
const analysisContext: AnalysisContext = {
userQuery: context.userQuery,
mode: context.mode,
embeddingsSimilarities: context.embeddingsSimilarities,
selectedTools: context.selectedTools
};
const confidence = confidenceScoring.calculateRecommendationConfidence(
st.tool,
analysisContext,
st.taskRelevance || 70,
st.limitations || []
);
auditService.addConfidenceCalculation(
st.tool.name,
confidence,
Date.now(),
{
rank: st.tool.evaluation?.rank || 1,
toolType: st.tool.type,
moderatedTaskRelevance: st.taskRelevance
}
);
return {
name: st.tool.name,
type: st.tool.type,
rank: st.tool.evaluation?.rank || 1,
suitability_score: st.priority,
detailed_explanation: st.tool.evaluation?.detailed_explanation || '',
implementation_approach: st.tool.evaluation?.implementation_approach || '',
pros: st.tool.evaluation?.pros || [],
cons: st.tool.evaluation?.limitations || [],
alternatives: st.tool.evaluation?.alternatives || '',
confidence: confidence,
recommendationStrength: confidenceScoring.getConfidenceLevel(confidence.overall)
};
}) || [];
return {
...base,
recommended_tools: recommendedToolsWithConfidence,
additional_considerations: finalContent
};
}
}
private async callMicroTaskAI(
prompt: string,
context: PipelineContext,
maxTokens: number = 500,
taskType: string = 'micro-task'
): Promise<MicroTaskResult> {
const startTime = Date.now();
let contextPrompt = prompt;
if (context.contextHistory.length > 0) {
const contextSection = `BISHERIGE ANALYSE:\n${context.contextHistory.join('\n\n')}\n\nAKTUELLE AUFGABE:\n`;
const combinedPrompt = contextSection + prompt;
if (aiService.estimateTokens(combinedPrompt) <= this.config.maxPromptTokens) {
contextPrompt = combinedPrompt;
}
}
try {
const response = await aiService.callMicroTaskAI(contextPrompt, maxTokens);
return {
taskType,
content: response.content,
processingTimeMs: Date.now() - startTime,
success: true,
aiUsage: response.usage
};
} catch (error) {
return {
taskType,
content: '',
processingTimeMs: Date.now() - startTime,
success: false,
error: error.message
};
}
}
private addToContextHistory(context: PipelineContext, newEntry: string): void {
const entryTokens = aiService.estimateTokens(newEntry);
context.contextHistory.push(newEntry);
context.currentContextLength += entryTokens;
while (context.currentContextLength > this.config.maxContextTokens && context.contextHistory.length > 1) {
const removed = context.contextHistory.shift()!;
context.currentContextLength -= aiService.estimateTokens(removed);
}
}
private addToolToSelection(
context: PipelineContext,
tool: any,
phase: string,
priority: string,
justification?: string,
taskRelevance?: number,
limitations?: string[]
): boolean {
context.seenToolNames.add(tool.name);
if (!context.selectedTools) context.selectedTools = [];
context.selectedTools.push({
tool,
phase,
priority,
justification,
taskRelevance,
limitations
});
return true;
}
private derivePriorityFromScore(taskRelevance: number): string {
if (taskRelevance >= 80) return 'high';
if (taskRelevance >= 60) return 'medium';
return 'low';
}
private trackTokenUsage(usage?: { promptTokens?: number; completionTokens?: number; totalTokens?: number }): void {
if (usage?.totalTokens) {
this.totalTokensUsed += usage.totalTokens;
}
}
private async delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
export const aiPipeline = new AIPipeline();
export type { AnalysisResult };