feat(qw4): bench rigoureux LLM safety_checks → gemma4:latest par défaut
Some checks failed
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped

Bench 5 modèles × 5 scénarios × cold+warm sur RTX 5070 :
- gemma4:latest : warm 2.9s, JSON 92%, détection 46% → gagnant
- qwen2.5vl:7b : warm 6.6s, détection 23% (trop lent)
- qwen2.5vl:3b : warm 2.0s, détection 8% (vérifie pour vérifier)
- medgemma:4b : warm 0.5s, détection 0% (refuse de signaler) → mauvais
  défaut initial, corrigé
- qwen3-vl:8b : 0% JSON valide (ignore format=json Ollama) → écarté

Modifications safety_checks_provider.py :
- RPA_SAFETY_CHECKS_LLM_MODEL défaut: medgemma:4b → gemma4:latest
- RPA_SAFETY_CHECKS_LLM_TIMEOUT_S défaut: 5 → 7 (warm 2.9s + marge)

Doc complète : docs/BENCH_SAFETY_CHECKS_2026-05-06.md
Script : tools/bench_safety_checks_models.py (reproductible, ~10-15 min)

Limite assumée : 46% de détection. À présenter en démo comme aide médecin,
pas certification. Amélioration V2 = prompt plus dirigé sur champs à vérifier.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-05-06 09:23:09 +02:00
parent 83be93e121
commit 0a02a6ec9c
3 changed files with 539 additions and 2 deletions

View File

@@ -0,0 +1,95 @@
# Bench QW4 safety_checks — sélection du LLM contextuel
**Date** : 2026-05-06
**Contexte** : QW4 du sprint mai. La fonction `_call_llm_for_contextual_checks`
appelle Ollama avec un screenshot + prompt court pour générer 0-3 checks de
vérification supplémentaires que l'humain doit acquitter avant la reprise
d'un replay en pause supervisée (`safety_level=medical_critical`).
## Méthodologie
- **5 scénarios** : screenshots synthétiques de dossiers patient avec UNE
anomalie volontaire chacun (date de naissance aberrante, IPP incohérent,
diagnostic vide, code CIM inadapté à l'âge, forfait incohérent avec durée).
- **5 candidats** : `gemma4:latest`, `qwen3-vl:8b`, `qwen2.5vl:7b`,
`qwen2.5vl:3b`, `medgemma:4b`.
- **Protocole par modèle** : déchargement VRAM (keep_alive=0 sur tous les
modèles loaded) → 1er appel = cold start chronométré → 4 autres screenshots
× 3 runs = 12 mesures warm.
- **Métriques** : cold start, warm avg, warm p95, % JSON valide, % détection
(anomalie cible présente dans label/evidence d'au moins un check renvoyé).
- **Script** : `tools/bench_safety_checks_models.py`.
## Résultats
| Modèle | Cold (s) | Warm avg (s) | Warm p95 (s) | JSON | Détection |
|---|---:|---:|---:|---:|---:|
| `gemma4:latest` | 10.6 | **2.9** | 3.4 | 92% (12/13) | **46% (6/13)** |
| `qwen3-vl:8b` | 5.6 | — | — | **0%** (0/12) | 0% (0/12) |
| `qwen2.5vl:7b` | 9.4 | 6.6 | 8.1 | 100% (13/13) | 23% (3/13) |
| `qwen2.5vl:3b` | 6.0 | 2.0 | 2.5 | 100% (13/13) | 8% (1/13) |
| `medgemma:4b` | 2.0 | 0.5 | 0.7 | 100% (13/13) | **0%** (0/13) |
## Lecture
- **`medgemma:4b` retourne systématiquement `[]`** sur les 13 mesures.
Trop obéissant à "Si rien d'inhabituel à signaler, retourne []", refuse
de pointer ne serait-ce qu'une date 1900-01-01. **Mauvais choix par défaut**
malgré sa rapidité et sa spécialisation médicale revendiquée.
- **`qwen3-vl:8b` ignore `format=json` Ollama** : 0 réponse parsable. À écarter
pour cette tâche tant que le tooling Ollama / le modèle ne convergent pas.
- **`qwen2.5vl:7b`** détecte mais 2× plus lent (warm 6.6s) que gemma4 et tend
à inventer des anomalies de format de date qui ne sont pas la vraie cible.
- **`qwen2.5vl:3b`** rapide mais détection 8% — il "vérifie pour vérifier"
(renvoie souvent "vérification de la date de naissance" même quand la date
est correcte).
- **`gemma4:latest` gagne** : meilleur taux de détection (46%) ET deuxième
meilleur warm (2.9s). Tend à raisonner cohérence motif/diagnostic plutôt
que valeurs aberrantes brutes.
## Détail détection par scénario
| Scénario | gemma4 | qwen2.5vl:7b | qwen2.5vl:3b | medgemma:4b |
|---|:---:|:---:|:---:|:---:|
| Date naissance aberrante (1900) | ❌ | ✅ | ✅ | ❌ |
| IPP incohérent (`ABC@@##XYZ`) | ❌ | ❌ | ❌ | ❌ |
| Diagnostic principal vide | ✅ | ❌ | ❌ | ❌ |
| Code CIM inadapté à l'âge | ✅ | ❌ | ❌ | ❌ |
| Forfait UHCD vs durée 1h | ❌ | ❌ | ❌ | ❌ |
Aucun modèle ne détecte les 5 scénarios. **L'IPP corrompu et le forfait
incohérent ne sont détectés par personne** — ces anomalies demanderaient
soit un prompt plus dirigé (liste explicite des champs à vérifier), soit
un modèle plus large.
## Décision
- **Défaut serveur** : `RPA_SAFETY_CHECKS_LLM_MODEL=gemma4:latest`
- **Timeout** : `RPA_SAFETY_CHECKS_LLM_TIMEOUT_S=7` (warm 2.9s + marge)
- **Persistance VRAM** : `OLLAMA_KEEP_ALIVE=24h` recommandé pour éviter le
cold start de 10s en démo
Modifications appliquées dans `agent_v0/server_v1/safety_checks_provider.py`.
## Limites & travail futur
1. **46% de détection est faible** : à présenter comme aide au médecin, pas
comme certification. Le médecin reste le décideur.
2. **Prompt actuel trop générique** : un prompt qui liste explicitement les
champs à vérifier (DDN, IPP, diagnostic, forfait, cohérence âge/diagnostic)
donnerait probablement de meilleurs résultats. À mesurer en V2.
3. **Bench sur 5 anomalies seulement** : à étendre dès qu'on a un corpus de
vrais dossiers Easily Assure avec anomalies confirmées par Pauline / Amina.
4. **Pas de test sur des dossiers SANS anomalie** (faux positifs) : à ajouter.
5. **Pas de bench des modèles cloud** (gemma3:27b-cloud, deepseek, gpt-oss)
par contrainte 100% local — mais à explorer si on lève cette contrainte
pour les checks contextuels (qui ne contiennent pas de PII si on
anonymise les screenshots).
## Reproductibilité
```bash
cd /home/dom/ai/rpa_vision_v3
.venv/bin/python tools/bench_safety_checks_models.py
# (BENCH_TIMEOUT=60 par défaut, ~10-15 min sur RTX 5070)
```