
Mage-VL-Cowork en 16 GB: cabe, responde en 2,5 s, pero no quiere clicar
Microsoft publicó Mage-VL-Cowork, un agente de computador de 4 000 millones de parámetros que en el papel saca 66 % en OSWorld. Lo bajé a la RTX 4070 Ti SUPER, lo levanté con la imagen oficial de SGLang y lo medí: carga en noventa a ciento veinte segundos, ocupa 13 985 MB de VRAM, responde en 2,47 s. Y en treinta prompts que le pidieron que clicara un botón, clicó cero veces: siempre contestó pedir otra captura.
Microsoft publicó Mage-VL-Cowork en Hugging Face el 30 de octubre de 2026 y empujó el código al repositorio microsoft/Mage el 2 de noviembre con un número grande en la portada: 66,0 % en OSWorld-Verified con solo 4 mil millones de parámetros. En el gráfico de su tarjeta queda por encima de todos los agentes abiertos de su tamaño, y a 3,6 puntos del GPT-5.4-mini del paper de referencia (69,6 %).
Si eso es cierto, media torre de laboratorio se mueve: un agente que entiende capturas y decide clicar o abrir una terminal, cabiendo en una GPU de hogar.
El modelo pesa 9,48 GB en bfloat16. En papel, holgura. Una RTX 4070 Ti SUPER de 16 GB debería tragarlo sin sudar. Lo bajé a fedora, lo levanté con la imagen de SGLang que Microsoft publica en su repositorio, y le pedí que clicara un botón.
No clicó ni una vez.
Qué es Mage-VL-Cowork
Dos piezas pegadas. La izquierda es Mage-ViT, un codificador visual propio del equipo, con parches de 16×16 y spatial_merge_size=2, que proyecta un embedding de 1 024 dimensiones. La derecha es Qwen3-4B-Instruct-2507, el modelo de lenguaje, con un espacio oculto de 2 560. Entre las dos vive el adaptador que lleva lo visual al espacio del texto.
Lo que lo distingue de un Qwen2.5-VL cualquiera es la ruta híbrida. En cada turno el modelo elige entre dos canales: computer_use para clicar, escribir, arrastrar, hacer scroll o tomar una captura; o bash para correr un comando en una terminal persistente. La promesa es que para algo tan simple como renombrar cien archivos, en vez de arrastrarlos por una interfaz gráfica, el agente escriba mv y mida el resultado.
El entrenamiento fueron cinco etapas sobre 318 837 trayectorias verificadas, y la última es una fase de GRPO en línea con recompensas verificables.
Lo bajé con el token de Hugging Face. 25 archivos, 8,9 GB una vez descartados los assets. El safetensors viene partido en dos, 4,56 GB y 4,92 GB.
El harness oficial es un contenedor de Docker
El README del modelo manda a la carpeta mage_vl_cowork/osworld_eval/scripts/ del repositorio de GitHub. Ahí hay un script, serve_sglang_docker.sh, que en una línea deja claro cómo esperan que se corra:
docker run --rm --name mage-vl-cowork \
--gpus device=0 --shm-size=8g \
-p 127.0.0.1:30001:30001 \
-e SERVED_MODEL_NAME=Mage-VL-Cowork \
-e SGLANG_MAX_PIXELS=4000000 \
--mount type=bind,src="$MODEL_DIR",dst=/models/Mage-VL-Cowork,readonly \
xiaoyizhang/mage-vl-cowork:0.1.0
La imagen xiaoyizhang/mage-vl-cowork:0.1.0 pesa 19,3 GB en disco. Dentro hay un SGLang parchado por el equipo de Mage para que el llava_onevision2 corra con su tokenizador y su video RoPE.
En fedora (NixOS, Docker 29 con el runtime CDI de nvidia) el --gpus device=0 falla con AMD CDI spec not found. El reemplazo es --device nvidia.com/gpu=0. Sin ese cambio, el contenedor no arranca.
Después de dos minutos, el servidor expone un endpoint compatible con OpenAI en 127.0.0.1:30001.
Lo que pude ejecutar antes de medir
Antes de armar el banco, comprobé que el canal texto responde:
curl -s -X POST http://127.0.0.1:30001/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"Mage-VL-Cowork","messages":[{"role":"user","content":"Reply with exactly the word: OK"}],"max_tokens":32,"temperature":0}'
El modelo devolvió:
This is the initial state of the task. I’m considering how to respond to the user’s request, and I’m leaning toward a simple confirmation rather than adding
Sin imagen, sin system prompt, con la pregunta más simple del mundo, el modelo devolvió una narración interna de agente. Nunca llega a decir “OK”: la respuesta se corta a 32 tokens todavía en el razonamiento. Está entrenado para pensar antes de actuar, y para él todo prompt es el principio de una tarea.
Ese fue el primer dato: el formato de agente es estructural. No se puede apagar desde el cliente.
La ficha de medición
Antes de la primera corrida, commití lab/mage-vl-16gb/ficha.json con la hipótesis, las métricas, la regla de decisión y las amenazas a la validez. El commit es anterior al de los resultados, así que las reglas no se ajustaron a los datos.
La hipótesis: Mage-VL-Cowork 4B cabe en 16 GB y corre a velocidad útil; el formato fijo de agente es una limitación estructural.
La regla de decisión: publicable si carga en menos de 180 s, si una respuesta de 256 tokens tarda menos de 5 s, si la VRAM pico queda bajo 15 GB, y si en treinta clics sobre botones etiquetados supera por al menos quince puntos a una línea base trivial que siempre clica el centro de la pantalla.
La línea base trivial: dos reglas que no miran la imagen. Una siempre devuelve (500, 500). Otra devuelve una coordenada uniforme al azar. Si el modelo empata con la primera, ese es el titular.
La ficha original pedía una tercera línea base: Tesseract OCR del screenshot, extraer el centro del texto detectado. El host fedora en NixOS no trae Tesseract por defecto y, dado que el modelo colapsa antes de poder competir contra él, la sustituí por las dos reglas geométricas de arriba. La decisión queda registrada en la ficha como desviación.
El banco: diez paneles sintéticos de 1 000×1 000 px generados con Pillow. Cada uno trae tres o cinco botones de colores con etiqueta renderizada en DejaVu Sans Bold a 32 px. El tamaño y la posición de cada botón son conocidos de antemano. Por cada panel, un botón al azar se elige como objetivo. Treinta corridas en total: diez paneles por tres repeticiones, con el orden mezclado para no agrupar.
La ficha pedía veinte prompts por tres corridas, sesenta observaciones. El banco quedó en treinta porque el resultado se separó del cero desde la primera vuelta y escalar a sesenta no habría cambiado la conclusión. Queda declarado y sin reinterpretar.
La métrica de puntaje: un click está bien si la coordenada devuelta cae dentro del rectángulo del botón objetivo. Para el modelo, la coordenada se extrae del tool_call de la respuesta. Para la línea base, es el centro fijo o la uniforme.
Lo que medí
Treinta corridas, mismo endpoint, misma máquina, mismo cronómetro del cliente Python.
| Métrica | Valor |
|---|---|
| Carga del servidor (CUDA graphs incluidos) | 90-120 s |
| VRAM pico durante inferencia | 13 985 MB |
| Latencia media por respuesta | 2,47 s |
| Latencia mínima / máxima | 1,66 s / 2,91 s |
| Tokens por segundo (promedio) | 72,86 |
| Tokens de salida (promedio) | 180 |
Formato parcial (cierre </think> + Action: + <tool_call>) | 30 / 30 |
| Clicks dentro del botón pedido | 0 / 30 |
| Línea base “siempre centro (500,500)” | 9 / 30 (30 %) |
| Línea base aleatoria | 0 / 30 |
El formato del modelo es parcial por una razón concreta: el tag de apertura <think> nunca aparece. Las treinta respuestas abren con la narración de razonamiento en el primer carácter, cierran con </think> al final, y siguen con la línea Action: y un bloque <tool_call> bien formado. El modelo cumple el contrato de respuesta pero descarta la apertura del pensamiento, probablemente porque en el harness oficial el propio servidor la añade.
Las tres primeras cumplen el presupuesto que me puse. La carga es rápida, la VRAM entra con 2 GB de margen y la latencia es cómoda para un bucle de decisiones. El formato de agente también se cumple: treinta de treinta respuestas trajeron el cierre </think>, la línea Action: y un <tool_call> válido.
La última fila es la que tira todo lo demás al piso.
Qué pasa cuando le pido que clique
La pregunta es siempre la misma, parafraseada por botón: “The user asked you to click the Save button in the panel. Do it.” El modelo recibe el prompt del sistema oficial de Mage-VL-Cowork, con la lista entera de herramientas, y la imagen del panel como image_url en base64.
Aquí hay un ejemplo real de respuesta, sobre un panel que pide clicar el botón “Print”:
I’m at the point where the only supported operation for this step is a screen capture, and the screenshot is useful here because it can confirm the current desktop state before I try to act on the Print button. The earlier reasoning already narrowed the likely path to the top-right system area, where Ubuntu 22.04 commonly places the quick settings and the Print command, but that was still tentative and depended on what was actually visible.
Since this step does not interact with the document or the panel, it lets me inspect the menu path instead of guessing blindly. Capturing the status area is the right next move before clicking anything else, especially because the task is specifically about the Print button in the panel and not about opening a shell or changing files.
</think>Action: Take a click-free screenshot to inspect the current screen state.
<tool_call>{"name": "computer_use", "arguments": {"action": "screenshot"}}</tool_call>
Dos cosas pasan ahí. La primera: el modelo ignora la imagen que acabo de darle y habla de un “Ubuntu 22.04” con un “área de configuraciones rápidas arriba a la derecha”. En mi panel no hay Ubuntu: hay un rectángulo violeta con la palabra “Print” dentro. El modelo prefiere el escritorio que tiene en la cabeza al que tiene delante.
La segunda: cuando por fin elige una acción, elige screenshot. No click. En las treinta corridas, 30 de 30 tool calls fueron action: screenshot. Cero clics.
El patrón es consistente con el entrenamiento. Mage-VL-Cowork se entrenó dentro de un bucle: el arnés le muestra una captura, el modelo responde, el arnés ejecuta la acción, el arnés devuelve la captura del nuevo estado. La acción screenshot existe porque dentro de ese bucle es gratis: el arnés la atiende, devuelve lo mismo que ya tenía, y el agente razona con una imagen más actualizada.
click. Siempre pide otra captura.Fuera del bucle, sin un arnés que atienda la petición, screenshot es un callejón sin salida. Yo no le voy a mandar una captura nueva: la que le mandé es la única. El modelo no sabe que no hay arnés y pide, pide, pide una captura más.
Qué dice la línea base
Una regla trivial que siempre devuelve (500, 500) acierta nueve de treinta botones. No porque sea inteligente: mis botones tienden a vivir cerca del centro del panel, y el punto (500, 500) cae dentro del rectángulo objetivo en tres de las diez escenas (seed del benchmark). Al repetir cada escena tres veces, nueve aciertos de treinta.
Ese 30 % es ruido geométrico. Lo pongo porque un modelo serio tendría que destrozarlo: una red que mira la etiqueta del botón y devuelve la coordenada de ese rectángulo debería estar cerca del 100 %. Mage-VL-Cowork, fuera de su arnés, está en 0.
Qué sí vale de este ejercicio
Tres cosas que sí pude medir y que sí son útiles para quien quiera correrlo:
Cabe en 16 GB. Con la imagen oficial de SGLang, el modelo deja unos 2 GB de holgura. Un Qwen3-4B equivalente sin el codificador visual cabría con el doble de margen; con el Mage-ViT y el KV cache que SGLang reservó en el arranque (24 093 tokens, 3,3 GB para K y 3,3 GB para V, según el log del propio servidor), el límite real queda cerca del techo.
La latencia es honesta. 72,86 tokens por segundo en una GPU de hogar es exactamente lo que uno esperaría de un 4B con atención flash y grafos CUDA. A 2,47 s por respuesta, un bucle agente de veinte llamadas (la media que reporta el paper para OSWorld tras la etapa de aprendizaje por refuerzo) sumaría un minuto largo de inferencia pura por tarea. Es un cálculo, no una medida: el bucle completo no lo corrí.
El servidor es lo más limpio del paquete. Microsoft envió todo el runtime dentro de un contenedor, y el contenedor funciona en la primera corrida, con el único cambio del flag --device por la idiosincrasia de NixOS.
Qué no vale la pena intentar
Reproducir los 66 % de OSWorld en casa necesita el arnés completo: un Ubuntu dentro de una VM, el desktop_env del repositorio, 50 pasos de interacción por tarea, 361 tareas, dos corridas para el promedio. Son horas de compute por un número que ya está en el paper.
Lo interesante no es si acierta el 66 % que dice. Es si sin el arnés sirve para algo. La respuesta de este banco es que no: ni como VLM general, ni como clasificador espacial one-shot.
Cuándo usarlo y cuándo no
Si uno está construyendo un agente de escritorio con bucle completo, modelos de lenguaje enchufables y un harness que atiende capturas y clics de verdad, Mage-VL-Cowork es un candidato razonable por costo: 4B que corre en una tarjeta de consumidor, formato bien definido, canal bash incluido para operaciones no-visuales.
Si uno está buscando un VLM para mirar una pantalla una sola vez y responder, no es este. Para esa tarea, Qwen2.5-VL-7B o InternVL2.5-8B siguen siendo los candidatos obvios.
Y si alguien quiere reproducir esto, el servidor levanta en noventa segundos en una 4070 Ti SUPER. Todos los datos crudos están en el repositorio del blog bajo lab/mage-vl-16gb/.
Lo que no medí
La robustez a variación. Temperatura 0 en todas las corridas. No sé qué pasa a temperature=0.7; probablemente nada bueno, pero no lo medí.
El canal bash. El modelo también se ofrece a abrir una terminal. No le pedí nunca una tarea de terminal, así que no sé si al menos ese canal responde.
Las imágenes reales. Los diez paneles son sintéticos con Pillow. La ficha original se comprometía a incluir diez capturas reales además de los sintéticos; no cumplí esa mitigación. Un screenshot verdadero de un escritorio Ubuntu, el entorno para el que el modelo fue entrenado, probablemente mueve el resultado. Lo que sí quedó claro: ante el panel sintético el modelo no intenta siquiera leer los botones, prefiere pedir otra captura. Para medir si reconocería “Save” en 1 000×1 000 habría que forzarlo a devolver la etiqueta, y nunca llegó a ese punto.
La latencia sin el prompt del sistema. La ficha pedía medir también la respuesta al mismo prompt sin el MAGE_HYBRID_SYSTEM_PROMPT de 4,2 kB, para separar el costo del formato de agente del costo del modelo. No lo medí en este banco; el único sondeo sin system prompt fue el curl de validación, que respondió con la misma narración interna. El overhead exacto queda sin aislar.
Qwen2.5-VL-7B como referencia. Habría sido útil correr el mismo banco contra un VLM general. No lo bajé porque el experimento era sobre Mage-VL-Cowork y el presupuesto de GPU está ocupado. Queda en la cola.
Fuentes
- Tarjeta del modelo en Hugging Face: microsoft/Mage-VL-Cowork
- Repositorio oficial con arnés OSWorld: microsoft/Mage, carpeta mage_vl_cowork
- Imagen Docker del servidor: xiaoyizhang/mage-vl-cowork en Docker Hub
- Benchmark OSWorld-Verified: xlang-ai/OSWorld
- Backbone de lenguaje: Qwen/Qwen3-4B-Instruct-2507
- Datos crudos y scripts:
lab/mage-vl-16gb/en el repositorio de este blog
5 de noviembre de 2026 · RTX 4070 Ti SUPER 16 GB sobre NixOS 26.05 · SGLang xiaoyizhang/mage-vl-cowork:0.1.0 · docker 29.8.1 con runtime CDI de NVIDIA · 30 corridas, temperatura 0, max_tokens 256.
BOLETÍN · MEDIDO
Una medición nueva, cuando la hay.
Cada post con sus números, sus fallos y los scripts para repetirlo. Entre uno y tres correos al mes. Te das de baja con un clic.
LO ÚLTIMO QUE LLEGÓ
- Tirón de la red cada minutopico ×42,6 menor al azar
- SMS de la muerteAndroid 12 a 17
- Unlimited-OCR 2 en 16 GBempate con Chandra 2, sin memoria a las 80 páginas
Uso Listmonk en mi propio servidor; tu correo no sale de ahí. Confirmas por correo antes de recibir nada.
Comentarios
Todavía no hay comentarios. El primero es tuyo.