Skip to main content
Glama
MetacognitionModule.ts27.7 kB
/** * Metacognitive Monitoring Module * Implements self-monitoring and bias detection for cognitive processes * Provides confidence assessment, coherence evaluation, and reasoning strategy suggestions */ import { ComponentStatus, IMetacognitionModule, } from "../interfaces/cognitive.js"; import { ReasoningStep, ReasoningType, ReasoningTypeValue, } from "../types/core.js"; interface BiasPattern { name: string; description: string; indicators: string[]; severity: number; mitigation: string; } interface ConfidenceFactors { consistency: number; evidence_strength: number; reasoning_depth: number; alternative_consideration: number; uncertainty_acknowledgment: number; } interface CoherenceMetrics { logical_consistency: number; narrative_flow: number; premise_conclusion_alignment: number; contradiction_count: number; gap_count: number; } export interface MetacognitiveAssessment { confidence: number; coherence: number; biases_detected: string[]; completeness: number; quality_score: number; suggestions: string[]; should_reconsider: boolean; reasoning: string; } interface StrategyRecommendation { strategy: string; reasoning: string; confidence: number; applicable_contexts: string[]; } export class MetacognitionModule implements IMetacognitionModule { private initialized: boolean = false; private lastActivity: number = 0; private config: MetacognitiveConfig = {}; private biasPatterns: Map<string, BiasPattern> = new Map(); private performanceHistory: PerformanceRecord[] = []; constructor() { this.initializeBiasPatterns(); } async initialize(config: Record<string, unknown>): Promise<void> { this.config = { confidence_threshold: 0.6, coherence_threshold: 0.7, bias_detection_sensitivity: 0.5, max_suggestions: 5, performance_history_size: 100, quality_weight_confidence: 0.3, quality_weight_coherence: 0.3, quality_weight_completeness: 0.2, quality_weight_bias_absence: 0.2, ...config, } as MetacognitiveConfig; this.initialized = true; } async process(input: unknown): Promise<unknown> { if (!Array.isArray(input)) { throw new Error( "MetacognitionModule expects array of ReasoningStep as input" ); } const reasoning = input as ReasoningStep[]; return this.assessReasoning(reasoning); } monitorConfidence(reasoning: ReasoningStep[]): number { this.lastActivity = Date.now(); if (reasoning.length === 0) { return 0.1; // Very low confidence for empty reasoning } const factors = this.analyzeConfidenceFactors(reasoning); // Weighted combination of confidence factors const weights = { consistency: 0.2, evidence_strength: 0.35, reasoning_depth: 0.15, alternative_consideration: 0.2, uncertainty_acknowledgment: 0.1, }; const confidence = factors.consistency * weights.consistency + factors.evidence_strength * weights.evidence_strength + factors.reasoning_depth * weights.reasoning_depth + factors.alternative_consideration * weights.alternative_consideration + factors.uncertainty_acknowledgment * weights.uncertainty_acknowledgment; return Math.max(0, Math.min(1, confidence)); } detectBiases(reasoning: ReasoningStep[]): string[] { this.lastActivity = Date.now(); const detectedBiases: string[] = []; const reasoningText = reasoning .map((step) => step.content) .join(" ") .toLowerCase(); const reasoningTypes = reasoning.map((step) => step.type); for (const [biasName, pattern] of this.biasPatterns) { if (this.detectBiasPattern(reasoningText, reasoningTypes, pattern)) { detectedBiases.push(biasName); } } // Additional bias detection based on reasoning structure detectedBiases.push(...this.detectStructuralBiases(reasoning)); return [...new Set(detectedBiases)]; // Remove duplicates } assessCoherence(reasoning: ReasoningStep[]): number { this.lastActivity = Date.now(); if (reasoning.length === 0) { return 0.1; } const metrics = this.computeCoherenceMetrics(reasoning); // Weighted combination of coherence metrics const coherence = metrics.logical_consistency * 0.4 + metrics.narrative_flow * 0.3 + metrics.premise_conclusion_alignment * 0.3 - metrics.contradiction_count * 0.1 - metrics.gap_count * 0.05; return Math.max(0, Math.min(1, coherence)); } suggestImprovements(reasoning: ReasoningStep[]): string[] { this.lastActivity = Date.now(); const suggestions: string[] = []; // Calculate individual metrics without calling assessReasoning to avoid circular dependency const confidence = this.monitorConfidence(reasoning); const coherence = this.assessCoherence(reasoning); const biases = this.detectBiases(reasoning); const completeness = this.assessCompleteness(reasoning); // Confidence-based suggestions const confidenceThreshold = this.config.confidence_threshold ?? 0.6; if (confidence < confidenceThreshold) { suggestions.push( ...this.generateConfidenceSuggestionsFromMetrics(confidence) ); } // Coherence-based suggestions const coherenceThreshold = this.config.coherence_threshold ?? 0.7; if (coherence < coherenceThreshold) { suggestions.push( ...this.generateCoherenceSuggestionsFromMetrics(coherence) ); } // Bias-based suggestions if (biases.length > 0) { suggestions.push(...this.generateBiasSuggestions(biases)); } // Completeness-based suggestions if (completeness < 0.7) { suggestions.push(...this.generateCompletenessSuggestions(reasoning)); } // Strategy recommendations suggestions.push(...this.recommendStrategies(reasoning)); return suggestions.slice(0, this.config.max_suggestions); } // Main assessment method that combines all metacognitive functions assessReasoning(reasoning: ReasoningStep[]): MetacognitiveAssessment { const confidence = this.monitorConfidence(reasoning); const coherence = this.assessCoherence(reasoning); const biases = this.detectBiases(reasoning); const completeness = this.assessCompleteness(reasoning); const qualityScore = this.computeQualityScore( confidence, coherence, completeness, biases ); // Generate suggestions without circular dependency const suggestions = this.generateSuggestionsFromMetrics( confidence, coherence, biases, completeness, reasoning ); const confidenceThreshold = this.config.confidence_threshold ?? 0.6; const coherenceThreshold = this.config.coherence_threshold ?? 0.7; const shouldReconsider = confidence < confidenceThreshold || coherence < coherenceThreshold || biases.length > 3 || completeness < 0.5; return { confidence, coherence, biases_detected: biases, completeness, quality_score: qualityScore, suggestions, should_reconsider: shouldReconsider, reasoning: this.generateAssessmentReasoning( confidence, coherence, biases, completeness ), }; } private initializeBiasPatterns(): void { const patterns: BiasPattern[] = [ { name: "confirmation_bias", description: "Tendency to search for or interpret information that confirms pre-existing beliefs", indicators: [ "confirms", "supports my view", "as expected", "obviously", "clearly", ], severity: 0.8, mitigation: "Actively seek disconfirming evidence and consider alternative explanations", }, { name: "anchoring_bias", description: "Over-reliance on first piece of information encountered", indicators: [ "initially", "first impression", "starting point", "based on", ], severity: 0.6, mitigation: "Consider multiple starting points and reference frames", }, { name: "availability_heuristic", description: "Overestimating likelihood of events based on memory availability", indicators: ["remember", "recall", "comes to mind", "recent example"], severity: 0.7, mitigation: "Seek statistical data and consider base rates", }, { name: "representativeness_heuristic", description: "Judging probability by similarity to mental prototypes", indicators: ["typical", "representative", "looks like", "similar to"], severity: 0.6, mitigation: "Consider base rates and statistical reasoning", }, { name: "overconfidence_bias", description: "Excessive confidence in one's own answers or judgments", indicators: [ "definitely", "certainly", "without doubt", "obviously", "clearly", ], severity: 0.8, mitigation: "Acknowledge uncertainty and consider confidence intervals", }, { name: "hindsight_bias", description: "Tendency to perceive past events as more predictable than they were", indicators: [ "should have known", "predictable", "obvious in retrospect", ], severity: 0.5, mitigation: "Consider what information was available at the time", }, { name: "framing_effect", description: "Drawing different conclusions from same information based on presentation", indicators: ["depends on how you look at it", "from this perspective"], severity: 0.6, mitigation: "Consider multiple framings and presentations of the same information", }, ]; patterns.forEach((pattern) => { this.biasPatterns.set(pattern.name, pattern); }); } private analyzeConfidenceFactors( reasoning: ReasoningStep[] ): ConfidenceFactors { const confidenceVariance = this.calculateVariance( reasoning.map((step) => step.confidence) ); return { consistency: Math.max(0, 1 - confidenceVariance), // Lower variance = higher consistency evidence_strength: this.assessEvidenceStrength(reasoning), reasoning_depth: Math.min(1, reasoning.length / 5), // Normalize to 5 steps alternative_consideration: this.assessAlternativeConsideration(reasoning), uncertainty_acknowledgment: this.assessUncertaintyAcknowledgment(reasoning), }; } private assessEvidenceStrength(reasoning: ReasoningStep[]): number { let evidenceScore = 0; const evidenceKeywords = [ "evidence", "data", "research", "study", "fact", "proven", "demonstrated", "based on", "supported", "shows", ]; reasoning.forEach((step) => { const content = step.content.toLowerCase(); const evidenceCount = evidenceKeywords.filter((keyword) => content.includes(keyword) ).length; evidenceScore += evidenceCount * 0.3; // Bonus for specific types of reasoning if (step.type === ReasoningType.LOGICAL_INFERENCE) evidenceScore += 0.2; if (step.type === ReasoningType.CAUSAL) evidenceScore += 0.25; if (step.type === ReasoningType.PROBABILISTIC) evidenceScore += 0.15; // Base score for having any reasoning evidenceScore += 0.1; }); return Math.min(1, evidenceScore / reasoning.length); } private assessAlternativeConsideration(reasoning: ReasoningStep[]): number { let alternativeScore = 0; reasoning.forEach((step) => { // Handle cases where alternatives might be undefined const alternativesLength = step.alternatives?.length ?? 0; alternativeScore += alternativesLength * 0.4; const content = step.content.toLowerCase(); const alternativeKeywords = [ "however", "alternatively", "on the other hand", "but", "although", "consider", "alternative", ]; const alternativeCount = alternativeKeywords.filter((keyword) => content.includes(keyword) ).length; alternativeScore += alternativeCount * 0.2; }); return Math.min(1, alternativeScore / reasoning.length); } private assessUncertaintyAcknowledgment(reasoning: ReasoningStep[]): number { let uncertaintyScore = 0; const uncertaintyKeywords = [ "might", "could", "possibly", "perhaps", "uncertain", "unclear", "may", ]; reasoning.forEach((step) => { const content = step.content.toLowerCase(); const uncertaintyCount = uncertaintyKeywords.filter((keyword) => content.includes(keyword) ).length; uncertaintyScore += uncertaintyCount * 0.15; // Lower confidence should increase uncertainty acknowledgment score if (step.confidence < 0.7) uncertaintyScore += 0.1; }); return Math.min(1, uncertaintyScore / reasoning.length); } private detectBiasPattern( text: string, _types: ReasoningTypeValue[], pattern: BiasPattern ): boolean { const indicatorCount = pattern.indicators.filter((indicator) => text.includes(indicator) ).length; // For confirmation bias, be more sensitive - require at least 2 indicators if (pattern.name === "confirmation_bias") { return indicatorCount >= 2; } // Use a minimum threshold of 1 indicator, or sensitivity-based threshold const biasDetectionSensitivity = this.config.bias_detection_sensitivity ?? 0.5; const threshold = Math.max( 1, Math.ceil(pattern.indicators.length * biasDetectionSensitivity) ); return indicatorCount >= threshold; } private detectStructuralBiases(reasoning: ReasoningStep[]): string[] { const biases: string[] = []; // Check for overconfidence (all steps have very high confidence) const avgConfidence = reasoning.reduce((sum, step) => sum + step.confidence, 0) / reasoning.length; if (avgConfidence > 0.9 && reasoning.length > 2) { biases.push("overconfidence_bias"); } // Check for lack of alternative consideration const stepsWithAlternatives = reasoning.filter( (step) => step.alternatives?.length > 0 ).length; if (stepsWithAlternatives / reasoning.length < 0.3) { biases.push("tunnel_vision"); } // Check for reasoning type diversity const uniqueTypes = new Set(reasoning.map((step) => step.type)); if (uniqueTypes.size === 1 && reasoning.length > 3) { biases.push("single_mode_thinking"); } return biases; } private computeCoherenceMetrics( reasoning: ReasoningStep[] ): CoherenceMetrics { return { logical_consistency: this.assessLogicalConsistency(reasoning), narrative_flow: this.assessNarrativeFlow(reasoning), premise_conclusion_alignment: this.assessPremiseConclusionAlignment(reasoning), contradiction_count: this.countContradictions(reasoning), gap_count: this.countLogicalGaps(reasoning), }; } private assessLogicalConsistency(reasoning: ReasoningStep[]): number { // Check for logical consistency between steps let consistencyScore = 1.0; for (let i = 1; i < reasoning.length; i++) { const currentStep = reasoning[i]; const previousStep = reasoning[i - 1]; // Simple consistency check based on confidence alignment const confidenceDiff = Math.abs( currentStep.confidence - previousStep.confidence ); if (confidenceDiff > 0.5) { consistencyScore -= 0.1; } // Check for contradictory content (simplified) if (this.areStepsContradictory(currentStep, previousStep)) { consistencyScore -= 0.2; } } return Math.max(0, consistencyScore); } private assessNarrativeFlow(reasoning: ReasoningStep[]): number { // Assess how well steps flow together let flowScore = 1.0; const transitionWords = [ "therefore", "thus", "consequently", "however", "furthermore", "moreover", ]; for (let i = 1; i < reasoning.length; i++) { const content = reasoning[i].content.toLowerCase(); const hasTransition = transitionWords.some((word) => content.includes(word) ); if (!hasTransition && reasoning.length > 2) { flowScore -= 0.1; } } return Math.max(0, flowScore); } private assessPremiseConclusionAlignment(reasoning: ReasoningStep[]): number { if (reasoning.length < 2) return 1.0; const firstStep = reasoning[0]; const lastStep = reasoning[reasoning.length - 1]; // Simple alignment check based on confidence progression const confidenceProgression = lastStep.confidence - firstStep.confidence; // Good alignment should show either stable or increasing confidence if (confidenceProgression >= -0.1) { return 1.0; } else { return Math.max(0, 1 + confidenceProgression); // Penalize large drops } } private countContradictions(reasoning: ReasoningStep[]): number { let contradictions = 0; for (let i = 0; i < reasoning.length; i++) { for (let j = i + 1; j < reasoning.length; j++) { if (this.areStepsContradictory(reasoning[i], reasoning[j])) { contradictions++; } } } return contradictions; } private countLogicalGaps(reasoning: ReasoningStep[]): number { // Count potential logical gaps (simplified heuristic) let gaps = 0; for (let i = 1; i < reasoning.length; i++) { const currentStep = reasoning[i]; const previousStep = reasoning[i - 1]; // Large confidence jumps might indicate gaps if (currentStep.confidence - previousStep.confidence > 0.4) { gaps++; } } return gaps; } private areStepsContradictory( step1: ReasoningStep, step2: ReasoningStep ): boolean { // Simplified contradiction detection const content1 = step1.content.toLowerCase(); const content2 = step2.content.toLowerCase(); const contradictionPairs = [ ["yes", "no"], ["true", "false"], ["correct", "incorrect"], ["valid", "invalid"], ["possible", "impossible"], ]; return contradictionPairs.some( ([word1, word2]) => (content1.includes(word1) && content2.includes(word2)) || (content1.includes(word2) && content2.includes(word1)) ); } private assessCompleteness(reasoning: ReasoningStep[]): number { let completenessScore = 0; // Check for different types of reasoning const types = new Set(reasoning.map((step) => step.type)); completenessScore += Math.min(1, types.size / 4) * 0.3; // Diversity bonus // Check for consideration of alternatives const totalAlternatives = reasoning.reduce( (sum, step) => sum + (step.alternatives?.length ?? 0), 0 ); completenessScore += Math.min(1, totalAlternatives / reasoning.length) * 0.3; // Check reasoning depth completenessScore += Math.min(1, reasoning.length / 5) * 0.2; // Check for metacognitive steps const metacognitiveSteps = reasoning.filter( (step) => step.type === ReasoningType.METACOGNITIVE ).length; completenessScore += Math.min(1, metacognitiveSteps / 2) * 0.2; return completenessScore; } private computeQualityScore( confidence: number, coherence: number, completeness: number, biases: string[] ): number { const biasScore = Math.max(0, 1 - biases.length * 0.2); const qualityWeightConfidence = this.config.quality_weight_confidence ?? 0.3; const qualityWeightCoherence = this.config.quality_weight_coherence ?? 0.3; const qualityWeightCompleteness = this.config.quality_weight_completeness ?? 0.2; const qualityWeightBiasAbsence = this.config.quality_weight_bias_absence ?? 0.2; return ( confidence * qualityWeightConfidence + coherence * qualityWeightCoherence + completeness * qualityWeightCompleteness + biasScore * qualityWeightBiasAbsence ); } private generateBiasSuggestions(biases: string[]): string[] { const suggestions: string[] = []; biases.forEach((bias) => { const pattern = this.biasPatterns.get(bias); if (pattern) { suggestions.push(`Address ${bias}: ${pattern.mitigation}`); } }); return suggestions; } private generateCompletenessSuggestions( reasoning: ReasoningStep[] ): string[] { const suggestions: string[] = []; const types = new Set(reasoning.map((step) => step.type)); if (!types.has(ReasoningType.ANALOGICAL)) { suggestions.push( "Consider analogical reasoning to find similar situations or patterns" ); } if (!types.has(ReasoningType.CAUSAL)) { suggestions.push( "Explore causal relationships and underlying mechanisms" ); } if (!types.has(ReasoningType.PROBABILISTIC)) { suggestions.push( "Consider probabilistic reasoning and uncertainty quantification" ); } return suggestions; } private recommendStrategies(_reasoning: ReasoningStep[]): string[] { const strategies: StrategyRecommendation[] = [ { strategy: "devil's_advocate", reasoning: "Actively argue against your own position to identify weaknesses", confidence: 0.8, applicable_contexts: ["decision_making", "analysis"], }, { strategy: "pre_mortem", reasoning: "Imagine your conclusion is wrong and work backwards to find potential causes", confidence: 0.7, applicable_contexts: ["planning", "risk_assessment"], }, { strategy: "base_rate_neglect_check", reasoning: "Consider statistical base rates and prior probabilities", confidence: 0.9, applicable_contexts: ["probability", "prediction"], }, ]; // Select most relevant strategies based on reasoning content return strategies .filter((strategy) => strategy.confidence > 0.6) .map((strategy) => `Try ${strategy.strategy}: ${strategy.reasoning}`) .slice(0, 2); } private generateAssessmentReasoning( confidence: number, coherence: number, biases: string[], completeness: number ): string { const parts: string[] = []; if (confidence < 0.6) { parts.push( `Low confidence (${confidence.toFixed( 2 )}) suggests need for more evidence` ); } if (coherence < 0.7) { parts.push( `Coherence issues (${coherence.toFixed( 2 )}) indicate logical inconsistencies` ); } if (biases.length > 0) { parts.push(`Detected biases: ${biases.join(", ")}`); } if (completeness < 0.7) { parts.push( `Incomplete analysis (${completeness.toFixed( 2 )}) - consider additional perspectives` ); } return parts.length > 0 ? parts.join("; ") : "Reasoning appears sound with good metacognitive quality"; } private calculateVariance(values: number[]): number { const mean = values.reduce((sum, val) => sum + val, 0) / values.length; const squaredDiffs = values.map((val) => Math.pow(val - mean, 2)); return squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length; } // Helper methods to avoid circular dependencies private generateSuggestionsFromMetrics( confidence: number, coherence: number, biases: string[], completeness: number, reasoning: ReasoningStep[] ): string[] { const suggestions: string[] = []; // Confidence-based suggestions const confidenceThreshold = this.config.confidence_threshold ?? 0.6; if (confidence < confidenceThreshold) { suggestions.push( ...this.generateConfidenceSuggestionsFromMetrics(confidence) ); } // Coherence-based suggestions const coherenceThreshold = this.config.coherence_threshold ?? 0.7; if (coherence < coherenceThreshold) { suggestions.push( ...this.generateCoherenceSuggestionsFromMetrics(coherence) ); } // Bias-based suggestions if (biases.length > 0) { suggestions.push(...this.generateBiasSuggestions(biases)); } // Completeness-based suggestions if (completeness < 0.7) { suggestions.push(...this.generateCompletenessSuggestions(reasoning)); } // Strategy recommendations suggestions.push(...this.recommendStrategies(reasoning)); return suggestions.slice(0, this.config.max_suggestions); } private generateConfidenceSuggestionsFromMetrics( confidence: number ): string[] { const suggestions: string[] = []; if (confidence < 0.4) { suggestions.push( "Consider gathering more evidence or information before drawing conclusions" ); suggestions.push( "Break down complex problems into smaller, more manageable components" ); } else if (confidence < 0.6) { suggestions.push( "Seek additional perspectives or expert opinions to validate your reasoning" ); suggestions.push( "Consider potential counterarguments to strengthen your position" ); } return suggestions; } private generateCoherenceSuggestionsFromMetrics(coherence: number): string[] { const suggestions: string[] = []; if (coherence < 0.5) { suggestions.push( "Review your reasoning chain for logical consistency and flow" ); suggestions.push( "Ensure each step follows logically from the previous ones" ); suggestions.push( "Consider reorganizing your reasoning to improve narrative flow" ); } else if (coherence < 0.7) { suggestions.push( "Add transitional statements to better connect your reasoning steps" ); suggestions.push( "Check for any contradictions or inconsistencies in your logic" ); } return suggestions; } reset(): void { this.lastActivity = 0; this.performanceHistory = []; } recordPerformance( assessment: MetacognitiveAssessment, outcome?: "success" | "failure" ): void { const record: PerformanceRecord = { timestamp: Date.now(), assessment, ...(outcome && { outcome }), }; this.performanceHistory.push(record); // Keep history within configured size if ( this.performanceHistory.length > (this.config.performance_history_size ?? 100) ) { this.performanceHistory.shift(); } } getPerformanceHistory(): PerformanceRecord[] { return [...this.performanceHistory]; } getStatus(): ComponentStatus { return { name: "MetacognitionModule", initialized: this.initialized, active: Date.now() - this.lastActivity < 30000, last_activity: this.lastActivity, }; } } interface MetacognitiveConfig { confidence_threshold?: number; coherence_threshold?: number; bias_detection_sensitivity?: number; max_suggestions?: number; performance_history_size?: number; quality_weight_confidence?: number; quality_weight_coherence?: number; quality_weight_completeness?: number; quality_weight_bias_absence?: number; } interface PerformanceRecord { timestamp: number; assessment: MetacognitiveAssessment; outcome?: "success" | "failure"; }

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