1415 lines
48 KiB
TypeScript
1415 lines
48 KiB
TypeScript
// 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<string>;
|
||
embeddingsSimilarities: Map<string, number>;
|
||
phaseMetadata?: {
|
||
phases: any[];
|
||
criticalPhaseIds: string[];
|
||
phaseComplexity: 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),
|
||
taskRelevanceModeration: {
|
||
maxInitialScore: 85,
|
||
maxWithPhaseBonus: 95,
|
||
moderationThreshold: 80
|
||
}
|
||
};
|
||
}
|
||
|
||
async processQuery(userQuery: string, mode: string): Promise<AnalysisResult> {
|
||
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<string>(),
|
||
embeddingsSimilarities: new Map<string, number>(),
|
||
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<string, number>;
|
||
} {
|
||
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<string, number>();
|
||
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<string, number>;
|
||
}
|
||
): 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<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) 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<MicroTaskResult> {
|
||
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<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) {
|
||
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<any> => 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<MicroTaskResult> {
|
||
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<MicroTaskResult> {
|
||
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<MicroTaskResult> {
|
||
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<MicroTaskResult> {
|
||
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<MicroTaskResult> {
|
||
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<MicroTaskResult> {
|
||
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<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`;
|
||
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<string, string> = {
|
||
'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<string, { min: number; max: number }> = {
|
||
'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<string, string> = {
|
||
'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<void> {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
}
|
||
|
||
export const aiPipeline = new AIPipeline();
|
||
export type { AnalysisResult }; |