feat: CODE_CORRECTIONS 12 règles déterministes + sentinel REJECT

- CODE_CORRECTIONS passe de 1 à 12 règles (corrections + rejets)
- REJECT_SENTINEL pour codes trop vagues (R69, R69.8, Z53.9, D71.9) ou inexistants
- Corrections : J96.0→J96.00, I50.9→I50.1 (IC gauche), N17.9→N17.0 (NTA),
  E11.9→E11.65 (DT2 insuline), K92.2→K92.0 (hématémèse), G40.9→G40.3 (épilepsie)
- _apply_code_corrections() gère REJECT : DP→None, DAS→supprimé + alerte
- 21 tests paramétrés (corrections, rejets, non-corrections)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-02-20 11:01:06 +01:00
parent 1a3c523987
commit 77ffbc56d4
3 changed files with 187 additions and 10 deletions

View File

@@ -6,7 +6,7 @@ import logging
from .cim10_dict import lookup as dict_lookup, normalize_code, validate_code as cim10_validate
from .ccam_dict import validate_code as ccam_validate
from .das_filter import correct_known_miscodes, apply_semantic_dedup
from .das_filter import correct_known_miscodes, apply_semantic_dedup, REJECT_SENTINEL
from ..config import Diagnostic, DossierMedical
from .diagnostic_extraction import CIM10_MAP
@@ -92,19 +92,46 @@ def _validate_cim10(dossier: DossierMedical) -> None:
def _apply_code_corrections(dossier: DossierMedical) -> None:
"""Corrige les codes CIM-10 systématiquement mal attribués par le LLM."""
all_diags = []
if dossier.diagnostic_principal:
all_diags.append(dossier.diagnostic_principal)
all_diags.extend(dossier.diagnostics_associes)
"""Corrige les codes CIM-10 systématiquement mal attribués par le LLM.
for diag in all_diags:
Si un code est marqué REJECT_SENTINEL, il est retiré :
- DP : code mis à None (pas de suppression du diagnostic principal)
- DAS : diagnostic supprimé de la liste
"""
# DP
if dossier.diagnostic_principal and dossier.diagnostic_principal.cim10_suggestion:
dp = dossier.diagnostic_principal
corrected = correct_known_miscodes(dp.cim10_suggestion, dp.texte)
if corrected == REJECT_SENTINEL:
logger.info(" Code rejeté : %s pour « %s » (DP) — code supprimé", dp.cim10_suggestion, dp.texte)
dossier.alertes_codage.append(
f"Code rejeté : {dp.cim10_suggestion} ({dp.texte}) — trop vague ou inexistant"
)
dp.cim10_suggestion = None
dp.cim10_confidence = None
elif corrected:
logger.info(" Code corrigé : %s%s pour « %s »", dp.cim10_suggestion, corrected, dp.texte)
dp.cim10_suggestion = corrected
# DAS
das_to_keep = []
for diag in dossier.diagnostics_associes:
if not diag.cim10_suggestion:
das_to_keep.append(diag)
continue
corrected = correct_known_miscodes(diag.cim10_suggestion, diag.texte)
if corrected == REJECT_SENTINEL:
logger.info(" Code rejeté : %s pour « %s » (DAS) — diagnostic supprimé", diag.cim10_suggestion, diag.texte)
dossier.alertes_codage.append(
f"Code rejeté : {diag.cim10_suggestion} ({diag.texte}) — trop vague ou inexistant"
)
continue # ne pas ajouter à das_to_keep
if corrected:
logger.info(" Code corrigé : %s%s pour « %s »", diag.cim10_suggestion, corrected, diag.texte)
diag.cim10_suggestion = corrected
das_to_keep.append(diag)
dossier.diagnostics_associes = das_to_keep
def _apply_exclusion_rules(dossier: DossierMedical) -> None: