feat: grounding CPAM — tags DP/DAS/ANT/COMPL + fuzzy matching CIM-10 + prompt renforcé

Cause racine du Tier C : le LLM inventait des tags ([C83.3], [Antécédents])
car _build_tagged_context() ne taguait que bio/img/trt/actes. Le DP, les DAS,
antécédents et complications n'avaient aucun tag citable.

- cpam_context: 4 nouveaux types de tags [DP], [DAS-N], [ANT-N], [COMPL-N]
- cpam_validation: fuzzy matching — résout les refs CIM-10 nues vers le tag contenant ce code
- templates: liste explicite des tags valides, interdiction d'inventer des tags
- tests: 18 nouveaux tests (tags, fuzzy match, grounding DAS/DP)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-02-20 13:56:07 +01:00
parent e77c10da7d
commit 4d49d4e114
5 changed files with 261 additions and 10 deletions

View File

@@ -14,9 +14,29 @@ from ..prompts import CPAM_ADVERSARIAL
logger = logging.getLogger(__name__)
def _fuzzy_match_ref(ref: str, tag_map: dict[str, str]) -> str | None:
"""Tente de résoudre une ref inventée vers un tag réel.
Stratégie : si la ref ressemble à un code CIM-10 (ex: "C83.3"),
chercher dans tag_map un tag dont le contenu contient ce code.
Returns:
Le tag réel trouvé, ou None si aucun match.
"""
ref_upper = ref.strip().upper()
# Match par code CIM-10 dans le contenu des tags
if re.match(r"^[A-Z]\d{2}\.?\d{0,2}$", ref_upper):
for tag, content in tag_map.items():
if ref_upper in content.upper() or ref in content:
return tag
return None
def _validate_grounding(response_data: dict, tag_map: dict[str, str]) -> list[str]:
"""Vérifie que les références dans preuves_dossier correspondent à des tags existants.
Applique un fuzzy matching par code CIM-10 avant de flaguer un warning.
Returns:
Liste de warnings pour les références inventées.
"""
@@ -35,6 +55,10 @@ def _validate_grounding(response_data: dict, tag_map: dict[str, str]) -> list[st
if not ref:
continue
if ref not in tag_map:
matched_tag = _fuzzy_match_ref(ref, tag_map)
if matched_tag:
logger.info("Grounding : ref [%s] résolue vers [%s]", ref, matched_tag)
continue # pas de warning
valeur = p.get("valeur", "?")
warnings.append(f"Preuve [{ref}] non traçable (« {valeur} »)")
logger.warning("Grounding : preuve [%s] introuvable dans les tags du dossier", ref)