6.1 KiB
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 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_resizeofficiel (max_size=1280, multiples de 28, code dans la discussion HF)
4. Étapes de migration (5)
- [Setup] Choix du backend grounding : vLLM, Transformers direct, ou réorientation de
core/grounding/server.pyvers Qwen3-VL plutôt que démarrer une 4ème stack. Décision à acter en début de chantier. - [Preprocessing] Implémenter
_smart_resizecôtéresolve_engine.pyavec la formule officielle (max_size=1280, multiples de 28). Capturer la nouvelle taille (resized_w,resized_h) et la passer au backend choisi. - [Parser] Adapter les 4 occurrences
bbox_2dparsing (resolve_engine.py:840-848, 870-880, 908-917, et_locate_popup_buttonL:2576) pour diviser parresized_w/resized_het non plussmall_w/h(=orig_w/h). Centraliser dans une seule fonction utilitaire. - [Prompt] Imposer le format JSON
bbox_2ddans le prompt envoyé (Qwen3-VL ne le sort pas spontanément, sortie liste nue par défaut). Ajouterthink:falseetnum_predict>=128aux options. - [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_resizecô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.5vldans 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_2dvalide. 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.