feat: dictionnaire CCAM complet (8 257 codes) + index FAISS enrichi + validation actes

Phase 2 (CCAM) :
- Nouveau src/medical/ccam_dict.py : build depuis CCAM_V81.xls via xlrd, lookup 3 niveaux, validation codes
- Intégration dans l'extracteur : fallback ccam_lookup + _validate_ccam() avec alertes
- CLI : --build-ccam-dict, --rebuild-index

Phase 3 (FAISS) :
- Chunks CCAM depuis le dictionnaire JSON (priorité sur le PDF)
- Chunks CIM-10 index alphabétique (terme → code)
- Priorisation cim10_alpha dans la recherche RAG

Viewer : endpoint reprocess + bloc scripts
Tests : 8 tests CCAM + tests raisonnement RAG (161 passed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-02-11 11:41:39 +01:00
parent 9df4465fef
commit 7e69f994b0
10 changed files with 893 additions and 46 deletions

View File

@@ -10,6 +10,7 @@ from typing import Optional
logger = logging.getLogger(__name__)
from .cim10_dict import lookup as dict_lookup, normalize_text
from .ccam_dict import lookup as ccam_lookup, validate_code as ccam_validate
from ..config import (
ActeCCAM,
BiologieCle,
@@ -113,6 +114,9 @@ def extract_medical_info(
if use_rag:
_enrich_with_rag(dossier)
# Post-processing : validation des codes CCAM contre le dictionnaire
_validate_ccam(dossier)
# Post-processing : exclusions symptôme vs diagnostic précis
_apply_exclusion_rules(dossier)
@@ -395,6 +399,13 @@ def _extract_actes(text: str, dossier: DossierMedical) -> None:
date=date,
))
# Fallback : tenter le lookup CCAM dict pour les actes sans code
for acte in dossier.actes_ccam:
if not acte.code_ccam_suggestion:
code = ccam_lookup(acte.texte, domain_overrides=CCAM_MAP)
if code:
acte.code_ccam_suggestion = code
def _extract_antecedents(text: str, dossier: DossierMedical) -> None:
"""Extrait les antécédents."""
@@ -625,6 +636,22 @@ def _is_negated_by_edsnlp(term: str, negated_terms: set[str]) -> bool:
return False
def _validate_ccam(dossier: DossierMedical) -> None:
"""Valide les codes CCAM suggérés contre le dictionnaire officiel."""
for acte in dossier.actes_ccam:
if not acte.code_ccam_suggestion:
acte.validite = "non_verifie"
continue
is_valid, desc = ccam_validate(acte.code_ccam_suggestion)
if is_valid:
acte.validite = "valide"
else:
acte.validite = "non_verifie"
dossier.alertes_codage.append(
f"CCAM {acte.code_ccam_suggestion} ({acte.texte}) : code absent du dictionnaire CCAM V81"
)
def _find_act_date(text: str, act_pattern: str) -> str | None:
"""Trouve la date associée à un acte."""
# Chercher "acte le DD/MM" ou "acte le DD/MM/YYYY"