// 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; embeddingsSimilarities: Map; } 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 { 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(), embeddingsSimilarities: new Map() }; // 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(); 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 { 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 => 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 => 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 => 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { return new Promise(resolve => setTimeout(resolve, ms)); } } export const aiPipeline = new AIPipeline(); export type { AnalysisResult };