This commit is contained in:
overcuriousity
2025-08-17 23:18:15 +02:00
parent 2cb25d1dd6
commit 70fb012d63
7 changed files with 132 additions and 250 deletions

View File

@@ -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> {
@@ -108,8 +99,6 @@ class AIPipeline {
let completedTasks = 0;
let failedTasks = 0;
this.totalTokensUsed = 0;
console.log('[AI-PIPELINE] Starting', mode, 'analysis pipeline');
auditService.clearAuditTrail();
@@ -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(