feat: 3 quick wins — source DAS, fallback code parent, filtre anatomique

1. Champ source sur Diagnostic : trackare/edsnlp/regex/llm_das
   - Renseigné dans les 8 constructeurs de cim10_extractor.py
   - Permet l'audit de provenance des DAS dans le JSON de sortie

2. Fallback code parent pour les codes LLM halluccinés :
   - fallback_parent_code() dans cim10_dict.py (D71.9→D71, R69.8→R69)
   - Intégré dans _apply_llm_result_diagnostic() de rag_search.py
   - Récupère les codes rejetés dont le parent 3-char est valide

3. Règle 12 filtre DAS : en-têtes anatomiques + catégories vagues
   - Rejette "Musculaire", "Digestif", "Hépatique" (mots isolés)
   - Rejette "Musculaire - masse musculaire" (catégorie + description)
   - 13 nouveaux tests unitaires au total

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-02-15 11:34:32 +01:00
parent 59365e3af9
commit 8c75941e40
7 changed files with 130 additions and 10 deletions

View File

@@ -205,6 +205,7 @@ def _extract_das_llm(text: str, dossier: DossierMedical) -> None:
texte=texte,
cim10_suggestion=code,
justification=das.get("justification"),
source="llm_das",
))
added += 1
@@ -297,6 +298,7 @@ def _extract_diagnostics(
d = Diagnostic(
texte=texte,
cim10_suggestion=diag.get("code_cim10"),
source="trackare",
)
if diag.get("type", "").lower() == "principal":
dossier.diagnostic_principal = d
@@ -331,6 +333,7 @@ def _extract_diagnostics(
code, texte = next(iter(edsnlp_codes.items()))
dossier.diagnostic_principal = Diagnostic(
texte=texte.capitalize(), cim10_suggestion=code,
source="edsnlp",
)
# Diagnostics associés depuis le texte (regex)
@@ -356,6 +359,7 @@ def _extract_diagnostics(
dossier.diagnostics_associes.append(Diagnostic(
texte=texte,
cim10_suggestion=ent.code,
source="edsnlp",
))
existing_codes.add(ent.code)
@@ -370,7 +374,7 @@ def _find_diagnostic_principal(text_lower: str, conclusion: str) -> Diagnostic |
# Chercher dans la conclusion d'abord via CIM10_MAP (domain override)
for terme, code in CIM10_MAP.items():
if normalize_text(terme) in conclusion_norm:
return Diagnostic(texte=terme.capitalize(), cim10_suggestion=code)
return Diagnostic(texte=terme.capitalize(), cim10_suggestion=code, source="regex")
text_norm = normalize_text(text_lower)
@@ -385,7 +389,7 @@ def _find_diagnostic_principal(text_lower: str, conclusion: str) -> Diagnostic |
if m:
matched = m.group(0)
code = _lookup_cim10(matched)
return Diagnostic(texte=matched.capitalize(), cim10_suggestion=code)
return Diagnostic(texte=matched.capitalize(), cim10_suggestion=code, source="regex")
return None
@@ -444,7 +448,7 @@ def _find_diagnostics_associes(
# Patterns DAS
for pat, label, code in _DAS_PATTERNS:
if re.search(pat, text_norm) and code not in existing_codes:
das.append(Diagnostic(texte=label, cim10_suggestion=code))
das.append(Diagnostic(texte=label, cim10_suggestion=code, source="regex"))
existing_codes.add(code)
# Obésité (IMC >= 30) — pattern spécial avec extraction de valeur
@@ -452,7 +456,7 @@ def _find_diagnostics_associes(
if m:
imc_val = float(m.group(1).replace(",", "."))
if imc_val >= 30 and "E66.0" not in existing_codes:
das.append(Diagnostic(texte=f"Obésité (IMC {imc_val})", cim10_suggestion="E66.0"))
das.append(Diagnostic(texte=f"Obésité (IMC {imc_val})", cim10_suggestion="E66.0", source="regex"))
existing_codes.add("E66.0")
return das