Spaces:
Running
Running
| """ | |
| 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 | |