""" Module 2: Analyse Comparative & Critique Raisonnement multi-documents profond """ from typing import List, Dict, Any from app.models.schemas import ( ScientificDocument, ComparativeAnalysis, ResearchGap, ExtractedHypothesis, Methodology ) class ComparativeAnalysisEngine: """Engine pour analyse comparative multi-documents""" def __init__(self): self.divergence_prompts = self._init_divergence_prompts() def _init_divergence_prompts(self) -> Dict[str, str]: """K2 Think prompts pour détection de divergences""" return { "detect_divergences": """Analyse les divergences entre documents. Cherche: 1. Conclusions opposées ou contradictoires 2. Méthodologies incompatibles ou conflictuelles 3. Variables confondantes non mentionnées 4. Dérives d'interprétation Pour chaque divergence: type + gravité (0-1) + explication""", "robustness_evaluation": """Évalue la robustesse des études. Critères: - Taille d'échantillon suffisante? - Type d'étude robuste? - Méthodes statistiques appropriées? - Reproductibilité? - Limitations acceptables? Retour: score de confiance (0-1) par document""", "gap_extraction": """Identifie les gaps de recherche: 1. Variables explorées partiellement 2. Paramètres ignorés 3. Hypothèses implicites jamais testées directement 4. Intersections disciplinaires non exploitées Importance: 0-1 pour chaque gap""", } def analyze_multiple_documents( self, documents: List[ScientificDocument], extracted_data: List[Dict[str, Any]] ) -> ComparativeAnalysis: """ Analyse comparative de plusieurs documents Args: documents: Liste de documents extracted_data: Données extraites par ingestion engine Returns: ComparativeAnalysis avec divergences, gaps, confiance """ analysis = ComparativeAnalysis( document_ids=[doc.id for doc in documents], divergences=self._detect_divergences(documents, extracted_data), contradictions=self._detect_contradictions(documents, extracted_data), common_findings=self._extract_common_findings(documents, extracted_data), research_gaps=self._identify_research_gaps(documents, extracted_data), confidence_score=self._calculate_overall_confidence(documents, extracted_data) ) return analysis def _detect_divergences( self, documents: List[ScientificDocument], extracted_data: List[Dict] ) -> List[Dict[str, Any]]: """ Détecte divergences entre documents K2 Think orchestrerait cette logique via LLM """ divergences = [] # Comparaison pairwise simple (la vraie logique serait en prompts LLM) for i, doc1 in enumerate(documents): for j, doc2 in enumerate(documents[i+1:], i+1): divergence_check = { "document_pair": [doc1.id, doc2.id], "divergence_type": "methodology_difference", "severity": 0.6, "description": f"Study types differ between {doc1.title} and {doc2.title}", "impact": "Could affect applicability of combined findings" } divergences.append(divergence_check) return divergences def _detect_contradictions( self, documents: List[ScientificDocument], extracted_data: List[Dict] ) -> List[Dict[str, Any]]: """Détecte contradictions directives dans les conclusions""" contradictions = [] # Placeholder pour logique K2 Think # En pratique: compare conclusions extraites, variables cibles, résultats return contradictions def _extract_common_findings( self, documents: List[ScientificDocument], extracted_data: List[Dict] ) -> List[str]: """Extrait résultats consensuels entre documents""" common = [ "All studies confirm primary hypothesis regarding mechanism X", "Consistent finding: variable Y significantly affects outcome Z" ] return common def _identify_research_gaps( self, documents: List[ScientificDocument], extracted_data: List[Dict] ) -> List[ResearchGap]: """ Identifie les gaps importants de recherche """ gaps = [ ResearchGap( gap_description="Long-term effects of treatment X on patient cohort Y not studied", importance_score=0.85, related_variables=["treatment_duration", "patient_age", "comorbidities"], suggested_investigation="5-year longitudinal study with N=500 subjects", source_documents=[] ), ResearchGap( gap_description="Interaction between variables A and B remains unexplored", importance_score=0.72, related_variables=["variable_A", "variable_B"], suggested_investigation="Factorial design with 2x3 configuration", source_documents=[] ) ] return gaps def _calculate_overall_confidence( self, documents: List[ScientificDocument], extracted_data: List[Dict] ) -> float: """ Calcule un score de confiance global (0-1) Basé sur: consensus, robustesse, couverture, conflits """ # Logique simplifiée robustness_scores = [] for data in extracted_data: # Chaque document a un score basé sur méthodologie score = 0.75 # Placeholder robustness_scores.append(score) # Score global = moyenne - pénalité si conflits conflict_penalty = len(self._detect_contradictions(documents, extracted_data)) * 0.1 avg_robustness = sum(robustness_scores) / len(robustness_scores) if robustness_scores else 0.5 confidence = max(0, min(1, avg_robustness - conflict_penalty)) return confidence def generate_audit_trace(self, analysis: ComparativeAnalysis) -> List[Dict[str, Any]]: """ Génère une chaîne d'audit complète Pour MODULE 9 (Transparency & Audit) """ trace = [ { "step": "document_ingestion", "decision": f"Analyzed {len(analysis.document_ids)} documents", "reasoning": "Multi-document analysis required", "timestamp": "2026-03-07T10:00:00Z" }, { "step": "divergence_detection", "decision": f"Found {len(analysis.divergences)} divergences", "reasoning": "Systematic pairwise comparison", "timestamp": "2026-03-07T10:01:00Z" }, { "step": "gap_identification", "decision": f"Identified {len(analysis.research_gaps)} research gaps", "reasoning": "Cross-document synthesis", "timestamp": "2026-03-07T10:02:00Z" }, { "step": "confidence_assessment", "decision": f"Overall confidence: {analysis.confidence_score:.2%}", "reasoning": f"Robustness evaluation + conflict analysis", "timestamp": "2026-03-07T10:03:00Z" } ] return trace