scientific-backend / app /modules /comparative_analysis.py
Dama12's picture
Initial clean backend deployment
0bd4ab4
Raw
History Blame Contribute Delete
7.66 kB
"""
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