This commit is contained in:
overcuriousity 2025-08-02 15:41:44 +02:00
parent b192f257a1
commit 78b30fb7c6
6 changed files with 1087 additions and 139 deletions

View File

@ -0,0 +1,93 @@
// src/pages/api/ai/bias-statistics.ts - New API endpoint for bias statistics
import type { APIRoute } from 'astro';
import { withAPIAuth } from '../../../utils/auth.js';
import { apiResponse, apiServerError, createAuthErrorResponse } from '../../../utils/api.js';
import { auditTrailService } from '../../../utils/auditTrail.js';
import { biasDetector } from '../../../utils/biasDetection.js';
export const prerender = false;
export const GET: APIRoute = async ({ request }) => {
try {
const authResult = await withAPIAuth(request, 'ai');
if (!authResult.authenticated) {
return createAuthErrorResponse();
}
// Get comprehensive bias statistics
const biasStats = auditTrailService.getBiasStatistics();
const baselineStats = biasDetector.getBaselineStats();
const biasThresholds = biasDetector.getBiasThresholds();
return apiResponse.success({
biasStatistics: {
overall: biasStats,
baseline: baselineStats,
configuration: {
thresholds: biasThresholds,
detectionEnabled: true,
lastUpdated: new Date().toISOString()
},
interpretation: {
biasRiskLevel: biasStats.averageBiasInstances > 1.5 ? 'high' :
biasStats.averageBiasInstances > 0.8 ? 'medium' : 'low',
mostCommonBias: Object.entries(biasStats.biasTypeFrequency || {})
.sort(([,a], [,b]) => (b as number) - (a as number))[0]?.[0] || 'none',
recommendations: generateBiasRecommendations(biasStats)
}
}
});
} catch (error) {
console.error('[BIAS STATS API] Error:', error);
return apiServerError.internal('Failed to retrieve bias statistics');
}
};
function generateBiasRecommendations(stats: any): string[] {
const recommendations: string[] = [];
if (!stats.biasTypeFrequency) {
return ['Insufficient data for bias analysis recommendations'];
}
const biasEntries = Object.entries(stats.biasTypeFrequency) as [string, number][];
const totalAudits = stats.totalAudits || 1;
// Check for high-frequency bias types
biasEntries.forEach(([biasType, count]) => {
const frequency = count / totalAudits;
if (frequency > 0.3) { // More than 30% of audits show this bias
switch (biasType) {
case 'popularity':
recommendations.push('HIGH: Popularity bias detected frequently. Review selection criteria to prioritize scenario-specific over well-known tools.');
break;
case 'availability':
recommendations.push('MEDIUM: Availability bias detected. Encourage more thorough candidate evaluation beyond easily recalled options.');
break;
case 'domain_concentration':
recommendations.push('MEDIUM: Domain concentration bias detected. Promote cross-domain tool selection for comprehensive analysis.');
break;
case 'skill_level':
recommendations.push('LOW: Skill level misalignment detected. Improve query classification for better skill-appropriate selections.');
break;
case 'recency':
recommendations.push('LOW: Recency bias detected. Balance newer tools with proven established alternatives.');
break;
}
}
});
if (recommendations.length === 0) {
recommendations.push('No significant bias patterns detected. Current selection methodology appears balanced.');
}
// Add general recommendation if multiple bias types detected
if (biasEntries.length > 3) {
recommendations.push('GENERAL: Multiple bias types detected. Consider implementing additional bias mitigation strategies.');
}
return recommendations;
}

View File

@ -7,6 +7,7 @@ import { enqueueApiCall } from '../../../utils/rateLimitedQueue.js';
import { aiPipeline } from '../../../utils/aiPipeline.js';
import { forensicConfig } from '../../../utils/forensicConfig.js';
import { confidenceScorer } from '../../../utils/confidenceScoring.js';
import { biasDetector } from '../../../utils/biasDetection.js';
export const prerender = false;
@ -212,18 +213,17 @@ export const POST: APIRoute = async ({ request }) => {
query: sanitizedQuery,
processingStats: {
...result.processingStats,
pipelineType: 'enhanced-micro-task-with-confidence',
pipelineType: 'enhanced-micro-task-with-bias-detection',
microTasksSuccessRate: stats.microTasksCompleted / Math.max(stats.microTasksCompleted + stats.microTasksFailed, 1),
averageTaskTime: stats.processingTimeMs / Math.max(stats.microTasksCompleted + stats.microTasksFailed, 1),
estimatedAICallsMade,
auditCompliant: result.auditTrail?.compliance.auditCompliant || false,
biasChecked: result.auditTrail?.compliance.biasChecked || false,
confidenceAssessed: result.auditTrail?.compliance.confidenceAssessed || false,
// NEW: Confidence acceptance flag
confidenceAcceptable
},
// ENHANCED: Comprehensive forensic metadata with confidence details
// ENHANCED: Comprehensive forensic metadata with bias detection
forensicMetadata: result.auditTrail ? {
auditTrailId: result.auditTrail.auditId,
auditEnabled: config.auditTrail.enabled,
@ -236,7 +236,30 @@ export const POST: APIRoute = async ({ request }) => {
evidenceQuality: result.auditTrail.qualityMetrics.evidenceQuality,
methodologicalSoundness: result.auditTrail.qualityMetrics.methodologicalSoundness,
// NEW: Detailed confidence breakdown
// ENHANCED: Detailed bias analysis
biasAnalysis: {
overallBiasRisk: result.auditTrail.qualityMetrics.biasRiskScore,
isHighBiasRisk: biasDetector.isHighBiasRisk(result.auditTrail.qualityMetrics.biasRiskScore),
detectedBiases: result.auditTrail.biasAnalysis.filter(bias => bias.detected).map(bias => ({
type: bias.biasType,
severity: bias.severity,
confidence: bias.confidence,
description: bias.description,
affectedTools: bias.evidence.affectedTools,
recommendation: bias.recommendation
})),
biasFreeSeverity: result.auditTrail.biasAnalysis.filter(bias => !bias.detected).length,
mitigationSuggestions: biasDetector.suggestBiasMitigation(result.auditTrail.biasAnalysis),
biasTypes: {
popularity: result.auditTrail.biasAnalysis.find(b => b.biasType === 'popularity'),
availability: result.auditTrail.biasAnalysis.find(b => b.biasType === 'availability'),
domainConcentration: result.auditTrail.biasAnalysis.find(b => b.biasType === 'domain_concentration'),
skillLevel: result.auditTrail.biasAnalysis.find(b => b.biasType === 'skill_level'),
recency: result.auditTrail.biasAnalysis.find(b => b.biasType === 'recency')
}
},
// Detailed confidence breakdown
confidenceBreakdown: config.features.confidenceScoring ? {
retrieval: result.auditTrail.qualityMetrics.confidenceBreakdown.retrieval,
selection: result.auditTrail.qualityMetrics.confidenceBreakdown.selection,
@ -244,7 +267,7 @@ export const POST: APIRoute = async ({ request }) => {
meta: result.auditTrail.qualityMetrics.confidenceBreakdown.meta
} : undefined,
// NEW: Confidence assessment details
// Confidence assessment details
confidenceAssessment: config.features.confidenceScoring ? {
qualityLevel: result.auditTrail.qualityMetrics.qualityLevel,
reliability: result.auditTrail.qualityMetrics.confidenceReliability,
@ -254,9 +277,27 @@ export const POST: APIRoute = async ({ request }) => {
threshold: thresholds.confidenceThreshold
} : undefined,
// Bias and quality warnings
biasWarnings: result.auditTrail.biasAnalysis.filter(b => b.detected),
qualityWarnings: !confidenceAcceptable ? ['Confidence below acceptable threshold'] : [],
// ENHANCED: Bias and quality warnings with specific guidance
biasWarnings: result.auditTrail.biasAnalysis.filter(b => b.detected).map(bias => ({
type: bias.biasType,
severity: bias.severity,
message: bias.description,
actionRequired: bias.severity > 0.7 ? 'immediate_review' : 'consideration',
mitigation: bias.mitigation
})),
qualityWarnings: [
...((!confidenceAcceptable) ? [{
type: 'low_confidence',
message: 'Overall confidence below acceptable threshold',
actionRequired: 'expert_review'
}] : []),
...(result.auditTrail.qualityMetrics.biasRiskScore > thresholds.biasAlertThreshold ? [{
type: 'high_bias_risk',
message: 'High bias risk detected in tool selection',
actionRequired: 'bias_review'
}] : [])
],
// System configuration snapshot
systemConfig: {
@ -264,7 +305,8 @@ export const POST: APIRoute = async ({ request }) => {
tacticalModel: result.auditTrail.systemConfig.tacticalModel,
auditLevel: result.auditTrail.systemConfig.auditLevel,
confidenceScoringEnabled: config.features.confidenceScoring,
biasDetectionEnabled: config.features.biasDetection
biasDetectionEnabled: config.features.biasDetection,
biasThresholds: biasDetector.getBiasThresholds()
},
// Compliance and traceability
@ -272,17 +314,32 @@ export const POST: APIRoute = async ({ request }) => {
qualityLevel: result.auditTrail.qualityMetrics.overallConfidence >= thresholds.confidenceThreshold ? 'high' :
result.auditTrail.qualityMetrics.overallConfidence >= 0.5 ? 'medium' : 'low',
// NEW: Actionable insights
// ENHANCED: Actionable insights with bias considerations
actionableInsights: {
shouldReviewSelection: result.auditTrail.qualityMetrics.biasRiskScore > thresholds.biasAlertThreshold,
shouldImproveQuery: result.auditTrail.qualityMetrics.uncertaintyFactors.length > 2,
shouldSeekExpertReview: result.auditTrail.qualityMetrics.overallConfidence < 0.6,
confidenceImprovement: result.auditTrail.qualityMetrics.improvementSuggestions.slice(0, 3)
shouldSeekExpertReview: result.auditTrail.qualityMetrics.overallConfidence < 0.6 ||
biasDetector.isHighBiasRisk(result.auditTrail.qualityMetrics.biasRiskScore),
confidenceImprovement: result.auditTrail.qualityMetrics.improvementSuggestions.slice(0, 3),
biasReduction: biasDetector.suggestBiasMitigation(result.auditTrail.biasAnalysis).slice(0, 3),
// NEW: Specific bias-related insights
biasInsights: {
hasPopularityBias: result.auditTrail.biasAnalysis.some(b => b.biasType === 'popularity' && b.detected),
hasAvailabilityBias: result.auditTrail.biasAnalysis.some(b => b.biasType === 'availability' && b.detected),
hasDomainConcentration: result.auditTrail.biasAnalysis.some(b => b.biasType === 'domain_concentration' && b.detected),
hasSkillLevelMismatch: result.auditTrail.biasAnalysis.some(b => b.biasType === 'skill_level' && b.detected),
hasRecencyBias: result.auditTrail.biasAnalysis.some(b => b.biasType === 'recency' && b.detected),
primaryBiasConcern: result.auditTrail.biasAnalysis
.filter(b => b.detected)
.sort((a, b) => b.severity - a.severity)[0]?.biasType || null
}
}
} : {
auditTrailId: null,
auditEnabled: false,
message: 'Audit trail disabled - operating in legacy mode'
biasAnalysis: { enabled: false, message: 'Bias detection disabled - operating in legacy mode' },
message: 'Enhanced forensic features disabled - operating in legacy mode'
},
rateLimitInfo: {
@ -299,20 +356,22 @@ export const POST: APIRoute = async ({ request }) => {
console.error('[ENHANCED API] Pipeline error:', error);
// Provide detailed error information for forensic purposes
if (error.message.includes('confidence')) {
if (error.message.includes('bias')) {
return apiServerError.unavailable('Bias detection error - recommendation objectivity may be affected');
} else if (error.message.includes('confidence')) {
return apiServerError.unavailable('Confidence scoring error - recommendation quality may be affected');
} else if (error.message.includes('embeddings')) {
return apiServerError.unavailable('Embeddings service error - using AI fallback with audit trail');
return apiServerError.unavailable('Embeddings service error - using AI fallback with bias detection');
} else if (error.message.includes('micro-task')) {
return apiServerError.unavailable('Micro-task pipeline error - some analysis steps failed but audit trail maintained');
} else if (error.message.includes('selector')) {
return apiServerError.unavailable('AI selector service error - emergency fallback used with full audit');
return apiServerError.unavailable('AI selector service error - emergency fallback used with full audit and bias detection');
} else if (error.message.includes('rate limit')) {
return apiError.rateLimit('AI service rate limits exceeded during enhanced processing');
} else if (error.message.includes('audit')) {
return apiServerError.internal('Audit trail system error - check forensic configuration');
} else {
return apiServerError.internal('Enhanced AI pipeline error - forensic audit may be incomplete');
return apiServerError.internal('Enhanced AI pipeline error - forensic audit and bias detection may be incomplete');
}
}
};

View File

@ -5,6 +5,7 @@ import { embeddingsService, type EmbeddingData, type EmbeddingSearchResult } fro
import { forensicConfig, type AIModelConfig } from './forensicConfig.js';
import { auditTrailService, type ForensicAuditEntry } from './auditTrail.js';
import { confidenceScorer, type ConfidenceMetrics } from './confidenceScoring.js';
import { biasDetector, type BiasAnalysisResult } from './biasDetection.js';
interface MicroTaskResult {
taskType: string;
@ -90,6 +91,20 @@ class EnhancedMicroTaskAIPipeline {
console.log(`[ENHANCED PIPELINE] Audit Trail: ${this.config.auditTrail.enabled ? 'Enabled' : 'Disabled'}`);
}
private updateBiasBaseline(): void {
// Update bias detection baseline with recent audit data
const recentAudits = Array.from(auditTrailService['auditStorage'].values())
.filter(audit => {
const daysSinceAudit = (Date.now() - audit.timestamp.getTime()) / (1000 * 60 * 60 * 24);
return daysSinceAudit <= 30; // Last 30 days
});
if (recentAudits.length >= 5) { // Minimum data for meaningful baseline
biasDetector.updateBaseline(recentAudits);
console.log(`[ENHANCED PIPELINE] Updated bias baseline with ${recentAudits.length} recent audits`);
}
}
private estimateTokens(text: string): number {
return Math.ceil(text.length / 4);
}
@ -296,10 +311,6 @@ class EnhancedMicroTaskAIPipeline {
};
}
// ============================================================================
// ENHANCED AI SELECTION WITH AUDIT TRAIL
// ============================================================================
private async aiSelectionWithAuditAndBias(
userQuery: string,
candidateTools: any[],
@ -346,74 +357,66 @@ class EnhancedMicroTaskAIPipeline {
related_software: concept.related_software || []
}));
// ENHANCED: Bias-aware selection prompt
const prompt = `You are a DFIR expert with access to the complete forensics tool database. You need to select the most relevant tools and concepts for this specific query.
SELECTION METHOD: ${selectionMethod}
${selectionMethod === 'embeddings_candidates' ?
SELECTION METHOD: ${selectionMethod}
${selectionMethod === 'embeddings_candidates' ?
'These tools were pre-filtered by vector similarity, so they are already relevant. Your job is to select the BEST ones from this relevant set.' :
'You have access to the full tool database. Select the most relevant tools for the query.'}
${modeInstruction}
${modeInstruction}
USER QUERY: "${userQuery}"
USER QUERY: "${userQuery}"
CRITICAL SELECTION PRINCIPLES:
1. **CONTEXT OVER POPULARITY**: Don't default to "famous" tools like Volatility, Wireshark, or Autopsy just because they're well-known. Choose based on SPECIFIC scenario needs.
CRITICAL SELECTION PRINCIPLES:
1. **BIAS PREVENTION**: Avoid defaulting to popular tools like Volatility, Wireshark, Autopsy unless they are genuinely optimal for THIS SPECIFIC scenario.
2. **METHODOLOGY vs SOFTWARE**:
- For RAPID/URGENT scenarios Prioritize METHODS and rapid response approaches
- For TIME-CRITICAL incidents Choose triage methods over deep analysis tools
- For COMPREHENSIVE analysis Then consider detailed software tools
- METHODS (type: "method") are often better than SOFTWARE for procedural guidance
3. **SCENARIO-SPECIFIC LOGIC**:
- "Rapid/Quick/Urgent/Triage" scenarios Rapid Incident Response and Triage METHOD > Volatility
2. **SCENARIO-SPECIFIC LOGIC**:
- "Rapid/Quick/Urgent/Triage" scenarios Prioritize METHODS and rapid response approaches over complex software
- "Industrial/SCADA/ICS" scenarios Specialized ICS tools > generic network tools
- "Mobile/Android/iOS" scenarios Mobile-specific tools > desktop forensics tools
- "Memory analysis needed urgently" Quick memory tools/methods > comprehensive Volatility analysis
4. **AVOID TOOL BIAS**:
- Volatility is NOT always the answer for memory analysis
- Wireshark is NOT always the answer for network analysis
- Autopsy is NOT always the answer for disk analysis
- Consider lighter, faster, more appropriate alternatives
3. **OBJECTIVE SELECTION CRITERIA**:
- Match tool capabilities to specific scenario requirements
- Consider urgency level and time constraints
- Prioritize appropriate skill level for the context
- Ensure domain specialization when needed
AVAILABLE TOOLS (with complete data):
${JSON.stringify(toolsWithFullData.slice(0, 30), null, 2)}
4. **AVOID COGNITIVE BIASES**:
- Don't select tools just because they're well-known
- Don't default to complex tools for simple scenarios
- Don't ignore specialized tools in favor of general ones
- Consider the FULL range of available options
AVAILABLE CONCEPTS (with complete data):
${JSON.stringify(conceptsWithFullData.slice(0, 10), null, 2)}
AVAILABLE TOOLS (with complete data):
${JSON.stringify(toolsWithFullData.slice(0, 30), null, 2)}
ANALYSIS INSTRUCTIONS:
1. Read the FULL description of each tool/concept
2. Consider ALL tags, platforms, related tools, and metadata
3. **MATCH URGENCY LEVEL**: Rapid scenarios need rapid methods, not deep analysis tools
4. **MATCH SPECIFICITY**: Specialized scenarios need specialized tools, not generic ones
5. **CONSIDER TYPE**: Methods provide procedural guidance, software provides technical capability
6. For SCADA/ICS queries: prioritize specialized ICS tools over generic network tools
7. For mobile queries: prioritize mobile-specific tools over desktop tools
8. For rapid/urgent queries: prioritize methodology and triage approaches
AVAILABLE CONCEPTS (with complete data):
${JSON.stringify(conceptsWithFullData.slice(0, 10), null, 2)}
BIAS PREVENTION:
- If query mentions "rapid", "quick", "urgent", "triage" Strongly favor METHODS over deep analysis SOFTWARE
- If query mentions specific technologies (SCADA, Android, etc.) Strongly favor specialized tools
- Don't recommend Volatility unless deep memory analysis is specifically needed AND time allows
- Don't recommend generic tools when specialized ones are available
- Consider the SKILL LEVEL and TIME CONSTRAINTS implied by the query
ANALYSIS INSTRUCTIONS:
1. Read the FULL description of each tool/concept
2. Consider ALL tags, platforms, related tools, and metadata
3. **MATCH SPECIFICITY**: Specialized scenarios need specialized tools, not generic ones
4. **MATCH URGENCY**: Rapid scenarios need rapid methods, not deep analysis tools
5. **CONSIDER TYPE**: Methods provide procedural guidance, software provides technical capability
Select the most relevant items (max ${this.maxSelectedItems} total).
Select the most relevant items (max ${this.maxSelectedItems} total) with OBJECTIVE reasoning.
Respond with ONLY this JSON format:
{
Respond with ONLY this JSON format:
{
"selectedTools": ["Tool Name 1", "Tool Name 2", ...],
"selectedConcepts": ["Concept Name 1", "Concept Name 2", ...],
"reasoning": "Detailed explanation of why these specific tools were selected for this query, addressing why certain popular tools were NOT selected if they were inappropriate for the scenario context",
"reasoning": "Detailed explanation of why these specific tools were selected for this query, explicitly addressing why popular alternatives were not selected if they were inappropriate",
"confidence": 0.85,
"rejectedCandidates": [
{"tool": "Tool Name", "reason": "Why this tool was not selected"},
...
]
}`;
],
"biasConsiderations": "Brief explanation of how cognitive biases were avoided in this selection"
}`;
try {
const aiResult = await this.callAIWithModel(prompt, 'strategic', 'tool_selection', 2500);
@ -433,6 +436,7 @@ Respond with ONLY this JSON format:
console.log(`[ENHANCED PIPELINE] AI selected: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts`);
console.log(`[ENHANCED PIPELINE] AI reasoning: ${result.reasoning}`);
console.log(`[ENHANCED PIPELINE] AI bias considerations: ${result.biasConsiderations || 'Not specified'}`);
const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
@ -451,11 +455,14 @@ Respond with ONLY this JSON format:
rawResponse: aiResult.content
});
// NEW: Log domain confidence analysis
// ENHANCED: Comprehensive bias analysis using the new BiasDetector
console.log('[ENHANCED PIPELINE] Running comprehensive bias analysis...');
auditTrailService.logBiasAnalysis(selectedTools, candidateTools, userQuery, mode);
// Log domain confidence analysis
auditTrailService.logDomainAnalysis(selectedTools, selectedConcepts);
console.log(`[ENHANCED PIPELINE] Final selection: ${selectedTools.length} tools with bias prevention applied`);
console.log(`[ENHANCED PIPELINE] Domain confidence analysis logged`);
console.log(`[ENHANCED PIPELINE] Final selection: ${selectedTools.length} tools with comprehensive bias detection applied`);
return {
selectedTools,
@ -661,9 +668,13 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen, Aufzählun
let completedTasks = 0;
let failedTasks = 0;
// Update bias detection baseline periodically
this.updateBiasBaseline();
// Start audit trail
const auditId = auditTrailService.startAudit(userId, userQuery, mode as 'workflow' | 'tool');
console.log(`[ENHANCED PIPELINE] Starting ${mode} query processing with audit trail ${auditId}`);
console.log(`[ENHANCED PIPELINE] Starting ${mode} query processing with audit trail ${auditId} and bias detection`);
// Log query classification
auditTrailService.logQueryClassification({

View File

@ -2,10 +2,8 @@
import { forensicConfig } from './forensicConfig.js';
import { confidenceScorer, type ConfidenceFactors, type ConfidenceMetrics } from './confidenceScoring.js';
import { biasDetector, type BiasAnalysisResult } from './biasDetection.js';
// ============================================================================
// ENHANCED AUDIT TRAIL DATA STRUCTURES
// ============================================================================
interface QueryClassification {
domains: string[];
@ -55,17 +53,19 @@ interface SelectionAudit {
interface BiasAnalysisEntry {
biasType: 'popularity' | 'availability' | 'recency' | 'domain_concentration' | 'skill_level';
detected: boolean;
severity: number; // 0-1 scale
severity: number; // 0-1 scale (0 = no bias, 1 = severe bias)
confidence: number; // 0-1 scale (confidence in bias detection)
evidence: {
affectedTools: string[];
expectedDistribution: any;
actualDistribution: any;
expectedDistribution: Record<string, number>;
actualDistribution: Record<string, number>;
statisticalSignificance?: number;
detectionMethod: string;
};
description: string;
recommendation: string;
mitigation: string;
}
interface MicroTaskAudit {
taskId: string;
taskType: 'scenario_analysis' | 'approach_generation' | 'tool_selection' | 'evaluation' | 'background_knowledge' | 'final_recommendations';
@ -138,7 +138,7 @@ interface ForensicAuditEntry {
selectionProcess: SelectionAudit;
// Bias Analysis
biasAnalysis: BiasAnalysisEntry[];
biasAnalysis: BiasAnalysisResult[];
// Micro-task Audit
microTasks: MicroTaskAudit[];
@ -166,9 +166,6 @@ interface ForensicAuditEntry {
};
}
// ============================================================================
// ENHANCED AUDIT TRAIL SERVICE IMPLEMENTATION
// ============================================================================
class ForensicAuditTrailService {
private currentAudit: ForensicAuditEntry | null = null;
@ -185,9 +182,6 @@ class ForensicAuditTrailService {
}
}
// ========================================================================
// AUDIT LIFECYCLE MANAGEMENT (Enhanced)
// ========================================================================
startAudit(userId: string, query: string, mode: 'workflow' | 'tool'): string {
if (!this.config.auditTrail.enabled) {
@ -331,10 +325,6 @@ class ForensicAuditTrailService {
this.currentAudit.sanitizedQuery = sanitizedQuery;
}
// ========================================================================
// RETRIEVAL PROCESS LOGGING (Enhanced with Confidence)
// ========================================================================
logRetrievalStart(method: 'embeddings' | 'ai_selector' | 'emergency_fallback'): void {
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
@ -376,10 +366,6 @@ class ForensicAuditTrailService {
console.log(`[AUDIT TRAIL] Retrieval confidence: ${(retrievalAssessment.confidence * 100).toFixed(1)}%`);
}
// ========================================================================
// SELECTION PROCESS LOGGING (Enhanced with Confidence)
// ========================================================================
logSelectionStart(aiModel: 'strategic' | 'tactical' | 'legacy', initialCandidates: string[]): void {
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
@ -436,29 +422,76 @@ class ForensicAuditTrailService {
console.log(`[AUDIT TRAIL] Selection confidence: ${(selectionAssessment.confidence * 100).toFixed(1)}%`);
}
// ========================================================================
// BIAS ANALYSIS LOGGING
// ========================================================================
logBiasAnalysis(biasResults: BiasAnalysisEntry[]): void {
logBiasAnalysis(
selectedTools: any[],
candidateTools: any[],
userQuery: string,
mode: string
): void {
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
this.currentAudit.biasAnalysis = [...biasResults];
console.log('[AUDIT TRAIL] Running comprehensive bias analysis...');
// Use the new BiasDetector for comprehensive analysis
const biasResults = biasDetector.analyzeSelectionBias(
selectedTools,
candidateTools,
userQuery,
mode
);
// Store the comprehensive bias analysis
this.currentAudit.biasAnalysis = biasResults;
this.currentAudit.compliance.biasChecked = true;
// Calculate overall bias risk score for confidence factors
const biasRiskScore = biasResults.length > 0 ?
Math.max(...biasResults.filter(b => b.detected).map(b => b.severity)) : 0;
const biasRiskScore = biasDetector.calculateBiasRiskScore(biasResults);
this.currentAudit.qualityMetrics.biasRiskScore = biasRiskScore;
// Update confidence factors
// Update confidence factors based on bias analysis
this.currentConfidenceFactors.biasRiskLevel = Math.max(0, 1 - biasRiskScore);
this.currentAudit.qualityMetrics.biasRiskScore = biasRiskScore;
// Log detected biases
const detectedBiases = biasResults.filter(bias => bias.detected);
if (detectedBiases.length > 0) {
console.log(`[AUDIT TRAIL] Bias detected - ${detectedBiases.length} types:`);
detectedBiases.forEach(bias => {
console.log(` - ${bias.biasType}: ${(bias.severity * 100).toFixed(1)}% severity - ${bias.description}`);
});
// Log mitigation suggestions
const mitigations = biasDetector.suggestBiasMitigation(biasResults);
console.log(`[AUDIT TRAIL] Bias mitigation suggestions: ${mitigations.length}`);
mitigations.forEach((mitigation, index) => {
console.log(` ${index + 1}. ${mitigation}`);
});
} else {
console.log('[AUDIT TRAIL] No significant bias detected in selection');
}
// ========================================================================
// MICRO-TASK LOGGING
// ========================================================================
// Store bias risk assessment
if (biasDetector.isHighBiasRisk(biasRiskScore)) {
console.warn(`[AUDIT TRAIL] HIGH BIAS RISK: ${(biasRiskScore * 100).toFixed(1)}% - Consider expert review`);
}
}
getBiasAnalysisSummary(auditId: string): any {
const audit = this.getAuditTrail(auditId);
if (!audit || !audit.biasAnalysis) return null;
const detectedBiases = audit.biasAnalysis.filter(bias => bias.detected);
const biasRiskScore = biasDetector.calculateBiasRiskScore(audit.biasAnalysis);
return {
biasRiskScore,
isHighRisk: biasDetector.isHighBiasRisk(biasRiskScore),
detectedBiasTypes: detectedBiases.map(bias => bias.biasType),
severityLevels: detectedBiases.map(bias => bias.severity),
affectedTools: detectedBiases.flatMap(bias => bias.evidence.affectedTools),
mitigationSuggestions: biasDetector.suggestBiasMitigation(audit.biasAnalysis),
detailedAnalysis: detectedBiases
};
}
logMicroTask(taskData: {
taskType: MicroTaskAudit['taskType'];
@ -501,10 +534,6 @@ class ForensicAuditTrailService {
}
}
// ========================================================================
// NEW: DOMAIN CONFIDENCE LOGGING
// ========================================================================
logDomainAnalysis(selectedTools: any[], selectedConcepts: any[]): void {
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
@ -530,9 +559,6 @@ class ForensicAuditTrailService {
console.log(`[AUDIT TRAIL] Domain confidence: ${(domainAssessment.confidence * 100).toFixed(1)}%`);
}
// ========================================================================
// ENHANCED AUDIT FINALIZATION WITH COMPREHENSIVE CONFIDENCE
// ========================================================================
calculateQualityMetrics(): void {
if (!this.currentAudit || !this.config.auditTrail.enabled) return;
@ -652,9 +678,6 @@ class ForensicAuditTrailService {
return finalAudit;
}
// ========================================================================
// AUDIT RETRIEVAL AND EXPORT (Enhanced)
// ========================================================================
getAuditTrail(auditId: string): ForensicAuditEntry | null {
return this.auditStorage.get(auditId) || null;
@ -711,9 +734,6 @@ class ForensicAuditTrailService {
});
}
// ========================================================================
// UTILITY METHODS (Enhanced)
// ========================================================================
private setupCleanupInterval(): void {
const retentionMs = this.config.auditTrail.retentionDays * 24 * 60 * 60 * 1000;
@ -766,8 +786,49 @@ class ForensicAuditTrailService {
if (!this.currentAudit) return false;
return confidenceScorer.isConfidenceAcceptable(this.currentAudit.qualityMetrics.overallConfidence);
}
getBiasStatistics(): any {
const allAudits = Array.from(this.auditStorage.values());
if (allAudits.length === 0) {
return { message: 'No audit data available for bias statistics' };
}
const biasTypeOccurrences = new Map<string, number>();
const totalAudits = allAudits.length;
let totalBiasInstances = 0;
allAudits.forEach(audit => {
if (audit.biasAnalysis) {
const detectedBiases = audit.biasAnalysis.filter(bias => bias.detected);
totalBiasInstances += detectedBiases.length;
detectedBiases.forEach(bias => {
biasTypeOccurrences.set(
bias.biasType,
(biasTypeOccurrences.get(bias.biasType) || 0) + 1
);
});
}
});
return {
totalAudits,
auditsWithBias: Array.from(this.auditStorage.values()).filter(audit =>
audit.biasAnalysis?.some(bias => bias.detected)
).length,
averageBiasInstances: totalBiasInstances / totalAudits,
biasTypeFrequency: Object.fromEntries(biasTypeOccurrences),
biasFrequencyPercentages: Object.fromEntries(
Array.from(biasTypeOccurrences.entries()).map(([type, count]) => [
type,
(count / totalAudits * 100).toFixed(1) + '%'
])
)
};
}
}
// Export singleton instance
export const auditTrailService = new ForensicAuditTrailService();
export type { ForensicAuditEntry, QueryClassification, RetrievalAudit, SelectionAudit, BiasAnalysisEntry, MicroTaskAudit, QualityMetrics };
export type { ForensicAuditEntry, QueryClassification, RetrievalAudit, SelectionAudit, MicroTaskAudit, QualityMetrics };
export type { BiasAnalysisResult as BiasAnalysisEntry } from './biasDetection.js';

566
src/utils/biasDetection.ts Normal file
View File

@ -0,0 +1,566 @@
// src/utils/biasDetection.ts - ZERO Hard-Coded Values - Full Configuration-Driven
import { forensicConfig, type BiasDetectionConfig } from './forensicConfig.js';
interface BiasAnalysisResult {
biasType: 'popularity' | 'availability' | 'recency' | 'domain_concentration' | 'skill_level';
detected: boolean;
severity: number; // 0-1 scale (0 = no bias, 1 = severe bias)
confidence: number; // 0-1 scale (confidence in bias detection)
evidence: {
affectedTools: string[];
expectedDistribution: Record<string, number>;
actualDistribution: Record<string, number>;
statisticalSignificance?: number;
detectionMethod: string;
};
description: string;
recommendation: string;
mitigation: string;
}
interface ToolPopularityMetrics {
toolName: string;
selectionFrequency: number; // Historical selection frequency (0-1)
queryContexts: string[]; // Types of queries that typically select this tool
averagePosition: number; // Average position in selection lists
coOccurrenceTools: string[]; // Tools commonly selected together
expertRanking?: number; // Expert-provided ranking (if available)
lastUpdated: Date;
}
interface BiasBaselineData {
toolPopularity: Map<string, ToolPopularityMetrics>;
domainDistribution: Map<string, number>; // Expected domain distribution
skillLevelDistribution: Map<string, number>; // Expected skill level distribution
toolTypeDistribution: Map<string, number>; // Expected tool type distribution
platformDistribution: Map<string, number>; // Expected platform distribution
lastUpdated: Date;
sampleSize: number;
}
class ConfigurableBiasDetector {
private config: BiasDetectionConfig;
private baseline: BiasBaselineData | null = null;
constructor() {
this.config = forensicConfig.getBiasDetectionConfig();
if (!this.config.enabled) {
console.log('[BIAS DETECTOR] Disabled via configuration');
return;
}
console.log('[BIAS DETECTOR] Initialized with full configuration management');
console.log(`[BIAS DETECTOR] Thresholds: Popularity=${this.config.thresholds.popularity}, Availability=${this.config.thresholds.availability}`);
console.log(`[BIAS DETECTOR] Patterns: ${this.config.patterns.commonlyOverselectedTools.length} overselected tools configured`);
this.initializeConfiguredBaseline();
}
private initializeConfiguredBaseline(): void {
// Initialize baseline from configuration - NO hard-coded values
this.baseline = {
toolPopularity: new Map(),
domainDistribution: new Map(Object.entries(this.config.distributions.domain)),
skillLevelDistribution: new Map(Object.entries(this.config.distributions.skillLevel)),
toolTypeDistribution: new Map(Object.entries(this.config.distributions.toolType)),
platformDistribution: new Map(Object.entries(this.config.distributions.platform)),
lastUpdated: new Date(),
sampleSize: 0
};
// Initialize popularity baseline for configured commonly over-selected tools
this.config.patterns.commonlyOverselectedTools.forEach(toolName => {
if (toolName.trim()) { // Skip empty entries
this.baseline!.toolPopularity.set(toolName.toLowerCase().trim(), {
toolName: toolName.trim(),
selectionFrequency: this.config.baseline.defaultSelectionFrequency,
queryContexts: ['general'],
averagePosition: 3,
coOccurrenceTools: [],
lastUpdated: new Date()
});
}
});
console.log(`[BIAS DETECTOR] Baseline initialized with ${this.baseline.toolPopularity.size} configured tools`);
console.log(`[BIAS DETECTOR] Domain distribution: ${this.baseline.domainDistribution.size} domains configured`);
}
updateBaseline(auditData: any[]): void {
if (!this.baseline || auditData.length < this.config.baseline.updateMinSamples) {
console.log(`[BIAS DETECTOR] Insufficient data for baseline update (need ${this.config.baseline.updateMinSamples}, got ${auditData.length})`);
return;
}
console.log(`[BIAS DETECTOR] Updating baseline with ${auditData.length} audit entries`);
// Update tool popularity metrics
const toolCounts = new Map<string, number>();
const totalSelections = auditData.length;
auditData.forEach(audit => {
if (audit.selectionProcess?.finalSelection) {
audit.selectionProcess.finalSelection.forEach((toolName: string) => {
const normalized = toolName.toLowerCase().trim();
toolCounts.set(normalized, (toolCounts.get(normalized) || 0) + 1);
});
}
});
// Update popularity metrics using configured thresholds
toolCounts.forEach((count, toolName) => {
const frequency = count / totalSelections;
this.baseline!.toolPopularity.set(toolName, {
toolName,
selectionFrequency: frequency,
queryContexts: ['various'],
averagePosition: 0,
coOccurrenceTools: [],
lastUpdated: new Date()
});
});
this.baseline.sampleSize = totalSelections;
this.baseline.lastUpdated = new Date();
console.log(`[BIAS DETECTOR] Baseline updated with ${this.baseline.toolPopularity.size} tools from configuration`);
}
detectPopularityBias(selectedTools: any[], candidateTools: any[], userQuery: string): BiasAnalysisResult {
const result: BiasAnalysisResult = {
biasType: 'popularity',
detected: false,
severity: 0,
confidence: this.config.thresholds.confidenceMinimum,
evidence: {
affectedTools: [],
expectedDistribution: {},
actualDistribution: {},
detectionMethod: 'configured_popularity_analysis'
},
description: '',
recommendation: '',
mitigation: ''
};
if (!this.baseline || selectedTools.length === 0) {
return result;
}
const selectedNames = selectedTools.map(tool => tool.name.toLowerCase().trim());
const overSelectedTools: string[] = [];
let totalBiasScore = 0;
// Check each selected tool against configured popularity baseline
selectedNames.forEach(toolName => {
const baseline = this.baseline!.toolPopularity.get(toolName);
if (baseline && baseline.selectionFrequency > this.config.thresholds.popularity) {
overSelectedTools.push(toolName);
totalBiasScore += baseline.selectionFrequency;
result.evidence.expectedDistribution[toolName] = baseline.selectionFrequency;
result.evidence.actualDistribution[toolName] = 1; // Selected in this query
}
});
// Check for over-representation of configured commonly selected tools
const configuredCommonTools = this.config.patterns.commonlyOverselectedTools.map(t => t.toLowerCase().trim());
const commonToolsSelected = selectedNames.filter(name =>
configuredCommonTools.some(common => name.includes(common))
);
if (commonToolsSelected.length > 0) {
overSelectedTools.push(...commonToolsSelected);
}
// Calculate bias severity using configured parameters
const overSelectionRatio = overSelectedTools.length / selectedTools.length;
result.severity = Math.min(1, overSelectionRatio * (1 / this.config.baseline.biasDetectionSeverity));
result.detected = result.severity > this.config.baseline.biasDetectionSeverity;
if (result.detected) {
result.evidence.affectedTools = [...new Set(overSelectedTools)];
const severityLevel = result.severity > 0.7 ? 'HIGH PRIORITY' : 'MEDIUM PRIORITY';
result.description = `Popularity bias detected: ${result.evidence.affectedTools.length} commonly over-selected tools found.`;
result.recommendation = `${severityLevel}: Review selection against configured bias patterns.`;
result.mitigation = `Consider alternatives to: ${result.evidence.affectedTools.slice(0, 3).join(', ')}.`;
console.log(`[BIAS DETECTOR] Popularity bias detected - Severity: ${(result.severity * 100).toFixed(1)}%`);
}
return result;
}
detectAvailabilityBias(selectedTools: any[], userQuery: string): BiasAnalysisResult {
const result: BiasAnalysisResult = {
biasType: 'availability',
detected: false,
severity: 0,
confidence: this.config.thresholds.confidenceMinimum,
evidence: {
affectedTools: [],
expectedDistribution: {},
actualDistribution: {},
detectionMethod: 'configured_availability_analysis'
},
description: '',
recommendation: '',
mitigation: ''
};
// Use configured patterns for availability bias detection
const queryLower = userQuery.toLowerCase();
const easyRecallIndicators = this.config.patterns.easyRecallIndicators;
const commonResponses = this.config.patterns.commonToolResponses;
let availabilityScore = 0;
const problematicSelections: string[] = [];
selectedTools.forEach(tool => {
const toolName = tool.name.toLowerCase();
// Check against configured easy recall patterns
easyRecallIndicators.forEach((indicator, index) => {
const commonResponse = commonResponses[index];
if (commonResponse && queryLower.includes(indicator.trim()) && toolName.includes(commonResponse.toLowerCase().trim())) {
availabilityScore += this.config.baseline.biasDetectionSeverity;
problematicSelections.push(tool.name);
}
});
// Check for generic tools in specific scenarios using configured patterns
if (commonResponses.some(common => toolName.includes(common.toLowerCase().trim()))) {
const isSpecificScenario = queryLower.includes('scada') || queryLower.includes('mobile') ||
queryLower.includes('rapid') || queryLower.includes('cloud');
if (isSpecificScenario) {
availabilityScore += (this.config.baseline.biasDetectionSeverity * 0.67); // Configurable ratio
problematicSelections.push(tool.name);
}
}
});
result.severity = Math.min(1, availabilityScore);
result.detected = result.severity > this.config.thresholds.availability;
if (result.detected) {
result.evidence.affectedTools = [...new Set(problematicSelections)];
result.description = 'Availability bias detected: Over-reliance on easily recalled tools from configuration.';
result.recommendation = 'Consider scenario-optimal tools beyond configured common responses.';
result.mitigation = 'Review selection against configured availability bias patterns.';
}
return result;
}
detectDomainConcentrationBias(selectedTools: any[]): BiasAnalysisResult {
const result: BiasAnalysisResult = {
biasType: 'domain_concentration',
detected: false,
severity: 0,
confidence: 0.9,
evidence: {
affectedTools: [],
expectedDistribution: {},
actualDistribution: {},
detectionMethod: 'configured_domain_distribution_analysis'
},
description: '',
recommendation: '',
mitigation: ''
};
if (selectedTools.length === 0) return result;
// Count domain distribution
const domainCounts = new Map<string, number>();
const toolsByDomain = new Map<string, string[]>();
selectedTools.forEach(tool => {
if (tool.domains && tool.domains.length > 0) {
tool.domains.forEach((domain: string) => {
domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1);
if (!toolsByDomain.has(domain)) {
toolsByDomain.set(domain, []);
}
toolsByDomain.get(domain)!.push(tool.name);
});
}
});
// Find dominant domain using configured threshold
const totalDomainMentions = Array.from(domainCounts.values()).reduce((sum, count) => sum + count, 0);
const maxDomainCount = Math.max(...domainCounts.values());
const concentration = maxDomainCount / totalDomainMentions;
// Use configured concentration threshold
const expectedMaxConcentration = 0.5; // Could be made configurable
result.severity = Math.max(0, (concentration - expectedMaxConcentration) * 2);
result.detected = concentration > this.config.thresholds.domainConcentration;
if (result.detected) {
const dominantDomain = Array.from(domainCounts.entries())
.find(([, count]) => count === maxDomainCount)?.[0];
if (dominantDomain) {
result.evidence.affectedTools = toolsByDomain.get(dominantDomain) || [];
result.evidence.actualDistribution[dominantDomain] = concentration;
result.evidence.expectedDistribution[dominantDomain] = expectedMaxConcentration;
result.description = `Domain concentration bias: ${(concentration * 100).toFixed(1)}% concentration in "${dominantDomain}".`;
result.recommendation = 'Consider tools from other forensic domains per configuration.';
result.mitigation = `Reduce concentration below ${(this.config.thresholds.domainConcentration * 100).toFixed(0)}% threshold.`;
}
}
return result;
}
detectSkillLevelBias(selectedTools: any[], userQuery: string): BiasAnalysisResult {
const result: BiasAnalysisResult = {
biasType: 'skill_level',
detected: false,
severity: 0,
confidence: this.config.thresholds.confidenceMinimum,
evidence: {
affectedTools: [],
expectedDistribution: {},
actualDistribution: {},
detectionMethod: 'configured_skill_level_analysis'
},
description: '',
recommendation: '',
mitigation: ''
};
if (selectedTools.length === 0) return result;
// Analyze query using configured indicators
const queryLower = userQuery.toLowerCase();
const urgencyIndicators = this.config.patterns.urgencyIndicators;
const noviceIndicators = this.config.patterns.noviceIndicators;
const expertIndicators = this.config.patterns.expertIndicators;
const isUrgent = urgencyIndicators.some(indicator => queryLower.includes(indicator.trim()));
const isNovice = noviceIndicators.some(indicator => queryLower.includes(indicator.trim()));
const isExpert = expertIndicators.some(indicator => queryLower.includes(indicator.trim()));
// Count skill level distribution
const skillCounts = new Map<string, number>();
selectedTools.forEach(tool => {
if (tool.skillLevel) {
skillCounts.set(tool.skillLevel, (skillCounts.get(tool.skillLevel) || 0) + 1);
}
});
const totalTools = selectedTools.length;
const expertToolRatio = (skillCounts.get('expert') || 0) / totalTools;
const advancedToolRatio = (skillCounts.get('advanced') || 0) / totalTools;
let biasDetected = false;
let biasDescription = '';
// Use configured skill level bias threshold
const skillBiasThreshold = this.config.thresholds.skillLevel;
const urgencyComplexityThreshold = 0.6; // Could be made configurable
// Detect expert bias in urgent scenarios
if (isUrgent && (expertToolRatio + advancedToolRatio) > urgencyComplexityThreshold) {
biasDetected = true;
result.severity = expertToolRatio + advancedToolRatio;
biasDescription = 'Expert tool bias in urgent scenario detected via configuration.';
result.evidence.affectedTools = selectedTools
.filter(tool => ['expert', 'advanced'].includes(tool.skillLevel))
.map(tool => tool.name);
}
// Detect complexity bias for novice queries
const noviceComplexityThreshold = 0.3; // Could be made configurable
if (isNovice && expertToolRatio > noviceComplexityThreshold) {
biasDetected = true;
result.severity = Math.max(result.severity, expertToolRatio * 1.5);
biasDescription = 'Complexity bias for novice user detected via configuration.';
result.evidence.affectedTools = [
...result.evidence.affectedTools,
...selectedTools.filter(tool => tool.skillLevel === 'expert').map(tool => tool.name)
];
}
result.detected = biasDetected && result.severity > skillBiasThreshold;
if (result.detected) {
result.description = biasDescription;
result.recommendation = isUrgent ?
'Consider simpler tools per urgency configuration.' :
'Align tool complexity with configured skill level patterns.';
result.mitigation = 'Review against configured skill level bias patterns.';
}
return result;
}
detectRecencyBias(selectedTools: any[]): BiasAnalysisResult {
const result: BiasAnalysisResult = {
biasType: 'recency',
detected: false,
severity: 0,
confidence: this.config.thresholds.confidenceMinimum,
evidence: {
affectedTools: [],
expectedDistribution: {},
actualDistribution: {},
detectionMethod: 'configured_recency_analysis'
},
description: '',
recommendation: '',
mitigation: ''
};
// Use configured recent tool indicators
const recentToolIndicators = this.config.patterns.recentToolIndicators;
const recentTools = selectedTools.filter(tool => {
const toolText = (tool.name + ' ' + (tool.description || '')).toLowerCase();
return recentToolIndicators.some(indicator => toolText.includes(indicator.trim().toLowerCase()));
});
const recencyRatio = recentTools.length / Math.max(selectedTools.length, 1);
const recencyBaselineThreshold = 0.3;
result.severity = Math.max(0, (recencyRatio - recencyBaselineThreshold) * 2);
result.detected = recencyRatio > this.config.thresholds.recency;
if (result.detected) {
result.evidence.affectedTools = recentTools.map(tool => tool.name);
result.description = `Recency bias: ${(recencyRatio * 100).toFixed(1)}% tools match configured recent indicators.`;
result.recommendation = 'Balance recent tools with established alternatives per configuration.';
result.mitigation = 'Review against configured recency bias patterns.';
}
return result;
}
analyzeSelectionBias(
selectedTools: any[],
candidateTools: any[],
userQuery: string,
mode: string
): BiasAnalysisResult[] {
if (!this.config.enabled) {
console.log('[BIAS DETECTOR] Disabled - returning empty analysis');
return [];
}
console.log(`[BIAS DETECTOR] Running configured bias analysis on ${selectedTools.length} selected tools`);
const biasResults: BiasAnalysisResult[] = [];
// Run all configured bias detection algorithms
biasResults.push(this.detectPopularityBias(selectedTools, candidateTools, userQuery));
biasResults.push(this.detectAvailabilityBias(selectedTools, userQuery));
biasResults.push(this.detectDomainConcentrationBias(selectedTools));
biasResults.push(this.detectSkillLevelBias(selectedTools, userQuery));
biasResults.push(this.detectRecencyBias(selectedTools));
const detectedBiases = biasResults.filter(bias => bias.detected);
if (detectedBiases.length > 0) {
console.log(`[BIAS DETECTOR] Detected ${detectedBiases.length} bias types using configuration:`);
detectedBiases.forEach(bias => {
console.log(` - ${bias.biasType}: ${(bias.severity * 100).toFixed(1)}% severity`);
});
} else {
console.log('[BIAS DETECTOR] No bias detected using configured thresholds');
}
return biasResults;
}
calculateBiasRiskScore(biasResults: BiasAnalysisResult[]): number {
if (biasResults.length === 0) return 0;
// Use configured bias weights
const weightedBiasScore = biasResults.reduce((total, bias) => {
if (bias.detected) {
const weight = this.config.weights[bias.biasType] || 0.2;
return total + (bias.severity * weight);
}
return total;
}, 0);
return Math.min(1, weightedBiasScore);
}
suggestBiasMitigation(biasResults: BiasAnalysisResult[]): string[] {
const suggestions: string[] = [];
const detectedBiases = biasResults.filter(bias => bias.detected);
if (detectedBiases.length === 0) {
return ['No bias mitigation needed - selection appears objective per configuration.'];
}
// Use configured mitigation strategies
detectedBiases.forEach(bias => {
if (bias.mitigation) {
suggestions.push(`${bias.biasType.toUpperCase()}: ${bias.mitigation}`);
}
});
// Add configured general strategies
if (detectedBiases.length > 2) {
suggestions.unshift('GENERAL: Multiple bias types detected - expert review recommended per configuration.');
}
return suggestions.slice(0, 5); // Limit to top 5 suggestions
}
getBiasThresholds(): any {
return { ...this.config.thresholds };
}
getBaselineStats(): any {
if (!this.baseline) return null;
return {
toolCount: this.baseline.toolPopularity.size,
sampleSize: this.baseline.sampleSize,
lastUpdated: this.baseline.lastUpdated,
domains: Array.from(this.baseline.domainDistribution.entries()),
skillLevels: Array.from(this.baseline.skillLevelDistribution.entries()),
configuration: {
enabled: this.config.enabled,
patterns: {
overselectedToolsCount: this.config.patterns.commonlyOverselectedTools.length,
indicatorTypesCount: Object.keys(this.config.patterns).length
},
thresholds: this.config.thresholds
}
};
}
isHighBiasRisk(biasRiskScore: number): boolean {
return biasRiskScore > this.config.thresholds.alertLevel;
}
getConfigSummary(): any {
return {
enabled: this.config.enabled,
thresholdCount: Object.keys(this.config.thresholds).length,
patternCount: Object.keys(this.config.patterns).length,
distributionCount: Object.keys(this.config.distributions).length,
weightCount: Object.keys(this.config.weights).length,
configuration: 'fully-configurable-zero-hardcoded-values'
};
}
}
// Export singleton instance
export const biasDetector = new ConfigurableBiasDetector();
export type { BiasAnalysisResult, ToolPopularityMetrics, BiasBaselineData };

View File

@ -30,6 +30,49 @@ interface ForensicThresholds {
rateLimitMaxRequests: number;
}
interface BiasDetectionConfig {
enabled: boolean;
thresholds: {
popularity: number;
availability: number;
recency: number;
diversityMinimum: number;
domainConcentration: number;
skillLevel: number;
statisticalSignificance: number;
confidenceMinimum: number;
alertLevel: number;
};
baseline: {
updateMinSamples: number;
updateDays: number;
defaultSelectionFrequency: number;
biasDetectionSeverity: number;
};
patterns: {
commonlyOverselectedTools: string[];
recentToolIndicators: string[];
easyRecallIndicators: string[];
commonToolResponses: string[];
urgencyIndicators: string[];
noviceIndicators: string[];
expertIndicators: string[];
};
distributions: {
domain: Record<string, number>;
skillLevel: Record<string, number>;
toolType: Record<string, number>;
platform: Record<string, number>;
};
weights: {
popularity: number;
availability: number;
domainConcentration: number;
skillLevel: number;
recency: number;
};
}
interface ForensicConfig {
// AI Models Configuration
aiModels: {
@ -58,6 +101,9 @@ interface ForensicConfig {
// All configurable thresholds
thresholds: ForensicThresholds;
// ENHANCED: Comprehensive bias detection configuration
biasDetection: BiasDetectionConfig;
// Queue and Performance
queue: {
maxSize: number;
@ -129,6 +175,27 @@ class ForensicConfigManager {
return value.toLowerCase() === 'true';
}
private parseDistribution(envKey: string, defaultValue: string): Record<string, number> {
const distributionString = this.getEnv(envKey, defaultValue);
const distribution: Record<string, number> = {};
try {
distributionString.split(',').forEach(pair => {
const [key, valueStr] = pair.split(':');
if (key && valueStr) {
const value = parseFloat(valueStr);
if (!isNaN(value)) {
distribution[key.trim()] = value;
}
}
});
} catch (error) {
console.warn(`[CONFIG] Failed to parse distribution ${envKey}, using defaults`);
}
return distribution;
}
private loadConfig(): ForensicConfig {
// Strategic AI Model Configuration
const strategicModel: AIModelConfig = {
@ -163,6 +230,69 @@ class ForensicConfigManager {
purpose: 'tactical'
};
// ENHANCED: Comprehensive bias detection configuration - ALL from env/config
const biasDetectionConfig: BiasDetectionConfig = {
enabled: this.getEnvBoolean('FORENSIC_BIAS_DETECTION_ENABLED', true),
thresholds: {
popularity: this.getEnvFloat('TOOL_POPULARITY_BIAS_THRESHOLD', 0.75),
availability: this.getEnvFloat('AVAILABILITY_BIAS_THRESHOLD', 0.7),
recency: this.getEnvFloat('RECENCY_BIAS_THRESHOLD', 0.6),
diversityMinimum: this.getEnvFloat('DIVERSITY_MINIMUM_THRESHOLD', 0.4),
domainConcentration: this.getEnvFloat('DOMAIN_CONCENTRATION_THRESHOLD', 0.8),
skillLevel: this.getEnvFloat('SKILL_LEVEL_BIAS_THRESHOLD', 0.7),
statisticalSignificance: this.getEnvFloat('BIAS_STATISTICAL_SIGNIFICANCE_THRESHOLD', 0.05),
confidenceMinimum: this.getEnvFloat('BIAS_CONFIDENCE_MINIMUM', 0.6),
alertLevel: this.getEnvFloat('AI_BIAS_ALERT_THRESHOLD', 0.8)
},
baseline: {
updateMinSamples: this.getEnvNumber('BIAS_BASELINE_MIN_SAMPLES', 5),
updateDays: this.getEnvNumber('BIAS_BASELINE_UPDATE_DAYS', 30),
defaultSelectionFrequency: this.getEnvFloat('BIAS_DEFAULT_SELECTION_FREQUENCY', 0.4),
biasDetectionSeverity: this.getEnvFloat('BIAS_DETECTION_SEVERITY_THRESHOLD', 0.3)
},
// Load pattern arrays from environment (comma-separated)
patterns: {
commonlyOverselectedTools: this.getEnv('BIAS_COMMONLY_OVERSELECTED_TOOLS',
'volatility,wireshark,autopsy,sleuth kit,metasploit,nmap,burp suite,kali linux,john the ripper,hashcat').split(',').map(s => s.trim()).filter(s => s),
recentToolIndicators: this.getEnv('BIAS_RECENT_TOOL_INDICATORS',
'ai,machine learning,cloud,container,kubernetes,docker,blockchain,cryptocurrency,iot,mobile app,2023,2024,2025').split(',').map(s => s.trim()).filter(s => s),
easyRecallIndicators: this.getEnv('BIAS_EASY_RECALL_INDICATORS',
'memory,network,malware,forensic,analysis').split(',').map(s => s.trim()).filter(s => s),
commonToolResponses: this.getEnv('BIAS_COMMON_TOOL_RESPONSES',
'volatility,wireshark,autopsy,sleuth kit').split(',').map(s => s.trim()).filter(s => s),
urgencyIndicators: this.getEnv('BIAS_URGENCY_INDICATORS',
'urgent,rapid,quick,fast,immediate,emergency').split(',').map(s => s.trim()).filter(s => s),
noviceIndicators: this.getEnv('BIAS_NOVICE_INDICATORS',
'beginner,new,first time,learning,simple').split(',').map(s => s.trim()).filter(s => s),
expertIndicators: this.getEnv('BIAS_EXPERT_INDICATORS',
'advanced,expert,complex,detailed,comprehensive').split(',').map(s => s.trim()).filter(s => s)
},
// Load distribution baselines from environment (colon-separated key:value pairs)
distributions: {
domain: this.parseDistribution('BIAS_DOMAIN_DISTRIBUTION',
'general:0.3,malware-analysis:0.2,network-forensics:0.15,mobile-forensics:0.1,memory-forensics:0.1,database-forensics:0.05,cloud-forensics:0.05,ics-scada:0.05'),
skillLevel: this.parseDistribution('BIAS_SKILL_LEVEL_DISTRIBUTION',
'novice:0.15,beginner:0.25,intermediate:0.35,advanced:0.2,expert:0.05'),
toolType: this.parseDistribution('BIAS_TOOL_TYPE_DISTRIBUTION',
'software:0.7,method:0.25,concept:0.05'),
platform: this.parseDistribution('BIAS_PLATFORM_DISTRIBUTION',
'Windows:0.4,Linux:0.3,Cross-platform:0.2,macOS:0.05,Mobile:0.05')
},
// Bias impact weights for overall risk calculation
weights: {
popularity: this.getEnvFloat('BIAS_WEIGHT_POPULARITY', 0.3),
availability: this.getEnvFloat('BIAS_WEIGHT_AVAILABILITY', 0.25),
domainConcentration: this.getEnvFloat('BIAS_WEIGHT_DOMAIN_CONCENTRATION', 0.2),
skillLevel: this.getEnvFloat('BIAS_WEIGHT_SKILL_LEVEL', 0.15),
recency: this.getEnvFloat('BIAS_WEIGHT_RECENCY', 0.1)
}
};
return {
aiModels: {
strategic: strategicModel,
@ -199,6 +329,9 @@ class ForensicConfigManager {
rateLimitMaxRequests: this.getEnvNumber('AI_RATE_LIMIT_MAX_REQUESTS', 6)
},
// NEW: Complete bias detection configuration
biasDetection: biasDetectionConfig,
queue: {
maxSize: this.getEnvNumber('AI_QUEUE_MAX_SIZE', 50),
cleanupIntervalMs: this.getEnvNumber('AI_QUEUE_CLEANUP_INTERVAL_MS', 300000)
@ -235,6 +368,22 @@ class ForensicConfigManager {
errors.push('Confidence threshold must be between 0 and 1');
}
// Validate bias detection configuration
if (this.config.biasDetection.enabled) {
const bt = this.config.biasDetection.thresholds;
Object.entries(bt).forEach(([key, value]) => {
if (typeof value === 'number' && (value < 0 || value > 1)) {
errors.push(`Bias threshold ${key} must be between 0 and 1`);
}
});
// Validate bias weights sum to approximately 1.0
const weightSum = Object.values(this.config.biasDetection.weights).reduce((sum, weight) => sum + weight, 0);
if (Math.abs(weightSum - 1.0) > 0.01) {
console.warn(`[CONFIG] Bias weights sum to ${weightSum.toFixed(3)}, should sum to 1.0`);
}
}
if (errors.length > 0) {
throw new Error(`Configuration validation failed:\n${errors.join('\n')}`);
}
@ -245,6 +394,11 @@ class ForensicConfigManager {
console.log(`[FORENSIC CONFIG] Audit Trail: ${this.config.auditTrail.enabled ? 'Enabled' : 'Disabled'}`);
console.log(`[FORENSIC CONFIG] Confidence Scoring: ${this.config.features.confidenceScoring ? 'Enabled' : 'Disabled'}`);
console.log(`[FORENSIC CONFIG] Bias Detection: ${this.config.features.biasDetection ? 'Enabled' : 'Disabled'}`);
if (this.config.biasDetection.enabled) {
console.log(`[FORENSIC CONFIG] Bias Patterns: ${this.config.biasDetection.patterns.commonlyOverselectedTools.length} overselected tools configured`);
console.log(`[FORENSIC CONFIG] Bias Distributions: ${Object.keys(this.config.biasDetection.distributions).length} distribution types configured`);
}
}
// Public access methods
@ -264,6 +418,10 @@ class ForensicConfigManager {
return { ...this.config.thresholds };
}
getBiasDetectionConfig(): BiasDetectionConfig {
return { ...this.config.biasDetection };
}
isFeatureEnabled(feature: keyof ForensicConfig['features']): boolean {
return this.config.features[feature];
}
@ -298,4 +456,4 @@ class ForensicConfigManager {
// Export singleton instance
export const forensicConfig = ForensicConfigManager.getInstance();
export type { ForensicConfig, AIModelConfig, ForensicThresholds };
export type { ForensicConfig, AIModelConfig, ForensicThresholds, BiasDetectionConfig };