fix tool mode ai pipiline logic
This commit is contained in:
		
							parent
							
								
									4ee1cc4984
								
							
						
					
					
						commit
						b14ca1d243
					
				@ -100,17 +100,11 @@ AI_SOFTWARE_SELECTION_RATIO=0.5  # 50% software tools (increase for more tool re
 | 
			
		||||
 | 
			
		||||
# AI selection limits
 | 
			
		||||
AI_MAX_SELECTED_ITEMS=25
 | 
			
		||||
AI_MAX_TOOLS_TO_ANALYZE=20
 | 
			
		||||
AI_MAX_CONCEPTS_TO_ANALYZE=10
 | 
			
		||||
 | 
			
		||||
# Efficiency thresholds
 | 
			
		||||
AI_EMBEDDINGS_MIN_TOOLS=8
 | 
			
		||||
AI_EMBEDDINGS_MAX_REDUCTION_RATIO=0.75
 | 
			
		||||
 | 
			
		||||
# Fallback limits when embeddings are disabled
 | 
			
		||||
AI_NO_EMBEDDINGS_TOOL_LIMIT=25
 | 
			
		||||
AI_NO_EMBEDDINGS_CONCEPT_LIMIT=10
 | 
			
		||||
 | 
			
		||||
# === Rate Limiting & Timing ===
 | 
			
		||||
AI_MICRO_TASK_TOTAL_LIMIT=30
 | 
			
		||||
AI_MICRO_TASK_DELAY_MS=500
 | 
			
		||||
 | 
			
		||||
@ -3,14 +3,16 @@
 | 
			
		||||
export const AI_PROMPTS = {
 | 
			
		||||
  
 | 
			
		||||
  toolSelection: (mode: string, userQuery: string, maxSelectedItems: number) => {
 | 
			
		||||
    const modeInstruction = mode === 'workflow' 
 | 
			
		||||
      ? 'Workflow mit 15-25 Items über alle Phasen. PFLICHT: Mindestens 40% Methoden, Rest Tools/Konzepte.'
 | 
			
		||||
      : 'Spezifische Lösung mit 4-10 Items. PFLICHT: Mindestens 30% Methoden wenn verfügbar.';
 | 
			
		||||
    const modeInstruction =
 | 
			
		||||
      mode === 'workflow'
 | 
			
		||||
        ? 'Workflow mit 15–25 Items über alle Phasen. PFLICHT: Mindestens 40% Methoden, Rest Tools/Konzepte.'
 | 
			
		||||
        : 'Spezifische Lösung mit 4–10 Items. PFLICHT: Mindestens 30% Methoden wenn verfügbar.';
 | 
			
		||||
 | 
			
		||||
    return `Du bist ein DFIR-Experte. Wähle die BESTEN Items aus dem vorgefilterten Set.
 | 
			
		||||
 | 
			
		||||
AUSWAHLMETHODE:
 | 
			
		||||
  '✓ Semantisch relevante Items bereits vorgefiltert\n✓ Wähle die BESTEN für die konkrete Aufgabe'}
 | 
			
		||||
  ✓ Semantisch relevante Items bereits vorgefiltert
 | 
			
		||||
  ✓ Wähle die BESTEN für die konkrete Aufgabe
 | 
			
		||||
 | 
			
		||||
${modeInstruction}
 | 
			
		||||
 | 
			
		||||
@ -22,28 +24,27 @@ VERFÜGBARE ITEM-TYPEN:
 | 
			
		||||
 | 
			
		||||
AUSWAHLSTRATEGIE:
 | 
			
		||||
1. **ERSTE PRIORITÄT: Relevanz zur Anfrage**
 | 
			
		||||
  - Direkt anwendbar auf das Problem
 | 
			
		||||
  - Löst die Kernherausforderung
 | 
			
		||||
   - Direkt anwendbar auf das Problem
 | 
			
		||||
   - Löst die Kernherausforderung
 | 
			
		||||
 | 
			
		||||
2. **ZWEITE PRIORITÄT: Ausgewogene Mischung**
 | 
			
		||||
  - Tools/Methoden für praktische Umsetzung → selectedTools
 | 
			
		||||
  - Konzepte für methodisches Verständnis → selectedConcepts
 | 
			
		||||
  - WICHTIG: Auch Konzepte auswählen, nicht nur Tools!
 | 
			
		||||
   - Tools/Methoden für praktische Umsetzung → selectedTools
 | 
			
		||||
   - Konzepte für methodisches Verständnis → selectedConcepts
 | 
			
		||||
   - WICHTIG: Auch Konzepte auswählen, nicht nur Tools!
 | 
			
		||||
 | 
			
		||||
3. **QUALITÄT > QUANTITÄT**
 | 
			
		||||
  - Lieber weniger perfekte Items als viele mittelmäßige
 | 
			
		||||
  - Jedes Item muss begründbar sein
 | 
			
		||||
   - Lieber weniger perfekte Items als viele mittelmäßige
 | 
			
		||||
   - 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%
 | 
			
		||||
4. **(Skalenharmonisierung – Info)**
 | 
			
		||||
   - Spätere Schritte vergeben "taskRelevance" nach EINHEITLICHER Skala:
 | 
			
		||||
     55–65 (Basis), 66–75 (gut), 76–85 (sehr gut), >85 nur bei perfekter Übereinstimmung.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
- Mindestens 1-2 Konzepte auswählen für methodische Fundierung
 | 
			
		||||
- Tools: 40% Methoden (type="method"), Rest Software (type="software")
 | 
			
		||||
- Mindestens 1–2 Konzepte auswählen für methodische Fundierung
 | 
			
		||||
- Tools: ca. 40% Methoden (type="method"), Rest Software (type="software"), wenn verfügbar
 | 
			
		||||
 | 
			
		||||
ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT:
 | 
			
		||||
{
 | 
			
		||||
@ -56,26 +57,26 @@ ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT:
 | 
			
		||||
  toolSelectionWithData: (basePrompt: string, toolsToSend: any[], conceptsToSend: any[]) => {
 | 
			
		||||
    return `${basePrompt}
 | 
			
		||||
 | 
			
		||||
VERFÜGBARE TOOLS (${toolsToSend.length} Items - Methoden und Software):
 | 
			
		||||
VERFÜGBARE TOOLS (${toolsToSend.length} Items – Methoden und Software):
 | 
			
		||||
${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)}
 | 
			
		||||
 | 
			
		||||
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`;
 | 
			
		||||
(Hinweis zur späteren Relevanz-Skala – EINHEITLICH ÜBER ALLE MODI)
 | 
			
		||||
- 55–65: Grundlegend relevant
 | 
			
		||||
- 66–75: Gut geeignet
 | 
			
		||||
- 76–85: Sehr gut geeignet
 | 
			
		||||
- >85 : NUR bei nahezu perfekter Übereinstimmung`;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  scenarioAnalysis: (isWorkflow: boolean, userQuery: string) => {
 | 
			
		||||
    const analysisType = isWorkflow ? 'Szenario' : 'Problem';
 | 
			
		||||
    const focus = isWorkflow ? 
 | 
			
		||||
      'Angriffsvektoren, betroffene Systeme, Zeitkritikalität' :
 | 
			
		||||
      'Kernherausforderung, verfügbare Daten, methodische Anforderungen';
 | 
			
		||||
    const focus = isWorkflow
 | 
			
		||||
      ? 'Angriffsvektoren, betroffene Systeme, Zeitkritikalität'
 | 
			
		||||
      : 'Kernherausforderung, verfügbare Daten, methodische Anforderungen';
 | 
			
		||||
 | 
			
		||||
    return `DFIR-Experte: Analysiere das ${analysisType}.
 | 
			
		||||
 | 
			
		||||
@ -88,9 +89,9 @@ Antwort: Fließtext ohne Listen, max 100 Wörter.`;
 | 
			
		||||
 | 
			
		||||
  investigationApproach: (isWorkflow: boolean, userQuery: string) => {
 | 
			
		||||
    const approachType = isWorkflow ? 'Untersuchungsansatz' : 'Lösungsansatz';
 | 
			
		||||
    const focus = isWorkflow ?
 | 
			
		||||
      'Triage-Prioritäten, Phasenabfolge, Kontaminationsvermeidung' :
 | 
			
		||||
      'Methodenauswahl, Validierung, Integration';
 | 
			
		||||
    const focus = isWorkflow
 | 
			
		||||
      ? 'Triage-Prioritäten, Phasenabfolge, Kontaminationsvermeidung'
 | 
			
		||||
      : 'Methodenauswahl, Validierung, Integration';
 | 
			
		||||
 | 
			
		||||
    return `Entwickle einen ${approachType}.
 | 
			
		||||
 | 
			
		||||
@ -102,9 +103,9 @@ Antwort: Fließtext ohne Listen, max 100 Wörter.`;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  criticalConsiderations: (isWorkflow: boolean, userQuery: string) => {
 | 
			
		||||
    const focus = isWorkflow ? 
 | 
			
		||||
      'Beweissicherung vs. Gründlichkeit, Chain of Custody' : 
 | 
			
		||||
      'Tool-Validierung, False Positives/Negatives, Qualifikationen';
 | 
			
		||||
    const focus = isWorkflow
 | 
			
		||||
      ? 'Beweissicherung vs. Gründlichkeit, Chain of Custody'
 | 
			
		||||
      : 'Tool-Validierung, False Positives/Negatives, Qualifikationen';
 | 
			
		||||
 | 
			
		||||
    return `Identifiziere kritische Überlegungen.
 | 
			
		||||
 | 
			
		||||
@ -118,12 +119,12 @@ Antwort: Fließtext ohne Listen, max 100 Wörter.`;
 | 
			
		||||
  phaseToolSelection: (userQuery: string, phase: any, phaseTools: any[]) => {
 | 
			
		||||
    const methods = phaseTools.filter(t => t.type === 'method');
 | 
			
		||||
    const tools = phaseTools.filter(t => t.type === 'software');
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    if (phaseTools.length === 0) {
 | 
			
		||||
      return `Keine Methoden/Tools für Phase "${phase.name}" verfügbar. Antworte mit leerem Array: []`;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return `Du bist ein DFIR-Experte. Wähle die 2-3 BESTEN Items für Phase "${phase.name}".
 | 
			
		||||
 | 
			
		||||
    return `Du bist ein DFIR-Experte. Wähle die 2–3 BESTEN Items für Phase "${phase.name}".
 | 
			
		||||
 | 
			
		||||
SZENARIO: "${userQuery}"
 | 
			
		||||
PHASE: ${phase.name} - ${phase.description || ''}
 | 
			
		||||
@ -131,7 +132,7 @@ PHASE: ${phase.name} - ${phase.description || ''}
 | 
			
		||||
VERFÜGBARE ITEMS (bereits von KI vorausgewählt):
 | 
			
		||||
${methods.length > 0 ? `
 | 
			
		||||
METHODEN (${methods.length}):
 | 
			
		||||
${methods.map((method: any) => 
 | 
			
		||||
${methods.map((method: any) =>
 | 
			
		||||
  `- ${method.name}
 | 
			
		||||
  Typ: ${method.type}
 | 
			
		||||
  Beschreibung: ${method.description}
 | 
			
		||||
@ -142,7 +143,7 @@ ${methods.map((method: any) =>
 | 
			
		||||
 | 
			
		||||
${tools.length > 0 ? `
 | 
			
		||||
SOFTWARE TOOLS (${tools.length}):
 | 
			
		||||
${tools.map((tool: any) => 
 | 
			
		||||
${tools.map((tool: any) =>
 | 
			
		||||
  `- ${tool.name}
 | 
			
		||||
  Typ: ${tool.type}
 | 
			
		||||
  Beschreibung: ${tool.description}
 | 
			
		||||
@ -152,16 +153,16 @@ ${tools.map((tool: any) =>
 | 
			
		||||
` : 'Keine Software-Tools verfügbar'}
 | 
			
		||||
 | 
			
		||||
AUSWAHLREGELN FÜR PHASE "${phase.name}":
 | 
			
		||||
1. Wähle die 2-3 BESTEN Items für diese spezifische Phase
 | 
			
		||||
1. Wähle die 2–3 BESTEN Items für diese spezifische Phase
 | 
			
		||||
2. Priorisiere Items, die DIREKT für "${phase.name}" relevant sind
 | 
			
		||||
3. Mindestens 1 Methode wenn verfügbar, Rest Software-Tools
 | 
			
		||||
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
 | 
			
		||||
TASK RELEVANCE – EINHEITLICHE SKALA (GANZZAHL 0–100):
 | 
			
		||||
- 55–65: Basis/ausreichend für diese Phase
 | 
			
		||||
- 66–75: Gut geeignet, klare Phasenrelevanz
 | 
			
		||||
- 76–85: Sehr gut geeignet, optimaler Fit
 | 
			
		||||
- >85 : Nur bei nahezu perfekter Übereinstimmung
 | 
			
		||||
 | 
			
		||||
WICHTIG: Verwende EXAKT die Namen wie oben aufgelistet (ohne Präfixe wie M1./T2.)!
 | 
			
		||||
 | 
			
		||||
@ -169,42 +170,50 @@ ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT OHNE JEGLICHEN TEXT AUSSERHALB:
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "toolName": "Exakter Name aus der Liste oben",
 | 
			
		||||
    "taskRelevance": 75,
 | 
			
		||||
    "justification": "Detaillierte Begründung (60-80 Wörter) warum optimal für ${phase.name} - erkläre Anwendung, Vorteile und spezifische Relevanz",
 | 
			
		||||
    "taskRelevance": 0,
 | 
			
		||||
    "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"]
 | 
			
		||||
  }
 | 
			
		||||
]`;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  toolEvaluation: (userQuery: string, tool: any, rank: number, taskRelevance: number) => {
 | 
			
		||||
  toolEvaluation: (userQuery: string, tool: any, rank: number) => {
 | 
			
		||||
    const itemType = tool.type === 'method' ? 'Methode' : 'Tool';
 | 
			
		||||
    
 | 
			
		||||
    return `Erkläre die Anwendung dieser/dieses ${itemType}.
 | 
			
		||||
 | 
			
		||||
    return `Erkläre die Anwendung dieser/dieses ${itemType} für die Aufgabe.
 | 
			
		||||
 | 
			
		||||
PROBLEM: "${userQuery}"
 | 
			
		||||
${itemType.toUpperCase()}: ${tool.name} (${taskRelevance}% Eignung)
 | 
			
		||||
${itemType.toUpperCase()}: ${tool.name}
 | 
			
		||||
TYP: ${tool.type}
 | 
			
		||||
 | 
			
		||||
Bereits als Rang ${rank} bewertet.
 | 
			
		||||
ANWEISUNGEN:
 | 
			
		||||
- Beurteile ausschließlich entlang der fachlichen Eignung zum PROBLEM.
 | 
			
		||||
- Nutze nur die dir vorliegenden Texte/Metadaten (keine externen Annahmen).
 | 
			
		||||
- Bestimme eine EIGENE ganzzahlige Bewertung "taskRelevance" von 0–100 (ohne %).
 | 
			
		||||
- Verwende die EINHEITLICHE SKALA:
 | 
			
		||||
  55–65 = Basis/ok · 66–75 = gut · 76–85 = sehr gut · >85 = nur bei nahezu perfekter Übereinstimmung.
 | 
			
		||||
- Bevorzuge realistische Scores im Bereich 60–80 für gute Fits.
 | 
			
		||||
 | 
			
		||||
Gib nur JSON im folgenden Schema zurück. Keine Texte davor oder danach.
 | 
			
		||||
 | 
			
		||||
ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT OHNE JEGLICHEN TEXT AUSSERHALB DER JSON-STRUKTUR:
 | 
			
		||||
{
 | 
			
		||||
  "detailed_explanation": "Warum und wie einsetzen",
 | 
			
		||||
  "implementation_approach": "Konkrete Schritte",
 | 
			
		||||
  "pros": ["Vorteil 1", "Vorteil 2"],
 | 
			
		||||
  "limitations": ["Einschränkung 1"],
 | 
			
		||||
  "alternatives": "Alternative Ansätze"
 | 
			
		||||
  "alternatives": "Alternative Ansätze",
 | 
			
		||||
  "taskRelevance": 0
 | 
			
		||||
}`;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  backgroundKnowledgeSelection: (userQuery: string, mode: string, selectedToolNames: string[], availableConcepts: any[]) => {
 | 
			
		||||
    return `Wähle 2-4 relevante Konzepte.
 | 
			
		||||
    return `Wähle 2–4 relevante Konzepte.
 | 
			
		||||
 | 
			
		||||
${mode === 'workflow' ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
			
		||||
AUSGEWÄHLTE TOOLS: ${selectedToolNames.join(', ')}
 | 
			
		||||
 | 
			
		||||
VERFÜGBARE KONZEPTE (${availableConcepts.length} KI-kuratiert):
 | 
			
		||||
${availableConcepts.map((c: any) => 
 | 
			
		||||
${availableConcepts.map((c: any) =>
 | 
			
		||||
  `- ${c.name}: ${c.description}...`
 | 
			
		||||
).join('\n')}
 | 
			
		||||
 | 
			
		||||
@ -253,7 +262,7 @@ Antwort: Prägnanter Fließtext, knappe Begründung für Nachergänzung. Vermeid
 | 
			
		||||
    candidateTools: any[],
 | 
			
		||||
    candidateConcepts: any[]
 | 
			
		||||
  ): string {
 | 
			
		||||
    return `Du bist ein DFIR-Experte. Die initiale KI-Auswahl war zu spezifisch - die Phase "${phase.name}" ist unterrepräsentiert.
 | 
			
		||||
    return `Du bist ein DFIR-Experte. Die initiale KI-Auswahl war zu spezifisch – die Phase "${phase.name}" ist unterrepräsentiert.
 | 
			
		||||
 | 
			
		||||
KONTEXT: Die Hauptauswahl hat zu wenige Tools für "${phase.name}" identifiziert. Wähle jetzt ergänzende Tools aus semantischer Nachsuche.
 | 
			
		||||
 | 
			
		||||
@ -278,10 +287,10 @@ ${candidateConcepts.map((concept: any) => `
 | 
			
		||||
` : ''}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
3. Ergänzen, nicht ersetzen - erweitere die zu spezifische Erstauswahl
 | 
			
		||||
4. Realistische Task Relevance (70-85% typisch für Nachergänzungen)
 | 
			
		||||
3. Ergänzen, nicht ersetzen – erweitere die zu spezifische Erstauswahl
 | 
			
		||||
4. Relevanz-Skala weiterhin EINHEITLICH: 55–65/66–75/76–85/>85 nur bei perfekter Übereinstimmung
 | 
			
		||||
 | 
			
		||||
ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT:
 | 
			
		||||
{
 | 
			
		||||
@ -292,9 +301,9 @@ ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT:
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  finalRecommendations: (isWorkflow: boolean, userQuery: string, selectedToolNames: string[]) => {
 | 
			
		||||
    const focus = isWorkflow ? 
 | 
			
		||||
      'Workflow-Schritte, Best Practices, Objektivität' :
 | 
			
		||||
      'Methodische Überlegungen, Validierung, Qualitätssicherung';
 | 
			
		||||
    const focus = isWorkflow
 | 
			
		||||
      ? 'Workflow-Schritte, Best Practices, Objektivität'
 | 
			
		||||
      : 'Methodische Überlegungen, Validierung, Qualitätssicherung';
 | 
			
		||||
 | 
			
		||||
    return `Erstelle ${isWorkflow ? 'Workflow-Empfehlung' : 'methodische Überlegungen'}.
 | 
			
		||||
 | 
			
		||||
@ -313,11 +322,12 @@ export function getPrompt(key: 'scenarioAnalysis', isWorkflow: boolean, userQuer
 | 
			
		||||
export function getPrompt(key: 'investigationApproach', isWorkflow: boolean, userQuery: string): string;
 | 
			
		||||
export function getPrompt(key: 'criticalConsiderations', isWorkflow: boolean, userQuery: string): string;
 | 
			
		||||
export function getPrompt(key: 'phaseToolSelection', userQuery: string, phase: any, phaseTools: any[]): string;
 | 
			
		||||
export function getPrompt(key: 'toolEvaluation', userQuery: string, tool: any, rank: number, taskRelevance: number): string;
 | 
			
		||||
export function getPrompt(key: 'toolEvaluation', userQuery: string, tool: any, rank: number): string;
 | 
			
		||||
export function getPrompt(key: 'backgroundKnowledgeSelection', userQuery: string, mode: string, selectedToolNames: string[], availableConcepts: any[]): string;
 | 
			
		||||
export function getPrompt(key: 'phaseCompletionReasoning', originalQuery: string, phase: any, selectedToolName: string, tool: any, completionContext: string): string;
 | 
			
		||||
export function getPrompt(key: 'finalRecommendations', isWorkflow: boolean, userQuery: string, selectedToolNames: string[]): string;
 | 
			
		||||
export function getPrompt(key: 'generatePhaseCompletionPrompt', originalQuery: string, phase: any, candidateTools: any[], candidateConcepts: any[]): string;
 | 
			
		||||
 | 
			
		||||
export function getPrompt(promptKey: keyof typeof AI_PROMPTS, ...args: any[]): string {
 | 
			
		||||
  try {
 | 
			
		||||
    const promptFunction = AI_PROMPTS[promptKey];
 | 
			
		||||
 | 
			
		||||
@ -470,15 +470,42 @@ class AIPipeline {
 | 
			
		||||
    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, toolsDataHash);
 | 
			
		||||
    // Evaluate ALL candidates handed over by the embeddings pre-filter.
 | 
			
		||||
    const candidates = context.filteredData.tools || [];
 | 
			
		||||
    if (!Array.isArray(candidates) || candidates.length === 0) {
 | 
			
		||||
      return { completed: completedTasks, failed: failedTasks };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Evaluate every candidate (no slicing here)
 | 
			
		||||
    for (let i = 0; i < candidates.length; i++) {
 | 
			
		||||
      const evaluationResult = await this.evaluateSpecificTool(context, candidates[i], i + 1, pipelineStart, toolsDataHash);
 | 
			
		||||
      if (evaluationResult.success) completedTasks++; else failedTasks++;
 | 
			
		||||
      this.trackTokenUsage(evaluationResult.aiUsage);
 | 
			
		||||
      await this.delay(this.config.microTaskDelay);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // At this point, context.selectedTools may contain 0..N evaluated items (added by evaluateSpecificTool).
 | 
			
		||||
    // Now we sort them by AI-derived taskRelevance (after moderation) and keep ONLY the top 3 for UI.
 | 
			
		||||
    if (Array.isArray(context.selectedTools) && context.selectedTools.length > 0) {
 | 
			
		||||
      context.selectedTools.sort((a: any, b: any) => {
 | 
			
		||||
        const ar = typeof a.taskRelevance === 'number' ? a.taskRelevance : -1;
 | 
			
		||||
        const br = typeof b.taskRelevance === 'number' ? b.taskRelevance : -1;
 | 
			
		||||
        if (br !== ar) return br - ar;
 | 
			
		||||
 | 
			
		||||
        // tie-breakers without domain heuristics:
 | 
			
		||||
        const aLen = (a.justification || '').length;
 | 
			
		||||
        const bLen = (b.justification || '').length;
 | 
			
		||||
        if (bLen !== aLen) return bLen - aLen;
 | 
			
		||||
 | 
			
		||||
        const aRank = a.tool?.evaluation?.rank ?? Number.MAX_SAFE_INTEGER;
 | 
			
		||||
        const bRank = b.tool?.evaluation?.rank ?? Number.MAX_SAFE_INTEGER;
 | 
			
		||||
        return aRank - bRank;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // Keep top 3 only
 | 
			
		||||
      context.selectedTools = context.selectedTools.slice(0, 3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return { completed: completedTasks, failed: failedTasks };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -849,68 +876,113 @@ class AIPipeline {
 | 
			
		||||
    toolsDataHash: string
 | 
			
		||||
  ): Promise<MicroTaskResult> {
 | 
			
		||||
    const taskStart = Date.now();
 | 
			
		||||
    const existingSelection = context.selectedTools?.find((st: any) => st.tool && st.tool.name === tool.name);
 | 
			
		||||
    const originalTaskRelevance = existingSelection?.taskRelevance || 70;
 | 
			
		||||
    const moderatedTaskRelevance = this.moderateTaskRelevance(originalTaskRelevance);
 | 
			
		||||
    const priority = this.derivePriorityFromScore(moderatedTaskRelevance);
 | 
			
		||||
    
 | 
			
		||||
    const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank, moderatedTaskRelevance);
 | 
			
		||||
 | 
			
		||||
    // Build prompt WITHOUT any baseline score
 | 
			
		||||
    const prompt = getPrompt('toolEvaluation', context.userQuery, tool, rank);
 | 
			
		||||
    const result = await this.callMicroTaskAI(prompt, context, 'tool-evaluation');
 | 
			
		||||
    
 | 
			
		||||
    if (result.success) {
 | 
			
		||||
      const evaluation = JSONParser.safeParseJSON(result.content, {
 | 
			
		||||
        detailed_explanation: 'Evaluation failed',
 | 
			
		||||
        implementation_approach: '',
 | 
			
		||||
        pros: [],
 | 
			
		||||
        limitations: [],
 | 
			
		||||
        alternatives: ''
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
      this.addToolToSelection(context, {
 | 
			
		||||
        ...tool,
 | 
			
		||||
        evaluation: {
 | 
			
		||||
          ...evaluation,
 | 
			
		||||
          rank,
 | 
			
		||||
          task_relevance: moderatedTaskRelevance
 | 
			
		||||
        }
 | 
			
		||||
      }, 'evaluation', priority, evaluation.detailed_explanation, moderatedTaskRelevance, evaluation.limitations);
 | 
			
		||||
      
 | 
			
		||||
      const responseConfidence = auditService.calculateAIResponseConfidence(
 | 
			
		||||
        result.content,
 | 
			
		||||
        { min: 200, max: 800 },
 | 
			
		||||
        'tool-evaluation'
 | 
			
		||||
      );
 | 
			
		||||
      
 | 
			
		||||
      const finalConfidence = Math.max(responseConfidence, moderatedTaskRelevance);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
    if (!result.success) {
 | 
			
		||||
      return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Parse strictly; do NOT provide a default with a score.
 | 
			
		||||
    const evaluation = JSONParser.safeParseJSON(result.content, null);
 | 
			
		||||
 | 
			
		||||
    // Require a numeric score produced by the model; otherwise, don't add this tool.
 | 
			
		||||
    const aiProvided = evaluation && typeof evaluation.taskRelevance === 'number' && Number.isFinite(evaluation.taskRelevance)
 | 
			
		||||
      ? Math.round(evaluation.taskRelevance)
 | 
			
		||||
      : null;
 | 
			
		||||
 | 
			
		||||
    if (aiProvided === null) {
 | 
			
		||||
      // Log the malformed output but avoid injecting a synthetic score.
 | 
			
		||||
      auditService.addAIDecision(
 | 
			
		||||
        'tool-evaluation',
 | 
			
		||||
        prompt,
 | 
			
		||||
        result.content,
 | 
			
		||||
        finalConfidence,
 | 
			
		||||
        `Bewertete Tool "${tool.name}" (Rang ${rank}) - Analysierte Eignung für spezifische Aufgabenstellung mit Fokus auf praktische Anwendbarkeit und methodische Integration`,
 | 
			
		||||
        0,
 | 
			
		||||
        `Bewertung für "${tool.name}" ignoriert: fehlender/ungültiger taskRelevance`,
 | 
			
		||||
        taskStart,
 | 
			
		||||
        {
 | 
			
		||||
          toolsDataHash: toolsDataHash,
 | 
			
		||||
          toolsDataHash,
 | 
			
		||||
          microTaskType: 'tool-evaluation',
 | 
			
		||||
          toolName: tool.name,
 | 
			
		||||
          toolType: tool.type,
 | 
			
		||||
          rank,
 | 
			
		||||
          originalTaskRelevance,
 | 
			
		||||
          moderatedTaskRelevance,
 | 
			
		||||
          responseConfidence,
 | 
			
		||||
          finalConfidence,
 | 
			
		||||
          moderationApplied: originalTaskRelevance !== moderatedTaskRelevance,
 | 
			
		||||
          evaluationParsed: !!evaluation.detailed_explanation,
 | 
			
		||||
          prosCount: evaluation.pros?.length || 0,
 | 
			
		||||
          limitationsCount: evaluation.limitations?.length || 0,
 | 
			
		||||
          evaluationParsed: false,
 | 
			
		||||
          decisionBasis: 'ai-analysis',
 | 
			
		||||
          aiModel: aiService.getConfig().model,
 | 
			
		||||
          ...result.aiUsage
 | 
			
		||||
          ...(result.aiUsage || {})
 | 
			
		||||
        }
 | 
			
		||||
      );
 | 
			
		||||
      return result;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    const moderatedTaskRelevance = this.moderateTaskRelevance(aiProvided);
 | 
			
		||||
    const priority = this.derivePriorityFromScore(moderatedTaskRelevance);
 | 
			
		||||
 | 
			
		||||
    // Keep original fields if present; coerce to strings/arrays safely.
 | 
			
		||||
    const detailed_explanation = String(evaluation?.detailed_explanation || '').trim();
 | 
			
		||||
    const implementation_approach = String(evaluation?.implementation_approach || '').trim();
 | 
			
		||||
    const pros = Array.isArray(evaluation?.pros) ? evaluation.pros : [];
 | 
			
		||||
    const limitations = Array.isArray(evaluation?.limitations) ? evaluation.limitations : [];
 | 
			
		||||
    const alternatives = String(evaluation?.alternatives || '').trim();
 | 
			
		||||
 | 
			
		||||
    this.addToolToSelection(
 | 
			
		||||
      context,
 | 
			
		||||
      {
 | 
			
		||||
        ...tool,
 | 
			
		||||
        evaluation: {
 | 
			
		||||
          detailed_explanation,
 | 
			
		||||
          implementation_approach,
 | 
			
		||||
          pros,
 | 
			
		||||
          limitations,
 | 
			
		||||
          alternatives,
 | 
			
		||||
          rank,
 | 
			
		||||
          task_relevance: moderatedTaskRelevance
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      'evaluation',
 | 
			
		||||
      priority,
 | 
			
		||||
      detailed_explanation,
 | 
			
		||||
      moderatedTaskRelevance,
 | 
			
		||||
      limitations
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const responseConfidence = auditService.calculateAIResponseConfidence(
 | 
			
		||||
      result.content,
 | 
			
		||||
      { min: 200, max: 800 },
 | 
			
		||||
      'tool-evaluation'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const finalConfidence = Math.max(responseConfidence, moderatedTaskRelevance);
 | 
			
		||||
 | 
			
		||||
    auditService.addAIDecision(
 | 
			
		||||
      'tool-evaluation',
 | 
			
		||||
      prompt,
 | 
			
		||||
      result.content,
 | 
			
		||||
      finalConfidence,
 | 
			
		||||
      `Bewertete Tool "${tool.name}" (Rang ${rank}) – AI-Score ${aiProvided}, moderiert ${moderatedTaskRelevance}`,
 | 
			
		||||
      taskStart,
 | 
			
		||||
      {
 | 
			
		||||
        toolsDataHash,
 | 
			
		||||
        microTaskType: 'tool-evaluation',
 | 
			
		||||
        toolName: tool.name,
 | 
			
		||||
        toolType: tool.type,
 | 
			
		||||
        rank,
 | 
			
		||||
        aiProvidedTaskRelevance: aiProvided,
 | 
			
		||||
        moderatedTaskRelevance,
 | 
			
		||||
        responseConfidence,
 | 
			
		||||
        finalConfidence,
 | 
			
		||||
        moderationApplied: aiProvided !== moderatedTaskRelevance,
 | 
			
		||||
        evaluationParsed: true,
 | 
			
		||||
        prosCount: pros.length,
 | 
			
		||||
        limitationsCount: limitations.length,
 | 
			
		||||
        decisionBasis: 'ai-analysis',
 | 
			
		||||
        aiModel: aiService.getConfig().model,
 | 
			
		||||
        ...(result.aiUsage || {})
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,14 +12,13 @@ export interface ToolSelectionConfig {
 | 
			
		||||
  similarityThreshold: number;
 | 
			
		||||
  embeddingSelectionLimit: number;
 | 
			
		||||
  embeddingConceptsLimit: number;
 | 
			
		||||
  noEmbeddingsToolLimit: number;
 | 
			
		||||
  noEmbeddingsConceptLimit: number;
 | 
			
		||||
  embeddingsMinTools: number;
 | 
			
		||||
  embeddingsMaxReductionRatio: number;
 | 
			
		||||
  methodSelectionRatio: number;
 | 
			
		||||
  softwareSelectionRatio: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export interface SelectionContext {
 | 
			
		||||
  userQuery: string;
 | 
			
		||||
  mode: string;
 | 
			
		||||
@ -51,14 +50,11 @@ class ToolSelector {
 | 
			
		||||
      similarityThreshold: this.getEnvFloat('AI_SIMILARITY_THRESHOLD', 0.3),
 | 
			
		||||
      embeddingSelectionLimit: this.getEnvInt('AI_EMBEDDING_SELECTION_LIMIT', 30),
 | 
			
		||||
      embeddingConceptsLimit: this.getEnvInt('AI_EMBEDDING_CONCEPTS_LIMIT', 15),
 | 
			
		||||
      noEmbeddingsToolLimit: this.getEnvInt('AI_NO_EMBEDDINGS_TOOL_LIMIT', 25),
 | 
			
		||||
      noEmbeddingsConceptLimit: this.getEnvInt('AI_NO_EMBEDDINGS_CONCEPT_LIMIT', 10),
 | 
			
		||||
      embeddingsMinTools: this.getEnvInt('AI_EMBEDDINGS_MIN_TOOLS', 8),
 | 
			
		||||
      embeddingsMaxReductionRatio: this.getEnvFloat('AI_EMBEDDINGS_MAX_REDUCTION_RATIO', 0.75),
 | 
			
		||||
      methodSelectionRatio: this.getEnvFloat('AI_METHOD_SELECTION_RATIO', 0.4),
 | 
			
		||||
      softwareSelectionRatio: this.getEnvFloat('AI_SOFTWARE_SELECTION_RATIO', 0.5)
 | 
			
		||||
      softwareSelectionRatio: this.getEnvFloat('AI_SOFTWARE_SELECTION_RATIO', 0.5),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    console.log('[TOOL-SELECTOR] Initialized with config:', this.config);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -185,43 +181,73 @@ class ToolSelector {
 | 
			
		||||
  ): Promise<ToolSelectionResult> {
 | 
			
		||||
    console.log('[TOOL-SELECTOR] Performing AI selection');
 | 
			
		||||
 | 
			
		||||
    const candidateMethods = candidateTools.filter((tool: any) => tool && tool.type === 'method');
 | 
			
		||||
    const candidateSoftware = candidateTools.filter((tool: any) => tool && tool.type === 'software');
 | 
			
		||||
    const candidateMethods = candidateTools.filter((t: any) => t && t.type === 'method');
 | 
			
		||||
    const candidateSoftware = candidateTools.filter((t: any) => t && t.type === 'software');
 | 
			
		||||
 | 
			
		||||
    console.log('[TOOL-SELECTOR] Candidates:', candidateMethods.length, 'methods,', candidateSoftware.length, 'software,', candidateConcepts.length, 'concepts');
 | 
			
		||||
    console.log('[TOOL-SELECTOR] Candidates:',
 | 
			
		||||
      candidateMethods.length, 'methods,',
 | 
			
		||||
      candidateSoftware.length, 'software,',
 | 
			
		||||
      candidateConcepts.length, 'concepts'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const methodsWithFullData = candidateMethods.map(this.createToolData);
 | 
			
		||||
    const softwareWithFullData = candidateSoftware.map(this.createToolData);
 | 
			
		||||
    const conceptsWithFullData = candidateConcepts.map(this.createConceptData);
 | 
			
		||||
 | 
			
		||||
    const maxTools = Math.min(this.config.embeddingSelectionLimit, this.config.noEmbeddingsToolLimit);
 | 
			
		||||
    const maxConcepts = Math.min(this.config.embeddingConceptsLimit, this.config.noEmbeddingsConceptLimit);
 | 
			
		||||
    const methodLimit = Math.ceil(maxTools * this.config.methodSelectionRatio);
 | 
			
		||||
    const softwareLimit = Math.floor(maxTools * this.config.softwareSelectionRatio);
 | 
			
		||||
    // Embeddings are always ON → only use embedding limits
 | 
			
		||||
    const maxTools = Math.min(this.config.embeddingSelectionLimit, candidateTools.length);
 | 
			
		||||
    const maxConcepts = Math.min(this.config.embeddingConceptsLimit, candidateConcepts.length);
 | 
			
		||||
 | 
			
		||||
    const toolsToSend: any[] = [
 | 
			
		||||
      ...methodsWithFullData.slice(0, methodLimit),
 | 
			
		||||
      ...softwareWithFullData.slice(0, softwareLimit),
 | 
			
		||||
    ];
 | 
			
		||||
    // Respect ratios first, then fill the remaining capacity
 | 
			
		||||
    const methodRatio = Math.max(0, Math.min(1, this.config.methodSelectionRatio));
 | 
			
		||||
    const softwareRatio = Math.max(0, Math.min(1, this.config.softwareSelectionRatio));
 | 
			
		||||
 | 
			
		||||
    const remainingCapacity = maxTools - toolsToSend.length;
 | 
			
		||||
    if (remainingCapacity > 0) {
 | 
			
		||||
      const extraMethods = methodsWithFullData.slice(methodLimit, methodLimit + remainingCapacity);
 | 
			
		||||
      const extraSoftware = softwareWithFullData.slice(softwareLimit, softwareLimit + (remainingCapacity - extraMethods.length));
 | 
			
		||||
      toolsToSend.push(...extraMethods, ...extraSoftware);
 | 
			
		||||
    let methodLimit = Math.round(maxTools * methodRatio);
 | 
			
		||||
    let softwareLimit = Math.round(maxTools * softwareRatio);
 | 
			
		||||
 | 
			
		||||
    // If rounded sum exceeds maxTools, scale down proportionally
 | 
			
		||||
    if (methodLimit + softwareLimit > maxTools) {
 | 
			
		||||
      const scale = maxTools / (methodLimit + softwareLimit);
 | 
			
		||||
      methodLimit = Math.floor(methodLimit * scale);
 | 
			
		||||
      softwareLimit = Math.floor(softwareLimit * scale);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const methodsPrimary = methodsWithFullData.slice(0, methodLimit);
 | 
			
		||||
    const softwarePrimary = softwareWithFullData.slice(0, softwareLimit);
 | 
			
		||||
 | 
			
		||||
    const toolsToSend: any[] = [...methodsPrimary, ...softwarePrimary];
 | 
			
		||||
 | 
			
		||||
    // Fill any remaining capacity from whichever pool still has candidates
 | 
			
		||||
    let mIdx = methodsPrimary.length;
 | 
			
		||||
    let sIdx = softwarePrimary.length;
 | 
			
		||||
 | 
			
		||||
    while (toolsToSend.length < maxTools && (mIdx < methodsWithFullData.length || sIdx < softwareWithFullData.length)) {
 | 
			
		||||
      const remM = methodsWithFullData.length - mIdx;
 | 
			
		||||
      const remS = softwareWithFullData.length - sIdx;
 | 
			
		||||
 | 
			
		||||
      if (remS >= remM && sIdx < softwareWithFullData.length) {
 | 
			
		||||
        toolsToSend.push(softwareWithFullData[sIdx++]);
 | 
			
		||||
      } else if (mIdx < methodsWithFullData.length) {
 | 
			
		||||
        toolsToSend.push(methodsWithFullData[mIdx++]);
 | 
			
		||||
      } else if (sIdx < softwareWithFullData.length) {
 | 
			
		||||
        toolsToSend.push(softwareWithFullData[sIdx++]);
 | 
			
		||||
      } else {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const conceptsToSend = conceptsWithFullData.slice(0, maxConcepts);
 | 
			
		||||
 | 
			
		||||
    const basePrompt = getPrompt('toolSelection', mode, userQuery, this.config.maxSelectedItems);
 | 
			
		||||
    const prompt = getPrompt('toolSelectionWithData', basePrompt, toolsToSend, conceptsToSend);
 | 
			
		||||
 | 
			
		||||
    console.log('[TOOL-SELECTOR-DEBUG] maxTools:', maxTools, 'maxConcepts:', maxConcepts);
 | 
			
		||||
    console.log('[TOOL-SELECTOR] Sending to AI:',
 | 
			
		||||
      toolsToSend.filter((t: any) => t.type === 'method').length, 'methods,',
 | 
			
		||||
      toolsToSend.filter((t: any) => t.type === 'software').length, 'software,',
 | 
			
		||||
      conceptsToSend.length, 'concepts'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const basePrompt = getPrompt('toolSelection', mode, userQuery, this.config.maxSelectedItems);
 | 
			
		||||
    const prompt = getPrompt('toolSelectionWithData', basePrompt, toolsToSend, conceptsToSend);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const response = await aiService.callAI(prompt);
 | 
			
		||||
      const result = JSONParser.safeParseJSON(response.content, null);
 | 
			
		||||
@ -250,7 +276,11 @@ class ToolSelector {
 | 
			
		||||
      const selectedMethods = selectedTools.filter((t: any) => t && t.type === 'method');
 | 
			
		||||
      const selectedSoftware = selectedTools.filter((t: any) => t && t.type === 'software');
 | 
			
		||||
 | 
			
		||||
      console.log('[TOOL-SELECTOR] AI selected:', selectedMethods.length, 'methods,', selectedSoftware.length, 'software,', selectedConcepts.length, 'concepts');
 | 
			
		||||
      console.log('[TOOL-SELECTOR] AI selected:',
 | 
			
		||||
        selectedMethods.length, 'methods,',
 | 
			
		||||
        selectedSoftware.length, 'software,',
 | 
			
		||||
        selectedConcepts.length, 'concepts'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const confidence = confidenceScoring.calculateSelectionConfidence(
 | 
			
		||||
        result,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user