restore old after-confidence-scoring
This commit is contained in:
parent
8bba0eefa9
commit
afbd8d2cd3
@ -50,7 +50,7 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
|
|||||||
|
|
||||||
<!-- Input Layout -->
|
<!-- Input Layout -->
|
||||||
<div class="ai-input-layout">
|
<div class="ai-input-layout">
|
||||||
<div class="ai-textarea-section">
|
<div class="ai-textarea-section" style="height: 200px">
|
||||||
<textarea
|
<textarea
|
||||||
id="ai-query-input"
|
id="ai-query-input"
|
||||||
placeholder="Beschreiben Sie Ihr forensisches Szenario..."
|
placeholder="Beschreiben Sie Ihr forensisches Szenario..."
|
||||||
@ -1158,12 +1158,10 @@ class AIQueryInterface {
|
|||||||
const lowConfidenceSteps = auditTrail.filter(entry => (entry.confidence || 0) < 60).length;
|
const lowConfidenceSteps = auditTrail.filter(entry => (entry.confidence || 0) < 60).length;
|
||||||
const mediumConfidenceSteps = auditTrail.length - highConfidenceSteps - lowConfidenceSteps;
|
const mediumConfidenceSteps = auditTrail.length - highConfidenceSteps - lowConfidenceSteps;
|
||||||
|
|
||||||
// Enhanced metrics
|
|
||||||
const aiDecisionCount = auditTrail.filter(entry => entry.action === 'ai-decision').length;
|
const aiDecisionCount = auditTrail.filter(entry => entry.action === 'ai-decision').length;
|
||||||
const embeddingsUsageCount = auditTrail.filter(entry => entry.metadata?.embeddingsUsed).length;
|
const embeddingsUsageCount = auditTrail.filter(entry => entry.metadata?.embeddingsUsed).length;
|
||||||
const toolSelectionCount = auditTrail.filter(entry => entry.action === 'selection-decision').length;
|
const toolSelectionCount = auditTrail.filter(entry => entry.action === 'selection-decision').length;
|
||||||
|
|
||||||
// Phase breakdown
|
|
||||||
const phaseBreakdown = {};
|
const phaseBreakdown = {};
|
||||||
auditTrail.forEach(entry => {
|
auditTrail.forEach(entry => {
|
||||||
const phase = entry.phase || 'unknown';
|
const phase = entry.phase || 'unknown';
|
||||||
@ -1175,7 +1173,6 @@ class AIQueryInterface {
|
|||||||
phaseBreakdown[phase].totalTime += entry.processingTimeMs || 0;
|
phaseBreakdown[phase].totalTime += entry.processingTimeMs || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Calculate average confidence per phase
|
|
||||||
Object.keys(phaseBreakdown).forEach(phase => {
|
Object.keys(phaseBreakdown).forEach(phase => {
|
||||||
const phaseEntries = auditTrail.filter(entry => entry.phase === phase);
|
const phaseEntries = auditTrail.filter(entry => entry.phase === phase);
|
||||||
const validEntries = phaseEntries.filter(entry => typeof entry.confidence === 'number');
|
const validEntries = phaseEntries.filter(entry => typeof entry.confidence === 'number');
|
||||||
@ -1191,7 +1188,6 @@ class AIQueryInterface {
|
|||||||
const aiTransparency = auditTrail.length > 0 ?
|
const aiTransparency = auditTrail.length > 0 ?
|
||||||
(auditTrail.filter(entry => entry.metadata?.aiPrompt || entry.metadata?.reasoning).length / auditTrail.length) * 100 : 0;
|
(auditTrail.filter(entry => entry.metadata?.aiPrompt || entry.metadata?.reasoning).length / auditTrail.length) * 100 : 0;
|
||||||
|
|
||||||
// RESTORED: Intelligent Analysis Quality Assessment
|
|
||||||
let analysisQuality;
|
let analysisQuality;
|
||||||
if (avgConfidence >= 85 && lowConfidenceSteps === 0) {
|
if (avgConfidence >= 85 && lowConfidenceSteps === 0) {
|
||||||
analysisQuality = 'excellent';
|
analysisQuality = 'excellent';
|
||||||
@ -1203,7 +1199,6 @@ class AIQueryInterface {
|
|||||||
analysisQuality = 'poor';
|
analysisQuality = 'poor';
|
||||||
}
|
}
|
||||||
|
|
||||||
// RESTORED: Intelligent Insights Generation
|
|
||||||
const keyInsights = [];
|
const keyInsights = [];
|
||||||
const embeddingsUsed = auditTrail.some(e => e.metadata?.embeddingsUsed);
|
const embeddingsUsed = auditTrail.some(e => e.metadata?.embeddingsUsed);
|
||||||
if (embeddingsUsed) {
|
if (embeddingsUsed) {
|
||||||
@ -1226,7 +1221,6 @@ class AIQueryInterface {
|
|||||||
keyInsights.push('Mehrheit der Analyseschritte mit hoher Sicherheit');
|
keyInsights.push('Mehrheit der Analyseschritte mit hoher Sicherheit');
|
||||||
}
|
}
|
||||||
|
|
||||||
// RESTORED: Automatic Issue Detection (excluding processing time warnings)
|
|
||||||
const potentialIssues = [];
|
const potentialIssues = [];
|
||||||
if (lowConfidenceSteps > 2) {
|
if (lowConfidenceSteps > 2) {
|
||||||
potentialIssues.push(`${lowConfidenceSteps} Analyseschritte mit niedriger Konfidenz`);
|
potentialIssues.push(`${lowConfidenceSteps} Analyseschritte mit niedriger Konfidenz`);
|
||||||
|
@ -36,6 +36,11 @@ AUSWAHLSTRATEGIE:
|
|||||||
- Lieber weniger perfekte Items als viele mittelmäßige
|
- Lieber weniger perfekte Items als viele mittelmäßige
|
||||||
- Jedes Item muss begründbar sein
|
- Jedes Item muss begründbar sein
|
||||||
|
|
||||||
|
4. **TASK RELEVANCE REALISM**
|
||||||
|
- Gib realistische Bewertungen (50-85% typisch)
|
||||||
|
- Vermeide übertriebene 90-100% Scores
|
||||||
|
- Nur bei perfekter Übereinstimmung >85%
|
||||||
|
|
||||||
AUSWAHLREGELN:
|
AUSWAHLREGELN:
|
||||||
- Wähle ${mode === 'workflow' ? '15-25' : '4-10'} Items total, max ${maxSelectedItems}
|
- Wähle ${mode === 'workflow' ? '15-25' : '4-10'} Items total, max ${maxSelectedItems}
|
||||||
- BEIDE Arrays füllen: selectedTools UND selectedConcepts
|
- BEIDE Arrays füllen: selectedTools UND selectedConcepts
|
||||||
@ -59,7 +64,13 @@ ${JSON.stringify(toolsToSend, null, 2)}
|
|||||||
VERFÜGBARE KONZEPTE (${conceptsToSend.length} Items - theoretisches Wissen):
|
VERFÜGBARE KONZEPTE (${conceptsToSend.length} Items - theoretisches Wissen):
|
||||||
${JSON.stringify(conceptsToSend, null, 2)}
|
${JSON.stringify(conceptsToSend, null, 2)}
|
||||||
|
|
||||||
WICHTIGER HINWEIS: Wähle sowohl aus TOOLS als auch aus KONZEPTEN aus! Konzepte sind essentiell für methodische Fundierung.`;
|
WICHTIGER HINWEIS: Wähle sowohl aus TOOLS als auch aus KONZEPTEN aus! Konzepte sind essentiell für methodische Fundierung.
|
||||||
|
|
||||||
|
TASK RELEVANCE GUIDELINES:
|
||||||
|
- 50-65%: Grundlegend relevant, aber nicht optimal
|
||||||
|
- 66-75%: Gut geeignet für die Aufgabe
|
||||||
|
- 76-85%: Sehr gut geeignet, klare Vorteile
|
||||||
|
- 86-100%: NUR für perfekte Übereinstimmung verwenden`;
|
||||||
},
|
},
|
||||||
|
|
||||||
scenarioAnalysis: (isWorkflow: boolean, userQuery: string) => {
|
scenarioAnalysis: (isWorkflow: boolean, userQuery: string) => {
|
||||||
@ -148,13 +159,19 @@ AUSWAHLREGELN FÜR PHASE "${phase.name}":
|
|||||||
3. Mindestens 1 Methode wenn verfügbar, Rest Software-Tools
|
3. Mindestens 1 Methode wenn verfügbar, Rest Software-Tools
|
||||||
4. Begründe WARUM jedes Item für diese Phase optimal ist
|
4. Begründe WARUM jedes Item für diese Phase optimal ist
|
||||||
|
|
||||||
|
TASK RELEVANCE GUIDELINES:
|
||||||
|
- 60-70%: Grundlegend für diese Phase geeignet
|
||||||
|
- 71-80%: Gut geeignet, klare Phasenrelevanz
|
||||||
|
- 81-90%: Sehr gut geeignet, optimal für Phase
|
||||||
|
- 91-100%: NUR für perfekte Phasenübereinstimmung
|
||||||
|
|
||||||
WICHTIG: Verwende EXAKT die Namen wie oben aufgelistet (ohne Präfixe wie M1./T2.)!
|
WICHTIG: Verwende EXAKT die Namen wie oben aufgelistet (ohne Präfixe wie M1./T2.)!
|
||||||
|
|
||||||
ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT OHNE JEGLICHEN TEXT AUSSERHALB:
|
ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT OHNE JEGLICHEN TEXT AUSSERHALB:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"toolName": "Exakter Name aus der Liste oben",
|
"toolName": "Exakter Name aus der Liste oben",
|
||||||
"taskRelevance": 85,
|
"taskRelevance": 75,
|
||||||
"justification": "Detaillierte Begründung (60-80 Wörter) warum optimal für ${phase.name} - erkläre Anwendung, Vorteile und spezifische Relevanz",
|
"justification": "Detaillierte Begründung (60-80 Wörter) warum optimal für ${phase.name} - erkläre Anwendung, Vorteile und spezifische Relevanz",
|
||||||
"limitations": ["Mögliche Einschränkung für diese Phase"]
|
"limitations": ["Mögliche Einschränkung für diese Phase"]
|
||||||
}
|
}
|
||||||
@ -266,6 +283,7 @@ AUSWAHLREGELN FÜR NACHERGÄNZUNG:
|
|||||||
1. Wähle 1-2 BESTE Methoden/Tools die die ${phase.name}-Phase optimal ergänzen
|
1. Wähle 1-2 BESTE Methoden/Tools die die ${phase.name}-Phase optimal ergänzen
|
||||||
2. Methoden/Tools müssen für die ursprüngliche Anfrage relevant sein
|
2. Methoden/Tools müssen für die ursprüngliche Anfrage relevant sein
|
||||||
3. Ergänzen, nicht ersetzen - erweitere die zu spezifische Erstauswahl
|
3. Ergänzen, nicht ersetzen - erweitere die zu spezifische Erstauswahl
|
||||||
|
4. Realistische Task Relevance (70-85% typisch für Nachergänzungen)
|
||||||
|
|
||||||
ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT:
|
ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT:
|
||||||
{
|
{
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// src/utils/aiPipeline.ts - Enhanced with comprehensive audit logging
|
// src/utils/aiPipeline.ts - Enhanced with comprehensive audit logging and restored sophisticated logic
|
||||||
import { getCompressedToolsDataForAI, getDataVersion } from './dataService.js';
|
import { getCompressedToolsDataForAI, getDataVersion } from './dataService.js';
|
||||||
import { aiService } from './aiService.js';
|
import { aiService } from './aiService.js';
|
||||||
import { toolSelector, type SelectionContext } from './toolSelector.js';
|
import { toolSelector, type SelectionContext } from './toolSelector.js';
|
||||||
import { confidenceScoring, type AnalysisContext } from './confidenceScoring.js';
|
import { confidenceScoring, type AnalysisContext } from './confidenceScoring.js';
|
||||||
import { embeddingsService } from './embeddings.js';
|
import { embeddingsService } from './embeddings.js';
|
||||||
import { auditService, type AuditEntry } from './auditService.js';
|
import { auditService, type AuditEntry } from './auditService.js';
|
||||||
import { JSONParser } from './jsonUtils.js'; // FIXED: Use centralized JSON parsing
|
import { JSONParser } from './jsonUtils.js';
|
||||||
import { getPrompt } from '../config/prompts.js';
|
import { getPrompt } from '../config/prompts.js';
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
|
|
||||||
@ -13,6 +13,11 @@ interface PipelineConfig {
|
|||||||
microTaskDelay: number;
|
microTaskDelay: number;
|
||||||
maxContextTokens: number;
|
maxContextTokens: number;
|
||||||
maxPromptTokens: number;
|
maxPromptTokens: number;
|
||||||
|
taskRelevanceModeration: {
|
||||||
|
maxInitialScore: number;
|
||||||
|
maxWithPhaseBonus: number;
|
||||||
|
moderationThreshold: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MicroTaskResult {
|
interface MicroTaskResult {
|
||||||
@ -40,6 +45,10 @@ interface AnalysisResult {
|
|||||||
contextContinuityUsed: boolean;
|
contextContinuityUsed: boolean;
|
||||||
totalAITokensUsed: number;
|
totalAITokensUsed: number;
|
||||||
auditEntriesGenerated: number;
|
auditEntriesGenerated: number;
|
||||||
|
aiModel: string;
|
||||||
|
toolsDataHash: string;
|
||||||
|
temperature: number;
|
||||||
|
maxTokensUsed: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,10 +87,15 @@ class AIPipeline {
|
|||||||
this.config = {
|
this.config = {
|
||||||
microTaskDelay: parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10),
|
microTaskDelay: parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10),
|
||||||
maxContextTokens: parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10),
|
maxContextTokens: parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10),
|
||||||
maxPromptTokens: parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10)
|
maxPromptTokens: parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10),
|
||||||
|
taskRelevanceModeration: {
|
||||||
|
maxInitialScore: 85,
|
||||||
|
maxWithPhaseBonus: 95,
|
||||||
|
moderationThreshold: 80
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[AI-PIPELINE] Initialized orchestration pipeline');
|
console.log('[AI-PIPELINE] Initialized orchestration pipeline with enhanced logic');
|
||||||
}
|
}
|
||||||
|
|
||||||
async processQuery(userQuery: string, mode: string): Promise<AnalysisResult> {
|
async processQuery(userQuery: string, mode: string): Promise<AnalysisResult> {
|
||||||
@ -92,7 +106,6 @@ class AIPipeline {
|
|||||||
|
|
||||||
console.log('[AI-PIPELINE] Starting', mode, 'analysis pipeline');
|
console.log('[AI-PIPELINE] Starting', mode, 'analysis pipeline');
|
||||||
|
|
||||||
// Initialize audit trail
|
|
||||||
auditService.clearAuditTrail();
|
auditService.clearAuditTrail();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -111,7 +124,6 @@ class AIPipeline {
|
|||||||
embeddingsSimilarities: new Map<string, number>()
|
embeddingsSimilarities: new Map<string, number>()
|
||||||
};
|
};
|
||||||
|
|
||||||
// AUDIT: Pipeline initialization
|
|
||||||
auditService.addEntry(
|
auditService.addEntry(
|
||||||
'initialization',
|
'initialization',
|
||||||
'pipeline-start',
|
'pipeline-start',
|
||||||
@ -132,17 +144,15 @@ class AIPipeline {
|
|||||||
toolsDataHash,
|
toolsDataHash,
|
||||||
aiModel: aiConfig.model,
|
aiModel: aiConfig.model,
|
||||||
embeddingsUsed: embeddingsService.isEnabled(),
|
embeddingsUsed: embeddingsService.isEnabled(),
|
||||||
pipelineVersion: '2.0-enhanced'
|
pipelineVersion: '2.1-enhanced'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Phase 1: Get intelligent tool candidates with enhanced logging
|
|
||||||
console.log('[AI-PIPELINE] Phase 1: Tool candidate selection');
|
console.log('[AI-PIPELINE] Phase 1: Tool candidate selection');
|
||||||
const candidateSelectionStart = Date.now();
|
const candidateSelectionStart = Date.now();
|
||||||
|
|
||||||
const candidateData = await toolSelector.getIntelligentCandidates(userQuery, toolsData, mode, context);
|
const candidateData = await toolSelector.getIntelligentCandidates(userQuery, toolsData, mode, context);
|
||||||
|
|
||||||
// AUDIT: Tool candidate selection
|
|
||||||
auditService.addToolSelection(
|
auditService.addToolSelection(
|
||||||
candidateData.tools.map(t => t.name),
|
candidateData.tools.map(t => t.name),
|
||||||
toolsData.tools.map(t => t.name),
|
toolsData.tools.map(t => t.name),
|
||||||
@ -159,7 +169,6 @@ class AIPipeline {
|
|||||||
|
|
||||||
context.filteredData = candidateData;
|
context.filteredData = candidateData;
|
||||||
|
|
||||||
// Phase 2: Contextual analysis micro-tasks with enhanced logging
|
|
||||||
console.log('[AI-PIPELINE] Phase 2: Contextual analysis');
|
console.log('[AI-PIPELINE] Phase 2: Contextual analysis');
|
||||||
|
|
||||||
const analysisResult = await this.analyzeScenario(context, startTime);
|
const analysisResult = await this.analyzeScenario(context, startTime);
|
||||||
@ -177,7 +186,6 @@ class AIPipeline {
|
|||||||
this.trackTokenUsage(considerationsResult.aiUsage);
|
this.trackTokenUsage(considerationsResult.aiUsage);
|
||||||
await this.delay(this.config.microTaskDelay);
|
await this.delay(this.config.microTaskDelay);
|
||||||
|
|
||||||
// Phase 3: Tool-specific analysis with enhanced logging
|
|
||||||
console.log('[AI-PIPELINE] Phase 3: Tool-specific analysis');
|
console.log('[AI-PIPELINE] Phase 3: Tool-specific analysis');
|
||||||
|
|
||||||
if (mode === 'workflow') {
|
if (mode === 'workflow') {
|
||||||
@ -190,7 +198,6 @@ class AIPipeline {
|
|||||||
failedTasks = toolResults.failed;
|
failedTasks = toolResults.failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 4: Knowledge and finalization with enhanced logging
|
|
||||||
console.log('[AI-PIPELINE] Phase 4: Knowledge synthesis');
|
console.log('[AI-PIPELINE] Phase 4: Knowledge synthesis');
|
||||||
|
|
||||||
const knowledgeResult = await this.selectBackgroundKnowledge(context, startTime);
|
const knowledgeResult = await this.selectBackgroundKnowledge(context, startTime);
|
||||||
@ -202,10 +209,8 @@ class AIPipeline {
|
|||||||
if (finalResult.success) completedTasks++; else failedTasks++;
|
if (finalResult.success) completedTasks++; else failedTasks++;
|
||||||
this.trackTokenUsage(finalResult.aiUsage);
|
this.trackTokenUsage(finalResult.aiUsage);
|
||||||
|
|
||||||
// Build final recommendation
|
|
||||||
const recommendation = this.buildRecommendation(context, mode, finalResult.content);
|
const recommendation = this.buildRecommendation(context, mode, finalResult.content);
|
||||||
|
|
||||||
// AUDIT: Pipeline completion
|
|
||||||
auditService.addEntry(
|
auditService.addEntry(
|
||||||
'completion',
|
'completion',
|
||||||
'pipeline-end',
|
'pipeline-end',
|
||||||
@ -235,7 +240,11 @@ class AIPipeline {
|
|||||||
microTasksFailed: failedTasks,
|
microTasksFailed: failedTasks,
|
||||||
contextContinuityUsed: true,
|
contextContinuityUsed: true,
|
||||||
totalAITokensUsed: this.totalTokensUsed,
|
totalAITokensUsed: this.totalTokensUsed,
|
||||||
auditEntriesGenerated: auditService.getCurrentAuditTrail().length
|
auditEntriesGenerated: auditService.getCurrentAuditTrail().length,
|
||||||
|
aiModel: aiConfig.model,
|
||||||
|
toolsDataHash,
|
||||||
|
temperature: 0.3,
|
||||||
|
maxTokensUsed: 2500
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[AI-PIPELINE] Pipeline completed successfully:', {
|
console.log('[AI-PIPELINE] Pipeline completed successfully:', {
|
||||||
@ -248,13 +257,13 @@ class AIPipeline {
|
|||||||
auditEntries: processingStats.auditEntriesGenerated
|
auditEntries: processingStats.auditEntriesGenerated
|
||||||
});
|
});
|
||||||
|
|
||||||
// Finalize audit trail
|
|
||||||
const finalAuditTrail = auditService.finalizeAuditTrail();
|
const finalAuditTrail = auditService.finalizeAuditTrail();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recommendation: {
|
recommendation: {
|
||||||
...recommendation,
|
...recommendation,
|
||||||
auditTrail: auditService.isEnabled() ? finalAuditTrail : undefined
|
auditTrail: auditService.isEnabled() ? finalAuditTrail : undefined,
|
||||||
|
processingStats
|
||||||
},
|
},
|
||||||
processingStats
|
processingStats
|
||||||
};
|
};
|
||||||
@ -262,7 +271,6 @@ class AIPipeline {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[AI-PIPELINE] Pipeline failed:', error);
|
console.error('[AI-PIPELINE] Pipeline failed:', error);
|
||||||
|
|
||||||
// AUDIT: Pipeline failure
|
|
||||||
auditService.addEntry(
|
auditService.addEntry(
|
||||||
'error',
|
'error',
|
||||||
'pipeline-failure',
|
'pipeline-failure',
|
||||||
@ -286,7 +294,6 @@ class AIPipeline {
|
|||||||
): Promise<{ completed: number; failed: number }> {
|
): Promise<{ completed: number; failed: number }> {
|
||||||
const phases = toolsData.phases || [];
|
const phases = toolsData.phases || [];
|
||||||
|
|
||||||
// Select tools for each phase with enhanced logging
|
|
||||||
for (const phase of phases) {
|
for (const phase of phases) {
|
||||||
const phaseStart = Date.now();
|
const phaseStart = Date.now();
|
||||||
const phaseTools = context.filteredData.tools.filter((tool: any) =>
|
const phaseTools = context.filteredData.tools.filter((tool: any) =>
|
||||||
@ -295,7 +302,6 @@ class AIPipeline {
|
|||||||
|
|
||||||
const selections = await toolSelector.selectToolsForPhase(context.userQuery, phase, phaseTools, context);
|
const selections = await toolSelector.selectToolsForPhase(context.userQuery, phase, phaseTools, context);
|
||||||
|
|
||||||
// AUDIT: Phase tool selection
|
|
||||||
auditService.addEntry(
|
auditService.addEntry(
|
||||||
'workflow-phase',
|
'workflow-phase',
|
||||||
'phase-tool-selection',
|
'phase-tool-selection',
|
||||||
@ -321,21 +327,23 @@ class AIPipeline {
|
|||||||
selections.forEach((sel: any) => {
|
selections.forEach((sel: any) => {
|
||||||
const tool = phaseTools.find((t: any) => t && t.name === sel.toolName);
|
const tool = phaseTools.find((t: any) => t && t.name === sel.toolName);
|
||||||
if (tool) {
|
if (tool) {
|
||||||
const priority = this.derivePriorityFromScore(sel.taskRelevance);
|
const moderatedTaskRelevance = this.moderateTaskRelevance(sel.taskRelevance);
|
||||||
this.addToolToSelection(context, tool, phase.id, priority, sel.justification, sel.taskRelevance, sel.limitations);
|
const priority = this.derivePriorityFromScore(moderatedTaskRelevance);
|
||||||
|
|
||||||
|
this.addToolToSelection(context, tool, phase.id, priority, sel.justification, moderatedTaskRelevance, sel.limitations);
|
||||||
|
|
||||||
// AUDIT: Individual tool selection reasoning
|
|
||||||
auditService.addEntry(
|
auditService.addEntry(
|
||||||
'tool-reasoning',
|
'tool-reasoning',
|
||||||
'tool-added-to-phase',
|
'tool-added-to-phase',
|
||||||
{ toolName: tool.name, phaseId: phase.id, taskRelevance: sel.taskRelevance },
|
{ toolName: tool.name, phaseId: phase.id, originalTaskRelevance: sel.taskRelevance, moderatedTaskRelevance },
|
||||||
{ justification: sel.justification, limitations: sel.limitations },
|
{ justification: sel.justification, limitations: sel.limitations },
|
||||||
sel.taskRelevance || 70,
|
moderatedTaskRelevance || 70,
|
||||||
phaseStart,
|
phaseStart,
|
||||||
{
|
{
|
||||||
toolType: tool.type,
|
toolType: tool.type,
|
||||||
priority,
|
priority,
|
||||||
selectionReasoning: sel.justification
|
selectionReasoning: sel.justification,
|
||||||
|
moderationApplied: sel.taskRelevance !== moderatedTaskRelevance
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -345,8 +353,9 @@ class AIPipeline {
|
|||||||
await this.delay(this.config.microTaskDelay);
|
await this.delay(this.config.microTaskDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete underrepresented phases with enhanced logging
|
|
||||||
const completionResult = await this.completeUnderrepresentedPhases(context, toolsData, pipelineStart);
|
const completionResult = await this.completeUnderrepresentedPhases(context, toolsData, pipelineStart);
|
||||||
|
completedTasks += completionResult.completed;
|
||||||
|
failedTasks += completionResult.failed;
|
||||||
|
|
||||||
return { completed: completedTasks, failed: failedTasks };
|
return { completed: completedTasks, failed: failedTasks };
|
||||||
}
|
}
|
||||||
@ -369,6 +378,267 @@ class AIPipeline {
|
|||||||
return { completed: completedTasks, failed: failedTasks };
|
return { completed: completedTasks, failed: failedTasks };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async completeUnderrepresentedPhases(
|
||||||
|
context: PipelineContext,
|
||||||
|
toolsData: any,
|
||||||
|
pipelineStart: number
|
||||||
|
): Promise<{ completed: number; failed: number }> {
|
||||||
|
const phases = toolsData.phases || [];
|
||||||
|
const selectedPhases = new Map<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) {
|
||||||
|
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(', '));
|
||||||
|
|
||||||
|
auditService.addEntry(
|
||||||
|
'phase-completion',
|
||||||
|
'underrepresented-phases-detected',
|
||||||
|
{
|
||||||
|
underrepresentedPhases: underrepresentedPhases.map(p => p.id),
|
||||||
|
currentPhaseDistribution: Array.from(selectedPhases.entries())
|
||||||
|
},
|
||||||
|
{
|
||||||
|
phasesToComplete: underrepresentedPhases.length,
|
||||||
|
completionStrategy: 'semantic-search-with-ai-reasoning'
|
||||||
|
},
|
||||||
|
70,
|
||||||
|
pipelineStart,
|
||||||
|
{
|
||||||
|
totalPhases: phases.length,
|
||||||
|
adequatelyRepresented: phases.length - underrepresentedPhases.length,
|
||||||
|
completionMethod: 'sophisticated-ai-reasoning'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const phase of underrepresentedPhases) {
|
||||||
|
const result = await this.completePhaseWithSemanticSearchAndAI(context, phase, toolsData, pipelineStart);
|
||||||
|
if (result.success) completedTasks++; else failedTasks++;
|
||||||
|
await this.delay(this.config.microTaskDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { completed: completedTasks, failed: failedTasks };
|
||||||
|
}
|
||||||
|
|
||||||
|
private async completePhaseWithSemanticSearchAndAI(
|
||||||
|
context: PipelineContext,
|
||||||
|
phase: any,
|
||||||
|
toolsData: any,
|
||||||
|
pipelineStart: number
|
||||||
|
): Promise<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);
|
||||||
|
|
||||||
|
auditService.addEmbeddingsSearch(
|
||||||
|
phaseQuery,
|
||||||
|
phaseResults,
|
||||||
|
0.2,
|
||||||
|
phaseStart,
|
||||||
|
{
|
||||||
|
phaseId: phase.id,
|
||||||
|
phaseName: phase.name,
|
||||||
|
completionPurpose: 'underrepresented-phase-enhancement'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (phaseResults.length === 0) {
|
||||||
|
console.log('[AI-PIPELINE] No semantic results for phase:', phase.id);
|
||||||
|
return {
|
||||||
|
taskType: 'phase-completion',
|
||||||
|
content: '',
|
||||||
|
processingTimeMs: Date.now() - phaseStart,
|
||||||
|
success: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolsMap = new Map(toolsData.tools.map((tool: any) => [tool.name, tool]));
|
||||||
|
const conceptsMap = new Map(toolsData.concepts.map((concept: any) => [concept.name, concept]));
|
||||||
|
|
||||||
|
const phaseTools = phaseResults
|
||||||
|
.filter((result: any) => result.type === 'tool')
|
||||||
|
.map((result: any) => toolsMap.get(result.name))
|
||||||
|
.filter((tool: any): tool is NonNullable<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) {
|
||||||
|
console.log('[AI-PIPELINE] No suitable tools for phase completion:', phase.id);
|
||||||
|
return {
|
||||||
|
taskType: 'phase-completion',
|
||||||
|
content: '',
|
||||||
|
processingTimeMs: Date.now() - phaseStart,
|
||||||
|
success: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectionPrompt = getPrompt('generatePhaseCompletionPrompt', context.userQuery, phase, phaseTools, phaseConcepts);
|
||||||
|
const selectionResult = await this.callMicroTaskAI(selectionPrompt, context, 800, 'phase-completion-selection');
|
||||||
|
|
||||||
|
if (!selectionResult.success) {
|
||||||
|
console.error('[AI-PIPELINE] Phase completion selection failed for:', phase.id);
|
||||||
|
return {
|
||||||
|
taskType: 'phase-completion',
|
||||||
|
content: '',
|
||||||
|
processingTimeMs: Date.now() - phaseStart,
|
||||||
|
success: false,
|
||||||
|
error: 'Selection micro-task failed'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = JSONParser.safeParseJSON(selectionResult.content, {
|
||||||
|
selectedTools: [],
|
||||||
|
selectedConcepts: [],
|
||||||
|
completionReasoning: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const validTools = selection.selectedTools
|
||||||
|
.map((name: string) => phaseTools.find((t: any) => t && t.name === name))
|
||||||
|
.filter((tool: any): tool is NonNullable<any> => tool !== undefined && tool !== null)
|
||||||
|
.slice(0, 2);
|
||||||
|
|
||||||
|
if (validTools.length === 0) {
|
||||||
|
console.log('[AI-PIPELINE] No valid tools selected for phase completion:', phase.id);
|
||||||
|
return {
|
||||||
|
taskType: 'phase-completion',
|
||||||
|
content: selection.completionReasoning || '',
|
||||||
|
processingTimeMs: Date.now() - phaseStart,
|
||||||
|
success: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tool of validTools) {
|
||||||
|
console.log('[AI-PIPELINE] Generating AI reasoning for phase completion tool:', tool.name);
|
||||||
|
|
||||||
|
const reasoningPrompt = getPrompt(
|
||||||
|
'phaseCompletionReasoning',
|
||||||
|
context.userQuery,
|
||||||
|
phase,
|
||||||
|
tool.name,
|
||||||
|
tool,
|
||||||
|
selection.completionReasoning || 'Nachergänzung zur Vervollständigung der Phasenabdeckung'
|
||||||
|
);
|
||||||
|
|
||||||
|
const reasoningResult = await this.callMicroTaskAI(reasoningPrompt, context, 400, 'phase-completion-reasoning');
|
||||||
|
|
||||||
|
let detailedJustification: string;
|
||||||
|
let moderatedTaskRelevance = 75;
|
||||||
|
|
||||||
|
if (reasoningResult.success && reasoningResult.content.trim()) {
|
||||||
|
detailedJustification = reasoningResult.content.trim();
|
||||||
|
moderatedTaskRelevance = this.moderateTaskRelevance(80);
|
||||||
|
} else {
|
||||||
|
detailedJustification = `Nachträglich hinzugefügt zur Vervollständigung der ${phase.name}-Phase. Die ursprüngliche KI-Auswahl war zu spezifisch und hat wichtige Tools für diese Phase übersehen.`;
|
||||||
|
moderatedTaskRelevance = this.moderateTaskRelevance(75);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addToolToSelection(
|
||||||
|
context,
|
||||||
|
tool,
|
||||||
|
phase.id,
|
||||||
|
'medium',
|
||||||
|
detailedJustification,
|
||||||
|
moderatedTaskRelevance,
|
||||||
|
['Nachträgliche Ergänzung via semantische Phasensuche mit KI-Bewertung']
|
||||||
|
);
|
||||||
|
|
||||||
|
auditService.addPhaseCompletion(
|
||||||
|
phase.id,
|
||||||
|
[tool.name],
|
||||||
|
detailedJustification,
|
||||||
|
phaseStart,
|
||||||
|
{
|
||||||
|
toolName: tool.name,
|
||||||
|
toolType: tool.type,
|
||||||
|
semanticSimilarity: phaseResults.find(r => r.name === tool.name)?.similarity,
|
||||||
|
completionReason: 'underrepresented-phase',
|
||||||
|
originalSelectionMissed: true,
|
||||||
|
aiReasoningUsed: reasoningResult.success,
|
||||||
|
moderatedTaskRelevance
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('[AI-PIPELINE] Added phase completion tool with AI reasoning:', tool.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
taskType: 'phase-completion',
|
||||||
|
content: selection.completionReasoning || '',
|
||||||
|
processingTimeMs: Date.now() - phaseStart,
|
||||||
|
success: true
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[AI-PIPELINE] Phase completion failed for:', phase.id, error);
|
||||||
|
|
||||||
|
auditService.addEntry(
|
||||||
|
'phase-completion',
|
||||||
|
'completion-failed',
|
||||||
|
{ phaseId: phase.id, error: error.message },
|
||||||
|
{ success: false },
|
||||||
|
20,
|
||||||
|
phaseStart,
|
||||||
|
{
|
||||||
|
errorType: error.constructor.name,
|
||||||
|
phaseId: phase.id
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
taskType: 'phase-completion',
|
||||||
|
content: '',
|
||||||
|
processingTimeMs: Date.now() - phaseStart,
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private moderateTaskRelevance(taskRelevance: number): number {
|
||||||
|
if (typeof taskRelevance !== 'number') {
|
||||||
|
return 70;
|
||||||
|
}
|
||||||
|
|
||||||
|
let moderated = Math.min(taskRelevance, this.config.taskRelevanceModeration.maxInitialScore);
|
||||||
|
|
||||||
|
if (moderated > this.config.taskRelevanceModeration.moderationThreshold) {
|
||||||
|
const excess = moderated - this.config.taskRelevanceModeration.moderationThreshold;
|
||||||
|
moderated = this.config.taskRelevanceModeration.moderationThreshold + (excess * 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round(Math.min(moderated, this.config.taskRelevanceModeration.maxInitialScore));
|
||||||
|
}
|
||||||
|
|
||||||
private async analyzeScenario(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
|
private async analyzeScenario(context: PipelineContext, pipelineStart: number): Promise<MicroTaskResult> {
|
||||||
console.log('[AI-PIPELINE] Micro-task: Scenario analysis');
|
console.log('[AI-PIPELINE] Micro-task: Scenario analysis');
|
||||||
const taskStart = Date.now();
|
const taskStart = Date.now();
|
||||||
@ -386,7 +656,6 @@ class AIPipeline {
|
|||||||
|
|
||||||
this.addToContextHistory(context, `${isWorkflow ? 'Szenario' : 'Problem'}-Analyse: ${result.content.slice(0, 200)}...`);
|
this.addToContextHistory(context, `${isWorkflow ? 'Szenario' : 'Problem'}-Analyse: ${result.content.slice(0, 200)}...`);
|
||||||
|
|
||||||
// AUDIT: Scenario analysis
|
|
||||||
auditService.addAIDecision(
|
auditService.addAIDecision(
|
||||||
'contextual-analysis',
|
'contextual-analysis',
|
||||||
prompt,
|
prompt,
|
||||||
@ -418,7 +687,6 @@ class AIPipeline {
|
|||||||
context.investigationApproach = result.content;
|
context.investigationApproach = result.content;
|
||||||
this.addToContextHistory(context, `${isWorkflow ? 'Untersuchungs' : 'Lösungs'}ansatz: ${result.content.slice(0, 200)}...`);
|
this.addToContextHistory(context, `${isWorkflow ? 'Untersuchungs' : 'Lösungs'}ansatz: ${result.content.slice(0, 200)}...`);
|
||||||
|
|
||||||
// AUDIT: Investigation approach
|
|
||||||
auditService.addAIDecision(
|
auditService.addAIDecision(
|
||||||
'contextual-analysis',
|
'contextual-analysis',
|
||||||
prompt,
|
prompt,
|
||||||
@ -451,7 +719,6 @@ class AIPipeline {
|
|||||||
context.criticalConsiderations = result.content;
|
context.criticalConsiderations = result.content;
|
||||||
this.addToContextHistory(context, `Kritische Überlegungen: ${result.content.slice(0, 200)}...`);
|
this.addToContextHistory(context, `Kritische Überlegungen: ${result.content.slice(0, 200)}...`);
|
||||||
|
|
||||||
// AUDIT: Critical considerations
|
|
||||||
auditService.addAIDecision(
|
auditService.addAIDecision(
|
||||||
'contextual-analysis',
|
'contextual-analysis',
|
||||||
prompt,
|
prompt,
|
||||||
@ -479,14 +746,14 @@ class AIPipeline {
|
|||||||
console.log('[AI-PIPELINE] Micro-task: Tool evaluation for:', tool.name);
|
console.log('[AI-PIPELINE] Micro-task: Tool evaluation for:', tool.name);
|
||||||
const taskStart = Date.now();
|
const taskStart = Date.now();
|
||||||
const existingSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name);
|
const existingSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name);
|
||||||
const taskRelevance = existingSelection?.taskRelevance || 70;
|
const originalTaskRelevance = existingSelection?.taskRelevance || 70;
|
||||||
const priority = this.derivePriorityFromScore(taskRelevance);
|
const moderatedTaskRelevance = this.moderateTaskRelevance(originalTaskRelevance);
|
||||||
|
const priority = this.derivePriorityFromScore(moderatedTaskRelevance);
|
||||||
|
|
||||||
const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank, taskRelevance);
|
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, 1000, 'tool-evaluation');
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// FIXED: Use centralized JSON parsing
|
|
||||||
const evaluation = JSONParser.safeParseJSON(result.content, {
|
const evaluation = JSONParser.safeParseJSON(result.content, {
|
||||||
detailed_explanation: 'Evaluation failed',
|
detailed_explanation: 'Evaluation failed',
|
||||||
implementation_approach: '',
|
implementation_approach: '',
|
||||||
@ -500,16 +767,15 @@ class AIPipeline {
|
|||||||
evaluation: {
|
evaluation: {
|
||||||
...evaluation,
|
...evaluation,
|
||||||
rank,
|
rank,
|
||||||
task_relevance: taskRelevance
|
task_relevance: moderatedTaskRelevance
|
||||||
}
|
}
|
||||||
}, 'evaluation', priority, evaluation.detailed_explanation, taskRelevance, evaluation.limitations);
|
}, 'evaluation', priority, evaluation.detailed_explanation, moderatedTaskRelevance, evaluation.limitations);
|
||||||
|
|
||||||
// AUDIT: Tool evaluation
|
|
||||||
auditService.addAIDecision(
|
auditService.addAIDecision(
|
||||||
'tool-evaluation',
|
'tool-evaluation',
|
||||||
prompt,
|
prompt,
|
||||||
result.content,
|
result.content,
|
||||||
taskRelevance,
|
moderatedTaskRelevance,
|
||||||
`Evaluated tool ${tool.name} for ${context.mode} mode`,
|
`Evaluated tool ${tool.name} for ${context.mode} mode`,
|
||||||
taskStart,
|
taskStart,
|
||||||
{
|
{
|
||||||
@ -517,7 +783,9 @@ class AIPipeline {
|
|||||||
toolName: tool.name,
|
toolName: tool.name,
|
||||||
toolType: tool.type,
|
toolType: tool.type,
|
||||||
rank,
|
rank,
|
||||||
taskRelevance,
|
originalTaskRelevance,
|
||||||
|
moderatedTaskRelevance,
|
||||||
|
moderationApplied: originalTaskRelevance !== moderatedTaskRelevance,
|
||||||
evaluationParsed: !!evaluation.detailed_explanation,
|
evaluationParsed: !!evaluation.detailed_explanation,
|
||||||
prosCount: evaluation.pros?.length || 0,
|
prosCount: evaluation.pros?.length || 0,
|
||||||
limitationsCount: evaluation.limitations?.length || 0,
|
limitationsCount: evaluation.limitations?.length || 0,
|
||||||
@ -548,7 +816,6 @@ class AIPipeline {
|
|||||||
const result = await this.callMicroTaskAI(prompt, context, 700, 'background-knowledge');
|
const result = await this.callMicroTaskAI(prompt, context, 700, 'background-knowledge');
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// FIXED: Use centralized JSON parsing
|
|
||||||
const selections = JSONParser.safeParseJSON(result.content, []);
|
const selections = JSONParser.safeParseJSON(result.content, []);
|
||||||
|
|
||||||
if (Array.isArray(selections)) {
|
if (Array.isArray(selections)) {
|
||||||
@ -559,7 +826,6 @@ class AIPipeline {
|
|||||||
relevance: sel.relevance
|
relevance: sel.relevance
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// AUDIT: Background knowledge selection
|
|
||||||
auditService.addEntry(
|
auditService.addEntry(
|
||||||
'knowledge-synthesis',
|
'knowledge-synthesis',
|
||||||
'concept-selection',
|
'concept-selection',
|
||||||
@ -596,7 +862,6 @@ class AIPipeline {
|
|||||||
const result = await this.callMicroTaskAI(prompt, context, 350, 'final-recommendations');
|
const result = await this.callMicroTaskAI(prompt, context, 350, 'final-recommendations');
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// AUDIT: Final recommendations
|
|
||||||
auditService.addAIDecision(
|
auditService.addAIDecision(
|
||||||
'synthesis',
|
'synthesis',
|
||||||
prompt,
|
prompt,
|
||||||
@ -617,152 +882,6 @@ class AIPipeline {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async completeUnderrepresentedPhases(context: PipelineContext, toolsData: any, pipelineStart: number): Promise<void> {
|
|
||||||
const phases = toolsData.phases || [];
|
|
||||||
const selectedPhases = new Map<string, number>();
|
|
||||||
|
|
||||||
context.selectedTools?.forEach((st: any) => {
|
|
||||||
const count = selectedPhases.get(st.phase) || 0;
|
|
||||||
selectedPhases.set(st.phase, count + 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
const underrepresentedPhases = phases.filter((phase: any) => {
|
|
||||||
const count = selectedPhases.get(phase.id) || 0;
|
|
||||||
return count <= 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (underrepresentedPhases.length === 0) {
|
|
||||||
console.log('[AI-PIPELINE] All phases adequately represented');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[AI-PIPELINE] Completing underrepresented phases:', underrepresentedPhases.map((p: any) => p.id).join(', '));
|
|
||||||
|
|
||||||
// AUDIT: Phase completion start
|
|
||||||
auditService.addEntry(
|
|
||||||
'phase-completion',
|
|
||||||
'underrepresented-phases-detected',
|
|
||||||
{
|
|
||||||
underrepresentedPhases: underrepresentedPhases.map(p => p.id),
|
|
||||||
currentPhaseDistribution: Array.from(selectedPhases.entries())
|
|
||||||
},
|
|
||||||
{
|
|
||||||
phasesToComplete: underrepresentedPhases.length,
|
|
||||||
completionStrategy: 'semantic-search'
|
|
||||||
},
|
|
||||||
70,
|
|
||||||
pipelineStart,
|
|
||||||
{
|
|
||||||
totalPhases: phases.length,
|
|
||||||
adequatelyRepresented: phases.length - underrepresentedPhases.length,
|
|
||||||
completionMethod: 'semantic-phase-search'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const phase of underrepresentedPhases) {
|
|
||||||
await this.completePhaseWithSemanticSearch(context, phase, toolsData, pipelineStart);
|
|
||||||
await this.delay(this.config.microTaskDelay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async completePhaseWithSemanticSearch(context: PipelineContext, phase: any, toolsData: any, pipelineStart: number): Promise<void> {
|
|
||||||
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);
|
|
||||||
|
|
||||||
// AUDIT: Embeddings search for phase completion
|
|
||||||
auditService.addEmbeddingsSearch(
|
|
||||||
phaseQuery,
|
|
||||||
phaseResults,
|
|
||||||
0.2,
|
|
||||||
phaseStart,
|
|
||||||
{
|
|
||||||
phaseId: phase.id,
|
|
||||||
phaseName: phase.name,
|
|
||||||
completionPurpose: 'underrepresented-phase-enhancement'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (phaseResults.length === 0) {
|
|
||||||
console.log('[AI-PIPELINE] No semantic results for phase:', phase.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const toolsMap = new Map(toolsData.tools.map((tool: any) => [tool.name, tool]));
|
|
||||||
|
|
||||||
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, 2);
|
|
||||||
|
|
||||||
if (phaseTools.length === 0) {
|
|
||||||
console.log('[AI-PIPELINE] No suitable tools for phase completion:', phase.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add tools with justification and audit each addition
|
|
||||||
for (const tool of phaseTools) {
|
|
||||||
const justification = `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.`;
|
|
||||||
|
|
||||||
this.addToolToSelection(
|
|
||||||
context,
|
|
||||||
tool,
|
|
||||||
phase.id,
|
|
||||||
'medium',
|
|
||||||
justification,
|
|
||||||
75,
|
|
||||||
['Nachträgliche Ergänzung via semantische Phasensuche']
|
|
||||||
);
|
|
||||||
|
|
||||||
// AUDIT: Phase completion tool addition
|
|
||||||
auditService.addPhaseCompletion(
|
|
||||||
phase.id,
|
|
||||||
[tool.name],
|
|
||||||
justification,
|
|
||||||
phaseStart,
|
|
||||||
{
|
|
||||||
toolName: tool.name,
|
|
||||||
toolType: tool.type,
|
|
||||||
semanticSimilarity: phaseResults.find(r => r.name === tool.name)?.similarity,
|
|
||||||
completionReason: 'underrepresented-phase',
|
|
||||||
originalSelectionMissed: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('[AI-PIPELINE] Added phase completion tool:', tool.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[AI-PIPELINE] Phase completion failed for:', phase.id, error);
|
|
||||||
|
|
||||||
// AUDIT: Phase completion failure
|
|
||||||
auditService.addEntry(
|
|
||||||
'phase-completion',
|
|
||||||
'completion-failed',
|
|
||||||
{ phaseId: phase.id, error: error.message },
|
|
||||||
{ success: false },
|
|
||||||
20,
|
|
||||||
phaseStart,
|
|
||||||
{
|
|
||||||
errorType: error.constructor.name,
|
|
||||||
phaseId: phase.id
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildRecommendation(context: PipelineContext, mode: string, finalContent: string): any {
|
private buildRecommendation(context: PipelineContext, mode: string, finalContent: string): any {
|
||||||
const isWorkflow = mode === 'workflow';
|
const isWorkflow = mode === 'workflow';
|
||||||
|
|
||||||
@ -795,7 +914,6 @@ class AIPipeline {
|
|||||||
st.limitations || []
|
st.limitations || []
|
||||||
);
|
);
|
||||||
|
|
||||||
// AUDIT: Confidence calculation for each tool
|
|
||||||
auditService.addConfidenceCalculation(
|
auditService.addConfidenceCalculation(
|
||||||
st.tool.name,
|
st.tool.name,
|
||||||
confidence,
|
confidence,
|
||||||
@ -803,7 +921,8 @@ class AIPipeline {
|
|||||||
{
|
{
|
||||||
phase: st.phase,
|
phase: st.phase,
|
||||||
priority: st.priority,
|
priority: st.priority,
|
||||||
toolType: st.tool.type
|
toolType: st.tool.type,
|
||||||
|
moderatedTaskRelevance: st.taskRelevance
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -839,14 +958,14 @@ class AIPipeline {
|
|||||||
st.limitations || []
|
st.limitations || []
|
||||||
);
|
);
|
||||||
|
|
||||||
// AUDIT: Confidence calculation for each tool
|
|
||||||
auditService.addConfidenceCalculation(
|
auditService.addConfidenceCalculation(
|
||||||
st.tool.name,
|
st.tool.name,
|
||||||
confidence,
|
confidence,
|
||||||
Date.now(),
|
Date.now(),
|
||||||
{
|
{
|
||||||
rank: st.tool.evaluation?.rank || 1,
|
rank: st.tool.evaluation?.rank || 1,
|
||||||
toolType: st.tool.type
|
toolType: st.tool.type,
|
||||||
|
moderatedTaskRelevance: st.taskRelevance
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -873,7 +992,6 @@ class AIPipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
private async callMicroTaskAI(
|
private async callMicroTaskAI(
|
||||||
prompt: string,
|
prompt: string,
|
||||||
context: PipelineContext,
|
context: PipelineContext,
|
||||||
|
@ -83,26 +83,21 @@ export const apiServerError = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const apiSpecial = {
|
export const apiSpecial = {
|
||||||
// JSON parsing error
|
|
||||||
invalidJSON: (): Response =>
|
invalidJSON: (): Response =>
|
||||||
apiError.badRequest('Invalid JSON in request body'),
|
apiError.badRequest('Invalid JSON in request body'),
|
||||||
|
|
||||||
// Missing required fields
|
|
||||||
missingRequired: (fields: string[]): Response =>
|
missingRequired: (fields: string[]): Response =>
|
||||||
apiError.badRequest(`Missing required fields: ${fields.join(', ')}`),
|
apiError.badRequest(`Missing required fields: ${fields.join(', ')}`),
|
||||||
|
|
||||||
// Empty request body
|
|
||||||
emptyBody: (): Response =>
|
emptyBody: (): Response =>
|
||||||
apiError.badRequest('Request body cannot be empty'),
|
apiError.badRequest('Request body cannot be empty'),
|
||||||
|
|
||||||
// File upload responses
|
|
||||||
uploadSuccess: (data: { url: string; filename: string; size: number; storage: string }): Response =>
|
uploadSuccess: (data: { url: string; filename: string; size: number; storage: string }): Response =>
|
||||||
apiResponse.created(data),
|
apiResponse.created(data),
|
||||||
|
|
||||||
uploadFailed: (error: string): Response =>
|
uploadFailed: (error: string): Response =>
|
||||||
apiServerError.internal(`Upload failed: ${error}`),
|
apiServerError.internal(`Upload failed: ${error}`),
|
||||||
|
|
||||||
// Contribution responses
|
|
||||||
contributionSuccess: (data: { prUrl?: string; branchName?: string; message: string }): Response =>
|
contributionSuccess: (data: { prUrl?: string; branchName?: string; message: string }): Response =>
|
||||||
apiResponse.created({ success: true, ...data }),
|
apiResponse.created({ success: true, ...data }),
|
||||||
|
|
||||||
@ -114,7 +109,6 @@ export const apiWithHeaders = {
|
|||||||
successWithHeaders: (data: any, headers: Record<string, string>): Response =>
|
successWithHeaders: (data: any, headers: Record<string, string>): Response =>
|
||||||
createAPIResponse(data, 200, headers),
|
createAPIResponse(data, 200, headers),
|
||||||
|
|
||||||
// Redirect response
|
|
||||||
redirect: (location: string, temporary: boolean = true): Response =>
|
redirect: (location: string, temporary: boolean = true): Response =>
|
||||||
new Response(null, {
|
new Response(null, {
|
||||||
status: temporary ? 302 : 301,
|
status: temporary ? 302 : 301,
|
||||||
|
@ -101,7 +101,6 @@ class AuditService {
|
|||||||
|
|
||||||
this.activeAuditTrail.push(entry);
|
this.activeAuditTrail.push(entry);
|
||||||
|
|
||||||
// Enforce max entries limit
|
|
||||||
if (this.activeAuditTrail.length > this.config.maxEntries) {
|
if (this.activeAuditTrail.length > this.config.maxEntries) {
|
||||||
this.activeAuditTrail.shift();
|
this.activeAuditTrail.shift();
|
||||||
}
|
}
|
||||||
@ -109,7 +108,6 @@ class AuditService {
|
|||||||
console.log(`[AUDIT-SERVICE] ${phase}/${action}: ${confidence}% confidence, ${entry.processingTimeMs}ms`);
|
console.log(`[AUDIT-SERVICE] ${phase}/${action}: ${confidence}% confidence, ${entry.processingTimeMs}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Specialized audit methods for forensic transparency
|
|
||||||
addAIDecision(
|
addAIDecision(
|
||||||
phase: string,
|
phase: string,
|
||||||
aiPrompt: string,
|
aiPrompt: string,
|
||||||
@ -162,24 +160,28 @@ class AuditService {
|
|||||||
|
|
||||||
addPhaseCompletion(
|
addPhaseCompletion(
|
||||||
phaseId: string,
|
phaseId: string,
|
||||||
toolsAdded: string[],
|
addedTools: string[],
|
||||||
completionReasoning: string,
|
reasoning: string,
|
||||||
startTime: number,
|
startTime: number,
|
||||||
metadata: Record<string, any> = {}
|
metadata: Record<string, any> = {}
|
||||||
): void {
|
): void {
|
||||||
this.addEntry(
|
this.addEntry(
|
||||||
'phase-completion',
|
'phase-completion',
|
||||||
'phase-enhancement',
|
'phase-enhancement',
|
||||||
{ phaseId, underrepresentedPhase: true },
|
{
|
||||||
{ toolsAdded },
|
phaseId,
|
||||||
75, // Default confidence for phase completion
|
addedTools,
|
||||||
|
reasoning: reasoning.slice(0, 200)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolsAddedCount: addedTools.length,
|
||||||
|
enhancementMethod: 'semantic-search-with-ai-reasoning'
|
||||||
|
},
|
||||||
|
metadata.moderatedTaskRelevance || 75,
|
||||||
startTime,
|
startTime,
|
||||||
{
|
{
|
||||||
...metadata,
|
...metadata,
|
||||||
phaseId,
|
phaseCompletionMethod: 'sophisticated-ai-reasoning'
|
||||||
toolsAdded,
|
|
||||||
completionReasoning,
|
|
||||||
enhancementType: 'semantic-phase-completion'
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -215,25 +217,30 @@ class AuditService {
|
|||||||
|
|
||||||
addConfidenceCalculation(
|
addConfidenceCalculation(
|
||||||
toolName: string,
|
toolName: string,
|
||||||
confidenceBreakdown: any,
|
confidence: any,
|
||||||
startTime: number,
|
startTime: number,
|
||||||
metadata: Record<string, any> = {}
|
metadata: Record<string, any> = {}
|
||||||
): void {
|
): void {
|
||||||
this.addEntry(
|
this.addEntry(
|
||||||
'confidence-scoring',
|
'confidence-scoring',
|
||||||
'tool-confidence',
|
'tool-confidence',
|
||||||
{ toolName },
|
{
|
||||||
{ confidenceBreakdown },
|
toolName,
|
||||||
confidenceBreakdown.overall || 50,
|
confidence: {
|
||||||
|
overall: confidence.overall,
|
||||||
|
semantic: confidence.semanticRelevance,
|
||||||
|
task: confidence.taskSuitability
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uncertaintyFactorsCount: confidence.uncertaintyFactors?.length || 0,
|
||||||
|
strengthIndicatorsCount: confidence.strengthIndicators?.length || 0
|
||||||
|
},
|
||||||
|
confidence.overall,
|
||||||
startTime,
|
startTime,
|
||||||
{
|
{
|
||||||
...metadata,
|
...metadata,
|
||||||
confidenceFactors: [
|
confidenceCalculation: true
|
||||||
...(confidenceBreakdown.strengthIndicators || []),
|
|
||||||
...(confidenceBreakdown.uncertaintyFactors || [])
|
|
||||||
],
|
|
||||||
semanticRelevance: confidenceBreakdown.semanticRelevance,
|
|
||||||
taskSuitability: confidenceBreakdown.taskSuitability
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -300,7 +307,6 @@ class AuditService {
|
|||||||
return { ...this.config };
|
return { ...this.config };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Statistics and analysis methods (enhanced)
|
|
||||||
getAuditStatistics(auditTrail: AuditEntry[]): {
|
getAuditStatistics(auditTrail: AuditEntry[]): {
|
||||||
totalTime: number;
|
totalTime: number;
|
||||||
avgConfidence: number;
|
avgConfidence: number;
|
||||||
@ -346,12 +352,10 @@ class AuditService {
|
|||||||
const lowConfidenceSteps = auditTrail.filter(entry => (entry.confidence || 0) < 60).length;
|
const lowConfidenceSteps = auditTrail.filter(entry => (entry.confidence || 0) < 60).length;
|
||||||
const mediumConfidenceSteps = auditTrail.length - highConfidenceSteps - lowConfidenceSteps;
|
const mediumConfidenceSteps = auditTrail.length - highConfidenceSteps - lowConfidenceSteps;
|
||||||
|
|
||||||
// Enhanced metrics
|
|
||||||
const aiDecisionCount = auditTrail.filter(entry => entry.action === 'ai-decision').length;
|
const aiDecisionCount = auditTrail.filter(entry => entry.action === 'ai-decision').length;
|
||||||
const embeddingsUsageCount = auditTrail.filter(entry => entry.metadata?.embeddingsUsed).length;
|
const embeddingsUsageCount = auditTrail.filter(entry => entry.metadata?.embeddingsUsed).length;
|
||||||
const toolSelectionCount = auditTrail.filter(entry => entry.action === 'selection-decision').length;
|
const toolSelectionCount = auditTrail.filter(entry => entry.action === 'selection-decision').length;
|
||||||
|
|
||||||
// Phase breakdown
|
|
||||||
const phaseBreakdown: Record<string, { count: number; avgConfidence: number; totalTime: number }> = {};
|
const phaseBreakdown: Record<string, { count: number; avgConfidence: number; totalTime: number }> = {};
|
||||||
|
|
||||||
auditTrail.forEach(entry => {
|
auditTrail.forEach(entry => {
|
||||||
@ -364,7 +368,6 @@ class AuditService {
|
|||||||
phaseBreakdown[phase].totalTime += entry.processingTimeMs || 0;
|
phaseBreakdown[phase].totalTime += entry.processingTimeMs || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Calculate average confidence per phase
|
|
||||||
Object.keys(phaseBreakdown).forEach(phase => {
|
Object.keys(phaseBreakdown).forEach(phase => {
|
||||||
const phaseEntries = auditTrail.filter(entry => entry.phase === phase);
|
const phaseEntries = auditTrail.filter(entry => entry.phase === phase);
|
||||||
const validEntries = phaseEntries.filter(entry => typeof entry.confidence === 'number');
|
const validEntries = phaseEntries.filter(entry => typeof entry.confidence === 'number');
|
||||||
@ -425,7 +428,6 @@ class AuditService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Required fields validation
|
|
||||||
const requiredFields = ['timestamp', 'phase', 'action'];
|
const requiredFields = ['timestamp', 'phase', 'action'];
|
||||||
requiredFields.forEach(field => {
|
requiredFields.forEach(field => {
|
||||||
if (!(field in entry)) {
|
if (!(field in entry)) {
|
||||||
@ -433,7 +435,6 @@ class AuditService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enhanced validation for audit quality
|
|
||||||
if (entry.action === 'ai-decision' && !entry.metadata?.aiPrompt && !entry.metadata?.reasoning) {
|
if (entry.action === 'ai-decision' && !entry.metadata?.aiPrompt && !entry.metadata?.reasoning) {
|
||||||
warnings.push(`Entry ${index}: AI decision lacks transparency (no prompt or reasoning)`);
|
warnings.push(`Entry ${index}: AI decision lacks transparency (no prompt or reasoning)`);
|
||||||
}
|
}
|
||||||
@ -442,7 +443,6 @@ class AuditService {
|
|||||||
warnings.push(`Entry ${index}: Tool selection lacks methodology info`);
|
warnings.push(`Entry ${index}: Tool selection lacks methodology info`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data type validation
|
|
||||||
if (typeof entry.confidence !== 'number' || entry.confidence < 0 || entry.confidence > 100) {
|
if (typeof entry.confidence !== 'number' || entry.confidence < 0 || entry.confidence > 100) {
|
||||||
warnings.push(`Entry ${index} has invalid confidence value: ${entry.confidence}`);
|
warnings.push(`Entry ${index} has invalid confidence value: ${entry.confidence}`);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// src/utils/clientUtils.ts - Consolidated (removes duplicates from toolHelpers.ts)
|
// src/utils/clientUtils.ts
|
||||||
|
|
||||||
// Tool helper functions (moved here to avoid circular imports)
|
|
||||||
export function createToolSlug(toolName: string): string {
|
export function createToolSlug(toolName: string): string {
|
||||||
if (!toolName || typeof toolName !== 'string') {
|
if (!toolName || typeof toolName !== 'string') {
|
||||||
console.warn('[CLIENT-UTILS] Invalid toolName provided to createToolSlug:', toolName);
|
console.warn('[CLIENT-UTILS] Invalid toolName provided to createToolSlug:', toolName);
|
||||||
@ -30,7 +29,6 @@ export function isToolHosted(tool: any): boolean {
|
|||||||
tool.projectUrl.trim() !== "";
|
tool.projectUrl.trim() !== "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text and display utilities
|
|
||||||
export function sanitizeText(text: string): string {
|
export function sanitizeText(text: string): string {
|
||||||
if (typeof text !== 'string') return '';
|
if (typeof text !== 'string') return '';
|
||||||
|
|
||||||
@ -60,7 +58,6 @@ export function truncateText(text: string, maxLength: number): string {
|
|||||||
return text.slice(0, maxLength) + '...';
|
return text.slice(0, maxLength) + '...';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data summarization utilities
|
|
||||||
export function summarizeData(data: any): string {
|
export function summarizeData(data: any): string {
|
||||||
if (data === null || data === undefined) return 'null';
|
if (data === null || data === undefined) return 'null';
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
@ -85,7 +82,6 @@ export function summarizeData(data: any): string {
|
|||||||
return String(data);
|
return String(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time formatting utilities
|
|
||||||
export function formatDuration(ms: number): string {
|
export function formatDuration(ms: number): string {
|
||||||
if (ms < 1000) return '< 1s';
|
if (ms < 1000) return '< 1s';
|
||||||
if (ms < 60000) return `${Math.ceil(ms / 1000)}s`;
|
if (ms < 60000) return `${Math.ceil(ms / 1000)}s`;
|
||||||
@ -94,7 +90,6 @@ export function formatDuration(ms: number): string {
|
|||||||
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOM utilities
|
|
||||||
export function showElement(element: HTMLElement | null): void {
|
export function showElement(element: HTMLElement | null): void {
|
||||||
if (element) {
|
if (element) {
|
||||||
element.style.display = 'block';
|
element.style.display = 'block';
|
||||||
@ -109,7 +104,6 @@ export function hideElement(element: HTMLElement | null): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Autocomplete functionality (kept from original clientUtils.ts as it's UI-specific)
|
|
||||||
interface AutocompleteOptions {
|
interface AutocompleteOptions {
|
||||||
minLength?: number;
|
minLength?: number;
|
||||||
maxResults?: number;
|
maxResults?: number;
|
||||||
|
@ -44,7 +44,7 @@ class ConfidenceScoring {
|
|||||||
highThreshold: this.getEnvInt('CONFIDENCE_HIGH_THRESHOLD', 80)
|
highThreshold: this.getEnvInt('CONFIDENCE_HIGH_THRESHOLD', 80)
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[CONFIDENCE-SCORING] Initialized with config:', this.config);
|
console.log('[CONFIDENCE-SCORING] Initialized with restored config:', this.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getEnvFloat(key: string, defaultValue: number): number {
|
private getEnvFloat(key: string, defaultValue: number): number {
|
||||||
@ -70,7 +70,6 @@ class ConfidenceScoring {
|
|||||||
|
|
||||||
let enhancedTaskSuitability = taskRelevance;
|
let enhancedTaskSuitability = taskRelevance;
|
||||||
|
|
||||||
// Phase alignment bonus for workflow mode
|
|
||||||
if (context.mode === 'workflow') {
|
if (context.mode === 'workflow') {
|
||||||
const toolSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name);
|
const toolSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name);
|
||||||
if (toolSelection && tool.phases && Array.isArray(tool.phases) && tool.phases.includes(toolSelection.phase)) {
|
if (toolSelection && tool.phases && Array.isArray(tool.phases) && tool.phases.includes(toolSelection.phase)) {
|
||||||
@ -116,18 +115,15 @@ class ConfidenceScoring {
|
|||||||
): string[] {
|
): string[] {
|
||||||
const factors: string[] = [];
|
const factors: string[] = [];
|
||||||
|
|
||||||
// Add explicit limitations
|
|
||||||
if (limitations?.length > 0) {
|
if (limitations?.length > 0) {
|
||||||
factors.push(...limitations.slice(0, 2));
|
factors.push(...limitations.slice(0, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Semantic similarity concerns
|
|
||||||
const similarity = context.embeddingsSimilarities.get(tool.name) || 0.5;
|
const similarity = context.embeddingsSimilarities.get(tool.name) || 0.5;
|
||||||
if (similarity < 0.7) {
|
if (similarity < 0.7) {
|
||||||
factors.push('Geringe semantische Ähnlichkeit zur Anfrage');
|
factors.push('Geringe semantische Ähnlichkeit zur Anfrage');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skill level vs query complexity mismatches
|
|
||||||
if (tool.skillLevel === 'expert' && /schnell|rapid|triage|urgent|sofort/i.test(context.userQuery)) {
|
if (tool.skillLevel === 'expert' && /schnell|rapid|triage|urgent|sofort/i.test(context.userQuery)) {
|
||||||
factors.push('Experten-Tool für zeitkritisches Szenario');
|
factors.push('Experten-Tool für zeitkritisches Szenario');
|
||||||
}
|
}
|
||||||
@ -136,17 +132,14 @@ class ConfidenceScoring {
|
|||||||
factors.push('Einsteiger-Tool für komplexe Analyse');
|
factors.push('Einsteiger-Tool für komplexe Analyse');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Technical accessibility concerns
|
|
||||||
if (tool.type === 'software' && !isToolHosted(tool) && tool.accessType === 'download') {
|
if (tool.type === 'software' && !isToolHosted(tool) && tool.accessType === 'download') {
|
||||||
factors.push('Installation und Setup erforderlich');
|
factors.push('Installation und Setup erforderlich');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Licensing concerns
|
|
||||||
if (tool.license === 'Proprietary') {
|
if (tool.license === 'Proprietary') {
|
||||||
factors.push('Kommerzielle Software - Lizenzkosten zu beachten');
|
factors.push('Kommerzielle Software - Lizenzkosten zu beachten');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overall confidence concerns
|
|
||||||
if (confidence < 60) {
|
if (confidence < 60) {
|
||||||
factors.push('Moderate Gesamtbewertung - alternative Ansätze empfohlen');
|
factors.push('Moderate Gesamtbewertung - alternative Ansätze empfohlen');
|
||||||
}
|
}
|
||||||
@ -157,28 +150,23 @@ class ConfidenceScoring {
|
|||||||
private identifyStrengthIndicators(tool: any, context: AnalysisContext, confidence: number): string[] {
|
private identifyStrengthIndicators(tool: any, context: AnalysisContext, confidence: number): string[] {
|
||||||
const indicators: string[] = [];
|
const indicators: string[] = [];
|
||||||
|
|
||||||
// High semantic relevance
|
|
||||||
const similarity = context.embeddingsSimilarities.get(tool.name) || 0.5;
|
const similarity = context.embeddingsSimilarities.get(tool.name) || 0.5;
|
||||||
if (similarity >= 0.7) {
|
if (similarity >= 0.7) {
|
||||||
indicators.push('Sehr gute semantische Übereinstimmung mit Ihrer Anfrage');
|
indicators.push('Sehr gute semantische Übereinstimmung mit Ihrer Anfrage');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Documentation availability
|
|
||||||
if (tool.knowledgebase === true) {
|
if (tool.knowledgebase === true) {
|
||||||
indicators.push('Umfassende Dokumentation und Wissensbasis verfügbar');
|
indicators.push('Umfassende Dokumentation und Wissensbasis verfügbar');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Immediate availability
|
|
||||||
if (isToolHosted(tool)) {
|
if (isToolHosted(tool)) {
|
||||||
indicators.push('Sofort verfügbar über gehostete Lösung');
|
indicators.push('Sofort verfügbar über gehostete Lösung');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Balanced skill requirements
|
|
||||||
if (tool.skillLevel === 'intermediate' || tool.skillLevel === 'advanced') {
|
if (tool.skillLevel === 'intermediate' || tool.skillLevel === 'advanced') {
|
||||||
indicators.push('Ausgewogenes Verhältnis zwischen Funktionalität und Benutzerfreundlichkeit');
|
indicators.push('Ausgewogenes Verhältnis zwischen Funktionalität und Benutzerfreundlichkeit');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method-query alignment
|
|
||||||
if (tool.type === 'method' && /methodik|vorgehen|prozess|ansatz/i.test(context.userQuery)) {
|
if (tool.type === 'method' && /methodik|vorgehen|prozess|ansatz/i.test(context.userQuery)) {
|
||||||
indicators.push('Methodischer Ansatz passt zu Ihrer prozeduralen Anfrage');
|
indicators.push('Methodischer Ansatz passt zu Ihrer prozeduralen Anfrage');
|
||||||
}
|
}
|
||||||
@ -197,12 +185,10 @@ class ConfidenceScoring {
|
|||||||
|
|
||||||
let confidence = 60;
|
let confidence = 60;
|
||||||
|
|
||||||
// Selection ratio scoring
|
|
||||||
if (selectionRatio > 0.05 && selectionRatio < 0.3) confidence += 20;
|
if (selectionRatio > 0.05 && selectionRatio < 0.3) confidence += 20;
|
||||||
else if (selectionRatio <= 0.05) confidence -= 10;
|
else if (selectionRatio <= 0.05) confidence -= 10;
|
||||||
else confidence -= 15;
|
else confidence -= 15;
|
||||||
|
|
||||||
// Quality indicators
|
|
||||||
if (hasReasoning) confidence += 15;
|
if (hasReasoning) confidence += 15;
|
||||||
if (result.selectedConcepts?.length > 0) confidence += 5;
|
if (result.selectedConcepts?.length > 0) confidence += 5;
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
// src/utils/jsonUtils.ts - Centralized JSON parsing and utilities
|
// src/utils/jsonUtils.ts
|
||||||
export class JSONParser {
|
export class JSONParser {
|
||||||
static safeParseJSON(jsonString: string, fallback: any = null): any {
|
static safeParseJSON(jsonString: string, fallback: any = null): any {
|
||||||
try {
|
try {
|
||||||
let cleaned = jsonString.trim();
|
let cleaned = jsonString.trim();
|
||||||
|
|
||||||
// Remove code block markers
|
|
||||||
const jsonBlockPatterns = [
|
const jsonBlockPatterns = [
|
||||||
/```json\s*([\s\S]*?)\s*```/i,
|
/```json\s*([\s\S]*?)\s*```/i,
|
||||||
/```\s*([\s\S]*?)\s*```/i,
|
/```\s*([\s\S]*?)\s*```/i,
|
||||||
@ -19,7 +18,6 @@ export class JSONParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle truncated JSON
|
|
||||||
if (!cleaned.endsWith('}') && !cleaned.endsWith(']')) {
|
if (!cleaned.endsWith('}') && !cleaned.endsWith(']')) {
|
||||||
console.warn('[JSON-PARSER] JSON appears truncated, attempting recovery');
|
console.warn('[JSON-PARSER] JSON appears truncated, attempting recovery');
|
||||||
cleaned = this.repairTruncatedJSON(cleaned);
|
cleaned = this.repairTruncatedJSON(cleaned);
|
||||||
@ -27,7 +25,6 @@ export class JSONParser {
|
|||||||
|
|
||||||
const parsed = JSON.parse(cleaned);
|
const parsed = JSON.parse(cleaned);
|
||||||
|
|
||||||
// Ensure proper structure for tool selection responses
|
|
||||||
if (parsed && typeof parsed === 'object') {
|
if (parsed && typeof parsed === 'object') {
|
||||||
if (!parsed.selectedTools) parsed.selectedTools = [];
|
if (!parsed.selectedTools) parsed.selectedTools = [];
|
||||||
if (!parsed.selectedConcepts) parsed.selectedConcepts = [];
|
if (!parsed.selectedConcepts) parsed.selectedConcepts = [];
|
||||||
@ -109,7 +106,6 @@ export class JSONParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: extract any quoted strings that look like tool names
|
|
||||||
if (selectedTools.length === 0 && selectedConcepts.length === 0) {
|
if (selectedTools.length === 0 && selectedConcepts.length === 0) {
|
||||||
const allMatches = jsonString.match(/"([^"]+)"/g);
|
const allMatches = jsonString.match(/"([^"]+)"/g);
|
||||||
if (allMatches) {
|
if (allMatches) {
|
||||||
@ -139,7 +135,6 @@ export class JSONParser {
|
|||||||
throw new Error(`JSON string too large (${jsonString.length} bytes, max ${maxSize})`);
|
throw new Error(`JSON string too large (${jsonString.length} bytes, max ${maxSize})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Security checks for potentially malicious content
|
|
||||||
const suspiciousPatterns = [
|
const suspiciousPatterns = [
|
||||||
/<script/i,
|
/<script/i,
|
||||||
/javascript:/i,
|
/javascript:/i,
|
||||||
@ -158,7 +153,6 @@ export class JSONParser {
|
|||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(jsonString);
|
const parsed = JSON.parse(jsonString);
|
||||||
|
|
||||||
// Validate basic structure
|
|
||||||
if (typeof parsed !== 'object' || parsed === null) {
|
if (typeof parsed !== 'object' || parsed === null) {
|
||||||
throw new Error('JSON must be an object');
|
throw new Error('JSON must be an object');
|
||||||
}
|
}
|
||||||
@ -182,11 +176,9 @@ export class JSONParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof obj === 'string') {
|
if (typeof obj === 'string') {
|
||||||
// Sanitize strings that might contain sensitive data
|
|
||||||
if (obj.length > 500) {
|
if (obj.length > 500) {
|
||||||
return obj.slice(0, 500) + '...[truncated]';
|
return obj.slice(0, 500) + '...[truncated]';
|
||||||
}
|
}
|
||||||
// Remove potential code injection patterns
|
|
||||||
return obj.replace(/<script[\s\S]*?<\/script>/gi, '[script removed]');
|
return obj.replace(/<script[\s\S]*?<\/script>/gi, '[script removed]');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +209,6 @@ export class JSONParser {
|
|||||||
|
|
||||||
const sanitized: any = {};
|
const sanitized: any = {};
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
// Skip potential dangerous properties
|
|
||||||
if (['__proto__', 'constructor', 'prototype'].includes(key)) {
|
if (['__proto__', 'constructor', 'prototype'].includes(key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -237,7 +228,6 @@ export class JSONParser {
|
|||||||
return { isValid: false, errors };
|
return { isValid: false, errors };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check required top-level properties
|
|
||||||
const requiredProps = ['metadata', 'recommendation', 'auditTrail'];
|
const requiredProps = ['metadata', 'recommendation', 'auditTrail'];
|
||||||
for (const prop of requiredProps) {
|
for (const prop of requiredProps) {
|
||||||
if (!(prop in data)) {
|
if (!(prop in data)) {
|
||||||
@ -245,7 +235,6 @@ export class JSONParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate metadata
|
|
||||||
if (data.metadata && typeof data.metadata === 'object') {
|
if (data.metadata && typeof data.metadata === 'object') {
|
||||||
const requiredMetadataProps = ['timestamp', 'version', 'userQuery', 'mode'];
|
const requiredMetadataProps = ['timestamp', 'version', 'userQuery', 'mode'];
|
||||||
for (const prop of requiredMetadataProps) {
|
for (const prop of requiredMetadataProps) {
|
||||||
@ -257,7 +246,6 @@ export class JSONParser {
|
|||||||
errors.push('Invalid metadata structure');
|
errors.push('Invalid metadata structure');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate audit trail
|
|
||||||
if (!Array.isArray(data.auditTrail)) {
|
if (!Array.isArray(data.auditTrail)) {
|
||||||
errors.push('auditTrail must be an array');
|
errors.push('auditTrail must be an array');
|
||||||
} else {
|
} else {
|
||||||
@ -293,7 +281,7 @@ export class JSONParser {
|
|||||||
metadata: {
|
metadata: {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
version: "1.0",
|
version: "1.0",
|
||||||
userQuery: userQuery.slice(0, 1000), // Truncate for security
|
userQuery: userQuery.slice(0, 1000),
|
||||||
mode,
|
mode,
|
||||||
exportedBy: 'ForensicPathways',
|
exportedBy: 'ForensicPathways',
|
||||||
toolsDataHash: additionalMetadata.toolsDataHash || 'unknown',
|
toolsDataHash: additionalMetadata.toolsDataHash || 'unknown',
|
||||||
@ -316,16 +304,13 @@ export class JSONParser {
|
|||||||
const issues: string[] = [];
|
const issues: string[] = [];
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
|
|
||||||
// Basic structure validation
|
|
||||||
const structureValidation = this.validateAuditExportStructure(data);
|
const structureValidation = this.validateAuditExportStructure(data);
|
||||||
if (!structureValidation.isValid) {
|
if (!structureValidation.isValid) {
|
||||||
issues.push(...structureValidation.errors);
|
issues.push(...structureValidation.errors);
|
||||||
return { isValid: false, issues, warnings };
|
return { isValid: false, issues, warnings };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional validation for uploaded content
|
|
||||||
if (data.metadata) {
|
if (data.metadata) {
|
||||||
// Check timestamp validity
|
|
||||||
const timestamp = new Date(data.metadata.timestamp);
|
const timestamp = new Date(data.metadata.timestamp);
|
||||||
if (isNaN(timestamp.getTime())) {
|
if (isNaN(timestamp.getTime())) {
|
||||||
warnings.push('Invalid timestamp in metadata');
|
warnings.push('Invalid timestamp in metadata');
|
||||||
@ -337,13 +322,11 @@ export class JSONParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate mode
|
|
||||||
if (!['workflow', 'tool'].includes(data.metadata.mode)) {
|
if (!['workflow', 'tool'].includes(data.metadata.mode)) {
|
||||||
warnings.push(`Unknown analysis mode: ${data.metadata.mode}`);
|
warnings.push(`Unknown analysis mode: ${data.metadata.mode}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate audit trail quality
|
|
||||||
if (Array.isArray(data.auditTrail)) {
|
if (Array.isArray(data.auditTrail)) {
|
||||||
const aiDecisions = data.auditTrail.filter(e => e.action === 'ai-decision').length;
|
const aiDecisions = data.auditTrail.filter(e => e.action === 'ai-decision').length;
|
||||||
const toolSelections = data.auditTrail.filter(e => e.action === 'selection-decision').length;
|
const toolSelections = data.auditTrail.filter(e => e.action === 'selection-decision').length;
|
||||||
@ -356,7 +339,6 @@ export class JSONParser {
|
|||||||
warnings.push('No tool selections found in audit trail');
|
warnings.push('No tool selections found in audit trail');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for confidence values
|
|
||||||
const entriesWithConfidence = data.auditTrail.filter(e => typeof e.confidence === 'number').length;
|
const entriesWithConfidence = data.auditTrail.filter(e => typeof e.confidence === 'number').length;
|
||||||
const confidenceRatio = entriesWithConfidence / data.auditTrail.length;
|
const confidenceRatio = entriesWithConfidence / data.auditTrail.length;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { aiService } from './aiService.js';
|
import { aiService } from './aiService.js';
|
||||||
import { embeddingsService, type SimilarityResult } from './embeddings.js';
|
import { embeddingsService, type SimilarityResult } from './embeddings.js';
|
||||||
import { confidenceScoring } from './confidenceScoring.js';
|
import { confidenceScoring } from './confidenceScoring.js';
|
||||||
import { JSONParser } from './jsonUtils.js'; // FIXED: Use centralized JSON parsing
|
import { JSONParser } from './jsonUtils.js';
|
||||||
import { getPrompt } from '../config/prompts.js';
|
import { getPrompt } from '../config/prompts.js';
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
|
|
||||||
@ -111,7 +111,6 @@ class ToolSelector {
|
|||||||
|
|
||||||
console.log('[TOOL-SELECTOR] Embeddings found', similarItems.length, 'similar items');
|
console.log('[TOOL-SELECTOR] Embeddings found', similarItems.length, 'similar items');
|
||||||
|
|
||||||
// Store similarities for confidence calculation
|
|
||||||
similarItems.forEach(item => {
|
similarItems.forEach(item => {
|
||||||
context.embeddingsSimilarities.set(item.name, item.similarity);
|
context.embeddingsSimilarities.set(item.name, item.similarity);
|
||||||
});
|
});
|
||||||
@ -238,7 +237,6 @@ class ToolSelector {
|
|||||||
const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.config.maxSelectedItems);
|
const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.config.maxSelectedItems);
|
||||||
const prompt = getPrompt('toolSelectionWithData', basePrompt, toolsToSend, conceptsToSend);
|
const prompt = getPrompt('toolSelectionWithData', basePrompt, toolsToSend, conceptsToSend);
|
||||||
|
|
||||||
// Validate prompt length
|
|
||||||
aiService.validatePromptLength(prompt);
|
aiService.validatePromptLength(prompt);
|
||||||
|
|
||||||
console.log('[TOOL-SELECTOR] Sending to AI:',
|
console.log('[TOOL-SELECTOR] Sending to AI:',
|
||||||
@ -249,7 +247,6 @@ class ToolSelector {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await aiService.callAI(prompt, { maxTokens: 2500 });
|
const response = await aiService.callAI(prompt, { maxTokens: 2500 });
|
||||||
// FIXED: Use centralized JSON parsing
|
|
||||||
const result = JSONParser.safeParseJSON(response.content, null);
|
const result = JSONParser.safeParseJSON(response.content, null);
|
||||||
|
|
||||||
if (!result || !Array.isArray(result.selectedTools) || !Array.isArray(result.selectedConcepts)) {
|
if (!result || !Array.isArray(result.selectedTools) || !Array.isArray(result.selectedConcepts)) {
|
||||||
@ -315,7 +312,6 @@ class ToolSelector {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await aiService.callMicroTaskAI(prompt, 1000);
|
const response = await aiService.callMicroTaskAI(prompt, 1000);
|
||||||
// FIXED: Use centralized JSON parsing
|
|
||||||
const selections = JSONParser.safeParseJSON(response.content, []);
|
const selections = JSONParser.safeParseJSON(response.content, []);
|
||||||
|
|
||||||
if (Array.isArray(selections)) {
|
if (Array.isArray(selections)) {
|
||||||
@ -368,8 +364,6 @@ class ToolSelector {
|
|||||||
related_software: concept.related_software || []
|
related_software: concept.related_software || []
|
||||||
});
|
});
|
||||||
|
|
||||||
// REMOVED: safeParseJSON method - now using centralized version from jsonUtils.ts
|
|
||||||
|
|
||||||
getConfig(): ToolSelectionConfig {
|
getConfig(): ToolSelectionConfig {
|
||||||
return { ...this.config };
|
return { ...this.config };
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user