Files
t2a_v2/docs/NUKE3_NEXT_STEPS.md
2026-03-05 00:37:41 +01:00

6.3 KiB
Raw Blame History

NUKE-3 — Plan de sprint court

3 Objectifs

  1. Réduire le taux REVIEW sur les dossiers sans DP : 43/43 → cible < 50% REVIEW
  2. Améliorer la qualité du pool : éliminer le bruit OCR et fusionner les doublons
  3. Activer le signal synthèse : rendre motif_align opérant même sur les dossiers trackare

5 Patchs proposés (ordre d'impact)

Patch 1 — Dedup par code dans build_candidates()

Symptôme : Même code CIM-10 apparaît 2× (edsnlp + regex) → le candidat se bat contre lui-même, delta artificiellement réduit.

Patch minimal : src/medical/dp_selector.py, fonction build_candidates()

# Après la boucle de construction des candidats :
# Dedup par code : garder le meilleur section_strength, ajouter bonus multi-source
seen: dict[str, DPCandidate] = {}
for c in candidates:
    if not c.code:
        continue
    if c.code in seen:
        existing = seen[c.code]
        existing.num_occurrences += 1
        if c.section_strength > existing.section_strength:
            existing.section_strength = c.section_strength
            existing.source = c.source
    else:
        seen[c.code] = c
candidates = list(seen.values())
# Réindexer
for i, c in enumerate(candidates):
    c.index = i

Pourquoi ça aide : Les 4 dossiers avec doublons (I26.9, K81.0, D69.6, J18.9) gagnent un candidat fusionné plus fort. Le bonus occurrences existant (+1 ou +2) s'active déjà sur num_occurrences.

Test : test_dedup_same_code_merged() — 2 candidats avec même code, sources différentes → 1 seul candidat avec meilleur section_strength.

Effort : 1h


Patch 2 — Filtre bruit OCR dans build_candidates()

Symptôme : Candidats avec texte OCR corrompu ("C : 9.4", "C omprend décollement de la (d") polluent le pool et consomment des places dans top_k=7.

Patch minimal : src/medical/dp_selector.py, dans build_candidates()

MIN_TERM_WORDS = 2

def _is_ocr_noise(text: str) -> bool:
    """Rejette les candidats dont le texte est du bruit OCR."""
    clean = text.strip()
    if len(clean) < 4:
        return True
    words = clean.split()
    if len(words) < MIN_TERM_WORDS:
        return True
    # Ratio de caractères non-alpha suspect
    alpha = sum(1 for ch in clean if ch.isalpha())
    if alpha / max(len(clean), 1) < 0.5:
        return True
    return False

Pourquoi ça aide : Réduit le pool de 7 à ~4-5 candidats pertinents, améliore le delta.

Test : test_ocr_noise_excluded() — candidat "C : 9.4" exclu du pool.

Effort : 1h


Patch 3 — Synthèse depuis top-level JSON + sections trackare

Symptôme : 100% des REVIEW ont synthèse vide → motif_align ne fonctionne jamais. Le JSON a sejour.motif mais build_synthese() ne lit que sections.motif_hospitalisation.

Patch minimal : src/medical/dp_selector.py, fonction build_synthese()

def build_synthese(dossier, parsed_data):
    sections = parsed_data.get("sections", {})
    motif = sections.get("motif_hospitalisation", "")
    conclusion = sections.get("conclusion", "")
    # Fallback : motif depuis le séjour (trackare)
    if not motif and dossier.sejour.motif:
        motif = dossier.sejour.motif
    return {"motif": motif, "conclusion": conclusion, ...}

Pourquoi ça aide : Active le bonus motif_align (+2) sur les trackare qui stockent le motif dans sejour.motif, discriminant les candidats.

Test : test_synthese_fallback_sejour_motif() — synthèse avec motif depuis séjour.

Effort : 30min


Patch 4 — Abaisser DELTA_CONFIRMED de 3.0 à 2.0

Symptôme : 13/43 REVIEW ont delta 2.0-2.9 — cas où le pré-ranker a une préférence nette mais pas assez pour le seuil actuel. Exemple : T83.5 (4.0) vs R50.9 (2.0) = delta 2.0 → REVIEW, alors que le symptôme (R50.9) est clairement un mauvais DP.

Patch minimal : src/medical/dp_selector.py

DELTA_CONFIRMED = 2.0  # Était 3.0

Pourquoi ça aide : +13 CONFIRMED (30% des REVIEW), mais uniquement si les gardes A1/A2/A3 valident. Le hardening empêche les faux positifs.

Test : Adapter les fixtures existantes (dp_acute_vs_comorbidity.json utilise un delta attendu).

Effort : 30min (+ vérification non-régression)


Patch 5 — DAS order bonus (premier listé = plus saillant)

Symptôme : En cas d'ex-aequo total (15 cas), il n'y a aucun signal pour départager. Or l'ordre des DAS dans le document médical n'est pas aléatoire — les premiers listés sont souvent les plus pertinents.

Patch minimal : src/medical/dp_selector.py, dans score_candidates()

# 9. Bonus d'ordre (premier DAS listé = +1)
if c.index == 0:
    score += 1
    details["first_listed"] = 1
elif c.index == 1:
    score += 0.5
    details["second_listed"] = 0.5

Pourquoi ça aide : Brise les ex-aequo dans 15 cas (36% des REVIEW) avec un signal faible mais non-arbitraire. Le premier DAS listé reflète l'ordre du document source.

Test : test_first_listed_bonus() — 2 candidats identiques, le premier listé gagne.

Effort : 30min


Ordre d'exécution

# Patch Effort Impact estimé Pré-requis
1 Dedup par code 1h 4 dossiers améliorés Aucun
2 Filtre bruit OCR 1h ~5 dossiers pool nettoyé Aucun
3 Synthèse fallback 30min Jusqu'à 43 dossiers (si motif trackare dispo) Vérifier séjour.motif
4 DELTA 3.0 → 2.0 30min +13 CONFIRMED Patches 1+2 (pool propre)
5 DAS order bonus 30min Brise 15 ex-aequo Après patch 4

Total estimé : 3h30

Critères d'acceptation (KPI)

KPI Avant Cible
REVIEW rate (sans-DP) 100% (43/43) < 50%
CONFIRMED + evidence 100% 100% (maintenu)
DP symptôme R* (CONFIRMED) 0% < 5%
DP comorbidité (CONFIRMED) 0% < 3%
Candidats bruit OCR dans pool ~5% 0%
Ex-aequo (delta=0) 36% (15/43) < 15%

Prochaines étapes hors sprint

  • Gold standard CRH : sélectionner 20 CRH avec DP expert pour mesurer confirmed_accuracy
  • Benchmark LLM ON : relancer le pipeline avec T2A_DP_RANKER_LLM=1 sur les 43 REVIEW
  • Extraction synthèse trackare : parser le motif d'hospitalisation depuis les PDF trackare

Plan rédigé le 2026-02-24 — benchmark offline sur 249 dossiers