Files
rpa_vision_v3/docs/MIGRATION_VLM_PLAN_2026-05-09.md
2026-05-09 11:32:52 +02:00

74 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Migration grounding VLM — qwen2.5vl Ollama → vLLM/Transformers (Qwen3-VL)
Date plan : 2026-05-09 (rédigé le 2026-05-08 au soir)
Branche cible : feature/<à-créer-demain>
État actuel : grounding passe par Ollama, qwen2.5vl:7b en split CPU/GPU 42/58.
## 1. Constat
Deux problèmes structurels relevés le 8 mai 2026.
### 1.1. VRAM saturée
`qwen2.5vl:7b` chargé via Ollama pèse 14 GB en mémoire totale alors que la machine n'a que 12 GB de VRAM. Ollama bascule en split CPU/GPU 42/58. Latence mesurée par appel grounding : ~11 s. À titre de référence, `qwen3-vl:8b` (6 GB) tient en full GPU et descend à 1.7 s sur le même cas.
### 1.2. Bug d'échelle bbox_2d (root cause documentée)
La doc officielle Qwen2.5-VL précise que les coordonnées renvoyées sont dans la résolution post-`smart_resize`, pas dans la résolution de l'image envoyée par le client. Or Ollama applique son propre `smart_resize` en interne sans exposer la taille effective au client. Conséquence : le code prod divise par `small_w = orig_w` (taille envoyée) au lieu de `resized_w` → coordonnées toutes shiftées vers le top-left. Bug systémique présent dans 4 occurrences de `agent_v0/server_v1/resolve_engine.py` et dans `_locate_popup_button` (L:2576).
Tant que le grounding passe par Ollama, le fix ne peut être qu'une rustine (resize forcé côté serveur AVANT envoi pour matcher la convention que Ollama va appliquer ensuite — fragile, dépend de la version Ollama).
Source : [HF discussion #13 sur Qwen2.5-VL-7B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct/discussions/13)
Citation mainteneur : « The bbox_2d coordinates ... will be relative to your **resized image size** if you are resizing. »
Citation discussion : « **resized dimensions parameter is not supported in OLLAMA**, which complicates coordinate translation. »
## 2. Rappel bench 8 mai 2026
Screenshot fixture : `data/training/live_sessions/bg_DESKTOP-58D5CAC_windows/shots/heartbeat_1773792436.png` (2560×1600, boîte de dialogue OK/Cancel).
| Modèle / config | Latence | Format bbox_2d | Parse regex prod | Coords cohérentes |
|---|---|---|---|---|
| qwen2.5vl:7b Ollama (num_predict=50, prod) | 11.0 s | `{"bbox_2d":[422,604,462,624]}` | oui | non (cx ≈ 0.17, top-left) |
| qwen3-vl:8b Ollama (params prod stricts) | 8.0 s | vide (50 tokens absorbés par thinking) | non | n/a |
| qwen3-vl:8b Ollama (think:false, num_predict=256) | 1.7 s | liste nue `[332,487,362,507]` | non (regex attend `"bbox_2d":[...]`) | n/a |
| qwen3-vl:8b Ollama (prompt JSON explicite) | 1.8 s | `{"bbox_2d":[...]}` | oui | non (même bug d'échelle) |
## 3. Chemin technique cible
vLLM ou Transformers direct, avec passage explicite de `resized_width` et `resized_height` au modèle (paramètre supporté par les deux backends), garantissant que les `bbox_2d` retournés sont dans la même résolution que celle qu'on a passée. Le service `core/grounding/server.py` (déjà en place pour InfiGUI via Unix socket, avec `_smart_resize`, `MIN_PIXELS=100*28*28`, `MAX_PIXELS=5600*28*28`) sert de référence architecturale.
Modèle pressenti : `qwen3-vl:8b` (6 GB en VRAM, full GPU possible), avec :
- `think:false` (désactiver le mode thinking par défaut)
- `num_predict >= 128` (50 insuffisant : tokens absorbés par thinking quand activé)
- prompt imposant le format JSON `[{"bbox_2d":[...],"label":"..."}]`
- preprocessing image côté serveur : `_smart_resize` officiel (max_size=1280, multiples de 28, code dans la discussion HF)
## 4. Étapes de migration (5)
1. **[Setup] Choix du backend grounding** : vLLM, Transformers direct, ou réorientation de `core/grounding/server.py` vers Qwen3-VL plutôt que démarrer une 4ème stack. Décision à acter en début de chantier.
2. **[Preprocessing] Implémenter `_smart_resize` côté `resolve_engine.py`** avec la formule officielle (max_size=1280, multiples de 28). Capturer la nouvelle taille (`resized_w`, `resized_h`) et la passer au backend choisi.
3. **[Parser] Adapter les 4 occurrences `bbox_2d` parsing** (`resolve_engine.py:840-848`, 870-880, 908-917, et `_locate_popup_button` L:2576) pour diviser par `resized_w`/`resized_h` et non plus `small_w/h` (= `orig_w/h`). Centraliser dans une seule fonction utilitaire.
4. **[Prompt] Imposer le format JSON `bbox_2d`** dans le prompt envoyé (Qwen3-VL ne le sort pas spontanément, sortie liste nue par défaut). Ajouter `think:false` et `num_predict>=128` aux options.
5. **[Test bbox_2d cible] Refaire le bench du 8 mai** avec la convention contrôlée. Critère de validation : sur le screenshot heartbeat de référence, le bouton OK doit être localisé à un `cx ≈ 0.45-0.55` (mid-screen) et non plus 0.17.
## 5. Test bbox_2d à refaire (post-migration)
Reproduire le test du 8 mai avec :
- Même screenshot fixture : `data/training/live_sessions/bg_DESKTOP-58D5CAC_windows/shots/heartbeat_1773792436.png`
- Même cible : bouton "OK" de la boîte de dialogue
- Backend nouveau (vLLM ou Transformers + `smart_resize` côté client)
Sortie attendue : `bbox_2d` en pixels de l'image redimensionnée connue → division par `resized_w``cx` visuellement ≈ centre du bouton (vérification par overlay sur le screenshot).
## 6. Hors scope du chantier migration (à traiter après)
- Nettoyage de la logique « décharger gemma4 pour qwen2.5vl » dans `agent_v0/server_v1/stream_processor.py:442,1742` — devient inutile une fois qwen2.5vl écarté.
- Nettoyage des 9 mentions/commentaires `qwen2.5vl` dans le code.
- Décision sur le maintien d'une compat fallback Ollama (utile en dev sans GPU) ou abandon complet.
## 7. Risques / points de vigilance
- Si vLLM tourne sur le même GPU que streaming/agent-chat, vérifier que la VRAM tient : qwen3-vl 6 GB + streaming 0.8 GB + agent-chat 0.8 GB = 7.6 GB → marge OK sur 12 GB.
- Le bench du 8 mai montrait des coordonnées visuellement incorrectes MÊME avec un format `bbox_2d` valide. La résolution du bug d'échelle est nécessaire mais peut-être pas suffisante. Vérifier après l'étape 5.
- `_locate_popup_button` (`resolve_engine.py:2536-2585`) est plus simple à migrer que la cascade principale. Faire un POC sur cette fonction avant d'attaquer la cascade.