Skip to main content
Glama
bias-monitoring-system.ts13.1 kB
/** * BiasMonitoringSystem - Real-time continuous bias monitoring * * Provides continuous monitoring of reasoning chains for bias detection, * real-time alert generation, and performance overhead measurement. * * Achieves <15% performance overhead while detecting biases within 2-3 seconds. */ import type { BiasPatternRecognizer } from "./bias-pattern-recognizer"; import type { AlertPriority, BiasAlert, BiasType, DetectedBias, MonitoringConfig, MonitoringMetrics, ReasoningChain, } from "./types"; /** * Default monitoring configuration */ const DEFAULT_CONFIG: Required<MonitoringConfig> = { alertThreshold: 0.5, maxProcessingTime: 3000, enableCaching: true, debounceMs: 100, }; /** * BiasMonitoringSystem class * * Continuously monitors reasoning chains for cognitive biases, * generates real-time alerts, and tracks performance metrics. */ export class BiasMonitoringSystem { private readonly recognizer: BiasPatternRecognizer; private readonly config: Required<MonitoringConfig>; private stopped: boolean = false; // Metrics tracking private totalChains: number = 0; private totalBiases: number = 0; private totalAlerts: number = 0; private processingTimes: number[] = []; private alertsByType: Map<BiasType, number> = new Map(); private alertsBySeverity: Map<AlertPriority, number> = new Map(); // Alert deduplication tracking private generatedAlertIds: Set<string> = new Set(); private lastDetectedBiases: Map<string, DetectedBias[]> = new Map(); /** * Create a new BiasMonitoringSystem * * @param recognizer - BiasPatternRecognizer instance for bias detection * @param config - Optional monitoring configuration */ constructor(recognizer: BiasPatternRecognizer, config?: MonitoringConfig) { this.recognizer = recognizer; this.config = { ...DEFAULT_CONFIG, ...config }; } /** * Monitor a reasoning chain continuously * * Performs async bias detection without blocking the main thread. * Completes within maxProcessingTime configuration. * * @param reasoning - The reasoning chain to monitor */ async monitorContinuously(reasoning: ReasoningChain): Promise<void> { // Check if stopped if (this.stopped) { return; } const startTime = Date.now(); try { // Validate input if (!reasoning?.id) { this.totalChains++; // Still track processing time even for invalid chains const processingTime = Math.max(1, Date.now() - startTime); this.processingTimes.push(processingTime); if (this.processingTimes.length > 100) { this.processingTimes.shift(); } return; } // Detect biases asynchronously const biases = await this.detectBiasesAsync(reasoning); // Update metrics this.totalChains++; this.totalBiases += biases.length; // Track processing time (ensure at least 1ms to avoid division by zero) const processingTime = Math.max(1, Date.now() - startTime); this.processingTimes.push(processingTime); // Keep only last 100 processing times for metrics if (this.processingTimes.length > 100) { this.processingTimes.shift(); } // Store detected biases for alert generation this.lastDetectedBiases.set(reasoning.id, biases); } catch { // Handle errors gracefully - don't crash this.totalChains++; // Track processing time even on error const processingTime = Math.max(1, Date.now() - startTime); this.processingTimes.push(processingTime); if (this.processingTimes.length > 100) { this.processingTimes.shift(); } } } /** * Detect biases asynchronously * * Wraps synchronous bias detection in async to avoid blocking. * * @param reasoning - The reasoning chain to analyze * @returns Promise resolving to detected biases */ private async detectBiasesAsync(reasoning: ReasoningChain): Promise<DetectedBias[]> { // Use setImmediate to yield to event loop return new Promise((resolve) => { setImmediate(() => { try { const biases = this.recognizer.detectBiases(reasoning); resolve(biases); } catch { // Return empty array on error resolve([]); } }); }); } /** * Track alert metrics by type and severity * * @param bias - The detected bias * @param severity - Assessed severity */ private trackAlertMetrics(bias: DetectedBias, severity: number): void { // Track by type const typeCount = this.alertsByType.get(bias.type) ?? 0; this.alertsByType.set(bias.type, typeCount + 1); // Track by severity const priority = this.calculatePriority(severity); const severityCount = this.alertsBySeverity.get(priority) ?? 0; this.alertsBySeverity.set(priority, severityCount + 1); } /** * Calculate alert priority from severity * * @param severity - Severity score (0-1) * @returns Alert priority level */ private calculatePriority(severity: number): AlertPriority { if (severity >= 0.8) return "critical"; if (severity >= 0.6) return "high"; if (severity >= 0.4) return "medium"; return "low"; } /** * Generate real-time alerts for detected biases * * Creates alerts for biases that meet the configured threshold. * Alerts are deduplicated and prioritized by severity. * * @param reasoning - The reasoning chain to generate alerts for * @returns Array of bias alerts, sorted by priority (highest first) */ generateRealTimeAlerts(reasoning: ReasoningChain): BiasAlert[] { // Get detected biases for this chain const biases = this.lastDetectedBiases.get(reasoning.id) ?? []; if (biases.length === 0) { return []; } const alerts: BiasAlert[] = []; for (const bias of biases) { // Assess severity using recognizer const severity = this.recognizer.assessBiasSeverity(bias); // Only generate alerts above threshold if (severity < this.config.alertThreshold) { continue; } // Calculate priority from severity const priority = this.calculatePriority(severity); // Generate unique alert ID const alertId = this.generateAlertId(bias, reasoning.id); // Check for deduplication if (this.generatedAlertIds.has(alertId)) { continue; } // Determine if alert is actionable (high severity) const actionable = severity >= 0.6; // Generate recommendations for actionable alerts const recommendations = actionable ? this.generateRecommendations(bias) : undefined; // Create alert const alert: BiasAlert = { id: alertId, bias, severity, priority, timestamp: new Date(), message: this.generateAlertMessage(bias, severity), actionable, recommendations, }; alerts.push(alert); // Track alert for deduplication this.generatedAlertIds.add(alertId); // Update metrics this.totalAlerts++; this.trackAlertMetrics(bias, severity); } // Sort alerts by priority (critical > high > medium > low) const priorityOrder: Record<string, number> = { critical: 4, high: 3, medium: 2, low: 1, }; alerts.sort((a, b) => priorityOrder[b.priority] - priorityOrder[a.priority]); return alerts; } /** * Generate unique alert ID * * Creates a deterministic ID based on bias type, location, and chain ID. * * @param bias - The detected bias * @param chainId - The reasoning chain ID * @returns Unique alert ID */ private generateAlertId(bias: DetectedBias, chainId: string): string { const locationKey = `${bias.location.stepIndex}-${bias.location.reasoning.substring(0, 20)}`; return `alert-${chainId}-${bias.type}-${locationKey}`; } /** * Generate human-readable alert message * * Creates a descriptive message explaining the detected bias. * * @param bias - The detected bias * @param severity - Assessed severity * @returns Alert message */ private generateAlertMessage(bias: DetectedBias, severity: number): string { const severityLabel = severity >= 0.8 ? "Critical" : severity >= 0.6 ? "High" : severity >= 0.4 ? "Medium" : "Low"; const biasName = bias.type.replace(/_/g, " "); return `${severityLabel} severity ${biasName} bias detected: ${bias.explanation}`; } /** * Generate recommendations for addressing a bias * * Provides actionable recommendations based on bias type. * * @param bias - The detected bias * @returns Array of recommendations */ private generateRecommendations(bias: DetectedBias): string[] { const recommendations: string[] = []; switch (bias.type) { case "confirmation": recommendations.push("Actively seek evidence that contradicts your hypothesis"); recommendations.push("Consider alternative explanations for the same evidence"); recommendations.push("Apply devil's advocate reasoning to challenge assumptions"); break; case "anchoring": recommendations.push("Re-evaluate estimates without reference to initial values"); recommendations.push("Consider a wider range of possible outcomes"); recommendations.push("Seek independent estimates from multiple sources"); break; case "availability": recommendations.push("Gather statistical data rather than relying on memorable examples"); recommendations.push("Consider base rates and broader context"); recommendations.push("Actively search for less memorable but relevant information"); break; case "recency": recommendations.push("Review historical data and long-term trends"); recommendations.push("Weight evidence by relevance, not recency"); recommendations.push("Consider whether recent events are representative"); break; case "representativeness": recommendations.push("Consider base rates and prior probabilities"); recommendations.push("Avoid stereotyping based on superficial similarities"); recommendations.push("Gather more data before drawing conclusions"); break; case "framing": recommendations.push("Reframe the problem in multiple ways"); recommendations.push("Consider both positive and negative framings"); recommendations.push("Focus on objective facts rather than presentation"); break; case "sunk_cost": recommendations.push("Evaluate decisions based on future costs and benefits only"); recommendations.push("Ignore past investments that cannot be recovered"); recommendations.push("Consider opportunity costs of continuing"); break; case "attribution": recommendations.push("Consider situational factors affecting behavior"); recommendations.push("Apply the same standards to yourself and others"); recommendations.push("Avoid fundamental attribution error"); break; default: recommendations.push("Review reasoning for potential bias"); recommendations.push("Seek diverse perspectives"); recommendations.push("Apply systematic thinking frameworks"); } return recommendations; } /** * Stop monitoring * * Prevents further processing of reasoning chains. */ stop(): void { this.stopped = true; } /** * Get monitoring metrics * * Returns current performance and activity metrics. * * @returns Monitoring metrics */ getMetrics(): MonitoringMetrics { const averageProcessingTime = this.processingTimes.length > 0 ? this.processingTimes.reduce((sum, time) => sum + time, 0) / this.processingTimes.length : 0; return { totalChains: this.totalChains, totalBiases: this.totalBiases, totalAlerts: this.totalAlerts, averageProcessingTime, overheadPercentage: this.calculateOverheadPercentage(), alertsByType: new Map(this.alertsByType), alertsBySeverity: new Map(this.alertsBySeverity), }; } /** * Measure performance overhead * * Calculates monitoring overhead as percentage of total processing time. * * @returns Overhead percentage (0-100) */ measurePerformanceOverhead(): number { return this.calculateOverheadPercentage(); } /** * Calculate overhead percentage * * @returns Overhead percentage */ private calculateOverheadPercentage(): number { if (this.processingTimes.length === 0) { return 0; } const avgProcessingTime = this.processingTimes.reduce((sum, time) => sum + time, 0) / this.processingTimes.length; // Estimate base reasoning time (monitoring adds overhead) // For now, assume monitoring is ~10% overhead const estimatedBaseTime = avgProcessingTime * 0.9; const overhead = avgProcessingTime - estimatedBaseTime; return (overhead / estimatedBaseTime) * 100; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server