feat: architecture multi-modèles LLM + quality engine + benchmark

- Multi-modèles : 4 rôles LLM (coding=gemma3:27b-cloud, cpam=gemma3:27b-cloud,
  validation=deepseek-v3.2:cloud, qc=gemma3:12b) avec get_model(role)
- Prompts externalisés : 7 templates dans src/prompts/templates.py
- Cache Ollama : modèle stocké par entrée (migration auto ancien format)
- call_ollama() : paramètre role= (priorité: model > role > global)
- Quality engine : veto_engine + decision_engine + rules_router (YAML)
- Benchmark qualité : scripts/benchmark_quality.py (A/B, métriques CIM-10)
- Fix biologie : valeurs qualitatives (troponine négative) non filtrées
- Fix CPAM : gemma3:27b-cloud au lieu de deepseek (JSON tronqué par thinking)
- CPAM max_tokens 4000→6000, viewer admin multi-modèles
- Benchmark 10 dossiers : 100% DAS valides, 10/10 CPAM, 243s/dossier

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dom
2026-02-20 00:21:09 +01:00
parent 5c8c2817ec
commit 909e051cc9
39 changed files with 5092 additions and 574 deletions

68
config/rules/README.md Normal file
View File

@@ -0,0 +1,68 @@
# Règles (vetos + décisions)
Ce dossier contient la configuration "métier" pour piloter le moteur qualité.
## Fichiers
- `base.yaml` : socle commun (règles activées par défaut).
- `enabled.yaml` : choisit les overlays à activer (site/spécialité).
- `specialties/*.yaml` : overrides par spécialité.
- `sites/*.yaml` : overrides par établissement.
## Principe
- Une règle **non listée** est considérée **activée**.
- Ça évite de casser le comportement historique lors d'une montée de version.
- Une règle listée peut être :
- `enabled: false` → désactivée
- (VETO) `force_severity: "HARD"|"MEDIUM"|"LOW"` → force la sévérité
## Exemple d'override
Créer `config/rules/sites/chu_poitiers.yaml` :
```yaml
version: 1
rules:
VETO-12:
enabled: false
VETO-09:
force_severity: "HARD"
```
Puis activer dans `enabled.yaml` :
```yaml
active:
site: "chu_poitiers"
specialty: ""
extra: []
```
## Routage automatique (router.yaml)
Le fichier `router.yaml` permet dactiver automatiquement des **packs** de règles en fonction des signaux du dossier (codes, biologie, extraits). Concrètement :
- Par défaut, seuls les packs listés dans `defaults.enabled_packs` sont actifs.
- Quand un trigger match, on ajoute ses `enable_packs`.
- Le routage est appliqué **par dossier** (et re-appliqué sur la version fusionnée).
### Mode strict
Quand `mode: strict`, une règle *non listée* dans `base.yaml` est considérée **désactivée** dès que le routage runtime est actif.
Ça force une approche “catalogue explicite” : tout ce qui tourne en prod est visible et gouvernable.
### Exemple
Activer les règles ionogramme uniquement si un code `E87.*` est détecté ou si la biologie mentionne Sodium/Potassium :
```yaml
triggers:
- id: TRG-ELECTROLYTES
enable_packs: [bio_electrolytes]
when_any:
codes_prefix: ["E87."]
lab_tests: ["sodium", "potassium"]
```

82
config/rules/base.yaml Normal file
View File

@@ -0,0 +1,82 @@
version: 1
# Catalogue "socle" de règles.
#
# Objectif : piloter (sans toucher au code) :
# - l'activation/désactivation de règles (vetos + décisions)
# - éventuellement un forçage de sévérité pour un VETO
#
# Important : si une règle n'est pas listée ici, elle est considérée activée.
# (=> comportement historique conservé)
packs:
vetos_core:
enabled: true
rules:
VETO-02:
enabled: true
description: "Code sans preuve exploitable"
VETO-03:
enabled: true
description: "Conditionnel / négation / contradictions dans la preuve"
VETO-06:
enabled: true
description: "DP dupliqué dans les DAS"
VETO-07:
enabled: true
description: "Doublons DAS"
VETO-09:
enabled: true
description: "Contradiction biologique (plaquettes/créat)"
# force_severity: "HARD" # Optionnel : forcer la sévérité globale
VETO-12:
enabled: true
description: "Sur-confiance (high sans preuve)"
VETO-15:
enabled: true
description: "Preuve issue d'un score/test (risque de sur-codage)"
VETO-16:
enabled: true
description: "Heuristique libellé→code (hors-sujet probable)"
VETO-17:
enabled: true
description: "Preuve biologique manquante => NEED_INFO (non bloquant)"
decisions_core:
enabled: true
rules:
RULE-D50-NEEDS-IRON:
enabled: true
description: "D50 sans preuve martiale => downgrade D64.9 + NEED_INFO"
RULE-D69.6-PLT-NORMAL:
enabled: true
description: "D69.6 incompatible avec plaquettes normales => ruled_out (barré)"
bio_electrolytes:
enabled: true
rules:
RULE-E87.1-NA-NORMAL:
enabled: true
description: "E87.1 suggérée mais Na normal => ruled_out"
RULE-E87.1-MISSING-NA:
enabled: true
description: "E87.1 suggérée mais Na absent => NEED_INFO"
RULE-E87.5-K-NORMAL:
enabled: true
description: "E87.5 suggérée mais K normal => ruled_out"
RULE-E87.5-MISSING-K:
enabled: true
description: "E87.5 suggérée mais K absent => NEED_INFO"
RULE-E87.6-K-NORMAL:
enabled: true
description: "E87.6 suggérée mais K normal => ruled_out"
RULE-E87.6-MISSING-K:
enabled: true
description: "E87.6 suggérée mais K absent => NEED_INFO"
placeholders_future:
enabled: false
rules:
RULE-PDF-PROTECTED-NEED_INFO:
enabled: false
description: "PDF protégé => NEED_INFO (à implémenter si besoin)"

12
config/rules/enabled.yaml Normal file
View File

@@ -0,0 +1,12 @@
version: 1
# Sélection d'overlays (facile à brancher plus tard sur une UI).
#
# - specialty : charge config/rules/specialties/<specialty>.yaml
# - site : charge config/rules/sites/<site>.yaml
# - extra : charge des fichiers YAML additionnels (chemins relatifs à config/rules/)
active:
specialty: ""
site: ""
extra: []

35
config/rules/router.yaml Normal file
View File

@@ -0,0 +1,35 @@
version: 1
# 'strict' => si un rule_id n'est pas listé dans base.yaml, il est considéré OFF
# quand le routage runtime est actif (objectif: pro / pas de surprise).
mode: strict
defaults:
# Socle pro: toujours actif (peu coûteux, structure la contestabilité)
enabled_packs:
- vetos_core
- decisions_core
# (Optionnel) règles toujours ON même si leur pack n'est pas actif
always_on_rules: []
# Triggers : activer des packs additionnels seulement si le dossier a des signaux pertinents
triggers:
- id: TRG-ELECTROLYTES
enable_packs: ["bio_electrolytes"]
when_any:
# Codes souvent porteurs d'ionogramme (hyponatrémie/hyperkaliémie/hypokaliémie)
codes_prefix: ["E87."]
# Ou biologie présente
lab_tests: ["ionogramme", "sodium", "potassium", "na", "k"]
# Ou texte
keywords:
- "ionogramme"
- "hypokali"
- "hyperkali"
- "hyponatr"
- "hypernatr"
- "kaliémie"
- "natrémie"
- "sodium"
- "potassium"

View File

@@ -0,0 +1,9 @@
version: 1
# Overlay établissement (ex: chu_poitiers, clinique_x, etc.)
# Ce fichier ne contient que des overrides.
rules:
# Exemple : forcer VETO-09 en HARD
# VETO-09:
# force_severity: "HARD"

View File

@@ -0,0 +1,13 @@
version: 1
# Overlay spécialité (ex: digestif, cardio, pneumo, onco...)
# Ce fichier ne contient que des overrides.
rules:
# Exemple : être plus strict sur le conditionnel
# VETO-03:
# force_severity: "MEDIUM"
# Exemple : désactiver un downgrade jugé trop agressif
# RULE-D50-NEEDS-IRON:
# enabled: false