// src/utils/aiPipeline.ts 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 } from './auditService.js'; import { JSONParser } from './jsonUtils.js'; import { getPrompt } from '../config/prompts.js'; interface PipelineConfig { microTaskDelay: 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: { 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[]; 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; phaseMetadata?: { phases: any[]; criticalPhaseIds: string[]; phaseComplexity: Map; }; } class AIPipeline { private config: PipelineConfig; private totalTokensUsed: number = 0; constructor() { this.config = { microTaskDelay: parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10), taskRelevanceModeration: { maxInitialScore: 85, maxWithPhaseBonus: 95, moderationThreshold: 80 } }; } async processQuery(userQuery: string, mode: string): Promise { const startTime = Date.now(); let completedTasks = 0; let failedTasks = 0; this.totalTokensUsed = 0; auditService.clearAuditTrail(); try { const toolsData = await getCompressedToolsDataForAI(); const aiConfig = aiService.getConfig(); const toolsDataHash = getDataVersion?.() || 'unknown'; auditService.addEntry( 'initialization', 'tools-data-loaded', { toolsFile: 'tools.yaml', hashAlgorithm: 'SHA256' }, { toolsDataHash: toolsDataHash, toolsCount: toolsData.tools.length, conceptsCount: toolsData.concepts.length, domainsCount: toolsData.domains.length, phasesCount: toolsData.phases.length }, 100, Date.now(), { toolsDataHash: toolsDataHash, verification: `Users can verify with: sha256sum src/data/tools.yaml`, dataVersion: toolsDataHash.slice(0, 12), reasoning: `Geladen: ${toolsData.tools.length} Tools, ${toolsData.concepts.length} Konzepte aus tools.yaml (Hash: ${toolsDataHash.slice(0, 12)}...)` } ); const context: PipelineContext = { userQuery, mode, filteredData: {}, contextHistory: [], currentContextLength: 0, seenToolNames: new Set(), embeddingsSimilarities: new Map(), phaseMetadata: this.initializePhaseMetadata(toolsData.phases) }; const candidateSelectionStart = Date.now(); const candidateData = await toolSelector.getIntelligentCandidates(userQuery, toolsData, mode, context); const selectionConfidence = this.calculateToolSelectionConfidence( candidateData.tools.length, toolsData.tools.length, candidateData.concepts.length ); auditService.addToolSelection( candidateData.tools.map(t => t.name), toolsData.tools.map(t => t.name), selectionConfidence, candidateSelectionStart, { toolsDataHash: toolsDataHash, totalCandidatesFound: candidateData.tools.length + candidateData.concepts.length, reductionRatio: candidateData.tools.length / toolsData.tools.length } ); context.filteredData = candidateData; const analysisResult = await this.analyzeScenario(context, startTime, toolsDataHash); if (analysisResult.success) completedTasks++; else failedTasks++; this.trackTokenUsage(analysisResult.aiUsage); await this.delay(this.config.microTaskDelay); const approachResult = await this.generateApproach(context, startTime, toolsDataHash); if (approachResult.success) completedTasks++; else failedTasks++; this.trackTokenUsage(approachResult.aiUsage); await this.delay(this.config.microTaskDelay); const considerationsResult = await this.generateCriticalConsiderations(context, startTime, toolsDataHash); if (considerationsResult.success) completedTasks++; else failedTasks++; this.trackTokenUsage(considerationsResult.aiUsage); await this.delay(this.config.microTaskDelay); if (mode === 'workflow') { const workflowResults = await this.processWorkflowMode(context, toolsData, completedTasks, failedTasks, startTime, toolsDataHash); completedTasks = workflowResults.completed; failedTasks = workflowResults.failed; } else { const toolResults = await this.processToolMode(context, completedTasks, failedTasks, startTime, toolsDataHash); completedTasks = toolResults.completed; failedTasks = toolResults.failed; } const knowledgeResult = await this.selectBackgroundKnowledge(context, startTime, toolsDataHash); if (knowledgeResult.success) completedTasks++; else failedTasks++; this.trackTokenUsage(knowledgeResult.aiUsage); await this.delay(this.config.microTaskDelay); const finalResult = await this.generateFinalRecommendations(context, startTime, toolsDataHash); if (finalResult.success) completedTasks++; else failedTasks++; this.trackTokenUsage(finalResult.aiUsage); const recommendation = this.buildRecommendation(context, mode, finalResult.content, toolsDataHash); const processingStats = { 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: 32768 }; 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 initializePhaseMetadata(phases: any[]): { phases: any[]; criticalPhaseIds: string[]; phaseComplexity: Map; } { if (!phases || !Array.isArray(phases)) { return { phases: [], criticalPhaseIds: [], phaseComplexity: new Map() }; } const criticalPhaseIds = phases .filter(phase => { const typicalToolsCount = phase.typical_tools?.length || 0; const keyActivitiesCount = phase.key_activities?.length || 0; return typicalToolsCount >= 3 || keyActivitiesCount >= 2; }) .map(phase => phase.id); const phaseComplexity = new Map(); phases.forEach(phase => { let complexity = 1; if (phase.typical_tools?.length > 5) complexity += 1; if (phase.key_activities?.length > 3) complexity += 1; if (phase.description?.length > 100) complexity += 1; phaseComplexity.set(phase.id, complexity); }); return { phases, criticalPhaseIds, phaseComplexity }; } private calculateToolSelectionConfidence( selectedCount: number, totalCount: number, conceptsCount: number ): number { let confidence = 50; const selectionRatio = selectedCount / totalCount; if (selectionRatio >= 0.05 && selectionRatio <= 0.20) { confidence += 25; } else if (selectionRatio < 0.05) { confidence += 15; } else if (selectionRatio > 0.30) { confidence -= 15; } if (conceptsCount > 0) confidence += 10; 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, toolsDataHash: string ): 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) continue; const selections = await toolSelector.selectToolsForPhase(context.userQuery, phase, phaseTools, context); const phaseConfidence = this.calculatePhaseSelectionConfidence( selections.length, phaseTools.length, phase.id, selections, context.phaseMetadata ); 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, { toolsDataHash: toolsDataHash, 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); const dynamicLimitations = this.generateDynamicLimitations(tool, phase, sel); this.addToolToSelection(context, tool, phase.id, priority, sel.justification, moderatedTaskRelevance, dynamicLimitations); auditService.addEntry( 'tool-reasoning', 'tool-added-to-phase', { toolName: tool.name, phaseId: phase.id, taskRelevance: moderatedTaskRelevance, priority: priority }, { justification: sel.justification, limitations: dynamicLimitations, addedToPhase: phase.name }, moderatedTaskRelevance || 70, phaseStart, { toolsDataHash: toolsDataHash, 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, toolsDataHash); completedTasks += completionResult.completed; failedTasks += completionResult.failed; return { completed: completedTasks, failed: failedTasks }; } private calculatePhaseSelectionConfidence( selectedCount: number, availableCount: number, phaseId: string, selections: any[], phaseMetadata?: { phases: any[]; criticalPhaseIds: string[]; phaseComplexity: Map; } ): number { let confidence = 60; const isCritical = phaseMetadata?.criticalPhaseIds.includes(phaseId) || false; const phaseComplexity = phaseMetadata?.phaseComplexity.get(phaseId) || 1; if (selectedCount > 0) { confidence += 20; } else { return 30; } const ratio = selectedCount / availableCount; if (ratio >= 0.2 && ratio <= 0.5) { confidence += 15; } else if (ratio < 0.2 && selectedCount >= 1) { confidence += 10; } if (isCritical && selectedCount >= 2) confidence += 10; if (phaseComplexity > 2 && selectedCount >= phaseComplexity) confidence += 5; 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 generateDynamicLimitations(tool: any, phase: any, selection: any): string[] { const limitations: string[] = []; if (selection.limitations && Array.isArray(selection.limitations)) { limitations.push(...selection.limitations); } if (tool.type === 'software' && !tool.projectUrl) { limitations.push('Installation und Konfiguration erforderlich'); } if (tool.skillLevel === 'expert') { limitations.push('Erfordert spezialisierte Kenntnisse'); } if (tool.license === 'Proprietary') { limitations.push('Kommerzielle Lizenz erforderlich'); } if (phase.id === 'acquisition' && tool.type === 'method') { limitations.push('Sorgfältige Dokumentation für Chain of Custody erforderlich'); } return limitations.slice(0, 3); } private async processToolMode( context: PipelineContext, completedTasks: number, failedTasks: number, pipelineStart: number, toolsDataHash: string ): Promise<{ completed: number; failed: number }> { const candidates = context.filteredData.tools || []; if (!Array.isArray(candidates) || candidates.length === 0) { return { completed: completedTasks, failed: failedTasks }; } for (let i = 0; i < candidates.length; i++) { const evaluationResult = await this.evaluateSpecificTool(context, candidates[i], i + 1, pipelineStart, toolsDataHash); if (evaluationResult.success) completedTasks++; else failedTasks++; this.trackTokenUsage(evaluationResult.aiUsage); await this.delay(this.config.microTaskDelay); } if (Array.isArray(context.selectedTools) && context.selectedTools.length > 0) { context.selectedTools.sort((a: any, b: any) => { const ar = typeof a.taskRelevance === 'number' ? a.taskRelevance : -1; const br = typeof b.taskRelevance === 'number' ? b.taskRelevance : -1; if (br !== ar) return br - ar; const aLen = (a.justification || '').length; const bLen = (b.justification || '').length; if (bLen !== aLen) return bLen - aLen; const aRank = a.tool?.evaluation?.rank ?? Number.MAX_SAFE_INTEGER; const bRank = b.tool?.evaluation?.rank ?? Number.MAX_SAFE_INTEGER; return aRank - bRank; }); context.selectedTools = context.selectedTools.slice(0, 3); } return { completed: completedTasks, failed: failedTasks }; } private async completeUnderrepresentedPhases( context: PipelineContext, toolsData: any, pipelineStart: number, toolsDataHash: string ): 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) return { completed: 0, failed: 0 }; for (const phase of underrepresentedPhases) { const result = await this.completePhaseWithSemanticSearchAndAI(context, phase, toolsData, pipelineStart, toolsDataHash); 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, toolsDataHash: string ): Promise { const phaseStart = Date.now(); const phaseQuery = `forensic ${phase.name.toLowerCase()} tools methods`; try { const phaseResults = await embeddingsService.findSimilar(phaseQuery, 20, 0.2); auditService.addEmbeddingsSearch( phaseQuery, phaseResults, 0.2, phaseStart, { toolsDataHash: toolsDataHash, phaseId: phase.id, phaseName: phase.name, completionPurpose: 'underrepresented-phase-enhancement' } ); if (phaseResults.length === 0) { 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) { 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, 'phase-completion-selection'); if (!selectionResult.success) { 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) { return { taskType: 'phase-completion', content: selection.completionReasoning || '', processingTimeMs: Date.now() - phaseStart, success: true }; } const actualToolsAdded = validTools.map(tool => tool.name); for (const tool of validTools) { 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, '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); } const dynamicLimitations = this.generateCompletionLimitations(tool, phase, selection); this.addToolToSelection( context, tool, phase.id, 'medium', detailedJustification, moderatedTaskRelevance, dynamicLimitations ); } auditService.addPhaseCompletion( phase.id, actualToolsAdded, selection.completionReasoning || `${actualToolsAdded.length} Tools für ${phase.name} hinzugefügt`, phaseStart, { toolsDataHash: toolsDataHash, 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) { return { taskType: 'phase-completion', content: '', processingTimeMs: Date.now() - phaseStart, success: false, error: error.message }; } } private generateCompletionLimitations(tool: any, phase: any, selection: any): string[] { const limitations: string[] = []; limitations.push('Nachträgliche Ergänzung - ursprünglich nicht als Hauptmethode identifiziert'); if (tool.skillLevel === 'expert') { limitations.push('Erfordert Expertenwissen für optimale Nutzung'); } if (tool.type === 'software' && !tool.projectUrl) { limitations.push('Zusätzliche Setup-Zeit erforderlich'); } if (phase.typical_tools && !phase.typical_tools.includes(tool.name)) { limitations.push(`Nicht typisch für ${phase.name}-Phase - alternative Anwendung`); } return limitations.slice(0, 3); } 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, toolsDataHash: string): Promise { const taskStart = Date.now(); const isWorkflow = context.mode === 'workflow'; const prompt = getPrompt('scenarioAnalysis', isWorkflow, context.userQuery); const result = await this.callMicroTaskAI(prompt, context, '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 Untersuchung`, taskStart, { toolsDataHash: toolsDataHash, 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, toolsDataHash: string): Promise { const taskStart = Date.now(); const isWorkflow = context.mode === 'workflow'; const prompt = getPrompt('investigationApproach', isWorkflow, context.userQuery); const result = await this.callMicroTaskAI(prompt, context, '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, { toolsDataHash: toolsDataHash, 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, toolsDataHash: string): Promise { const taskStart = Date.now(); const isWorkflow = context.mode === 'workflow'; const prompt = getPrompt('criticalConsiderations', isWorkflow, context.userQuery); const result = await this.callMicroTaskAI(prompt, context, '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, { toolsDataHash: toolsDataHash, 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, toolsDataHash: string ): Promise { const taskStart = Date.now(); const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank); const result = await this.callMicroTaskAI(prompt, context, 'tool-evaluation'); if (!result.success) { return result; } const evaluation = JSONParser.safeParseJSON(result.content, null); const aiProvided = evaluation && typeof evaluation.taskRelevance === 'number' && Number.isFinite(evaluation.taskRelevance) ? Math.round(evaluation.taskRelevance) : null; if (aiProvided === null) { auditService.addAIDecision( 'tool-evaluation', prompt, result.content, 0, `Bewertung für "${tool.name}" ignoriert: fehlender/ungültiger taskRelevance`, taskStart, { toolsDataHash, microTaskType: 'tool-evaluation', toolName: tool.name, toolType: tool.type, rank, evaluationParsed: false, decisionBasis: 'ai-analysis', aiModel: aiService.getConfig().model, ...(result.aiUsage || {}) } ); return result; } const moderatedTaskRelevance = this.moderateTaskRelevance(aiProvided); const priority = this.derivePriorityFromScore(moderatedTaskRelevance); const detailed_explanation = String(evaluation?.detailed_explanation || '').trim(); const implementation_approach = String(evaluation?.implementation_approach || '').trim(); const pros = Array.isArray(evaluation?.pros) ? evaluation.pros : []; const limitations = Array.isArray(evaluation?.limitations) ? evaluation.limitations : []; const alternatives = String(evaluation?.alternatives || '').trim(); this.addToolToSelection( context, { ...tool, evaluation: { detailed_explanation, implementation_approach, pros, limitations, alternatives, rank, task_relevance: moderatedTaskRelevance } }, 'evaluation', priority, detailed_explanation, moderatedTaskRelevance, 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}) – AI-Score ${aiProvided}, moderiert ${moderatedTaskRelevance}`, taskStart, { toolsDataHash, microTaskType: 'tool-evaluation', toolName: tool.name, toolType: tool.type, rank, aiProvidedTaskRelevance: aiProvided, moderatedTaskRelevance, responseConfidence, finalConfidence, moderationApplied: aiProvided !== moderatedTaskRelevance, evaluationParsed: true, prosCount: pros.length, limitationsCount: limitations.length, decisionBasis: 'ai-analysis', aiModel: aiService.getConfig().model, ...(result.aiUsage || {}) } ); return result; } private async selectBackgroundKnowledge(context: PipelineContext, pipelineStart: number, toolsDataHash: string): Promise { 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, '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' ); 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, { toolsDataHash: toolsDataHash, 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; const ratio = selectedKnowledge.length / availableConcepts.length; if (ratio >= 0.1 && ratio <= 0.3) bonus += 15; const hasGoodReasonings = selectedKnowledge.some(bk => bk.relevance && bk.relevance.length > 30 ); if (hasGoodReasonings) bonus += 10; return bonus; } private async generateFinalRecommendations(context: PipelineContext, pipelineStart: number, toolsDataHash: string): Promise { 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, 'final-recommendations'); if (result.success) { const confidence = auditService.calculateAIResponseConfidence( result.content, { min: 60, max: 250 }, 'final-recommendations' ); 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, { toolsDataHash: toolsDataHash, 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, toolsDataHash: string): any { const isWorkflow = mode === 'workflow'; 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(), { toolsDataHash: toolsDataHash, 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(), { toolsDataHash: toolsDataHash, 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, 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`; contextPrompt = contextSection + prompt; } try { const response = await aiService.callMicroTaskAI(contextPrompt); const toolsDataHash = getDataVersion?.() || 'unknown'; const aiConfig = aiService.getConfig(); const responseConfidence = auditService.calculateAIResponseConfidence( response.content, this.getExpectedLengthForTaskType(taskType), taskType ); auditService.addAIDecision( this.getPhaseForTaskType(taskType), prompt, response.content, responseConfidence, this.getReasoningForTaskType(taskType, response.content), startTime, { toolsDataHash: toolsDataHash, microTaskType: taskType, aiModel: aiConfig.model, contextLength: contextPrompt.length, originalPromptLength: prompt.length, contextHistoryUsed: context.contextHistory.length > 0, decisionBasis: 'ai-analysis', ...response.usage } ); return { taskType, content: response.content, processingTimeMs: Date.now() - startTime, success: true, aiUsage: response.usage }; } catch (error) { auditService.addEntry( this.getPhaseForTaskType(taskType), 'ai-decision-failed', { prompt: prompt.slice(0, 200) + '...', taskType: taskType, error: error.message }, { error: error.message, success: false }, 0, startTime, { toolsDataHash: getDataVersion?.() || 'unknown', microTaskType: taskType, failed: true, decisionBasis: 'ai-analysis' } ); return { taskType, content: '', processingTimeMs: Date.now() - startTime, success: false, error: error.message }; } } private getPhaseForTaskType(taskType: string): string { const phaseMap: Record = { 'scenario-analysis': 'contextual-analysis', 'investigation-approach': 'contextual-analysis', 'critical-considerations': 'contextual-analysis', 'tool-evaluation': 'tool-evaluation', 'background-knowledge': 'knowledge-synthesis', 'final-recommendations': 'synthesis', 'phase-completion-selection': 'phase-completion', 'phase-completion-reasoning': 'phase-completion' }; return phaseMap[taskType] || 'contextual-analysis'; } private getExpectedLengthForTaskType(taskType: string): { min: number; max: number } { const lengthMap: Record = { 'scenario-analysis': { min: 100, max: 500 }, 'investigation-approach': { min: 100, max: 400 }, 'critical-considerations': { min: 80, max: 300 }, 'tool-evaluation': { min: 200, max: 800 }, 'background-knowledge': { min: 50, max: 300 }, 'final-recommendations': { min: 150, max: 600 }, 'phase-completion-selection': { min: 50, max: 200 }, 'phase-completion-reasoning': { min: 100, max: 300 } }; return lengthMap[taskType] || { min: 50, max: 300 }; } private getReasoningForTaskType(taskType: string, response: string): string { const responseLength = response.length; const taskNames: Record = { 'scenario-analysis': 'Szenario-Analyse', 'investigation-approach': 'Untersuchungsansatz', 'critical-considerations': 'Kritische Überlegungen', 'tool-evaluation': 'Tool-Bewertung', 'background-knowledge': 'Hintergrundwissen-Auswahl', 'final-recommendations': 'Abschließende Empfehlungen', 'phase-completion-selection': 'Phasen-Vervollständigung', 'phase-completion-reasoning': 'Phasen-Begründung' }; const taskName = taskNames[taskType] || taskType; return `KI generierte ${taskName} (${responseLength} Zeichen) - Analyse mit methodischer Begründung`; } private addToContextHistory(context: PipelineContext, newEntry: string): void { const entryTokens = aiService.estimateTokens(newEntry); context.contextHistory.push(newEntry); context.currentContextLength += entryTokens; if (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 };