pipeline overhaul
This commit is contained in:
		
							parent
							
								
									d5a6fe7dec
								
							
						
					
					
						commit
						138a494730
					
				
							
								
								
									
										16
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								.env.example
									
									
									
									
									
								
							@ -80,6 +80,14 @@ AI_SIMILARITY_THRESHOLD=0.3
 | 
				
			|||||||
AI_EMBEDDING_SELECTION_LIMIT=30
 | 
					AI_EMBEDDING_SELECTION_LIMIT=30
 | 
				
			||||||
AI_EMBEDDING_CONCEPTS_LIMIT=15
 | 
					AI_EMBEDDING_CONCEPTS_LIMIT=15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# === METHOD/TOOL BALANCE CONFIGURATION ===
 | 
				
			||||||
 | 
					# Controls the ratio of methods vs software tools sent to AI
 | 
				
			||||||
 | 
					# Methods = procedural guidance, best practices, workflows
 | 
				
			||||||
 | 
					# Software = actual tools and applications
 | 
				
			||||||
 | 
					# Values should sum to less than 1.0 (remainder is buffer)
 | 
				
			||||||
 | 
					AI_METHOD_SELECTION_RATIO=0.4    # 40% methods (increase for more procedural guidance)
 | 
				
			||||||
 | 
					AI_SOFTWARE_SELECTION_RATIO=0.5  # 50% software tools (increase for more tool recommendations)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# AI selection limits
 | 
					# AI selection limits
 | 
				
			||||||
AI_MAX_SELECTED_ITEMS=25
 | 
					AI_MAX_SELECTED_ITEMS=25
 | 
				
			||||||
AI_MAX_TOOLS_TO_ANALYZE=20
 | 
					AI_MAX_TOOLS_TO_ANALYZE=20
 | 
				
			||||||
@ -155,6 +163,14 @@ CONFIDENCE_HIGH_THRESHOLD=80
 | 
				
			|||||||
# AI_MAX_TOOLS_TO_ANALYZE=10
 | 
					# AI_MAX_TOOLS_TO_ANALYZE=10
 | 
				
			||||||
# AI_EMBEDDINGS_ENABLED=false
 | 
					# AI_EMBEDDINGS_ENABLED=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 🔬 METHOD-FOCUSED (more procedural guidance, less tools):
 | 
				
			||||||
 | 
					# AI_METHOD_SELECTION_RATIO=0.6
 | 
				
			||||||
 | 
					# AI_SOFTWARE_SELECTION_RATIO=0.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 🛠️ TOOL-FOCUSED (more software recommendations, less methods):
 | 
				
			||||||
 | 
					# AI_METHOD_SELECTION_RATIO=0.2
 | 
				
			||||||
 | 
					# AI_SOFTWARE_SELECTION_RATIO=0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ============================================================================
 | 
					# ============================================================================
 | 
				
			||||||
# 🌐 AI SERVICE EXAMPLES
 | 
					# 🌐 AI SERVICE EXAMPLES
 | 
				
			||||||
# ============================================================================
 | 
					# ============================================================================
 | 
				
			||||||
 | 
				
			|||||||
@ -1,212 +1,207 @@
 | 
				
			|||||||
// src/config/prompts.ts
 | 
					// src/config/prompts.ts - Centralized German prompts for AI pipeline
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const AI_PROMPTS = {
 | 
					export const AI_PROMPTS = {
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  toolSelection: (mode: string, userQuery: string, selectionMethod: string, maxSelectedItems: number) => {
 | 
					  toolSelection: (mode: string, userQuery: string, selectionMethod: string, maxSelectedItems: number) => {
 | 
				
			||||||
    const modeInstruction = mode === 'workflow' 
 | 
					    const modeInstruction = mode === 'workflow' 
 | 
				
			||||||
      ? 'Der Benutzer möchte einen UMFASSENDEN WORKFLOW über mehrere Phasen. Wählen Sie 15–25 Elemente, die den Zyklus Datensammlung → Auswertung → Analyse → Bericht abdecken.'
 | 
					      ? 'Workflow mit 15-25 Items über alle Phasen. PFLICHT: Mindestens 40% Methoden, Rest Tools/Konzepte.'
 | 
				
			||||||
      : 'Der Benutzer möchte SPEZIFISCHE LÖSUNGEN. Wählen Sie 4–10 Elemente, die das Problem direkt adressieren.';
 | 
					      : 'Spezifische Lösung mit 4-10 Items. PFLICHT: Mindestens 30% Methoden wenn verfügbar.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `Sie sind DFIR-Experte mit Zugriff auf eine vollständige Datenbank aus Methoden (type: "method") und Tools (type: "software"/"os"). Ihre Aufgabe ist es, die relevantesten Elemente für die Anfrage auszuwählen.
 | 
					    return `Du bist ein DFIR-Experte. Wähle die BESTEN Items aus dem vorgefilterten Set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUSWAHLMETHODE: ${selectionMethod}
 | 
					AUSWAHLMETHODE: ${selectionMethod}
 | 
				
			||||||
${selectionMethod === 'embeddings_candidates' ? 
 | 
					${selectionMethod === 'embeddings_candidates' ? 
 | 
				
			||||||
  'Die angezeigten Kandidaten wurden bereits semantisch vorgefiltert. Wählen Sie daraus die BESTEN.' :
 | 
					  '✓ Semantisch relevante Items bereits vorgefiltert\n✓ Wähle die BESTEN für die konkrete Aufgabe' :
 | 
				
			||||||
  'Sie sehen die vollständige Datenbasis. Wählen Sie die relevantesten Elemente.'}
 | 
					  '✓ Vollständige Datenbank verfügbar\n✓ Wähle die relevantesten Items'}
 | 
				
			||||||
 | 
					 | 
				
			||||||
WICHTIG – MISCHUNG ERZWINGEN:
 | 
					 | 
				
			||||||
- Der finale Vorschlag MUSS eine Mischung aus Methoden UND Tools enthalten (sofern verfügbar).
 | 
					 | 
				
			||||||
- Ziel: grob 40–60 % Methoden und 40–60 % Tools. Wenn eine Kategorie knapp ist, wählen Sie so ausgewogen wie möglich. Begründen Sie Abweichungen im reasoning.
 | 
					 | 
				
			||||||
- Bezeichnen Sie Elemente konsequent als "Methode" oder "Tool" je nach Typ.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
${modeInstruction}
 | 
					${modeInstruction}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BENUTZER-ANFRAGE: "${userQuery}"
 | 
					ANFRAGE: "${userQuery}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
KRITISCHE AUSWAHLPRINZIPIEN:
 | 
					VERFÜGBARE ITEM-TYPEN:
 | 
				
			||||||
1) KONTEXT > POPULARITÄT: Nicht automatisch zu populären Tools greifen. Wählen Sie strikt szenariobezogen.
 | 
					- TOOLS (type: "software"/"method") → praktische Anwendungen und Vorgehensweisen  
 | 
				
			||||||
2) METHODIK vs. SOFTWARE:
 | 
					- KONZEPTE (type: "concept") → theoretisches Wissen und Methodiken
 | 
				
			||||||
   - Dringend/Triage → Methoden für schnelle Reaktion priorisieren.
 | 
					 | 
				
			||||||
   - Zeitkritisch → Triage-Methoden vor Tiefenanalyse-Tools.
 | 
					 | 
				
			||||||
   - Umfassende Analyse → dann spezialisierte Tools ergänzen.
 | 
					 | 
				
			||||||
3) SPEZIFITÄT:
 | 
					 | 
				
			||||||
   - ICS/SCADA → spezialisierte ICS-Methoden/-Tools statt generischer Netzwerk-Tools.
 | 
					 | 
				
			||||||
   - Mobile (Android/iOS) → mobile-spezifische Methoden/Tools.
 | 
					 | 
				
			||||||
   - Speicheranalyse dringend → schnelle Memory-Methoden/Tools vor Vollanalyse.
 | 
					 | 
				
			||||||
4) LESEN SIE DIE VOLLBESCHREIBUNG UND METADATEN (Tags, Plattformen, Phasen, Lizenz, Access).
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
UI-KÜRZE:
 | 
					AUSWAHLSTRATEGIE:
 | 
				
			||||||
- Denken Sie mit: Für das UI sind kurze, prägnante Beschreibungen nötig. Bevorzugen Sie Elemente, die sich knapp zusammenfassen lassen. Formulieren Sie Begründungen fokussiert und ohne Floskeln.
 | 
					1. **ERSTE PRIORITÄT: Relevanz zur Anfrage**
 | 
				
			||||||
 | 
					  - Direkt anwendbar auf das Problem
 | 
				
			||||||
 | 
					  - Löst die Kernherausforderung
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Wählen Sie die relevantesten Elemente (max ${maxSelectedItems} gesamt).
 | 
					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!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ANTWORTFORMAT (JSON, SCHEMA UNVERÄNDERT):
 | 
					3. **QUALITÄT > QUANTITÄT**
 | 
				
			||||||
 | 
					  - Lieber weniger perfekte Items als viele mittelmäßige
 | 
				
			||||||
 | 
					  - Jedes Item muss begründbar sein
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AUSWAHLREGELN:
 | 
				
			||||||
 | 
					- 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")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT:
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "selectedTools": ["Name 1", "Name 2", ...],
 | 
					  "selectedTools": ["ToolName1", "MethodName1", ...],
 | 
				
			||||||
  "selectedConcepts": ["Konzept 1", "Konzept 2", ...],
 | 
					  "selectedConcepts": ["ConceptName1", "ConceptName2", ...],
 | 
				
			||||||
  "reasoning": "Begründen Sie die Auswahl, die erzwungene Mischung Methode/Tool und warum bestimmte populäre Elemente nicht geeignet sind. Halten Sie die Sprache präzise und UI-tauglich."
 | 
					  "reasoning": "Kurze Begründung mit Erwähnung der Tool/Konzept-Balance"
 | 
				
			||||||
}`;
 | 
					}`;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  scenarioAnalysis: (isWorkflow: boolean, userQuery: string) => {
 | 
					  scenarioAnalysis: (isWorkflow: boolean, userQuery: string) => {
 | 
				
			||||||
    const analysisType = isWorkflow ? 'Untersuchungsszenario' : 'technische Problemstellung';
 | 
					    const analysisType = isWorkflow ? 'Szenario' : 'Problem';
 | 
				
			||||||
    const considerations = isWorkflow ? 
 | 
					    const focus = isWorkflow ? 
 | 
				
			||||||
      `- Angriffsvektoren (MITRE ATT&CK) und Bedrohungsmodell
 | 
					      'Angriffsvektoren, betroffene Systeme, Zeitkritikalität' :
 | 
				
			||||||
- Betroffene Systeme/Assets und Kritikalität
 | 
					      'Kernherausforderung, verfügbare Daten, methodische Anforderungen';
 | 
				
			||||||
- Zeitdruck, Beweiserhalt, Chain of Custody
 | 
					 | 
				
			||||||
- Relevante Artefakte und Datenquellen` :
 | 
					 | 
				
			||||||
      `- Konkrete forensische Hürden
 | 
					 | 
				
			||||||
- Verfügbare Datenquellen und Integrität
 | 
					 | 
				
			||||||
- Anforderungen für rechtssichere Auswertung`;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `Analysieren Sie das ${analysisType} prägnant in einem kurzen Fließtext (max. 220 Wörter).
 | 
					    return `DFIR-Experte: Analysiere das ${analysisType}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
${isWorkflow ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
					${isWorkflow ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Berücksichtigen:
 | 
					Fokus: ${focus}
 | 
				
			||||||
${considerations}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Stil: Deutsch, ohne Listen/Markdown; klare, knappe Formulierungen, aber nicht telegrafisch (vollständige Sätze).`;
 | 
					Antwort: Fließtext ohne Listen, max 100 Wörter.`;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  investigationApproach: (isWorkflow: boolean, userQuery: string) => {
 | 
					  investigationApproach: (isWorkflow: boolean, userQuery: string) => {
 | 
				
			||||||
    const approachType = isWorkflow ? 'Untersuchungsansatz' : 'Lösungsansatz';
 | 
					    const approachType = isWorkflow ? 'Untersuchungsansatz' : 'Lösungsansatz';
 | 
				
			||||||
    const considerations = isWorkflow ?
 | 
					    const focus = isWorkflow ?
 | 
				
			||||||
      `- Triage-Prioritäten nach forensischer Dringlichkeit
 | 
					      'Triage-Prioritäten, Phasenabfolge, Kontaminationsvermeidung' :
 | 
				
			||||||
- Phasenabfolge: Datensammlung → Auswertung → Analyse → Bericht
 | 
					      'Methodenauswahl, Validierung, Integration';
 | 
				
			||||||
- Kontaminationsvermeidung/Isolierung
 | 
					 | 
				
			||||||
- Objektivität und Nachvollziehbarkeit` :
 | 
					 | 
				
			||||||
      `- Methodenwahl nach wissenschaftlichen Kriterien
 | 
					 | 
				
			||||||
- Validierung/Verifizierung der Vorgehensweise
 | 
					 | 
				
			||||||
- Integration in bestehende DFIR-Workflows
 | 
					 | 
				
			||||||
- Reproduzierbarkeit und Transparenz`;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `Formulieren Sie einen knappen ${approachType} (max. 220 Wörter), der explizit eine Mischung aus Methoden und Tools vorsieht.
 | 
					    return `Entwickle einen ${approachType}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
${isWorkflow ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
					${isWorkflow ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Berücksichtigen:
 | 
					Fokus: ${focus}
 | 
				
			||||||
${considerations}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Stil: Deutsch, ohne Listen/Markdown, UI-tauglich und fokussiert.`;
 | 
					Antwort: Fließtext ohne Listen, max 100 Wörter.`;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  criticalConsiderations: (isWorkflow: boolean, userQuery: string) => {
 | 
					  criticalConsiderations: (isWorkflow: boolean, userQuery: string) => {
 | 
				
			||||||
    const considerationType = isWorkflow ? 'kritische forensische Überlegungen' : 'methodische Voraussetzungen';
 | 
					    const focus = isWorkflow ? 
 | 
				
			||||||
    const aspects = isWorkflow ?
 | 
					      'Beweissicherung vs. Gründlichkeit, Chain of Custody' : 
 | 
				
			||||||
      `- Beweissicherung vs. Gründlichkeit (Zeitdruck)
 | 
					      'Tool-Validierung, False Positives/Negatives, Qualifikationen';
 | 
				
			||||||
- Chain of Custody und rechtliche Verwertbarkeit
 | 
					 | 
				
			||||||
- Transparenz, Reproduzierbarkeit, Dokumentationspflichten` :
 | 
					 | 
				
			||||||
      `- Validierung/Nachvollziehbarkeit der Methode/Tools
 | 
					 | 
				
			||||||
- Risiken für False Positives/Negatives
 | 
					 | 
				
			||||||
- Qualifikationsanforderungen und Reporting-Standards`;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `Identifizieren Sie ${considerationType} in einem kurzen Fließtext (max. 220 Wörter) und achten Sie auf UI-Kürze.
 | 
					    return `Identifiziere kritische Überlegungen.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
${isWorkflow ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
					${isWorkflow ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Aspekte:
 | 
					Fokus: ${focus}
 | 
				
			||||||
${aspects}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Stil: Deutsch, ohne Listen/Markdown.`;
 | 
					Antwort: Fließtext ohne Listen, max 100 Wörter.`;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  phaseToolSelection: (userQuery: string, phase: any, phaseTools: any[]) => {
 | 
					  phaseToolSelection: (userQuery: string, phase: any, phaseTools: any[]) => {
 | 
				
			||||||
    // In der Liste bewusst kurze Ausschnitte und klare Typ-Bezeichnung (Methode/Tool).
 | 
					    const methods = phaseTools.filter(t => t.type === 'method');
 | 
				
			||||||
    const items = phaseTools.map((tool: any, index: number) => {
 | 
					    const tools = phaseTools.filter(t => t.type === 'software');
 | 
				
			||||||
      const typ = tool.type === 'method' ? 'Methode' : 'Tool';
 | 
					    
 | 
				
			||||||
      const desc = (tool.description || '').replace(/\s+/g, ' ').trim().slice(0, 320);
 | 
					    if (phaseTools.length === 0) {
 | 
				
			||||||
      return `${index + 1}. [${typ}] ${tool.name}: ${desc}…\n  - Plattformen: ${tool.platforms?.join(', ') || 'N/A'}\n  - Skill Level: ${tool.skillLevel}\n  - Tags: ${tool.tags?.join(', ') || 'N/A'}`;
 | 
					      return `Keine Tools für Phase "${phase.name}" verfügbar. Antworte mit leerem Array: []`;
 | 
				
			||||||
    }).join('\n\n');
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    return `Wählen Sie 2–3 Elemente für die Phase "${phase.name}" und bewerten Sie diese VERGLEICHEND. Erzwingen Sie eine Mischung aus mindestens einer Methode und einem Tool, sofern verfügbar.
 | 
					    return `Du bist ein DFIR-Experte. Wähle die 2-3 BESTEN Items für Phase "${phase.name}".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SZENARIO: "${userQuery}"
 | 
					SZENARIO: "${userQuery}"
 | 
				
			||||||
PHASE: ${phase.name} – ${phase.description || 'Forensische Untersuchungsphase'}
 | 
					PHASE: ${phase.name} - ${phase.description || ''}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERFÜGBARE ELEMENTE FÜR ${phase.name.toUpperCase()}:
 | 
					VERFÜGBARE ITEMS (bereits von KI vorausgewählt):
 | 
				
			||||||
${items}
 | 
					${methods.length > 0 ? `
 | 
				
			||||||
 | 
					METHODEN (${methods.length}):
 | 
				
			||||||
 | 
					${methods.map((method: any) => 
 | 
				
			||||||
 | 
					  `- ${method.name}
 | 
				
			||||||
 | 
					  Typ: ${method.type}
 | 
				
			||||||
 | 
					  Beschreibung: ${method.description.slice(0, 150)}...
 | 
				
			||||||
 | 
					  Domains: ${method.domains?.join(', ') || 'N/A'}
 | 
				
			||||||
 | 
					  Skill Level: ${method.skillLevel}`
 | 
				
			||||||
 | 
					).join('\n\n')}
 | 
				
			||||||
 | 
					` : 'Keine Methoden verfügbar'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BEWERTUNGSKRITERIEN:
 | 
					${tools.length > 0 ? `
 | 
				
			||||||
- Eignung für das konkrete Szenario und GENAU diese Phase
 | 
					SOFTWARE TOOLS (${tools.length}):
 | 
				
			||||||
- Vergleich zu den Alternativen in der Liste
 | 
					${tools.map((tool: any) => 
 | 
				
			||||||
- Praktikabilität (Zeit, Setup, Reproduzierbarkeit)
 | 
					  `- ${tool.name}
 | 
				
			||||||
 | 
					  Typ: ${tool.type}
 | 
				
			||||||
 | 
					  Beschreibung: ${tool.description.slice(0, 150)}...
 | 
				
			||||||
 | 
					  Plattformen: ${tool.platforms?.join(', ') || 'N/A'}
 | 
				
			||||||
 | 
					  Skill Level: ${tool.skillLevel}`
 | 
				
			||||||
 | 
					).join('\n\n')}
 | 
				
			||||||
 | 
					` : 'Keine Software-Tools verfügbar'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
UI-KÜRZE:
 | 
					AUSWAHLREGELN FÜR PHASE "${phase.name}":
 | 
				
			||||||
- Beginnen Sie in "justification" mit einer 2–4-Satz Kurzfassung (≈300–360 Zeichen), danach 2–3 kurze, präzise Sätze mit Details.
 | 
					1. Wähle die 2-3 BESTEN Items für diese spezifische Phase
 | 
				
			||||||
- Bezeichnen Sie Elemente als "Methode" oder "Tool".
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ANTWORT NUR ALS JSON (Schema UNVERÄNDERT):
 | 
					WICHTIG: Verwende EXAKT die Namen wie oben aufgelistet (ohne Präfixe wie M1./T2.)!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT OHNE JEGLICHEN TEXT AUSSERHALB:
 | 
				
			||||||
[
 | 
					[
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "toolName": "Exakter Methoden/Tool-Name",
 | 
					    "toolName": "Exakter Name aus der Liste oben",
 | 
				
			||||||
    "taskRelevance": 0–100,
 | 
					    "taskRelevance": 85,
 | 
				
			||||||
    "justification": "Kurzfassung zuerst (≈300–360 Zeichen). Danach knappe, vergleichende Begründung, warum dieses Element in dieser Phase besser geeignet ist.",
 | 
					    "justification": "Spezifische Begründung warum optimal für ${phase.name}",
 | 
				
			||||||
    "limitations": ["Konkrete Einschränkung 1", "Einschränkung 2"]
 | 
					    "limitations": ["Mögliche Einschränkung für diese Phase"]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
]
 | 
					]`;
 | 
				
			||||||
 | 
					 | 
				
			||||||
Wählen Sie nur die 2–3 BESTEN Elemente und achten Sie auf die Mischung Methode+Tool.`;
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					 | 
				
			||||||
  toolEvaluation: (userQuery: string, tool: any, rank: number, taskRelevance: number) => {
 | 
					  toolEvaluation: (userQuery: string, tool: any, rank: number, taskRelevance: number) => {
 | 
				
			||||||
    const typ = tool.type === 'method' ? 'Methode' : 'Tool';
 | 
					    const itemType = tool.type === 'method' ? 'Methode' : 'Tool';
 | 
				
			||||||
    return `Sie sind DFIR-Experte. Erklären Sie DETAILLIERT die Anwendung dieses bereits bewerteten Elements.
 | 
					    
 | 
				
			||||||
 | 
					    return `Erkläre die Anwendung dieser/dieses ${itemType}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PROBLEM: "${userQuery}"
 | 
					PROBLEM: "${userQuery}"
 | 
				
			||||||
ELEMENT: ${typ} ${tool.name} (Aufgaben-Eignung ${taskRelevance}%)
 | 
					${itemType.toUpperCase()}: ${tool.name} (${taskRelevance}% Eignung)
 | 
				
			||||||
BESCHREIBUNG: ${(tool.description || '').replace(/\s+/g, ' ').trim()}
 | 
					TYP: ${tool.type}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ANTWORT AUSSCHLIESSLICH ALS JSON (Schema UNVERÄNDERT):
 | 
					Bereits als Rang ${rank} bewertet.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT OHNE JEGLICHEN TEXT AUSSERHALB DER JSON-STRUKTUR:
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "detailed_explanation": "Beginnen Sie mit einer prägnanten Kurzfassung (≈300–360 Zeichen) für das UI: 'Kurz: …'. Danach eine verdichtete Hauptpassage (≈80–140 Wörter) mit präziser, technisch korrekter Erläuterung, warum und wie diese ${typ.toLowerCase()} für dieses Problem eingesetzt wird.",
 | 
					  "detailed_explanation": "Warum und wie einsetzen",
 | 
				
			||||||
  "implementation_approach": "Konkrete, reproduzierbare Schrittfolge. Kurze Schritte, kein Ausschweifen.",
 | 
					  "implementation_approach": "Konkrete Schritte",
 | 
				
			||||||
  "pros": ["Spezifischer Vorteil 1", "Vorteil 2"],
 | 
					  "pros": ["Vorteil 1", "Vorteil 2"],
 | 
				
			||||||
  "limitations": ["Konkrete Einschränkung 1", "Einschränkung 2"],
 | 
					  "limitations": ["Einschränkung 1"],
 | 
				
			||||||
  "alternatives": "Knappe Alternativen, falls dieses Element nicht verfügbar ist (Methoden und Tools anführen)."
 | 
					  "alternatives": "Alternative Ansätze"
 | 
				
			||||||
}
 | 
					}`;
 | 
				
			||||||
 | 
					 | 
				
			||||||
WICHTIG:
 | 
					 | 
				
			||||||
- Keine erneute Bewertung – nur Erklärung/Anwendung.
 | 
					 | 
				
			||||||
- Behalten Sie die korrekte Bezeichnung (Methode/Tool) bei.
 | 
					 | 
				
			||||||
- UI-Kürze: Kurzfassung am Anfang der detailed_explanation.`;
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  backgroundKnowledgeSelection: (userQuery: string, mode: string, selectedToolNames: string[], availableConcepts: any[]) => {
 | 
					  backgroundKnowledgeSelection: (userQuery: string, mode: string, selectedToolNames: string[], availableConcepts: any[]) => {
 | 
				
			||||||
    const conceptsList = availableConcepts.slice(0, 15).map((concept: any) => `- ${concept.name}: ${(concept.description || '').replace(/\s+/g, ' ').trim().slice(0, 80)}…`).join('\n');
 | 
					    return `Wähle 2-4 relevante Konzepte.
 | 
				
			||||||
    return `Wählen Sie 2–4 forensische Konzepte, die für die Anwendung der empfohlenen Elemente (Methoden + Tools) wesentlich sind.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
${mode === 'workflow' ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
					${mode === 'workflow' ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
				
			||||||
EMPFOHLENE ELEMENTE: ${selectedToolNames.join(', ')}
 | 
					AUSGEWÄHLTE TOOLS: ${selectedToolNames.join(', ')}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERFÜGBARE KONZEPTE:
 | 
					VERFÜGBARE KONZEPTE (${availableConcepts.length} KI-kuratiert):
 | 
				
			||||||
${conceptsList}
 | 
					${availableConcepts.map((c: any) => 
 | 
				
			||||||
 | 
					  `- ${c.name}: ${c.description}...`
 | 
				
			||||||
 | 
					).join('\n')}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ANTWORT NUR ALS JSON (Schema UNVERÄNDERT):
 | 
					ANTWORT AUSSCHLIESSLICH IM JSON-FORMAT OHNE JEGLICHEN TEXT AUSSERHALB DER JSON-STRUKTUR:
 | 
				
			||||||
[
 | 
					[
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "conceptName": "Exakter Konzept-Name",
 | 
					    "conceptName": "Name",
 | 
				
			||||||
    "relevance": "Knappe Begründung, warum dieses Konzept für das Verständnis der Methodik/Tools kritisch ist (UI-tauglich)."
 | 
					    "relevance": "Warum kritisch für Methodik"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
]`;
 | 
					]`;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  finalRecommendations: (isWorkflow: boolean, userQuery: string, selectedToolNames: string[]) => {
 | 
					  finalRecommendations: (isWorkflow: boolean, userQuery: string, selectedToolNames: string[]) => {
 | 
				
			||||||
    if (isWorkflow) {
 | 
					    const focus = isWorkflow ? 
 | 
				
			||||||
      return `Erstellen Sie einen knappen, methodisch korrekten WORKFLOW-Fließtext (max. 220 Wörter), der EXPLIZIT eine Mischung aus Methoden und Tools nutzt.
 | 
					      'Workflow-Schritte, Best Practices, Objektivität' :
 | 
				
			||||||
 | 
					      'Methodische Überlegungen, Validierung, Qualitätssicherung';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SZENARIO: "${userQuery}"
 | 
					    return `Erstelle ${isWorkflow ? 'Workflow-Empfehlung' : 'methodische Überlegungen'}.
 | 
				
			||||||
AUSGEWÄHLTE ELEMENTE: ${selectedToolNames.join(', ') || 'Keine Auswahl'}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vorgaben: klare Phasen (Datensammlung → Auswertung → Analyse → Bericht), Beweisführung/Chain of Custody, Reproduzierbarkeit. Sprache präzise und UI-tauglich. Keine Listen/Markdown.`;
 | 
					${isWorkflow ? 'SZENARIO' : 'PROBLEM'}: "${userQuery}"
 | 
				
			||||||
    }
 | 
					AUSGEWÄHLT: ${selectedToolNames.join(', ')}${selectedToolNames.length > 5 ? '...' : ''}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `Formulieren Sie knappe, kritische ÜBERLEGUNGEN (max. 200 Wörter) zur korrekten Anwendung der empfohlenen Elemente (Methoden + Tools).
 | 
					Fokus: ${focus}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PROBLEM: "${userQuery}"
 | 
					Antwort: Fließtext ohne Listen, max ${isWorkflow ? '100' : '80'} Wörter.`;
 | 
				
			||||||
EMPFOHLENE ELEMENTE: ${selectedToolNames.join(', ') || 'Keine Auswahl'}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fokus: Validierung/Qualitätssicherung, Risiken (FP/FN), Dokumentation/Reporting. Stil deutsch, ohne Listen/Markdown, UI-tauglich.`;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
} as const;
 | 
					} as const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -231,4 +226,4 @@ export function getPrompt(promptKey: keyof typeof AI_PROMPTS, ...args: any[]): s
 | 
				
			|||||||
    console.error(`[PROMPTS] Error generating prompt ${promptKey}:`, error);
 | 
					    console.error(`[PROMPTS] Error generating prompt ${promptKey}:`, error);
 | 
				
			||||||
    return 'Error: Failed to generate prompt';
 | 
					    return 'Error: Failed to generate prompt';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -67,6 +67,8 @@ interface AnalysisContext {
 | 
				
			|||||||
  auditTrail: AuditEntry[];
 | 
					  auditTrail: AuditEntry[];
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  embeddingsSimilarities: Map<string, number>;
 | 
					  embeddingsSimilarities: Map<string, number>;
 | 
				
			||||||
 | 
					  aiSelectedTools?: any[];
 | 
				
			||||||
 | 
					  aiSelectedConcepts?: any[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ConfidenceMetrics {
 | 
					interface ConfidenceMetrics {
 | 
				
			||||||
@ -93,6 +95,9 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
  private embeddingsMinTools: number;
 | 
					  private embeddingsMinTools: number;
 | 
				
			||||||
  private embeddingsMaxReductionRatio: number;
 | 
					  private embeddingsMaxReductionRatio: number;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					  private methodSelectionRatio: number;
 | 
				
			||||||
 | 
					  private softwareSelectionRatio: number;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  private maxContextTokens: number;
 | 
					  private maxContextTokens: number;
 | 
				
			||||||
  private maxPromptTokens: number;
 | 
					  private maxPromptTokens: number;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@ -127,12 +132,15 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
    this.embeddingSelectionLimit = parseInt(process.env.AI_EMBEDDING_SELECTION_LIMIT || '30', 10);
 | 
					    this.embeddingSelectionLimit = parseInt(process.env.AI_EMBEDDING_SELECTION_LIMIT || '30', 10);
 | 
				
			||||||
    this.embeddingConceptsLimit = parseInt(process.env.AI_EMBEDDING_CONCEPTS_LIMIT || '15', 10);
 | 
					    this.embeddingConceptsLimit = parseInt(process.env.AI_EMBEDDING_CONCEPTS_LIMIT || '15', 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.noEmbeddingsToolLimit = parseInt(process.env.AI_NO_EMBEDDINGS_TOOL_LIMIT || '0', 10);
 | 
					    this.noEmbeddingsToolLimit = parseInt(process.env.AI_NO_EMBEDDINGS_TOOL_LIMIT || '25', 10);
 | 
				
			||||||
    this.noEmbeddingsConceptLimit = parseInt(process.env.AI_NO_EMBEDDINGS_CONCEPT_LIMIT || '0', 10);
 | 
					    this.noEmbeddingsConceptLimit = parseInt(process.env.AI_NO_EMBEDDINGS_CONCEPT_LIMIT || '10', 10);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    this.embeddingsMinTools = parseInt(process.env.AI_EMBEDDINGS_MIN_TOOLS || '8', 10);
 | 
					    this.embeddingsMinTools = parseInt(process.env.AI_EMBEDDINGS_MIN_TOOLS || '8', 10);
 | 
				
			||||||
    this.embeddingsMaxReductionRatio = parseFloat(process.env.AI_EMBEDDINGS_MAX_REDUCTION_RATIO || '0.75');
 | 
					    this.embeddingsMaxReductionRatio = parseFloat(process.env.AI_EMBEDDINGS_MAX_REDUCTION_RATIO || '0.75');
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    this.methodSelectionRatio = parseFloat(process.env.AI_METHOD_SELECTION_RATIO || '0.4');
 | 
				
			||||||
 | 
					    this.softwareSelectionRatio = parseFloat(process.env.AI_SOFTWARE_SELECTION_RATIO || '0.5');
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    this.maxContextTokens = parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10);
 | 
					    this.maxContextTokens = parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10);
 | 
				
			||||||
    this.maxPromptTokens = parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10);
 | 
					    this.maxPromptTokens = parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -142,6 +150,7 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    console.log('[AI PIPELINE] Audit trail enabled:', this.auditConfig.enabled);
 | 
					    console.log('[AI PIPELINE] Audit trail enabled:', this.auditConfig.enabled);
 | 
				
			||||||
 | 
					    console.log('[AI PIPELINE] Method/Software balance:', `${(this.methodSelectionRatio * 100).toFixed(0)}%/${(this.softwareSelectionRatio * 100).toFixed(0)}%`);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    this.confidenceConfig = {
 | 
					    this.confidenceConfig = {
 | 
				
			||||||
      semanticWeight: parseFloat(process.env.CONFIDENCE_SEMANTIC_WEIGHT || '0.3'),   
 | 
					      semanticWeight: parseFloat(process.env.CONFIDENCE_SEMANTIC_WEIGHT || '0.3'),   
 | 
				
			||||||
@ -163,7 +172,7 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
      FORENSIC_AUDIT_DETAIL_LEVEL: process.env.FORENSIC_AUDIT_DETAIL_LEVEL,
 | 
					      FORENSIC_AUDIT_DETAIL_LEVEL: process.env.FORENSIC_AUDIT_DETAIL_LEVEL,
 | 
				
			||||||
      NODE_ENV: process.env.NODE_ENV,
 | 
					      NODE_ENV: process.env.NODE_ENV,
 | 
				
			||||||
      allEnvKeys: Object.keys(process.env).filter(k => k.includes('AUDIT')),
 | 
					      allEnvKeys: Object.keys(process.env).filter(k => k.includes('AUDIT')),
 | 
				
			||||||
      dotenvLoaded: !!process.env.PUBLIC_BASE_URL // Proxy for "dotenv worked"
 | 
					      dotenvLoaded: !!process.env.PUBLIC_BASE_URL 
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log('[AI PIPELINE] Final audit config:', this.auditConfig);
 | 
					    console.log('[AI PIPELINE] Final audit config:', this.auditConfig);
 | 
				
			||||||
@ -260,11 +269,40 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private safeParseJSON(jsonString: string, fallback: any = null): any {
 | 
					  private safeParseJSON(jsonString: string, fallback: any = null): any {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      let cleaned = jsonString
 | 
					      // First, try to extract JSON block from markdown or mixed content
 | 
				
			||||||
        .replace(/^```json\s*/i, '')
 | 
					      let cleaned = jsonString.trim();
 | 
				
			||||||
        .replace(/\s*```\s*$/g, '')
 | 
					 | 
				
			||||||
        .trim();
 | 
					 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
 | 
					      // Look for JSON block patterns (most specific first)
 | 
				
			||||||
 | 
					      const jsonBlockPatterns = [
 | 
				
			||||||
 | 
					        /```json\s*([\s\S]*?)\s*```/i,  // ```json ... ```
 | 
				
			||||||
 | 
					        /```\s*([\s\S]*?)\s*```/i,      // ``` ... ```
 | 
				
			||||||
 | 
					        /\{[\s\S]*\}/,                   // Anything that looks like JSON object
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      let jsonMatch: RegExpMatchArray | null = null;
 | 
				
			||||||
 | 
					      for (const pattern of jsonBlockPatterns) {
 | 
				
			||||||
 | 
					        jsonMatch = cleaned.match(pattern);
 | 
				
			||||||
 | 
					        if (jsonMatch) {
 | 
				
			||||||
 | 
					          cleaned = jsonMatch[1] || jsonMatch[0];
 | 
				
			||||||
 | 
					          console.log('[AI PIPELINE] Extracted JSON block using pattern:', pattern.source);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // If no pattern matched, try to find JSON-like content manually
 | 
				
			||||||
 | 
					      if (!jsonMatch) {
 | 
				
			||||||
 | 
					        const jsonStart = cleaned.indexOf('{');
 | 
				
			||||||
 | 
					        const jsonEnd = cleaned.lastIndexOf('}');
 | 
				
			||||||
 | 
					        if (jsonStart !== -1 && jsonEnd !== -1 && jsonEnd > jsonStart) {
 | 
				
			||||||
 | 
					          cleaned = cleaned.substring(jsonStart, jsonEnd + 1);
 | 
				
			||||||
 | 
					          console.log('[AI PIPELINE] Manually extracted JSON from position', jsonStart, 'to', jsonEnd);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Clean up the extracted content
 | 
				
			||||||
 | 
					      cleaned = cleaned.trim();
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Handle truncated JSON by finding the last complete structure
 | 
				
			||||||
      if (!cleaned.endsWith('}') && !cleaned.endsWith(']')) {
 | 
					      if (!cleaned.endsWith('}') && !cleaned.endsWith(']')) {
 | 
				
			||||||
        console.warn('[AI PIPELINE] JSON appears truncated, attempting recovery...');
 | 
					        console.warn('[AI PIPELINE] JSON appears truncated, attempting recovery...');
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@ -305,9 +343,10 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (lastCompleteStructure) {
 | 
					        if (lastCompleteStructure) {
 | 
				
			||||||
          console.log('[AI PIPELINE] Attempting to parse recovered JSON structure...');
 | 
					          console.log('[AI PIPELINE] Using recovered JSON structure');
 | 
				
			||||||
          cleaned = lastCompleteStructure;
 | 
					          cleaned = lastCompleteStructure;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					          // Try to close unclosed braces/brackets
 | 
				
			||||||
          if (braceCount > 0) {
 | 
					          if (braceCount > 0) {
 | 
				
			||||||
            cleaned += '}';
 | 
					            cleaned += '}';
 | 
				
			||||||
            console.log('[AI PIPELINE] Added closing brace to truncated JSON');
 | 
					            console.log('[AI PIPELINE] Added closing brace to truncated JSON');
 | 
				
			||||||
@ -321,40 +360,82 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
      
 | 
					      
 | 
				
			||||||
      const parsed = JSON.parse(cleaned);
 | 
					      const parsed = JSON.parse(cleaned);
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
 | 
					      // Ensure the structure has the expected fields
 | 
				
			||||||
      if (parsed && typeof parsed === 'object') {
 | 
					      if (parsed && typeof parsed === 'object') {
 | 
				
			||||||
        if (parsed.selectedTools === undefined) parsed.selectedTools = [];
 | 
					        if (parsed.selectedTools === undefined) parsed.selectedTools = [];
 | 
				
			||||||
        if (parsed.selectedConcepts === undefined) parsed.selectedConcepts = [];
 | 
					        if (parsed.selectedConcepts === undefined) parsed.selectedConcepts = [];
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (!Array.isArray(parsed.selectedTools)) parsed.selectedTools = [];
 | 
					        if (!Array.isArray(parsed.selectedTools)) parsed.selectedTools = [];
 | 
				
			||||||
        if (!Array.isArray(parsed.selectedConcepts)) parsed.selectedConcepts = [];
 | 
					        if (!Array.isArray(parsed.selectedConcepts)) parsed.selectedConcepts = [];
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        console.log(`[AI PIPELINE] Successfully parsed JSON: ${parsed.selectedTools.length} tools, ${parsed.selectedConcepts.length} concepts`);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      return parsed;
 | 
					      return parsed;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      console.warn('[AI PIPELINE] JSON parsing failed:', error.message);
 | 
					      console.warn('[AI PIPELINE] JSON parsing failed:', error.message);
 | 
				
			||||||
      console.warn('[AI PIPELINE] Raw content (first 300 chars):', jsonString.slice(0, 300));
 | 
					      console.warn('[AI PIPELINE] Raw content (first 300 chars):', jsonString.slice(0, 300));
 | 
				
			||||||
      console.warn('[AI PIPELINE] Raw content (last 300 chars):', jsonString.slice(-300));
 | 
					      console.warn('[AI PIPELINE] Raw content (last 300 chars):', jsonString.slice(-300));
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      if (jsonString.includes('selectedTools')) {
 | 
					      // Enhanced recovery mechanism that preserves tool/concept distinction
 | 
				
			||||||
        const toolMatches = jsonString.match(/"([^"]+)"/g);
 | 
					      if (jsonString.includes('selectedTools') || jsonString.includes('selectedConcepts')) {
 | 
				
			||||||
        if (toolMatches && toolMatches.length > 0) {
 | 
					        console.log('[AI PIPELINE] Attempting enhanced recovery from broken JSON...');
 | 
				
			||||||
          console.log('[AI PIPELINE] Attempting partial recovery from broken JSON...');
 | 
					        
 | 
				
			||||||
          const possibleTools = toolMatches
 | 
					        // Try to extract tool and concept arrays separately
 | 
				
			||||||
            .map(match => match.replace(/"/g, ''))
 | 
					        const selectedTools: string[] = [];
 | 
				
			||||||
            .filter(name => name.length > 2 && !['selectedTools', 'selectedConcepts', 'reasoning'].includes(name))
 | 
					        const selectedConcepts: string[] = [];
 | 
				
			||||||
            .slice(0, 15); 
 | 
					        
 | 
				
			||||||
          
 | 
					        // Look for selectedTools array
 | 
				
			||||||
          if (possibleTools.length > 0) {
 | 
					        const toolsMatch = jsonString.match(/"selectedTools"\s*:\s*\[([\s\S]*?)\]/i);
 | 
				
			||||||
            console.log(`[AI PIPELINE] Recovered ${possibleTools.length} possible tool names from broken JSON`);
 | 
					        if (toolsMatch) {
 | 
				
			||||||
            return {
 | 
					          const toolsContent = toolsMatch[1];
 | 
				
			||||||
              selectedTools: possibleTools,
 | 
					          const toolMatches = toolsContent.match(/"([^"]+)"/g);
 | 
				
			||||||
              selectedConcepts: [],
 | 
					          if (toolMatches) {
 | 
				
			||||||
              reasoning: 'Recovered from truncated response'
 | 
					            selectedTools.push(...toolMatches.map(match => match.replace(/"/g, '')));
 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Look for selectedConcepts array  
 | 
				
			||||||
 | 
					        const conceptsMatch = jsonString.match(/"selectedConcepts"\s*:\s*\[([\s\S]*?)\]/i);
 | 
				
			||||||
 | 
					        if (conceptsMatch) {
 | 
				
			||||||
 | 
					          const conceptsContent = conceptsMatch[1];
 | 
				
			||||||
 | 
					          const conceptMatches = conceptsContent.match(/"([^"]+)"/g);
 | 
				
			||||||
 | 
					          if (conceptMatches) {
 | 
				
			||||||
 | 
					            selectedConcepts.push(...conceptMatches.map(match => match.replace(/"/g, '')));
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // If we couldn't parse arrays separately, fall back to generic name extraction
 | 
				
			||||||
 | 
					        if (selectedTools.length === 0 && selectedConcepts.length === 0) {
 | 
				
			||||||
 | 
					          const allMatches = jsonString.match(/"([^"]+)"/g);
 | 
				
			||||||
 | 
					          if (allMatches) {
 | 
				
			||||||
 | 
					            const possibleNames = allMatches
 | 
				
			||||||
 | 
					              .map(match => match.replace(/"/g, ''))
 | 
				
			||||||
 | 
					              .filter(name => 
 | 
				
			||||||
 | 
					                name.length > 2 && 
 | 
				
			||||||
 | 
					                !['selectedTools', 'selectedConcepts', 'reasoning'].includes(name) &&
 | 
				
			||||||
 | 
					                !name.includes(':') && // Avoid JSON keys
 | 
				
			||||||
 | 
					                !name.match(/^\d+$/) // Avoid pure numbers
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					              .slice(0, 15);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Assume all recovered names are tools (since concepts are usually fewer)
 | 
				
			||||||
 | 
					            selectedTools.push(...possibleNames);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (selectedTools.length > 0 || selectedConcepts.length > 0) {
 | 
				
			||||||
 | 
					          console.log(`[AI PIPELINE] Recovery successful: ${selectedTools.length} tools, ${selectedConcepts.length} concepts`);
 | 
				
			||||||
 | 
					          return {
 | 
				
			||||||
 | 
					            selectedTools,
 | 
				
			||||||
 | 
					            selectedConcepts,
 | 
				
			||||||
 | 
					            reasoning: 'Recovered from malformed JSON response'
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
 | 
					      console.error('[AI PIPELINE] All recovery attempts failed');
 | 
				
			||||||
      return fallback;
 | 
					      return fallback;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -491,7 +572,29 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    const selectionStart = Date.now();
 | 
					    const selectionStart = Date.now();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    const toolsWithFullData = candidateTools.map((tool: any) => ({
 | 
					    const candidateMethods = candidateTools.filter(tool => tool.type === 'method');
 | 
				
			||||||
 | 
					    const candidateSoftware = candidateTools.filter(tool => tool.type === 'software');
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    console.log(`[AI PIPELINE] Candidates: ${candidateMethods.length} methods, ${candidateSoftware.length} software, ${candidateConcepts.length} concepts`);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    const methodsWithFullData = candidateMethods.map((tool: any) => ({
 | 
				
			||||||
 | 
					      name: tool.name,
 | 
				
			||||||
 | 
					      type: tool.type,
 | 
				
			||||||
 | 
					      description: tool.description,
 | 
				
			||||||
 | 
					      domains: tool.domains,
 | 
				
			||||||
 | 
					      phases: tool.phases,
 | 
				
			||||||
 | 
					      platforms: tool.platforms || [],
 | 
				
			||||||
 | 
					      tags: tool.tags || [],
 | 
				
			||||||
 | 
					      skillLevel: tool.skillLevel,
 | 
				
			||||||
 | 
					      license: tool.license,
 | 
				
			||||||
 | 
					      accessType: tool.accessType,
 | 
				
			||||||
 | 
					      projectUrl: tool.projectUrl,
 | 
				
			||||||
 | 
					      knowledgebase: tool.knowledgebase,
 | 
				
			||||||
 | 
					      related_concepts: tool.related_concepts || [],
 | 
				
			||||||
 | 
					      related_software: tool.related_software || []
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const softwareWithFullData = candidateSoftware.map((tool: any) => ({
 | 
				
			||||||
      name: tool.name,
 | 
					      name: tool.name,
 | 
				
			||||||
      type: tool.type,
 | 
					      type: tool.type,
 | 
				
			||||||
      description: tool.description,
 | 
					      description: tool.description,
 | 
				
			||||||
@ -524,36 +627,72 @@ class ImprovedMicroTaskAIPipeline {
 | 
				
			|||||||
    let conceptsToSend: any[];
 | 
					    let conceptsToSend: any[];
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (selectionMethod === 'embeddings_candidates') {
 | 
					    if (selectionMethod === 'embeddings_candidates') {
 | 
				
			||||||
      toolsToSend = toolsWithFullData.slice(0, this.embeddingSelectionLimit);
 | 
					      const totalLimit = this.embeddingSelectionLimit;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      const methodLimit = Math.ceil(totalLimit * this.methodSelectionRatio);
 | 
				
			||||||
 | 
					      const softwareLimit = Math.floor(totalLimit * this.softwareSelectionRatio);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      const selectedMethods = methodsWithFullData.slice(0, methodLimit);
 | 
				
			||||||
 | 
					      const selectedSoftware = softwareWithFullData.slice(0, softwareLimit);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      toolsToSend = [...selectedMethods, ...selectedSoftware];
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      const remainingCapacity = totalLimit - toolsToSend.length;
 | 
				
			||||||
 | 
					      if (remainingCapacity > 0) {
 | 
				
			||||||
 | 
					        if (methodsWithFullData.length > methodLimit) {
 | 
				
			||||||
 | 
					          toolsToSend.push(...methodsWithFullData.slice(methodLimit, methodLimit + remainingCapacity));
 | 
				
			||||||
 | 
					        } else if (softwareWithFullData.length > softwareLimit) {
 | 
				
			||||||
 | 
					          toolsToSend.push(...softwareWithFullData.slice(softwareLimit, softwareLimit + remainingCapacity));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
      conceptsToSend = conceptsWithFullData.slice(0, this.embeddingConceptsLimit);
 | 
					      conceptsToSend = conceptsWithFullData.slice(0, this.embeddingConceptsLimit);
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      console.log(`[AI PIPELINE] Embeddings enabled: sending top ${toolsToSend.length} similarity-ordered tools`);
 | 
					      console.log(`[AI PIPELINE] Balanced selection: ${selectedMethods.length} methods, ${selectedSoftware.length} software, ${conceptsToSend.length} concepts`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Method names: ${selectedMethods.slice(0, 5).map(m => m.name).join(', ')}${selectedMethods.length > 5 ? '...' : ''}`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Software names: ${selectedSoftware.slice(0, 5).map(s => s.name).join(', ')}${selectedSoftware.length > 5 ? '...' : ''}`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Concept names: ${conceptsToSend.map(c => c.name).join(', ')}`);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      const maxTools = this.noEmbeddingsToolLimit > 0 ? 
 | 
					      const maxTools = this.noEmbeddingsToolLimit > 0 ? this.noEmbeddingsToolLimit : 25;
 | 
				
			||||||
        Math.min(this.noEmbeddingsToolLimit, candidateTools.length) : 
 | 
					      const maxConcepts = this.noEmbeddingsConceptLimit > 0 ? this.noEmbeddingsConceptLimit : 10;
 | 
				
			||||||
        candidateTools.length;
 | 
					 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      const maxConcepts = this.noEmbeddingsConceptLimit > 0 ? 
 | 
					      const methodLimit = Math.ceil(maxTools * 0.4);
 | 
				
			||||||
        Math.min(this.noEmbeddingsConceptLimit, candidateConcepts.length) : 
 | 
					      const softwareLimit = Math.floor(maxTools * 0.5);
 | 
				
			||||||
        candidateConcepts.length;
 | 
					      
 | 
				
			||||||
 | 
					      const selectedMethods = methodsWithFullData.slice(0, methodLimit);
 | 
				
			||||||
 | 
					      const selectedSoftware = softwareWithFullData.slice(0, softwareLimit);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      toolsToSend = [...selectedMethods, ...selectedSoftware];
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      const remainingCapacity = maxTools - toolsToSend.length;
 | 
				
			||||||
 | 
					      if (remainingCapacity > 0) {
 | 
				
			||||||
 | 
					        if (methodsWithFullData.length > methodLimit) {
 | 
				
			||||||
 | 
					          toolsToSend.push(...methodsWithFullData.slice(methodLimit, methodLimit + remainingCapacity));
 | 
				
			||||||
 | 
					        } else if (softwareWithFullData.length > softwareLimit) {
 | 
				
			||||||
 | 
					          toolsToSend.push(...softwareWithFullData.slice(softwareLimit, softwareLimit + remainingCapacity));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      toolsToSend = toolsWithFullData.slice(0, maxTools);
 | 
					 | 
				
			||||||
      conceptsToSend = conceptsWithFullData.slice(0, maxConcepts);
 | 
					      conceptsToSend = conceptsWithFullData.slice(0, maxConcepts);
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      console.log(`[AI PIPELINE] Embeddings disabled: sending ${toolsToSend.length}/${candidateTools.length} tools (limit: ${this.noEmbeddingsToolLimit || 'none'})`);
 | 
					      console.log(`[AI PIPELINE] Balanced selection (no embeddings): ${selectedMethods.length} methods, ${selectedSoftware.length} software, ${conceptsToSend.length} concepts`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.maxSelectedItems);
 | 
					    const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.maxSelectedItems);
 | 
				
			||||||
    const prompt = `${basePrompt}
 | 
					    const prompt = `${basePrompt}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERFÜGBARE TOOLS (mit vollständigen Daten):
 | 
					  VERFÜGBARE TOOLS (${toolsToSend.length} Items - Methoden und Software):
 | 
				
			||||||
${JSON.stringify(toolsToSend, null, 2)}
 | 
					  ${JSON.stringify(toolsToSend, null, 2)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERFÜGBARE KONZEPTE (mit vollständigen Daten):
 | 
					  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.`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const estimatedTokens = this.estimateTokens(prompt);
 | 
					    const estimatedTokens = this.estimateTokens(prompt);
 | 
				
			||||||
    console.log(`[AI PIPELINE] Method: ${selectionMethod}, Tools: ${toolsToSend.length}, Estimated tokens: ~${estimatedTokens}`);
 | 
					    console.log(`[AI PIPELINE] Sending to AI: ${toolsToSend.filter(t => t.type === 'method').length} methods, ${toolsToSend.filter(t => t.type === 'software').length} software, ${conceptsToSend.length} concepts`);
 | 
				
			||||||
 | 
					    console.log(`[AI PIPELINE] Estimated tokens: ~${estimatedTokens}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (estimatedTokens > 35000) {
 | 
					    if (estimatedTokens > 35000) {
 | 
				
			||||||
      console.warn(`[AI PIPELINE] WARNING: Prompt tokens (${estimatedTokens}) may exceed model limits`);
 | 
					      console.warn(`[AI PIPELINE] WARNING: Prompt tokens (${estimatedTokens}) may exceed model limits`);
 | 
				
			||||||
@ -569,43 +708,61 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        if (this.auditConfig.enabled) {
 | 
					        if (this.auditConfig.enabled) {
 | 
				
			||||||
          this.addAuditEntry(context, 'selection', 'ai-tool-selection-failed',
 | 
					          this.addAuditEntry(context, 'selection', 'ai-tool-selection-failed',
 | 
				
			||||||
            { candidateCount: candidateTools.length, mode, prompt: prompt.slice(0, 200) },
 | 
					            { candidateCount: candidateTools.length, candidateConceptsCount: candidateConcepts.length, mode, prompt: prompt.slice(0, 200) },
 | 
				
			||||||
            { error: 'Invalid JSON structure', response: response.slice(0, 200) },
 | 
					            { error: 'Invalid JSON structure', response: response.slice(0, 200) },
 | 
				
			||||||
            10,
 | 
					            10,
 | 
				
			||||||
            selectionStart,
 | 
					            selectionStart,
 | 
				
			||||||
            { aiModel: this.config.model, selectionMethod, tokensSent: estimatedTokens, toolsSent: toolsToSend.length }
 | 
					            { aiModel: this.config.model, selectionMethod, tokensSent: estimatedTokens, toolsSent: toolsToSend.length, conceptsSent: conceptsToSend.length }
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        throw new Error('AI selection failed to return valid tool selection');
 | 
					        throw new Error('AI selection failed to return valid tool and concept selection');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const totalSelected = result.selectedTools.length + result.selectedConcepts.length;
 | 
					      const totalSelected = result.selectedTools.length + result.selectedConcepts.length;
 | 
				
			||||||
      if (totalSelected === 0) {
 | 
					      if (totalSelected === 0) {
 | 
				
			||||||
        console.error('[AI PIPELINE] AI selection returned no tools');
 | 
					        console.error('[AI PIPELINE] AI selection returned no tools or concepts');
 | 
				
			||||||
        throw new Error('AI selection returned empty selection');
 | 
					        throw new Error('AI selection returned empty selection');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      console.log(`[AI PIPELINE] AI selected: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts from ${toolsToSend.length} candidates`);
 | 
					      // Create lookup maps for efficient filtering
 | 
				
			||||||
 | 
					      const toolsMap = new Map<string, any>(candidateTools.map(tool => [tool.name, tool]));
 | 
				
			||||||
      const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
 | 
					      const conceptsMap = new Map<string, any>(candidateConcepts.map(concept => [concept.name, concept]));
 | 
				
			||||||
      const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
 | 
					 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
 | 
					      const selectedTools = result.selectedTools
 | 
				
			||||||
 | 
					        .map(name => toolsMap.get(name))
 | 
				
			||||||
 | 
					        .filter((tool): tool is any => tool !== undefined);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      const selectedConcepts = result.selectedConcepts
 | 
				
			||||||
 | 
					        .map(name => conceptsMap.get(name))
 | 
				
			||||||
 | 
					        .filter((concept): concept is any => concept !== undefined);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      const selectedMethods = selectedTools.filter(t => t.type === 'method');
 | 
				
			||||||
 | 
					      const selectedSoftware = selectedTools.filter(t => t.type === 'software');
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] AI selected: ${selectedMethods.length} methods, ${selectedSoftware.length} software, ${selectedConcepts.length} concepts`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Selection balance: ${((selectedMethods.length / (selectedTools.length || 1)) * 100).toFixed(0)}% methods`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Selected tool names: ${selectedTools.map(t => t.name).join(', ')}`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Selected concept names: ${selectedConcepts.map(c => c.name).join(', ')}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (this.auditConfig.enabled) {
 | 
					      if (this.auditConfig.enabled) {
 | 
				
			||||||
        const confidence = this.calculateSelectionConfidence(result, candidateTools.length);
 | 
					        const confidence = this.calculateSelectionConfidence(result, candidateTools.length + candidateConcepts.length);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        this.addAuditEntry(context, 'selection', 'ai-tool-selection',
 | 
					        this.addAuditEntry(context, 'selection', 'ai-tool-selection',
 | 
				
			||||||
          { candidateCount: candidateTools.length, mode, promptLength: prompt.length },
 | 
					          { candidateCount: candidateTools.length, candidateConceptsCount: candidateConcepts.length, mode, promptLength: prompt.length },
 | 
				
			||||||
          { 
 | 
					          { 
 | 
				
			||||||
            selectedToolCount: result.selectedTools.length, 
 | 
					            selectedMethodCount: selectedMethods.length,
 | 
				
			||||||
            selectedConceptCount: result.selectedConcepts.length,
 | 
					            selectedSoftwareCount: selectedSoftware.length, 
 | 
				
			||||||
 | 
					            selectedConceptCount: selectedConcepts.length,
 | 
				
			||||||
            reasoning: result.reasoning?.slice(0, 200) + '...',
 | 
					            reasoning: result.reasoning?.slice(0, 200) + '...',
 | 
				
			||||||
            finalToolNames: selectedTools.map(t => t.name),
 | 
					            finalToolNames: selectedTools.map(t => t.name),
 | 
				
			||||||
            selectionEfficiency: `${toolsToSend.length} → ${result.selectedTools.length}`
 | 
					            finalConceptNames: selectedConcepts.map(c => c.name),
 | 
				
			||||||
 | 
					            methodBalance: `${((selectedMethods.length / (selectedTools.length || 1)) * 100).toFixed(0)}%`,
 | 
				
			||||||
 | 
					            conceptsSelected: selectedConcepts.length > 0
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          confidence,
 | 
					          confidence,
 | 
				
			||||||
          selectionStart,
 | 
					          selectionStart,
 | 
				
			||||||
          { aiModel: this.config.model, selectionMethod, promptTokens: estimatedTokens, toolsSent: toolsToSend.length }
 | 
					          { aiModel: this.config.model, selectionMethod, promptTokens: estimatedTokens, toolsSent: toolsToSend.length, conceptsSent: conceptsToSend.length }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
@ -619,7 +776,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
      
 | 
					      
 | 
				
			||||||
      if (this.auditConfig.enabled) {
 | 
					      if (this.auditConfig.enabled) {
 | 
				
			||||||
        this.addAuditEntry(context, 'selection', 'ai-tool-selection-error',
 | 
					        this.addAuditEntry(context, 'selection', 'ai-tool-selection-error',
 | 
				
			||||||
          { candidateCount: candidateTools.length, mode },
 | 
					          { candidateCount: candidateTools.length, candidateConceptsCount: candidateConcepts.length, mode },
 | 
				
			||||||
          { error: error.message },
 | 
					          { error: error.message },
 | 
				
			||||||
          5,
 | 
					          5,
 | 
				
			||||||
          selectionStart,
 | 
					          selectionStart,
 | 
				
			||||||
@ -851,7 +1008,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
      tool.phases && tool.phases.includes(phase.id)
 | 
					      tool.phases && tool.phases.includes(phase.id)
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(`[AI PIPELINE] Phase ${phase.id} (${phase.name}): Found ${phaseTools.length} matching tools`);
 | 
				
			||||||
 | 
					    console.log(`[AI PIPELINE] Available tools for phase: ${phaseTools.map(t => `${t.name}(${t.type})`).join(', ')}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (phaseTools.length === 0) {
 | 
					    if (phaseTools.length === 0) {
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] No tools available for phase ${phase.id}, skipping`);
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        taskType: 'tool-selection',
 | 
					        taskType: 'tool-selection',
 | 
				
			||||||
        content: JSON.stringify([]),
 | 
					        content: JSON.stringify([]),
 | 
				
			||||||
@ -860,17 +1021,38 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const phaseMethods = phaseTools.filter(t => t.type === 'method');
 | 
				
			||||||
 | 
					    const phaseSoftware = phaseTools.filter(t => t.type === 'software');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(`[AI PIPELINE] Phase ${phase.id}: ${phaseMethods.length} methods, ${phaseSoftware.length} software`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const prompt = getPrompt('phaseToolSelection', context.userQuery, phase, phaseTools);
 | 
					    const prompt = getPrompt('phaseToolSelection', context.userQuery, phase, phaseTools);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const result = await this.callMicroTaskAI(prompt, context, 1000);
 | 
					    const result = await this.callMicroTaskAI(prompt, context, 1000);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (result.success) {
 | 
					    if (result.success) {
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Phase ${phase.id} AI response length: ${result.content.length}`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Phase ${phase.id} AI response preview: ${result.content.slice(0, 200)}...`);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
      const selections = this.safeParseJSON(result.content, []);
 | 
					      const selections = this.safeParseJSON(result.content, []);
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      if (Array.isArray(selections)) {
 | 
					      if (Array.isArray(selections)) {
 | 
				
			||||||
        const validSelections = selections.filter((sel: any) => 
 | 
					        console.log(`[AI PIPELINE] Phase ${phase.id}: Parsed ${selections.length} selections from AI`);
 | 
				
			||||||
          sel.toolName && phaseTools.some((tool: any) => tool.name === sel.toolName)
 | 
					        
 | 
				
			||||||
        );
 | 
					        const validSelections = selections.filter((sel: any) => {
 | 
				
			||||||
 | 
					          if (!sel.toolName) return false;
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          let matchingTool = phaseTools.find((tool: any) => tool.name === sel.toolName);
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          const isValid = !!matchingTool;
 | 
				
			||||||
 | 
					          if (!isValid) {
 | 
				
			||||||
 | 
					            console.warn(`[AI PIPELINE] Phase ${phase.id}: Invalid selection: ${JSON.stringify(sel)}`);
 | 
				
			||||||
 | 
					            console.warn(`[AI PIPELINE] Phase ${phase.id}: Available tool names: ${phaseTools.map(t => t.name).join(', ')}`);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return isValid;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        console.log(`[AI PIPELINE] Phase ${phase.id}: ${validSelections.length} valid selections after filtering`);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        validSelections.forEach((sel: any) => {
 | 
					        validSelections.forEach((sel: any) => {
 | 
				
			||||||
          const tool = phaseTools.find((t: any) => t.name === sel.toolName);
 | 
					          const tool = phaseTools.find((t: any) => t.name === sel.toolName);
 | 
				
			||||||
@ -880,25 +1062,35 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
            
 | 
					            
 | 
				
			||||||
            const priority = this.derivePriorityFromScore(taskRelevance);
 | 
					            const priority = this.derivePriorityFromScore(taskRelevance);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					            console.log(`[AI PIPELINE] Phase ${phase.id}: Adding ${tool.name} (${tool.type}) with priority ${priority}, relevance ${taskRelevance}%`);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            this.addToolToSelection(context, tool, phase.id, priority, sel.justification, taskRelevance, sel.limitations);
 | 
					            this.addToolToSelection(context, tool, phase.id, priority, sel.justification, taskRelevance, sel.limitations);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        this.addAuditEntry(context, 'micro-task', 'phase-tool-selection',
 | 
					        this.addAuditEntry(context, 'micro-task', 'phase-tool-selection',
 | 
				
			||||||
          { phase: phase.id, availableTools: phaseTools.length },
 | 
					          { phase: phase.id, availableTools: phaseTools.length, availableMethods: phaseMethods.length, availableSoftware: phaseSoftware.length },
 | 
				
			||||||
          { 
 | 
					          { 
 | 
				
			||||||
            validSelections: validSelections.length, 
 | 
					            validSelections: validSelections.length, 
 | 
				
			||||||
            selectedTools: validSelections.map(s => ({ 
 | 
					            selectedTools: validSelections.map(s => ({ 
 | 
				
			||||||
              name: s.toolName, 
 | 
					              name: s.toolName, 
 | 
				
			||||||
              taskRelevance: s.taskRelevance, 
 | 
					              taskRelevance: s.taskRelevance, 
 | 
				
			||||||
              derivedPriority: this.derivePriorityFromScore(s.taskRelevance) 
 | 
					              derivedPriority: this.derivePriorityFromScore(s.taskRelevance) 
 | 
				
			||||||
            }))
 | 
					            })),
 | 
				
			||||||
 | 
					            methodsSelected: validSelections.filter(s => {
 | 
				
			||||||
 | 
					              const tool = phaseTools.find(t => t.name === s.toolName);
 | 
				
			||||||
 | 
					              return tool && tool.type === 'method';
 | 
				
			||||||
 | 
					            }).length
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          validSelections.length > 0 ? 75 : 30,
 | 
					          validSelections.length > 0 ? 75 : 30,
 | 
				
			||||||
          Date.now() - result.processingTimeMs,
 | 
					          Date.now() - result.processingTimeMs,
 | 
				
			||||||
          { phaseName: phase.name, comparativeEvaluation: true, priorityDerived: true }
 | 
					          { phaseName: phase.name, comparativeEvaluation: true, priorityDerived: true }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        console.error(`[AI PIPELINE] Phase ${phase.id}: Failed to parse selections as array:`, selections);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.error(`[AI PIPELINE] Phase ${phase.id}: AI call failed:`, result.error);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
@ -1057,6 +1249,32 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
    return 'low';
 | 
					    return 'low';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private async performAISelection(
 | 
				
			||||||
 | 
					    filteredData: any,
 | 
				
			||||||
 | 
					    userQuery: string,
 | 
				
			||||||
 | 
					    mode: string,
 | 
				
			||||||
 | 
					    context: AnalysisContext
 | 
				
			||||||
 | 
					  ): Promise<{ tools: any[], concepts: any[] }> {
 | 
				
			||||||
 | 
					    const selectionStart = Date.now();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Call the existing aiSelectionWithFullData
 | 
				
			||||||
 | 
					    const result = await this.aiSelectionWithFullData(
 | 
				
			||||||
 | 
					      userQuery,
 | 
				
			||||||
 | 
					      filteredData.tools,
 | 
				
			||||||
 | 
					      filteredData.concepts,
 | 
				
			||||||
 | 
					      mode,
 | 
				
			||||||
 | 
					      embeddingsService.isEnabled() ? 'embeddings_candidates' : 'full_dataset',
 | 
				
			||||||
 | 
					      context
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    console.log(`[AI PIPELINE] AI selection complete: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts`);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      tools: result.selectedTools,
 | 
				
			||||||
 | 
					      concepts: result.selectedConcepts
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async processQuery(userQuery: string, mode: string): Promise<AnalysisResult> {
 | 
					  async processQuery(userQuery: string, mode: string): Promise<AnalysisResult> {
 | 
				
			||||||
    const startTime = Date.now();
 | 
					    const startTime = Date.now();
 | 
				
			||||||
    let completeTasks = 0;
 | 
					    let completeTasks = 0;
 | 
				
			||||||
@ -1078,15 +1296,34 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
        currentContextLength: 0,
 | 
					        currentContextLength: 0,
 | 
				
			||||||
        seenToolNames: new Set<string>(),
 | 
					        seenToolNames: new Set<string>(),
 | 
				
			||||||
        auditTrail: [],
 | 
					        auditTrail: [],
 | 
				
			||||||
        embeddingsSimilarities: new Map<string, number>()
 | 
					        embeddingsSimilarities: new Map<string, number>(),
 | 
				
			||||||
 | 
					        // Add this new property to store AI selections
 | 
				
			||||||
 | 
					        aiSelectedTools: [],
 | 
				
			||||||
 | 
					        aiSelectedConcepts: []
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Get embedding-filtered candidates
 | 
				
			||||||
      const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode, context);
 | 
					      const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode, context);
 | 
				
			||||||
      context.filteredData = filteredData;
 | 
					      
 | 
				
			||||||
 | 
					      // IMPORTANT: Now do the AI selection from those candidates
 | 
				
			||||||
 | 
					      const aiSelection = await this.performAISelection(filteredData, userQuery, mode, context);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Store the AI's selections in context
 | 
				
			||||||
 | 
					      context.aiSelectedTools = aiSelection.tools;
 | 
				
			||||||
 | 
					      context.aiSelectedConcepts = aiSelection.concepts;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Update filteredData to only include what AI selected
 | 
				
			||||||
 | 
					      context.filteredData = {
 | 
				
			||||||
 | 
					        tools: aiSelection.tools,
 | 
				
			||||||
 | 
					        concepts: aiSelection.concepts,
 | 
				
			||||||
 | 
					        domains: filteredData.domains,
 | 
				
			||||||
 | 
					        phases: filteredData.phases,
 | 
				
			||||||
 | 
					        'domain-agnostic-software': filteredData['domain-agnostic-software']
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      this.mergeTemporaryAuditEntries(context);
 | 
					      this.mergeTemporaryAuditEntries(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      console.log(`[AI PIPELINE] Starting micro-tasks with ${filteredData.tools.length} tools visible`);
 | 
					      console.log(`[AI PIPELINE] Starting micro-tasks with ${context.filteredData.tools.length} AI-selected tools`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.addAuditEntry(context, 'initialization', 'pipeline-start',
 | 
					      this.addAuditEntry(context, 'initialization', 'pipeline-start',
 | 
				
			||||||
        { userQuery, mode, toolsDataLoaded: !!toolsData },
 | 
					        { userQuery, mode, toolsDataLoaded: !!toolsData },
 | 
				
			||||||
@ -1110,6 +1347,27 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      if (mode === 'workflow') {
 | 
					      if (mode === 'workflow') {
 | 
				
			||||||
        const phases = toolsData.phases || [];
 | 
					        const phases = toolsData.phases || [];
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        console.log(`[AI PIPELINE] Debug: Starting phase-specific selection with ${context.filteredData.tools.length} AI-selected tools`);
 | 
				
			||||||
 | 
					        console.log(`[AI PIPELINE] Debug: Available phases: ${phases.map(p => p.id).join(', ')}`);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        context.filteredData.tools.forEach(tool => {
 | 
				
			||||||
 | 
					          console.log(`[AI PIPELINE] Debug: ${tool.name} (${tool.type}) - phases: ${tool.phases?.join(', ') || 'NO PHASES'}`);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        phases.forEach(phase => {
 | 
				
			||||||
 | 
					          const matchingTools = context.filteredData.tools.filter(tool => 
 | 
				
			||||||
 | 
					            tool.phases && tool.phases.includes(phase.id)
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					          const matchingMethods = matchingTools.filter(t => t.type === 'method');
 | 
				
			||||||
 | 
					          const matchingSoftware = matchingTools.filter(t => t.type === 'software');
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          console.log(`[AI PIPELINE] Debug: Phase ${phase.id} has ${matchingTools.length} matching tools (${matchingMethods.length} methods, ${matchingSoftware.length} software)`);
 | 
				
			||||||
 | 
					          if (matchingTools.length > 0) {
 | 
				
			||||||
 | 
					            console.log(`[AI PIPELINE] Debug: Phase ${phase.id} tools: ${matchingTools.map(t => `${t.name}(${t.type})`).join(', ')}`);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        for (const phase of phases) {
 | 
					        for (const phase of phases) {
 | 
				
			||||||
          const toolSelectionResult = await this.selectToolsForPhase(context, phase);
 | 
					          const toolSelectionResult = await this.selectToolsForPhase(context, phase);
 | 
				
			||||||
          if (toolSelectionResult.success) completeTasks++; else failedTasks++;
 | 
					          if (toolSelectionResult.success) completeTasks++; else failedTasks++;
 | 
				
			||||||
@ -1176,6 +1434,24 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
  private buildRecommendation(context: AnalysisContext, mode: string, finalContent: string): any {
 | 
					  private buildRecommendation(context: AnalysisContext, mode: string, finalContent: string): any {
 | 
				
			||||||
    const isWorkflow = mode === 'workflow';
 | 
					    const isWorkflow = mode === 'workflow';
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    console.log(`[AI PIPELINE] Building recommendation for ${mode} mode`);
 | 
				
			||||||
 | 
					    console.log(`[AI PIPELINE] Selected tools count: ${context.selectedTools?.length || 0}`);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (context.selectedTools && context.selectedTools.length > 0) {
 | 
				
			||||||
 | 
					      const methods = context.selectedTools.filter(st => st.tool.type === 'method');
 | 
				
			||||||
 | 
					      const software = context.selectedTools.filter(st => st.tool.type === 'software');
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Final selection breakdown: ${methods.length} methods, ${software.length} software`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Method names: ${methods.map(m => m.tool.name).join(', ')}`);
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Software names: ${software.map(s => s.tool.name).join(', ')}`);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      context.selectedTools.forEach((st, index) => {
 | 
				
			||||||
 | 
					        console.log(`[AI PIPELINE] Selected tool ${index + 1}: ${st.tool.name} (${st.tool.type}) - Phase: ${st.phase}, Priority: ${st.priority}`);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.warn(`[AI PIPELINE] WARNING: No tools in selectedTools array!`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    const base = {
 | 
					    const base = {
 | 
				
			||||||
      [isWorkflow ? 'scenario_analysis' : 'problem_analysis']: 
 | 
					      [isWorkflow ? 'scenario_analysis' : 'problem_analysis']: 
 | 
				
			||||||
        isWorkflow ? context.scenarioAnalysis : context.problemAnalysis,
 | 
					        isWorkflow ? context.scenarioAnalysis : context.problemAnalysis,
 | 
				
			||||||
@ -1201,7 +1477,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        this.addAuditEntry(context, 'validation', 'confidence-scoring',
 | 
					        this.addAuditEntry(context, 'validation', 'confidence-scoring',
 | 
				
			||||||
          { toolName: st.tool.name, phase: st.phase },
 | 
					          { toolName: st.tool.name, toolType: st.tool.type, phase: st.phase },
 | 
				
			||||||
          { 
 | 
					          { 
 | 
				
			||||||
            overall: confidence.overall,
 | 
					            overall: confidence.overall,
 | 
				
			||||||
            components: {
 | 
					            components: {
 | 
				
			||||||
@ -1216,6 +1492,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
          name: st.tool.name,
 | 
					          name: st.tool.name,
 | 
				
			||||||
 | 
					          type: st.tool.type,
 | 
				
			||||||
          phase: st.phase,
 | 
					          phase: st.phase,
 | 
				
			||||||
          priority: st.priority,
 | 
					          priority: st.priority,
 | 
				
			||||||
          justification: st.justification || `Empfohlen für ${st.phase}`,
 | 
					          justification: st.justification || `Empfohlen für ${st.phase}`,
 | 
				
			||||||
@ -1225,6 +1502,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
      }) || [];
 | 
					      }) || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Final workflow recommendations: ${recommendedToolsWithConfidence.length} tools`);
 | 
				
			||||||
 | 
					      const finalMethods = recommendedToolsWithConfidence.filter(r => r.type === 'method');
 | 
				
			||||||
 | 
					      const finalSoftware = recommendedToolsWithConfidence.filter(r => r.type === 'software');
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Final breakdown: ${finalMethods.length} methods, ${finalSoftware.length} software`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        ...base,
 | 
					        ...base,
 | 
				
			||||||
        recommended_tools: recommendedToolsWithConfidence,
 | 
					        recommended_tools: recommendedToolsWithConfidence,
 | 
				
			||||||
@ -1241,7 +1523,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        this.addAuditEntry(context, 'validation', 'confidence-scoring',
 | 
					        this.addAuditEntry(context, 'validation', 'confidence-scoring',
 | 
				
			||||||
          { toolName: st.tool.name, rank: st.tool.evaluation?.rank || 1 },
 | 
					          { toolName: st.tool.name, toolType: st.tool.type, rank: st.tool.evaluation?.rank || 1 },
 | 
				
			||||||
          { 
 | 
					          { 
 | 
				
			||||||
            overall: confidence.overall,
 | 
					            overall: confidence.overall,
 | 
				
			||||||
            suitabilityAlignment: st.priority === 'high' && confidence.overall >= this.confidenceConfig.highThreshold,
 | 
					            suitabilityAlignment: st.priority === 'high' && confidence.overall >= this.confidenceConfig.highThreshold,
 | 
				
			||||||
@ -1254,6 +1536,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
          name: st.tool.name,
 | 
					          name: st.tool.name,
 | 
				
			||||||
 | 
					          type: st.tool.type,
 | 
				
			||||||
          rank: st.tool.evaluation?.rank || 1,
 | 
					          rank: st.tool.evaluation?.rank || 1,
 | 
				
			||||||
          suitability_score: st.priority,
 | 
					          suitability_score: st.priority,
 | 
				
			||||||
          detailed_explanation: st.tool.evaluation?.detailed_explanation || '',
 | 
					          detailed_explanation: st.tool.evaluation?.detailed_explanation || '',
 | 
				
			||||||
@ -1267,6 +1550,8 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
      }) || [];
 | 
					      }) || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      console.log(`[AI PIPELINE] Final tool recommendations: ${recommendedToolsWithConfidence.length} tools`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        ...base,
 | 
					        ...base,
 | 
				
			||||||
        recommended_tools: recommendedToolsWithConfidence,
 | 
					        recommended_tools: recommendedToolsWithConfidence,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user