diff --git a/src/control/cpam_response.py b/src/control/cpam_response.py index d05573b..db94d87 100644 --- a/src/control/cpam_response.py +++ b/src/control/cpam_response.py @@ -15,10 +15,15 @@ logger = logging.getLogger(__name__) def _search_rag_for_control(controle: ControleCPAM, dossier: DossierMedical) -> list[dict]: """Recherche RAG ciblée pour le sujet du désaccord. - Effectue 2-3 recherches ciblées au lieu d'une requête fourre-tout : + Effectue 2-5 recherches ciblées au lieu d'une requête fourre-tout : 1. Codes contestés → règles de codage spécifiques 2. Argument CPAM → passages Guide Méthodo contradictoires 3. Contexte clinique (optionnel) → définitions CIM-10 des codes en jeu + 4. Définitions CIM-10 des codes contestés + 5. Règles explicitement citées dans l'argument CPAM + + Retourne [] si le RAG est indisponible (index absent, modèle embedding + inaccessible, etc.) — la contre-argumentation sera générée sans sources. """ try: from ..medical.rag_search import search_similar_cpam @@ -26,6 +31,20 @@ def _search_rag_for_control(controle: ControleCPAM, dossier: DossierMedical) -> logger.warning("Index RAG non disponible pour la contre-argumentation") return [] + try: + return _search_rag_queries(controle, dossier, search_similar_cpam) + except Exception: + logger.warning("Erreur RAG pour la contre-argumentation — génération sans sources", + exc_info=True) + return [] + + +def _search_rag_queries( + controle: ControleCPAM, + dossier: DossierMedical, + search_similar_cpam, +) -> list[dict]: + """Exécute les requêtes RAG (séparé pour permettre un try/except global).""" all_results: list[dict] = [] # Requête 1 — Codes contestés (règles de codage) @@ -294,6 +313,10 @@ IMPORTANT — CRÉDIBILITÉ DE L'ANALYSE : Une contre-argumentation crédible reconnaît TOUJOURS au moins un point valide dans le raisonnement adverse. Répondre "Aucun point d'accord" décrédibilise l'ensemble de l'argumentation. Tu DOIS identifier au moins un élément où la CPAM a un point légitime (même partiel), puis expliquer pourquoi cela ne suffit pas à invalider le codage. +IMPORTANT — CODES CIM-10 : +Ne parle JAMAIS de « codage initial » ou « codage contesté » sans citer explicitement le code CIM-10 et son libellé (ex: Z45.80 — Ajustement et entretien d'un dispositif implantable). +Chaque argument doit désigner précisément quel code est défendu ou contesté, avec son libellé complet. + DOSSIER MÉDICAL DE L'ÉTABLISSEMENT : {dossier_str} {asymetrie_str} @@ -355,7 +378,7 @@ Réponds UNIQUEMENT avec un objet JSON au format suivant : "references": [ {{"document": "nom du document source", "page": "numéro de page", "citation": "citation verbatim du passage"}} ], - "conclusion": "Synthèse : points reconnus à la CPAM, mais pourquoi le codage initial est néanmoins justifié" + "conclusion": "Synthèse en citant EXPLICITEMENT les codes CIM-10 défendus (ex: DP Z45.80 — libellé) : points reconnus à la CPAM, puis pourquoi ce codage précis est néanmoins justifié" }}""" diff --git a/src/medical/rag_search.py b/src/medical/rag_search.py index 2c405c9..b914105 100644 --- a/src/medical/rag_search.py +++ b/src/medical/rag_search.py @@ -45,8 +45,9 @@ def _get_embed_model(): logger.info("Chargement du modèle d'embedding (%s)...", _device) _embed_model = SentenceTransformer(EMBEDDING_MODEL, device=_device) except (torch.OutOfMemoryError, torch.cuda.CudaError, torch.AcceleratorError, RuntimeError) as exc: - if _device == "cuda" and "memory" in str(exc).lower(): - logger.warning("CUDA OOM pour l'embedding — fallback CPU") + exc_msg = str(exc).lower() + if _device == "cuda" and ("memory" in exc_msg or "meta tensor" in exc_msg): + logger.warning("CUDA erreur pour l'embedding — fallback CPU : %s", exc) torch.cuda.empty_cache() _embed_model = SentenceTransformer(EMBEDDING_MODEL, device="cpu") else: