""" MODULE: QUERY PLANNER (OAG) =========================== Responsabilité : Analyser la question, vérifier le schéma réel, et décider de la meilleure stratégie (SPARQL vs FAISS vs Multi-étapes). """ import json import re class QueryPlanner: def __init__(self, rdf_store, schema_info): self.rdf = rdf_store self.schema = schema_info # Provient de SchemaExtractor.get_real_schema() def analyze_and_plan(self, user_query): """ Détermine si la question est : 1. Sémantique (Recherche d'un dossier client/prêt spécifique) 2. Analytique (Comptage, Somme, Croisement global) 3. Invalide (Donnée absente du schéma) """ # 1. ANALYSE DES MOTS CLÉS (Simplifiée) is_analytical = any(word in user_query.lower() for word in [ 'combien', 'total', 'somme', 'moyenne', 'liste tous', 'liste les', 'nombre' ]) # 2. VÉRIFICATION DU SCHÉMA (Prévention d'hallucination) # On regarde si les termes de la question match avec nos prédicats réels available_preds = self.schema.get("predicates", []) found_preds = [p for p in available_preds if p.lower() in user_query.lower()] # 3. DÉCISION DE LA STRATÉGIE # CAS A : Recherche d'un individu ou d'un dossier précis if not is_analytical and not found_preds: return { "strategy": "OG-RAG", "tool": "search_semantic", "reason": "La question semble porter sur un individu ou un cas spécifique non défini par un prédicat exact.", "steps": [f"Rechercher le bloc contextuel pour : {user_query}"] } # CAS B : Question de masse ou croisement (Analytique) if is_analytical: return { "strategy": "OAG-SPARQL", "tool": "execute_sparql", "reason": "La question nécessite une agrégation ou un filtrage sur l'ensemble de la base.", "steps": ["Traduire en requête SPARQL optimisée", "Valider les prédicats contre le schéma réel"] } # CAS C : Hybride (Par défaut) return { "strategy": "HYBRID", "tool": "execute_sparql", "reason": "Usage de prédicats identifiés dans le schéma réel.", "steps": ["Générer SPARQL avec les prédicats validés"] } def get_planning_logs(self, plan): """Génère un affichage propre pour le chat utilisateur""" log = f"🎯 **Stratégie adoptée** : `{plan['strategy']}`\n" log += f"🤔 **Raisonnement** : {plan['reason']}\n" log += "📋 **Plan d'exécution** :\n" for i, step in enumerate(plan['steps'], 1): log += f" {i}. {step}\n" return log