|
|
|
@ -4,15 +4,12 @@ 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 { auditService } 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;
|
|
|
|
@ -36,7 +33,6 @@ interface MicroTaskResult {
|
|
|
|
|
interface AnalysisResult {
|
|
|
|
|
recommendation: any;
|
|
|
|
|
processingStats: {
|
|
|
|
|
//embeddingsUsed: boolean;
|
|
|
|
|
candidatesFromEmbeddings: number;
|
|
|
|
|
finalSelectedItems: number;
|
|
|
|
|
processingTimeMs: number;
|
|
|
|
@ -57,7 +53,6 @@ interface PipelineContext {
|
|
|
|
|
mode: string;
|
|
|
|
|
filteredData: any;
|
|
|
|
|
contextHistory: string[];
|
|
|
|
|
//maxContextLength: number;
|
|
|
|
|
currentContextLength: number;
|
|
|
|
|
scenarioAnalysis?: string;
|
|
|
|
|
problemAnalysis?: string;
|
|
|
|
@ -91,16 +86,12 @@ class AIPipeline {
|
|
|
|
|
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 dynamic phase handling');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async processQuery(userQuery: string, mode: string): Promise<AnalysisResult> {
|
|
|
|
@ -109,8 +100,6 @@ class AIPipeline {
|
|
|
|
|
let failedTasks = 0;
|
|
|
|
|
this.totalTokensUsed = 0;
|
|
|
|
|
|
|
|
|
|
console.log('[AI-PIPELINE] Starting', mode, 'analysis pipeline');
|
|
|
|
|
|
|
|
|
|
auditService.clearAuditTrail();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
@ -118,90 +107,102 @@ class AIPipeline {
|
|
|
|
|
const aiConfig = aiService.getConfig();
|
|
|
|
|
const toolsDataHash = getDataVersion?.() || 'unknown';
|
|
|
|
|
|
|
|
|
|
// Record the tools.yaml version being used
|
|
|
|
|
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: [],
|
|
|
|
|
//maxContextLength: this.config.maxContextTokens,
|
|
|
|
|
currentContextLength: 0,
|
|
|
|
|
seenToolNames: new Set<string>(),
|
|
|
|
|
embeddingsSimilarities: new Map<string, number>(),
|
|
|
|
|
phaseMetadata: this.initializePhaseMetadata(toolsData.phases)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
console.log('[AI-PIPELINE] Phase 1: Tool candidate selection');
|
|
|
|
|
const candidateSelectionStart = Date.now();
|
|
|
|
|
|
|
|
|
|
const candidateData = await toolSelector.getIntelligentCandidates(userQuery, toolsData, mode, context);
|
|
|
|
|
|
|
|
|
|
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(),
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
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);
|
|
|
|
|
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);
|
|
|
|
|
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);
|
|
|
|
|
const considerationsResult = await this.generateCriticalConsiderations(context, startTime, toolsDataHash);
|
|
|
|
|
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);
|
|
|
|
|
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);
|
|
|
|
|
const toolResults = await this.processToolMode(context, completedTasks, failedTasks, startTime, toolsDataHash);
|
|
|
|
|
completedTasks = toolResults.completed;
|
|
|
|
|
failedTasks = toolResults.failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('[AI-PIPELINE] Phase 4: Knowledge synthesis');
|
|
|
|
|
|
|
|
|
|
const knowledgeResult = await this.selectBackgroundKnowledge(context, startTime);
|
|
|
|
|
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);
|
|
|
|
|
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);
|
|
|
|
|
const recommendation = this.buildRecommendation(context, mode, finalResult.content, toolsDataHash);
|
|
|
|
|
|
|
|
|
|
const processingStats = {
|
|
|
|
|
//embeddingsUsed: embeddingsService.isEnabled(),
|
|
|
|
|
candidatesFromEmbeddings: candidateData.tools.length,
|
|
|
|
|
finalSelectedItems: (context.selectedTools?.length || 0) + (context.backgroundKnowledge?.length || 0),
|
|
|
|
|
processingTimeMs: Date.now() - startTime,
|
|
|
|
@ -216,16 +217,6 @@ class AIPipeline {
|
|
|
|
|
maxTokensUsed: 32768
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
@ -249,7 +240,6 @@ class AIPipeline {
|
|
|
|
|
phaseComplexity: Map<string, number>;
|
|
|
|
|
} {
|
|
|
|
|
if (!phases || !Array.isArray(phases)) {
|
|
|
|
|
console.warn('[AI-PIPELINE] No phases data available, using fallback');
|
|
|
|
|
return {
|
|
|
|
|
phases: [],
|
|
|
|
|
criticalPhaseIds: [],
|
|
|
|
@ -268,20 +258,12 @@ class AIPipeline {
|
|
|
|
|
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);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('[AI-PIPELINE] Initialized phase metadata:', {
|
|
|
|
|
totalPhases: phases.length,
|
|
|
|
|
criticalPhases: criticalPhaseIds.length,
|
|
|
|
|
avgComplexity: Array.from(phaseComplexity.values()).reduce((sum, c) => sum + c, 0) / phases.length
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
phases,
|
|
|
|
|
criticalPhaseIds,
|
|
|
|
@ -292,11 +274,9 @@ class AIPipeline {
|
|
|
|
|
private calculateToolSelectionConfidence(
|
|
|
|
|
selectedCount: number,
|
|
|
|
|
totalCount: number,
|
|
|
|
|
//method: string,
|
|
|
|
|
conceptsCount: number
|
|
|
|
|
): number {
|
|
|
|
|
let confidence = 50;
|
|
|
|
|
|
|
|
|
|
const selectionRatio = selectedCount / totalCount;
|
|
|
|
|
|
|
|
|
|
if (selectionRatio >= 0.05 && selectionRatio <= 0.20) {
|
|
|
|
@ -307,17 +287,8 @@ class AIPipeline {
|
|
|
|
|
confidence -= 15;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//if (method.includes('embeddings')) {
|
|
|
|
|
//confidence += 15;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
if (conceptsCount > 0) {
|
|
|
|
|
confidence += 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedCount >= 8 && selectedCount <= 25) {
|
|
|
|
|
confidence += 10;
|
|
|
|
|
}
|
|
|
|
|
if (conceptsCount > 0) confidence += 10;
|
|
|
|
|
if (selectedCount >= 8 && selectedCount <= 25) confidence += 10;
|
|
|
|
|
|
|
|
|
|
return Math.min(95, Math.max(40, confidence));
|
|
|
|
|
}
|
|
|
|
@ -327,7 +298,8 @@ class AIPipeline {
|
|
|
|
|
toolsData: any,
|
|
|
|
|
completedTasks: number,
|
|
|
|
|
failedTasks: number,
|
|
|
|
|
pipelineStart: number
|
|
|
|
|
pipelineStart: number,
|
|
|
|
|
toolsDataHash: string
|
|
|
|
|
): Promise<{ completed: number; failed: number }> {
|
|
|
|
|
const phases = toolsData.phases || [];
|
|
|
|
|
|
|
|
|
@ -337,10 +309,7 @@ class AIPipeline {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
if (phaseTools.length === 0) continue;
|
|
|
|
|
|
|
|
|
|
const selections = await toolSelector.selectToolsForPhase(context.userQuery, phase, phaseTools, context);
|
|
|
|
|
|
|
|
|
@ -370,6 +339,7 @@ class AIPipeline {
|
|
|
|
|
phaseConfidence,
|
|
|
|
|
phaseStart,
|
|
|
|
|
{
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
phaseId: phase.id,
|
|
|
|
|
availableToolsCount: phaseTools.length,
|
|
|
|
|
selectedToolsCount: selections.length,
|
|
|
|
@ -383,7 +353,6 @@ class AIPipeline {
|
|
|
|
|
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);
|
|
|
|
@ -405,6 +374,7 @@ class AIPipeline {
|
|
|
|
|
moderatedTaskRelevance || 70,
|
|
|
|
|
phaseStart,
|
|
|
|
|
{
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
toolType: tool.type,
|
|
|
|
|
priority,
|
|
|
|
|
moderationApplied: sel.taskRelevance !== moderatedTaskRelevance,
|
|
|
|
@ -418,7 +388,7 @@ class AIPipeline {
|
|
|
|
|
await this.delay(this.config.microTaskDelay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const completionResult = await this.completeUnderrepresentedPhases(context, toolsData, pipelineStart);
|
|
|
|
|
const completionResult = await this.completeUnderrepresentedPhases(context, toolsData, pipelineStart, toolsDataHash);
|
|
|
|
|
completedTasks += completionResult.completed;
|
|
|
|
|
failedTasks += completionResult.failed;
|
|
|
|
|
|
|
|
|
@ -437,7 +407,6 @@ class AIPipeline {
|
|
|
|
|
}
|
|
|
|
|
): number {
|
|
|
|
|
let confidence = 60;
|
|
|
|
|
|
|
|
|
|
const isCritical = phaseMetadata?.criticalPhaseIds.includes(phaseId) || false;
|
|
|
|
|
const phaseComplexity = phaseMetadata?.phaseComplexity.get(phaseId) || 1;
|
|
|
|
|
|
|
|
|
@ -454,13 +423,8 @@ class AIPipeline {
|
|
|
|
|
confidence += 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isCritical && selectedCount >= 2) {
|
|
|
|
|
confidence += 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (phaseComplexity > 2 && selectedCount >= phaseComplexity) {
|
|
|
|
|
confidence += 5;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
@ -504,12 +468,13 @@ class AIPipeline {
|
|
|
|
|
context: PipelineContext,
|
|
|
|
|
completedTasks: number,
|
|
|
|
|
failedTasks: number,
|
|
|
|
|
pipelineStart: number
|
|
|
|
|
pipelineStart: number,
|
|
|
|
|
toolsDataHash: string
|
|
|
|
|
): 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);
|
|
|
|
|
const evaluationResult = await this.evaluateSpecificTool(context, topTools[i], i + 1, pipelineStart, toolsDataHash);
|
|
|
|
|
if (evaluationResult.success) completedTasks++; else failedTasks++;
|
|
|
|
|
this.trackTokenUsage(evaluationResult.aiUsage);
|
|
|
|
|
await this.delay(this.config.microTaskDelay);
|
|
|
|
@ -521,7 +486,8 @@ class AIPipeline {
|
|
|
|
|
private async completeUnderrepresentedPhases(
|
|
|
|
|
context: PipelineContext,
|
|
|
|
|
toolsData: any,
|
|
|
|
|
pipelineStart: number
|
|
|
|
|
pipelineStart: number,
|
|
|
|
|
toolsDataHash: string
|
|
|
|
|
): Promise<{ completed: number; failed: number }> {
|
|
|
|
|
const phases = toolsData.phases || [];
|
|
|
|
|
const selectedPhases = new Map<string, number>();
|
|
|
|
@ -538,15 +504,10 @@ class AIPipeline {
|
|
|
|
|
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(', '));
|
|
|
|
|
if (underrepresentedPhases.length === 0) return { completed: 0, failed: 0 };
|
|
|
|
|
|
|
|
|
|
for (const phase of underrepresentedPhases) {
|
|
|
|
|
const result = await this.completePhaseWithSemanticSearchAndAI(context, phase, toolsData, pipelineStart);
|
|
|
|
|
const result = await this.completePhaseWithSemanticSearchAndAI(context, phase, toolsData, pipelineStart, toolsDataHash);
|
|
|
|
|
if (result.success) completedTasks++; else failedTasks++;
|
|
|
|
|
await this.delay(this.config.microTaskDelay);
|
|
|
|
|
}
|
|
|
|
@ -558,13 +519,12 @@ class AIPipeline {
|
|
|
|
|
context: PipelineContext,
|
|
|
|
|
phase: any,
|
|
|
|
|
toolsData: any,
|
|
|
|
|
pipelineStart: number
|
|
|
|
|
pipelineStart: number,
|
|
|
|
|
toolsDataHash: string
|
|
|
|
|
): Promise<MicroTaskResult> {
|
|
|
|
|
const phaseStart = Date.now();
|
|
|
|
|
const phaseQuery = `forensic ${phase.name.toLowerCase()} tools methods`;
|
|
|
|
|
|
|
|
|
|
console.log('[AI-PIPELINE] Phase completion for:', phase.id);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const phaseResults = await embeddingsService.findSimilar(phaseQuery, 20, 0.2);
|
|
|
|
|
|
|
|
|
@ -574,6 +534,7 @@ class AIPipeline {
|
|
|
|
|
0.2,
|
|
|
|
|
phaseStart,
|
|
|
|
|
{
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
phaseId: phase.id,
|
|
|
|
|
phaseName: phase.name,
|
|
|
|
|
completionPurpose: 'underrepresented-phase-enhancement'
|
|
|
|
@ -581,7 +542,6 @@ class AIPipeline {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (phaseResults.length === 0) {
|
|
|
|
|
console.log('[AI-PIPELINE] No semantic results for phase:', phase.id);
|
|
|
|
|
return {
|
|
|
|
|
taskType: 'phase-completion',
|
|
|
|
|
content: '',
|
|
|
|
@ -613,7 +573,6 @@ class AIPipeline {
|
|
|
|
|
.slice(0, 2);
|
|
|
|
|
|
|
|
|
|
if (phaseTools.length === 0) {
|
|
|
|
|
console.log('[AI-PIPELINE] No suitable tools for phase completion:', phase.id);
|
|
|
|
|
return {
|
|
|
|
|
taskType: 'phase-completion',
|
|
|
|
|
content: '',
|
|
|
|
@ -623,10 +582,9 @@ class AIPipeline {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const selectionPrompt = getPrompt('generatePhaseCompletionPrompt', context.userQuery, phase, phaseTools, phaseConcepts);
|
|
|
|
|
const selectionResult = await this.callMicroTaskAI(selectionPrompt, context, 800, 'phase-completion-selection');
|
|
|
|
|
const selectionResult = await this.callMicroTaskAI(selectionPrompt, context, 'phase-completion-selection');
|
|
|
|
|
|
|
|
|
|
if (!selectionResult.success) {
|
|
|
|
|
console.error('[AI-PIPELINE] Phase completion selection failed for:', phase.id);
|
|
|
|
|
return {
|
|
|
|
|
taskType: 'phase-completion',
|
|
|
|
|
content: '',
|
|
|
|
@ -648,7 +606,6 @@ class AIPipeline {
|
|
|
|
|
.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 || '',
|
|
|
|
@ -660,8 +617,6 @@ class AIPipeline {
|
|
|
|
|
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,
|
|
|
|
@ -671,7 +626,7 @@ class AIPipeline {
|
|
|
|
|
selection.completionReasoning || 'Nachergänzung zur Vervollständigung der Phasenabdeckung'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const reasoningResult = await this.callMicroTaskAI(reasoningPrompt, context, 400, 'phase-completion-reasoning');
|
|
|
|
|
const reasoningResult = await this.callMicroTaskAI(reasoningPrompt, context, 'phase-completion-reasoning');
|
|
|
|
|
|
|
|
|
|
let detailedJustification: string;
|
|
|
|
|
let moderatedTaskRelevance = 75;
|
|
|
|
@ -695,8 +650,6 @@ class AIPipeline {
|
|
|
|
|
moderatedTaskRelevance,
|
|
|
|
|
dynamicLimitations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
console.log('[AI-PIPELINE] Added phase completion tool with AI reasoning:', tool.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auditService.addPhaseCompletion(
|
|
|
|
@ -705,6 +658,7 @@ class AIPipeline {
|
|
|
|
|
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,
|
|
|
|
@ -723,8 +677,6 @@ class AIPipeline {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('[AI-PIPELINE] Phase completion failed for:', phase.id, error);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
taskType: 'phase-completion',
|
|
|
|
|
content: '',
|
|
|
|
@ -756,9 +708,7 @@ class AIPipeline {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private moderateTaskRelevance(taskRelevance: number): number {
|
|
|
|
|
if (typeof taskRelevance !== 'number') {
|
|
|
|
|
return 70;
|
|
|
|
|
}
|
|
|
|
|
if (typeof taskRelevance !== 'number') return 70;
|
|
|
|
|
|
|
|
|
|
let moderated = Math.min(taskRelevance, this.config.taskRelevanceModeration.maxInitialScore);
|
|
|
|
|
|
|
|
|
@ -770,13 +720,12 @@ class AIPipeline {
|
|
|
|
|
return Math.round(Math.min(moderated, this.config.taskRelevanceModeration.maxInitialScore));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async analyzeScenario(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
|
|
|
|
|
console.log('[AI-PIPELINE] Micro-task: Scenario analysis');
|
|
|
|
|
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, 400, 'scenario-analysis');
|
|
|
|
|
const result = await this.callMicroTaskAI(prompt, context, 'scenario-analysis');
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
if (isWorkflow) {
|
|
|
|
@ -801,6 +750,7 @@ class AIPipeline {
|
|
|
|
|
`Analysierte ${isWorkflow ? 'Szenario' : 'Problem'} basierend auf Nutzereingabe: "${context.userQuery.slice(0, 100)}..." - Identifizierte Kernaspekte und Herausforderungen für forensische Untersuchung`,
|
|
|
|
|
taskStart,
|
|
|
|
|
{
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
microTaskType: 'scenario-analysis',
|
|
|
|
|
analysisType: isWorkflow ? 'scenario' : 'problem',
|
|
|
|
|
contentLength: result.content.length,
|
|
|
|
@ -814,13 +764,12 @@ class AIPipeline {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async generateApproach(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
|
|
|
|
|
console.log('[AI-PIPELINE] Micro-task: Investigation approach');
|
|
|
|
|
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, 400, 'investigation-approach');
|
|
|
|
|
const result = await this.callMicroTaskAI(prompt, context, 'investigation-approach');
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
context.investigationApproach = result.content;
|
|
|
|
@ -840,6 +789,7 @@ class AIPipeline {
|
|
|
|
|
`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,
|
|
|
|
@ -854,13 +804,12 @@ class AIPipeline {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async generateCriticalConsiderations(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
|
|
|
|
|
console.log('[AI-PIPELINE] Micro-task: Critical considerations');
|
|
|
|
|
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, 350, 'critical-considerations');
|
|
|
|
|
const result = await this.callMicroTaskAI(prompt, context, 'critical-considerations');
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
context.criticalConsiderations = result.content;
|
|
|
|
@ -880,6 +829,7 @@ class AIPipeline {
|
|
|
|
|
'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',
|
|
|
|
@ -896,9 +846,9 @@ class AIPipeline {
|
|
|
|
|
context: PipelineContext,
|
|
|
|
|
tool: any,
|
|
|
|
|
rank: number,
|
|
|
|
|
pipelineStart: number
|
|
|
|
|
pipelineStart: number,
|
|
|
|
|
toolsDataHash: string
|
|
|
|
|
): Promise<MicroTaskResult> {
|
|
|
|
|
console.log('[AI-PIPELINE] Micro-task: Tool evaluation for:', tool.name);
|
|
|
|
|
const taskStart = Date.now();
|
|
|
|
|
const existingSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name);
|
|
|
|
|
const originalTaskRelevance = existingSelection?.taskRelevance || 70;
|
|
|
|
@ -906,7 +856,7 @@ class AIPipeline {
|
|
|
|
|
const priority = this.derivePriorityFromScore(moderatedTaskRelevance);
|
|
|
|
|
|
|
|
|
|
const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank, moderatedTaskRelevance);
|
|
|
|
|
const result = await this.callMicroTaskAI(prompt, context, 1000, 'tool-evaluation');
|
|
|
|
|
const result = await this.callMicroTaskAI(prompt, context, 'tool-evaluation');
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
const evaluation = JSONParser.safeParseJSON(result.content, {
|
|
|
|
@ -942,6 +892,7 @@ class AIPipeline {
|
|
|
|
|
`Bewertete Tool "${tool.name}" (Rang ${rank}) - Analysierte Eignung für spezifische Aufgabenstellung mit Fokus auf praktische Anwendbarkeit und methodische Integration`,
|
|
|
|
|
taskStart,
|
|
|
|
|
{
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
microTaskType: 'tool-evaluation',
|
|
|
|
|
toolName: tool.name,
|
|
|
|
|
toolType: tool.type,
|
|
|
|
@ -964,8 +915,7 @@ class AIPipeline {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async selectBackgroundKnowledge(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
|
|
|
|
|
console.log('[AI-PIPELINE] Micro-task: Background knowledge selection');
|
|
|
|
|
private async selectBackgroundKnowledge(context: PipelineContext, pipelineStart: number, toolsDataHash: string): Promise<MicroTaskResult> {
|
|
|
|
|
const taskStart = Date.now();
|
|
|
|
|
const availableConcepts = context.filteredData.concepts;
|
|
|
|
|
|
|
|
|
@ -980,7 +930,7 @@ class AIPipeline {
|
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
const result = await this.callMicroTaskAI(prompt, context, 'background-knowledge');
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
const selections = JSONParser.safeParseJSON(result.content, []);
|
|
|
|
@ -1017,6 +967,7 @@ class AIPipeline {
|
|
|
|
|
finalConfidence,
|
|
|
|
|
taskStart,
|
|
|
|
|
{
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
microTaskType: 'background-knowledge',
|
|
|
|
|
availableConceptsCount: availableConcepts.length,
|
|
|
|
|
selectedConceptsCount: context.backgroundKnowledge.length,
|
|
|
|
@ -1041,32 +992,25 @@ class AIPipeline {
|
|
|
|
|
): number {
|
|
|
|
|
let bonus = 0;
|
|
|
|
|
|
|
|
|
|
if (selectedKnowledge.length > 0) {
|
|
|
|
|
bonus += 10;
|
|
|
|
|
}
|
|
|
|
|
if (selectedKnowledge.length > 0) bonus += 10;
|
|
|
|
|
|
|
|
|
|
const ratio = selectedKnowledge.length / availableConcepts.length;
|
|
|
|
|
if (ratio >= 0.1 && ratio <= 0.3) {
|
|
|
|
|
bonus += 15;
|
|
|
|
|
}
|
|
|
|
|
if (ratio >= 0.1 && ratio <= 0.3) bonus += 15;
|
|
|
|
|
|
|
|
|
|
const hasGoodReasonings = selectedKnowledge.some(bk =>
|
|
|
|
|
bk.relevance && bk.relevance.length > 30
|
|
|
|
|
);
|
|
|
|
|
if (hasGoodReasonings) {
|
|
|
|
|
bonus += 10;
|
|
|
|
|
}
|
|
|
|
|
if (hasGoodReasonings) bonus += 10;
|
|
|
|
|
|
|
|
|
|
return bonus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async generateFinalRecommendations(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
|
|
|
|
|
console.log('[AI-PIPELINE] Micro-task: Final recommendations');
|
|
|
|
|
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, 350, 'final-recommendations');
|
|
|
|
|
const result = await this.callMicroTaskAI(prompt, context, 'final-recommendations');
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
const confidence = auditService.calculateAIResponseConfidence(
|
|
|
|
@ -1086,6 +1030,7 @@ class AIPipeline {
|
|
|
|
|
`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,
|
|
|
|
@ -1105,30 +1050,17 @@ class AIPipeline {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
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 {
|
|
|
|
|
private buildRecommendation(context: PipelineContext, mode: string, finalContent: string, toolsDataHash: 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,
|
|
|
|
@ -1161,6 +1093,7 @@ class AIPipeline {
|
|
|
|
|
confidence,
|
|
|
|
|
Date.now(),
|
|
|
|
|
{
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
phase: st.phase,
|
|
|
|
|
priority: st.priority,
|
|
|
|
|
toolType: st.tool.type,
|
|
|
|
@ -1205,6 +1138,7 @@ class AIPipeline {
|
|
|
|
|
confidence,
|
|
|
|
|
Date.now(),
|
|
|
|
|
{
|
|
|
|
|
toolsDataHash: toolsDataHash,
|
|
|
|
|
rank: st.tool.evaluation?.rank || 1,
|
|
|
|
|
toolType: st.tool.type,
|
|
|
|
|
moderatedTaskRelevance: st.taskRelevance
|
|
|
|
@ -1237,7 +1171,6 @@ class AIPipeline {
|
|
|
|
|
private async callMicroTaskAI(
|
|
|
|
|
prompt: string,
|
|
|
|
|
context: PipelineContext,
|
|
|
|
|
maxTokens: number = 500,
|
|
|
|
|
taskType: string = 'micro-task'
|
|
|
|
|
): Promise<MicroTaskResult> {
|
|
|
|
|
const startTime = Date.now();
|
|
|
|
@ -1245,15 +1178,11 @@ class AIPipeline {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
contextPrompt = contextSection + prompt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await aiService.callMicroTaskAI(contextPrompt, maxTokens);
|
|
|
|
|
const response = await aiService.callMicroTaskAI(contextPrompt);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
taskType,
|
|
|
|
@ -1280,12 +1209,10 @@ class AIPipeline {
|
|
|
|
|
context.contextHistory.push(newEntry);
|
|
|
|
|
context.currentContextLength += entryTokens;
|
|
|
|
|
|
|
|
|
|
/*while (context.currentContextLength > this.config.maxContextTokens && context.contextHistory.length > 1) {
|
|
|
|
|
if (context.contextHistory.length > 1) {
|
|
|
|
|
const removed = context.contextHistory.shift()!;
|
|
|
|
|
context.currentContextLength -= aiService.estimateTokens(removed);
|
|
|
|
|
}*/
|
|
|
|
|
const removed = context.contextHistory.shift()!;
|
|
|
|
|
context.currentContextLength -= aiService.estimateTokens(removed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private addToolToSelection(
|
|
|
|
|