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

6.1 KiB
Raw Permalink Blame History

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_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_wcx 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.