UNPKG

smart-thinking-mcp

Version:

Un serveur MCP avancé pour le raisonnement multi-dimensionnel, adaptatif et collaboratif

1,015 lines 48.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MetricsCalculator = void 0; /** * metrics-calculator.ts - VERSION OPTIMISÉE * * Système centralisé pour tous les calculs de métriques dans Smart-Thinking * Implémente des algorithmes avancés optimisés pour calculer la confiance, la pertinence, * la qualité et autres métriques utilisées par le système. */ /** * Classe qui centralise tous les calculs de métriques dans Smart-Thinking. * Cette classe implémente des algorithmes avancés pour le calcul de confiance, * la pertinence, la qualité et d'autres métriques utilisées dans le système. */ class MetricsCalculator { // Dictionnaires de mots indicateurs pour l'analyse linguistique positiveWords = [ 'précis', 'clair', 'cohérent', 'logique', 'détaillé', 'rigoureux', 'méthodique', 'analytique', 'systématique', 'fondé', 'approfondi', 'équilibré', 'objectif', 'exact', 'raisonnable', 'valide', 'pertinent', 'significatif' ]; negativeWords = [ 'vague', 'confus', 'incohérent', 'illogique', 'superficiel', 'flou', 'ambigu', 'subjectif', 'inexact', 'imprécis', 'douteux', 'spéculatif', 'non pertinent', 'biaisé', 'contradictoire', 'simpliste', 'circulaire' ]; // Indicateurs de qualité par type de pensée qualityIndicators = { 'regular': { positive: ['clairement formulé', 'bien structuré', 'idée développée'], negative: ['incomplet', 'hors sujet', 'mal structuré'] }, 'meta': { positive: ['auto-critique', 'réflexif', 'évaluatif', 'conscient'], negative: ['superficiel', 'non réflexif', 'auto-complaisant'] }, 'hypothesis': { positive: ['testable', 'falsifiable', 'précis', 'fondé sur', 'prédictif'], negative: ['vague', 'non testable', 'infalsifiable', 'sans fondement'] }, 'conclusion': { positive: ['synthétise', 'résume', 'découle de', 'cohérent avec'], negative: ['déconnecté', 'sans rapport', 'non justifié', 'contradictoire'] }, 'revision': { positive: ['améliore', 'corrige', 'précise', 'clarifie', 'nuance'], negative: ['répétitif', 'redondant', 'contredit sans justification'] } }; // Modalisateurs de certitude/incertitude pour l'analyse linguistique uncertaintyModifiers = [ 'peut-être', 'possible', 'probablement', 'semble', 'pourrait', 'hypothèse', 'suppose', 'doute', 'éventuellement', 'potentiellement', 'apparemment', 'suggère' ]; certaintyModifiers = [ 'certainement', 'clairement', 'évidemment', 'sans doute', 'indiscutablement', 'nécessairement', 'doit', 'est', 'démontré', 'prouvé', 'assurément', 'incontestablement', 'manifestement', 'définitivement', 'inévitablement' ]; // Mots-stop en français (stop words) - OPTIMISATION: Utilisation d'un Set pour recherche O(1) stopWords = new Set([ 'le', 'la', 'les', 'un', 'une', 'des', 'ce', 'cette', 'ces', 'et', 'ou', 'mais', 'donc', 'car', 'ni', 'que', 'qui', 'dans', 'sur', 'sous', 'avec', 'sans', 'pour', 'par', 'je', 'tu', 'il', 'elle', 'nous', 'vous', 'ils', 'elles', 'est', 'sont', 'être', 'avoir', 'fait', 'faire', 'plus', 'moins', 'très', 'trop', 'peu', 'beaucoup' ]); // OPTIMISATION: Pré-compilation des expressions régulières REGEX = { REFERENCES: /\(([^)]+)\)|\[[^\]]+\]/g, NUMBERS: /\d+([.,]\d+)?%?/g, PUNCTUATION: /[.,\/#!$%\^&\*;:{}=\-_`~()]/g, WHITESPACE: /\s+/, SENTENCES: /[.!?]+/, UNCERTAINTY_MODIFIERS: null, CERTAINTY_MODIFIERS: null, MATH_CALCULATION: /(?:\d+(?:\.\d+)?)\s*(?:[+\-*/^]|plus|moins|divisé|fois|multiplié)\s*(?:\d+(?:\.\d+)?)/i, MATHEMATICAL_PROOF: /(?:prouvons|démontrons|supposons|soit|démonstration|preuve|CQFD|théorème|lemme|corollaire)/i, LOGICAL_DEDUCTION: /(?:donc|par conséquent|ainsi|il s'ensuit que|cela implique|on en déduit|cela prouve)/i, FACTUAL_CLAIM: /(?:est|sont|était|étaient|a été|ont été|sera|seront)\s+(?:un|une|des|le|la|les|du|de la)/i, STRONG_EMOTION: /(?:!{2,}|incroyable|fantastique|horrible|déteste|adore|absolument|totalement|complètement|extrêmement)/i }; // OPTIMISATION: Mappage pour les types de pensée typeScoresMap = new Map([ ['conclusion', 0.8], ['hypothesis', 0.6], ['meta', 0.7], ['revision', 0.75], ['regular', 0.65] ]); // OPTIMISATION: Mappage pour les types de connexion connectionWeightsMap = new Map([ ['supports', 0.9], ['contradicts', 0.7], ['refines', 0.8], ['branches', 0.6], ['derives', 0.75], ['associates', 0.5], ['exemplifies', 0.7], ['generalizes', 0.75], ['compares', 0.6], ['contrasts', 0.65], ['questions', 0.5], ['extends', 0.7], ['analyzes', 0.8], ['synthesizes', 0.85], ['applies', 0.7], ['evaluates', 0.8], ['cites', 0.9], ['extended-by', 0.7], ['analyzed-by', 0.8], ['component-of', 0.7], ['applied-by', 0.7], ['evaluated-by', 0.8], ['cited-by', 0.9] ]); // OPTIMISATION: Mappage pour status de vérification verificationScoreMap = { 'verified': 0.95, 'partially_verified': 0.75, 'contradicted': 0.3, 'contradictory': 0.2, 'uncertain': 0.4, 'absence_of_information': 0.65, 'unverified': 0.45, 'inconclusive': 0.55 }; // Constantes centralisées pour les seuils utilisés dans les calculs THRESHOLDS = { // Confiance MIN_CONFIDENCE: 0.1, MAX_CONFIDENCE: 0.95, // Pertinence MIN_RELEVANCE: 0.1, MAX_RELEVANCE: 0.95, // Qualité MIN_QUALITY: 0.1, MAX_QUALITY: 0.95, // Fiabilité MIN_RELIABILITY: 0.1, MAX_RELIABILITY: 0.95, // Confiance HIGH_CONFIDENCE_THRESHOLD: 0.80, MEDIUM_CONFIDENCE_THRESHOLD: 0.50, LOW_CONFIDENCE_THRESHOLD: 0.30, // Fiabilité HIGH_RELIABILITY_THRESHOLD: 0.75, MEDIUM_RELIABILITY_THRESHOLD: 0.45, LOW_RELIABILITY_THRESHOLD: 0.25, // Vérification VERIFIED_THRESHOLD: 0.8, PARTIALLY_VERIFIED_THRESHOLD: 0.45, ABSENCE_THRESHOLD: 0.65, UNCERTAIN_THRESHOLD: 0.5, CONTRADICTION_THRESHOLD: 0.3, // Similarité HIGH_SIMILARITY: 0.85, // Erreur numérique WEIGHT_TOLERANCE: 0.001, // Nouveaux seuils pour les calculs mathématiques MATH_HIGH_CONFIDENCE: 0.8, MATH_MEDIUM_CONFIDENCE: 0.65 }; // Paramètres configurables pour ajuster les calculs config = { // Poids des différentes composantes dans le calcul de la confiance confidenceWeights: { modifierAnalysis: 0.4, // Analyse des modalisateurs de certitude/incertitude thoughtType: 0.2, // Type de pensée (hypothesis, conclusion, etc.) structuralIndicators: 0.2, // Indicateurs structurels (références, citations, etc.) sentimentBalance: 0.2 // Équilibre des sentiments positifs/négatifs }, // Poids des différentes composantes dans le calcul de la pertinence relevanceWeights: { keywordOverlap: 0.5, // Chevauchement de mots-clés connectionStrength: 0.5 // Force des connexions dans le graphe }, // Poids des différentes composantes dans le calcul de la qualité qualityWeights: { wordIndicators: 0.25, // Indicateurs de mots positifs/négatifs typeSpecificIndicators: 0.25, // Indicateurs spécifiques au type structuralBalance: 0.2, // Équilibre structurel coherence: 0.3 // Cohérence globale }, // Ajustements par type de pensée typeAdjustments: { 'hypothesis': { confidence: 0.9, quality: 1.0, coherence: 1.1 }, 'conclusion': { confidence: 1.1, quality: 1.2, coherence: 1.2 }, 'meta': { confidence: 1.0, quality: 1.1, coherence: 0.9 }, 'revision': { confidence: 1.05, quality: 1.1, coherence: 1.05 }, 'regular': { confidence: 1.0, quality: 1.0, coherence: 1.0 } }, // Paramètres pour l'algorithme TF-IDF tfIdf: { minFrequency: 2, // Fréquence minimale pour qu'un terme soit considéré maxDocumentPercentage: 0.7 // Pourcentage maximum de documents contenant un terme } }; /** * Constructeur * @param customConfig Configuration personnalisée (optionnelle) */ constructor(customConfig) { if (customConfig) { this.config = { ...this.config, ...customConfig }; } // Vérifier que les poids s'additionnent à 1.0 this.validateWeights(); // OPTIMISATION: Précompiler les expressions régulières pour les modalisateurs this.REGEX.UNCERTAINTY_MODIFIERS = new RegExp('\\b(' + this.uncertaintyModifiers.join('|') + ')\\b', 'gi'); this.REGEX.CERTAINTY_MODIFIERS = new RegExp('\\b(' + this.certaintyModifiers.join('|') + ')\\b', 'gi'); } /** * Valide et normalise tous les poids dans les configurations pour s'assurer qu'ils s'additionnent à 1.0 * Affiche un avertissement si ce n'est pas le cas et corrige automatiquement */ validateWeights() { const categories = [ { name: 'confiance', weights: this.config.confidenceWeights }, { name: 'pertinence', weights: this.config.relevanceWeights }, { name: 'qualité', weights: this.config.qualityWeights } ]; for (const category of categories) { const sum = Object.values(category.weights).reduce((total, w) => total + w, 0); // Vérifier si la somme des poids est proche de 1.0 if (Math.abs(sum - 1.0) > this.THRESHOLDS.WEIGHT_TOLERANCE) { console.error(`AVERTISSEMENT: Les poids de ${category.name} s'additionnent à ${sum}, pas à 1.0`); // Normaliser automatiquement les poids const factor = 1.0 / sum; for (const key in category.weights) { category.weights[key] *= factor; } console.error(`Correction automatique appliquée aux poids de ${category.name}`); } } } /** * OPTIMISATION: Compter les modalisateurs avec RegExp (plus rapide) * * @param content Contenu à analyser * @param regex Expression régulière à utiliser * @returns Nombre d'occurrences */ countModifiers(content, regex) { const matches = content.match(regex); return matches ? matches.length : 0; } /** * Calcule la métrique de confiance pour une pensée - VERSION OPTIMISÉE * Implémente un algorithme amélioré pour évaluer la confiance * * @param thought La pensée à évaluer * @returns Niveau de confiance entre 0 et 1 */ calculateConfidence(thought) { const content = thought.content.toLowerCase(); const typeWeight = this.config.typeAdjustments[thought.type].confidence; // 1. OPTIMISATION: Analyse des modalisateurs avec RegExp const uncertaintyCount = this.countModifiers(content, this.REGEX.UNCERTAINTY_MODIFIERS); const certaintyCount = this.countModifiers(content, this.REGEX.CERTAINTY_MODIFIERS); // Calculer le ratio de certitude de manière continue let modifierScore; if (uncertaintyCount === 0 && certaintyCount === 0) { modifierScore = 0.5; // Valeur neutre par défaut } else { const totalModifiers = uncertaintyCount + certaintyCount; // OPTIMISATION: Approximation plus rapide de la fonction sigmoïde const certitudeRatio = certaintyCount / totalModifiers; const x = -5 * (certitudeRatio - 0.5); if (Math.abs(x) < 1) { modifierScore = 0.5 + 0.25 * x; // Approximation linéaire pour |x| < 1 } else { modifierScore = x < 0 ? 0.95 : 0.05; // Valeurs limites pour les autres cas } } // 2. OPTIMISATION: Analyse des indicateurs structurels avec RegExp précompilées const referenceMatches = content.match(this.REGEX.REFERENCES) || []; const numberMatches = content.match(this.REGEX.NUMBERS) || []; // OPTIMISATION: Éviter les appels à Math.min en utilisant une condition const referenceScore = referenceMatches.length * 0.05 > 0.2 ? 0.2 : referenceMatches.length * 0.05; const numberScore = numberMatches.length * 0.025 > 0.1 ? 0.1 : numberMatches.length * 0.025; const structuralScore = 0.5 + referenceScore + numberScore; // 3. OPTIMISATION: Utiliser la Map pour accéder au type score const typeScore = this.typeScoresMap.get(thought.type) || 0.65; // Combinaison pondérée des facteurs avec normalisation intégrée const weights = this.config.confidenceWeights; let confidenceScore = (modifierScore * weights.modifierAnalysis + typeScore * weights.thoughtType + structuralScore * weights.structuralIndicators + 0.5 * weights.sentimentBalance // Simplification pour la sentimentBalance ) * typeWeight; // OPTIMISATION: Limite directement sans appels répétés à Math.min/max if (confidenceScore < this.THRESHOLDS.MIN_CONFIDENCE) { return this.THRESHOLDS.MIN_CONFIDENCE; } else if (confidenceScore > this.THRESHOLDS.MAX_CONFIDENCE) { return this.THRESHOLDS.MAX_CONFIDENCE; } return confidenceScore; } /** * Calcule la métrique de pertinence pour une pensée par rapport à son contexte - VERSION OPTIMISÉE * Utilise un algorithme hybride combinant TF-IDF et analyse de connexions * * @param thought La pensée à évaluer * @param connectedThoughts Les pensées connectées (contexte) * @returns Niveau de pertinence entre 0 et 1 */ calculateRelevance(thought, connectedThoughts) { // Si pas de pensées connectées, la pertinence est moyenne par défaut if (connectedThoughts.length === 0) { return 0.5; } // 1. OPTIMISATION: Cache les résultats des extractions de mots-clés const thoughtKeywords = this.extractKeywords(thought.content); // OPTIMISATION: Extraire le contenu une seule fois const contextContent = connectedThoughts.map(t => t.content).join(' '); const contextKeywords = this.extractAndWeightContextKeywords(contextContent); // Calculer la pertinence par chevauchement pondéré let keywordScore = 0; let totalWeight = 0; // OPTIMISATION: Utiliser des variables locales pour éviter les accès objet répétés for (const [keyword, weight] of Object.entries(contextKeywords)) { totalWeight += weight; if (thoughtKeywords.includes(keyword)) { keywordScore += weight; } } const keywordOverlapScore = totalWeight > 0 ? keywordScore / totalWeight : 0.5; // 2. Analyse des connexions - OPTIMISATION: Réutilisation des variables pour réduire GC let connectionScoreSum = 0; const connectionScoresLength = thought.connections.length; if (connectionScoresLength > 0) { for (let i = 0; i < connectionScoresLength; i++) { const conn = thought.connections[i]; // OPTIMISATION: Map lookups plus rapides const typeWeight = this.connectionWeightsMap.get(conn.type) || 0.5; connectionScoreSum += conn.strength * typeWeight; } } const connectionScore = connectionScoresLength > 0 ? connectionScoreSum / connectionScoresLength : 0.5; // 3. OPTIMISATION: Calcul de l'ancrage contextuel plus efficace const incomingConnections = []; for (const t of connectedThoughts) { for (const conn of t.connections) { if (conn.targetId === thought.id) { incomingConnections.push(conn); } } } let incomingScore = 0.5; if (incomingConnections.length > 0) { let incomingStrengthSum = 0; for (const conn of incomingConnections) { incomingStrengthSum += conn.strength; } incomingScore = incomingStrengthSum / incomingConnections.length; } // Combinaison pondérée avec distribution uniforme entre les composantes de connexion const relevanceScore = (keywordOverlapScore * this.config.relevanceWeights.keywordOverlap + ((connectionScore + incomingScore) / 2) * this.config.relevanceWeights.connectionStrength); // Ajustement par type de pensée const typeAdjustment = thought.type === 'meta' ? 0.9 : thought.type === 'revision' ? 1.1 : 1.0; // OPTIMISATION: Limite directement const adjustedScore = relevanceScore * typeAdjustment; if (adjustedScore < this.THRESHOLDS.MIN_RELEVANCE) { return this.THRESHOLDS.MIN_RELEVANCE; } else if (adjustedScore > this.THRESHOLDS.MAX_RELEVANCE) { return this.THRESHOLDS.MAX_RELEVANCE; } return adjustedScore; } /** * Calcule la métrique de qualité globale pour une pensée - VERSION OPTIMISÉE * Utilise une approche multi-factorielle * * @param thought La pensée à évaluer * @param connectedThoughts Les pensées connectées (contexte) * @returns Niveau de qualité entre 0 et 1 */ calculateQuality(thought, connectedThoughts) { const content = thought.content.toLowerCase(); // OPTIMISATION: Table de lookup pour les ajustements de type const typeAdjustmentMap = new Map([ ['conclusion', 1.05], ['revision', 1.0], ['hypothesis', 1.0], ['meta', 1.1], ['regular', 1.0] ]); const typeAdjustment = typeAdjustmentMap.get(thought.type) || 1.0; // 1. OPTIMISATION: Analyse des indicateurs lexicaux avec RegExp // Précompiler les regex pour les mots positifs et négatifs const positiveWordsRegex = new RegExp('\\b(' + this.positiveWords.join('|') + ')\\b', 'gi'); const negativeWordsRegex = new RegExp('\\b(' + this.negativeWords.join('|') + ')\\b', 'gi'); const positiveMatches = content.match(positiveWordsRegex) || []; const negativeMatches = content.match(negativeWordsRegex) || []; // Score basé sur le ratio positif/négatif const positiveCount = positiveMatches.length; const negativeCount = negativeMatches.length; let wordIndicatorScore = 0.5; if (positiveCount > 0 || negativeCount > 0) { const total = positiveCount + negativeCount; wordIndicatorScore = (positiveCount / total) * 0.5 + 0.3; } // 2. OPTIMISATION: Analyse des indicateurs spécifiques au type avec RegExp const typeIndicators = this.qualityIndicators[thought.type] || this.qualityIndicators['regular']; const positiveTypeRegex = new RegExp(typeIndicators.positive.join('|'), 'gi'); const negativeTypeRegex = new RegExp(typeIndicators.negative.join('|'), 'gi'); const positiveTypeMatches = content.match(positiveTypeRegex) || []; const negativeTypeMatches = content.match(negativeTypeRegex) || []; // Score basé sur les indicateurs spécifiques au type const positiveTypeCount = positiveTypeMatches.length; const negativeTypeCount = negativeTypeMatches.length; let typeIndicatorScore = 0.5; if (positiveTypeCount > 0 || negativeTypeCount > 0) { typeIndicatorScore = 0.5 + (positiveTypeCount - negativeTypeCount) * 0.1; // OPTIMISATION: Éviter les appels à Math.min/max en séquence typeIndicatorScore = typeIndicatorScore < 0.3 ? 0.3 : (typeIndicatorScore > 0.9 ? 0.9 : typeIndicatorScore); } // 3. OPTIMISATION: Analyse structurelle plus efficace // Cache les résultats de split qui sont utilisés plusieurs fois const wordsArray = content.split(this.REGEX.WHITESPACE); const wordCount = wordsArray.length; const sentencesArray = content.split(this.REGEX.SENTENCES).filter(s => s.trim().length > 0); const sentenceCount = sentencesArray.length; const avgSentenceLength = wordCount / Math.max(sentenceCount, 1); // OPTIMISATION: Utiliser une approche de lookup au lieu de branches conditionnelles let structuralScore; const isTypeWithSpecialHandling = thought.type === 'conclusion' || thought.type === 'revision'; if (isTypeWithSpecialHandling) { // Tables de lookup pour les conclusions/révisions structuralScore = wordCount < 5 ? 0.4 : (wordCount > 300 ? 0.5 : 0.8); } else { // Tables de lookup pour les autres types if (wordCount < 5) { structuralScore = 0.3; } else if (wordCount > 300) { structuralScore = 0.4; } else if (wordCount >= 150) { structuralScore = 0.6; } else if (wordCount >= 30) { structuralScore = 0.8; } else { structuralScore = 0.5; // Valeur par défaut } } // OPTIMISATION: Optimiser les pénalités pour la longueur des phrases if (avgSentenceLength > 25 || (avgSentenceLength < 5 && sentenceCount > 1)) { structuralScore *= 0.9; } // 4. OPTIMISATION: Analyse de la cohérence plus efficace let coherenceScore = 0.5; if (connectedThoughts.length > 0) { // OPTIMISATION: Compter directement au lieu de filter + length let contradictions = 0; let supports = 0; // OPTIMISATION: Utiliser des sets pour lookup rapide const connectedIds = new Set(connectedThoughts.map(t => t.id)); // Une seule boucle pour compter les deux for (let i = 0; i < thought.connections.length; i++) { const conn = thought.connections[i]; if (connectedIds.has(conn.targetId)) { if (conn.type === 'contradicts') { contradictions++; } else if (conn.type === 'supports') { supports++; } } } // OPTIMISATION: Optimiser les conditions avec une seule vérification if (thought.type === 'revision' || thought.type === 'conclusion') { if (thought.connections.length >= 2) { coherenceScore = 0.7; } } // OPTIMISATION: Optimiser les comparaisons de cohérence if (contradictions > 0) { if (supports === 0) { coherenceScore = coherenceScore < 0.45 ? 0.45 : coherenceScore; } else { coherenceScore = coherenceScore < 0.6 ? 0.6 : coherenceScore; } } else if (supports > 0) { coherenceScore = coherenceScore < 0.7 ? 0.7 : coherenceScore; } // Bonus pour les pensées bien connectées const connectionRatio = thought.connections.length / connectedThoughts.length; if (connectionRatio > 0.5) { coherenceScore += 0.1; } // OPTIMISATION: Limite directement coherenceScore = coherenceScore > 0.9 ? 0.9 : coherenceScore; } // OPTIMISATION: Combiner les scores avec pondération en évitant les accès répétés const weights = this.config.qualityWeights; const qualityScore = (wordIndicatorScore * weights.wordIndicators + typeIndicatorScore * weights.typeSpecificIndicators + structuralScore * weights.structuralBalance + coherenceScore * weights.coherence) * typeAdjustment; // OPTIMISATION: Limiter directement if (qualityScore < 0.3) { return 0.3; } else if (qualityScore > 0.95) { return 0.95; } return qualityScore; } /** * Calcule un score global de fiabilité basé sur différentes métriques et vérifications * avec une normalisation automatique des poids - VERSION OPTIMISÉE * * @param metrics Les métriques de base * @param verificationStatus Statut de vérification actuel * @param calculationResults Résultats de vérification des calculs (optionnel) * @param previousScore Score précédent (optionnel) * @returns Score de fiabilité entre 0 et 1 */ calculateReliabilityScore(metrics, verificationStatus, calculationResults, previousScore) { // OPTIMISATION: Utiliser Map pour accéder au score de vérification const verificationScore = this.verificationScoreMap[verificationStatus] || 0.45; // OPTIMISATION: Déterminer les poids avec moins de conditions // Poids pour chaque métrique let weights = calculationResults && calculationResults.length > 0 ? { confidence: 0.25, relevance: 0.10, quality: 0.10, verification: 0.55 } : { confidence: 0.35, relevance: 0.15, quality: 0.15, verification: 0.35 }; // Calcul du score brut pondéré let rawScore = weights.confidence * metrics.confidence + weights.relevance * metrics.relevance + weights.quality * metrics.quality + weights.verification * verificationScore; // OPTIMISATION: Bonus pour les calculs corrects en une seule passe if (calculationResults && calculationResults.length > 0) { let correctCalculations = 0; for (const result of calculationResults) { if (result.isCorrect) correctCalculations++; } const correctRatio = correctCalculations / calculationResults.length; rawScore += correctRatio * 0.1; } // OPTIMISATION: Ajustement selon le statut spécifique avec moins de conditions if (verificationStatus === 'verified' && metrics.confidence > this.THRESHOLDS.HIGH_CONFIDENCE_THRESHOLD) { rawScore *= 1.1; } else if (verificationStatus === 'absence_of_information') { rawScore = rawScore > 0.75 ? 0.75 : rawScore; } // Lissage temporel si score précédent disponible if (previousScore !== undefined) { rawScore = 0.7 * rawScore + 0.3 * previousScore; } // OPTIMISATION: Normalisation finale directe if (rawScore < this.THRESHOLDS.MIN_RELIABILITY) { return this.THRESHOLDS.MIN_RELIABILITY; } else if (rawScore > this.THRESHOLDS.MAX_RELIABILITY) { return this.THRESHOLDS.MAX_RELIABILITY; } return rawScore; } /** * Calcule un score de pertinence basé sur la correspondance avec le contexte - VERSION OPTIMISÉE * * @param thought La pensée à évaluer * @param context Le contexte actuel * @returns Score de pertinence (0-1) */ calculateRelevanceScore(thought, context) { // OPTIMISATION: Extraction des mots-clés const thoughtKeywords = this.extractKeywords(thought.content); const contextKeywordWeights = this.extractAndWeightContextKeywords(context); let relevanceScore = 0; let totalWeight = 0; // OPTIMISATION: Boucle unique pour calculer le score for (const keyword of thoughtKeywords) { const weight = contextKeywordWeights[keyword]; if (weight) { relevanceScore += weight; totalWeight += weight; } } // Normaliser le score if (totalWeight > 0) { relevanceScore = relevanceScore / totalWeight; } else { // Aucune correspondance, score minimal relevanceScore = this.THRESHOLDS.MIN_RELEVANCE; } // OPTIMISATION: Pondérer en fonction des connexions et du type de pensée let connectionFactor = 1.0; // Bonus pour les connexions fortes avec d'autres pensées pertinentes if (thought.connections && thought.connections.length > 0) { let connectionScore = 0; for (const connection of thought.connections) { // OPTIMISATION: Utiliser Map const typeWeight = this.connectionWeightsMap.get(connection.type) || 0.5; connectionScore += typeWeight * connection.strength; } connectionFactor += (connectionScore / thought.connections.length) * 0.5; } relevanceScore *= connectionFactor; // OPTIMISATION: Normaliser entre MIN et MAX directement if (relevanceScore < this.THRESHOLDS.MIN_RELEVANCE) { return this.THRESHOLDS.MIN_RELEVANCE; } else if (relevanceScore > this.THRESHOLDS.MAX_RELEVANCE) { return this.THRESHOLDS.MAX_RELEVANCE; } return relevanceScore; } /** * Extrait les mots-clés d'un texte donné - VERSION OPTIMISÉE * * @param text Le texte à analyser * @returns Un tableau de mots-clés */ extractKeywords(text) { // OPTIMISATION: Convertir en minuscules et supprimer la ponctuation const processedText = text.toLowerCase().replace(this.REGEX.PUNCTUATION, ''); // OPTIMISATION: Utiliser Map pour le comptage (plus efficace) const wordCountsMap = new Map(); // OPTIMISATION: Diviser et traiter en une seule passe const words = processedText.split(this.REGEX.WHITESPACE); for (const word of words) { if (word.length > 2 && !this.stopWords.has(word)) { wordCountsMap.set(word, (wordCountsMap.get(word) || 0) + 1); } } // OPTIMISATION: Convertir en tableau et trier const sortedEntries = Array.from(wordCountsMap.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 15); // Extraire les mots uniquement return sortedEntries.map(entry => entry[0]); } /** * Extrait et pondère les mots-clés du contexte - VERSION OPTIMISÉE * * @param text Le texte du contexte * @returns Un objet avec les mots-clés et leurs poids */ extractAndWeightContextKeywords(text) { const keywords = this.extractKeywords(text); const weightedKeywords = {}; // OPTIMISATION: Calculer la longueur une seule fois const keywordsLength = keywords.length; const factor = 1 / (keywordsLength * 2); // OPTIMISATION: Assigner des poids en une seule passe for (let i = 0; i < keywordsLength; i++) { const keyword = keywords[i]; weightedKeywords[keyword] = 1 - (i * factor); } return weightedKeywords; } /** * Obtient le poids associé à un type de connexion - VERSION OPTIMISÉE * * @param type Le type de connexion * @returns Le poids de ce type de connexion */ getConnectionTypeWeight(type) { // OPTIMISATION: Utiliser Map pour un accès plus rapide return this.connectionWeightsMap.get(type) || 0.5; } /** * Détermine le statut de vérification en fonction du score de confiance * et d'autres facteurs - VERSION OPTIMISÉE * * @param confidenceOrResults Niveau de confiance dans la vérification (0-1) ou résultats de vérification * @param hasContradictions Indique s'il y a des contradictions * @param hasInformation Indique s'il y a des informations disponibles * @returns Le statut de vérification */ determineVerificationStatus(confidenceOrResults, hasContradictions = false, hasInformation = true) { // Si le premier paramètre est un tableau, on est dans le cas d'un appel depuis verification-service if (Array.isArray(confidenceOrResults)) { return this.determineVerificationStatusFromResults(confidenceOrResults); } // OPTIMISATION: Logic simplifiée avec retours immédiats const confidence = confidenceOrResults; // Absence d'information if (!hasInformation) { return 'absence_of_information'; } // Contradictions if (hasContradictions) { return confidence < this.THRESHOLDS.CONTRADICTION_THRESHOLD ? 'contradictory' : 'uncertain'; } // Vérification normale basée sur le niveau de confiance if (confidence >= this.THRESHOLDS.VERIFIED_THRESHOLD) { return 'verified'; } else if (confidence >= this.THRESHOLDS.PARTIALLY_VERIFIED_THRESHOLD) { return 'partially_verified'; } return 'unverified'; } /** * Détermine le statut de vérification à partir des résultats de multiples sources - VERSION OPTIMISÉE * Algorithme amélioré pour gérer les cas ambigus * * @param results Résultats de vérification provenant de différentes sources * @returns Statut de vérification (VerificationStatus) */ determineVerificationStatusFromResults(results) { if (!results || results.length === 0) { return 'unverified'; } // OPTIMISATION: Comptage direct sans créer des objets intermédiaires let verifiedCount = 0; let contradictedCount = 0; let uncertainCount = 0; let absenceCount = 0; let inconclusiveCount = 0; let totalConfidence = 0; // OPTIMISATION: Une seule passe pour tous les comptages for (const result of results) { const confidence = result.confidence || 0.5; totalConfidence += confidence; const isValid = result.result?.isValid; if (isValid === true) { verifiedCount += confidence; } else if (isValid === false) { contradictedCount += confidence; } else if (isValid === 'uncertain') { uncertainCount += confidence; } else if (isValid === 'absence_of_information') { absenceCount += confidence; } else { inconclusiveCount += confidence; } } // Calcul des ratios const verifiedRatio = verifiedCount / totalConfidence; const contradictedRatio = contradictedCount / totalConfidence; const uncertainRatio = uncertainCount / totalConfidence; const absenceRatio = absenceCount / totalConfidence; // OPTIMISATION: Logique de décision simplifiée avec retours immédiats // 1. Contradiction forte prend priorité if (contradictedRatio > 0.5) { return 'contradictory'; } // 2. Désaccord franc entre vérifié et contredit if (verifiedRatio > 0.3 && contradictedRatio > 0.3) { return 'uncertain'; } // 3. Vérification forte si le ratio est suffisant if (verifiedRatio > this.THRESHOLDS.VERIFIED_THRESHOLD) { return 'verified'; } // 4. Vérification partielle pour les cas intermédiaires if (verifiedRatio > this.THRESHOLDS.PARTIALLY_VERIFIED_THRESHOLD) { return 'partially_verified'; } // 5. Cas d'incertitude forte if (uncertainRatio > this.THRESHOLDS.UNCERTAIN_THRESHOLD) { return 'uncertain'; } // 6. Absence d'information comme cas particulier if (absenceRatio > this.THRESHOLDS.ABSENCE_THRESHOLD) { return 'absence_of_information'; } // 7. Par défaut, vérification partielle si au moins quelques éléments positifs if (verifiedRatio > 0) { return 'partially_verified'; } // 8. Sinon, considérer comme non vérifié return 'unverified'; } /** * Génère un résumé explicatif du niveau de certitude - VERSION OPTIMISÉE * * @param status Statut de vérification * @param confidence Niveau de confiance (0-1) * @returns Résumé textuel du niveau de certitude */ generateCertaintySummary(status, confidence = 0.5) { // Formater le pourcentage pour l'affichage const percentage = Math.round(confidence * 100); // OPTIMISATION: Utiliser un modèle de chaîne de base et personnaliser selon le statut const statusDescriptions = { 'verified': `Information vérifiée avec un niveau de confiance de ${percentage}%. Plusieurs sources fiables confirment cette information.`, 'partially_verified': `Information partiellement vérifiée avec un niveau de confiance de ${percentage}%. Certains éléments sont confirmés par des sources fiables.`, 'unverified': `Information non vérifiée. Niveau de confiance: ${percentage}%. Aucune source ne confirme ou n'infirme cette information.`, 'contradicted': `Information contredite. Niveau de confiance: ${percentage}%. Des sources fiables contredisent cette information.`, 'contradictory': `Information contradictoire. Niveau de confiance: ${percentage}%. Des sources crédibles se contredisent sur ce sujet.`, 'absence_of_information': `Aucune information trouvée sur ce sujet. Niveau de confiance: ${percentage}%. Cette absence d'information est elle-même une information pertinente.`, 'uncertain': `Information incertaine. Niveau de confiance: ${percentage}%. Les sources disponibles ne permettent pas de conclure avec certitude.`, 'inconclusive': `Résultat non concluant. Niveau de confiance: ${percentage}%. Les données sont insuffisantes ou ambiguës pour tirer une conclusion définitive.` }; let summary = statusDescriptions[status] || `Niveau de confiance: ${percentage}%.`; // Ajouter des conseils supplémentaires selon le niveau de confiance if (confidence < 0.3) { summary += ' Cette information doit être considérée comme hautement spéculative.'; } else if (confidence > 0.85) { summary += ' Cette information peut être considérée comme fiable.'; } return summary; } /** * Détecte les biais potentiels dans une pensée - VERSION OPTIMISÉE * * @param thought La pensée à analyser * @returns Un tableau des biais détectés avec leur score (0-1) */ detectBiases(thought) { const content = thought.content.toLowerCase(); const biases = []; // OPTIMISATION: Liste des patterns de biais cognitifs courants const biasPatterns = [ { type: 'confirmation_bias', regex: /je savais déjà|comme prévu|confirme que|toujours été|évidemment/gi, patterns: ['je savais déjà', 'comme prévu', 'confirme que', 'toujours été', 'évidemment'], description: 'Tendance à favoriser les informations qui confirment des croyances préexistantes' }, { type: 'recency_bias', regex: /récemment|dernièrement|ces jours-ci|tendance actuelle|de nos jours/gi, patterns: ['récemment', 'dernièrement', 'ces jours-ci', 'tendance actuelle', 'de nos jours'], description: 'Tendance à donner plus d\'importance aux événements récents' }, { type: 'availability_heuristic', regex: /souvent|fréquemment|généralement|habituellement|couramment/gi, patterns: ['souvent', 'fréquemment', 'généralement', 'habituellement', 'couramment'], description: 'Jugement basé sur des exemples qui viennent facilement à l\'esprit' }, { type: 'black_white_thinking', regex: /toujours|jamais|impossible|absolument|parfaitement|totalement/gi, patterns: ['toujours', 'jamais', 'impossible', 'absolument', 'parfaitement', 'totalement'], description: 'Tendance à voir les choses en termes absolus sans nuances' }, { type: 'authority_bias', regex: /expert dit|selon les experts|études montrent|scientifiquement prouvé/gi, patterns: ['expert dit', 'selon les experts', 'études montrent', 'scientifiquement prouvé'], description: 'Tendance à attribuer plus de poids aux opinions des figures d\'autorité' } ]; // OPTIMISATION: Détecter les biais avec RegExp for (const bias of biasPatterns) { const matches = content.match(bias.regex); if (matches && matches.length > 0) { // Calculer un score basé sur le nombre de correspondances const score = Math.min(matches.length / bias.patterns.length * 1.5, 1); if (score > 0.2) { // Seuil minimum pour considérer un biais biases.push({ type: bias.type, score, description: bias.description }); } } } // Analyse de sentiment pour détecter le biais émotionnel if (this.REGEX.STRONG_EMOTION.test(content)) { biases.push({ type: 'emotional_bias', score: 0.7, description: 'Jugement influencé par une forte charge émotionnelle' }); } return biases; } /** * Détermine les besoins de vérification pour un contenu donné - VERSION OPTIMISÉE * * @param content Le contenu textuel à analyser * @returns Configuration recommandée pour la vérification */ determineVerificationRequirements(content) { const lowercaseContent = content.toLowerCase(); // Initialiser les résultats const result = { needsFactCheck: false, needsMathCheck: false, needsSourceCheck: false, priority: 'low', suggestedTools: [], requiresMultipleVerifications: false, reasons: [], recommendedVerificationsCount: 1 }; // OPTIMISATION: Détection par RegExp // Détection des affirmations factuelles if (this.REGEX.FACTUAL_CLAIM.test(content)) { result.needsFactCheck = true; result.suggestedTools.push('web_search'); result.reasons.push('Contient des affirmations factuelles'); } // Détection des calculs mathématiques if (this.REGEX.MATH_CALCULATION.test(content) || this.REGEX.MATHEMATICAL_PROOF.test(content)) { result.needsMathCheck = true; result.suggestedTools.push('math_evaluator'); result.reasons.push('Contient des calculs ou preuves mathématiques'); } // OPTIMISATION: Utiliser RegExp pour détecter les références à des sources const sourceRegex = /selon|d'après|source|cité|référence|étude|recherche|publication/i; if (sourceRegex.test(content)) { result.needsSourceCheck = true; result.suggestedTools.push('citation_checker'); result.reasons.push('Contient des références à des sources'); } // OPTIMISATION: Détermination de la priorité avec RegExp let score = 0; // Augmenter le score si contient des affirmations fortes if (/certainement|absolument|sans aucun doute|clairement|évidemment|forcément/i.test(lowercaseContent)) { score += 2; result.reasons.push('Contient des affirmations fortes'); } // Augmenter le score si contient des statistiques ou chiffres précis if (/\d+%|\d+\.\d+|millions?|milliards?/i.test(lowercaseContent)) { score += 2; result.reasons.push('Contient des statistiques ou chiffres précis'); } // Augmenter le score si contient des références temporelles récentes if (/récemment|cette année|ce mois|cette semaine|aujourd'hui|actuellement/i.test(lowercaseContent)) { score += 1; result.reasons.push('Contient des références temporelles récentes'); } // OPTIMISATION: Définir la priorité en fonction du score directement result.priority = score >= 3 ? 'high' : (score >= 1 ? 'medium' : 'low'); // OPTIMISATION: Déterminer si plusieurs vérifications sont nécessaires // Basé sur la complexité et l'importance du contenu result.requiresMultipleVerifications = (result.needsFactCheck && (result.needsMathCheck || result.needsSourceCheck)) || (result.priority === 'high'); // OPTIMISATION: Calculer le nombre de vérifications recommandées directement if (result.requiresMultipleVerifications) { result.recommendedVerificationsCount = (result.needsFactCheck && result.needsMathCheck && result.needsSourceCheck) ? 3 : 2; } return result; } /** * Calcule un niveau de confiance pour un ensemble de résultats de vérification - VERSION OPTIMISÉE * en utilisant une approche bayésienne * * @param results Résultats de vérification * @returns Score de confiance entre 0 et 1 */ calculateVerificationConfidence(results) { if (!results || results.length === 0) { return 0.5; // Confiance neutre par défaut } // OPTIMISATION: Facteurs de pondération pour différentes sources const sourceTypeWeights = new Map([ ['search', 0.8], ['database', 0.9], ['calculation', 0.95], ['external_api', 0.85] ]); const defaultWeight = 0.7; // OPTIMISATION: Compter directement sans objets intermédiaires let positiveWeight = 0; let negativeWeight = 0; // OPTIMISATION: Analyser chaque résultat en une seule passe for (const result of results) { // Déterminer le poids de cette source const sourceWeight = sourceTypeWeights.get(result.toolType) || defaultWeight; const confidence = result.confidence || 0.5; // Ajuster la confiance en fonction de la validité if (result.result?.isValid === true) { positiveWeight += sourceWeight * confidence; } else if (result.result?.isValid === false) { negativeWeight += sourceWeight * confidence; } } // Si aucun résultat positif ou négatif clair, retourner une confiance neutre if (positiveWeight === 0 && negativeWeight === 0) { return 0.5; } // OPTIMISATION: Calculer le ratio de vraisemblance bayésien directement const likelihoodRatio = (positiveWeight + 0.5) / (negativeWeight + 0.5); // Convertir les odds en probabilité let confidence = likelihoodRatio / (1 + likelihoodRatio); // Bonus pour le nombre de sources consultées const sourceCountBonus = Math.min(results.length * 0.05, 0.2); confidence = Math.min(confidence + sourceCountBonus, 0.95); // Limite inférieure pour éviter une confiance trop basse return Math.max(confidence, 0.2); } } exports.MetricsCalculator = MetricsCalculator; //# sourceMappingURL=metrics-calculator.js.map