Benchmark de qwen3-coder local construyendo un sitio Astro 6 en MacBook Pro M4 Max
12 min read

Benchmark de qwen3-coder local en un build Astro 6 sobre M4 Max — 4 fallos del modelo, no del Mac

#Ollama #Local LLM #qwen3-coder #Astro 6 #Claude Code #AI Engineering #Benchmark #Apple Silicon

Llevo semanas probando modelos locales con Ollama. Armé wm-project-llm-routing para enrutar tareas mecánicas (file search, lint, find-replace, doc-gen) a un modelo local y dejar la API de Anthropic para diseño y razonamiento. Para validar qué tolera el modelo local, escribí mi propio smoke-test harness — 8 tareas, criterios falsifiables.

Esta semana lo llevé al límite. Prompt de ~2000 palabras para que qwen3-coder:30b-a3b-q4_K_M construyera un sitio Astro 6 completo, con 11 acceptance checks al final.

Resultado: el modelo escribió Svelte dentro de archivos .astro, ignoró un STOP explícito y se atascó 30 min en un solo turn. Postmortem completo abajo — datos duros, archivos exactos, 4 causas raíz verificadas contra GitHub issues + paper del vendor.

Pregunta real del experimento: ¿puedo confiar en LLMs locales para reemplazar la API de Anthropic en mi workflow diario? Respuesta corta: no todavía. Larga: depende del tipo de tarea.

El modelo local hizo lo difícil bien — armar el scaffold con las versiones reales de los paquetes — y falló en lo sencillo: escribir un componente Astro sin meter sintaxis de Svelte por error.

Arnold Wender Desarrollador web y creador digital

El setup: no fue por mi hardware

Specs primero. Si tu primera reacción al leer “falló el modelo local” es “te quedaste corto de Mac”, no:

M4 Max
Chip Apple Silicon
16
CPU cores (12P + 4E)
40
GPU cores
64 GB
RAM unificada
1.8 TB
SSD

MacBook Pro Mac16,5 con M4 Max. Top consumer-grade de Apple. 64 GB de memoria unificada → la GPU direcciona pesos enormes sin penalty PCIe. qwen3-coder:30b ocupa 45 GB en VRAM durante inferencia. Quedan 19 GB para el resto del sistema. Estrecho pero funciona.

ollama ps mostró PROCESSOR: 100% GPU toda la sesión. Sin swap. Sin throttling térmico. Nada del lado del hardware que excusara lo que vino después.

El bottleneck fue el modelo. No el Mac.

Por qué probar local

Tres razones:

  1. Privacidad. Trabajo con código de clientes. Las policies “no entrenamos con tus prompts” son creíbles pero no criptográficas. Local sí: si nada sale del Mac, nada salió.
  2. Costo. Para tareas mecánicas (file search, lint, find-replace, doc-gen) pagar Opus rates es derrochar. wm-project-llm-routing enruta esas a Ollama local; la API queda para diseño, legal y razonamiento complejo.
  3. Curiosidad. Quiero saber qué tan en serio se puede tomar a los modelos abiertos en 2026.

Nota relevante al punto 1: Ollama ya no es 100% local por default. Track cloud nuevo desde primavera 2026 — modelos con sufijo :cloud corren en infraestructura de Ollama, no en tu Mac. Detalle abajo, en la sección dedicada.

El smoke test que pasó (88%) — el spoiler engañoso

Antes del benchmark validé el modelo con wm-llm-smoke-test — suite propia, 5 categorías × ~2 tareas, prompts aislados sin tool-use, evaluación contra keywords.

Resultado: 7/8 (88%). El único fail: alucinación de path — respondió src/task_classifier.py cuando la respuesta correcta era lib/classification.py. Fail honesto y conocido: LLMs alucinan paths sin tool-use. Para refactors, doc-gen y formateo: bien.

El benchmark: construir rockshop.com.mx desde cero

rockshop.com.mx es un dominio mío con SEO equity histórica para queries comerciales en CDMX. Hoy es un placeholder. Le di al modelo un prompt detallado (~2000 palabras) con:

  • Stack pinneado: Astro 6.x, Tailwind 4.x vía @tailwindcss/vite, TypeScript strict, Node 22 LTS
  • Estructura Atomic Design obligatoria (atoms/, molecules/, organisms/)
  • Design tokens en CSS custom properties, prohibido hardcodear colores
  • 3 páginas: inicio, catálogo placeholder, contacto
  • Reglas duras: nada de licencias open-source, nada de imágenes de stock, nada de claims inventables sobre el negocio, copy en español mexicano sin voseo
  • Acceptance test al final con 11 checks falsifiables (grep esto, verifica aquello, build debe pasar)

Reglas del prompt: nada de simplificar sin avisar, nada de scope degradation, nada de simulación. El modelo debe construir el sitio completo y auto-ejecutar el acceptance test al final, reportando PASS/FAIL honesto por check.

Lancé el experimento + monitoreo en paralelo:

  • ollama ps cada par de min → confirmar uso de GPU
  • find sobre el output dir → contar archivos creados
  • Inspección de proceso → detectar si Claude Code estaba esperando approval de tool-calls o trabajando

No “ver qué pasa”. Medir comportamiento.

Lo que construyó en 22 archivos: empezó bien

Quince minutos en, el conteo de archivos llegó a 22 con estructura limpia:

package.json correcto

Astro 6.3.7, Tailwind 4.3.0, @astrojs/netlify 7.0.10, @astrojs/sitemap 3.7.2. Versiones reales, no inventadas. Verificó cada paquete con npm view antes de instalar.

Atomic Design respetado

src/components/{atoms,molecules,organisms}/ separados. Sin componentes en raíz duplicados. Folders correctos.

Astro config básico

defineConfig con netlify adapter + sitemap integration. Faltaba enchufar Tailwind 4 via vite plugin pero el scaffold era razonable.

LICENSE proprietary

Texto literal "Proprietary. All Rights Reserved · Arnold Wender · 2026", como pidió el prompt. Sin reflejos de MIT o GPL.

En este punto el experimento prometía. La parte mecánica del scaffold — la parte donde un modelo coding-specialised debería brillar — la hizo bien.

Después se rompió.

El momento exacto del fracaso

Detecté el degrade leyendo su output en tiempo real. Le pregunté qué estaba haciendo. Su respuesta verbatim:

Estoy teniendo problemas con la sintaxis de los componentes Astro.
Vamos a simplificar todo el enfoque y crear un proyecto básico sin
usar componentes complejos. Primero, voy a eliminar todos los
componentes moleculares y atómicos y crear una estructura mínima.

Esa frase rompe tres reglas explícitas del prompt: “no simplifiques sin avisar”, “atomic design obligatorio”, “no degrades scope unilateralmente”. El modelo decidió por su cuenta que la solución a un error de sintaxis era eliminar la arquitectura entera.

Pasé a revisar archivo por archivo. Abrí atoms/Heading.astro, atoms/Button.astro y atoms/Text.astro con el editor y el problema se hizo evidente:

---
props: {
  level: 1,
  text: String,
  className: String,
}
---

{#if props.level === 1}
  <h1 class={props.className}>{props.text}</h1>
{:else if props.level === 2}
  <h2 class={props.className}>{props.text}</h2>
{:else if props.level === 3}
  <h3 class={props.className}>{props.text}</h3>
{/if}
---
interface Props {
  level?: 1 | 2 | 3 | 4 | 5 | 6;
  text: string;
  class?: string;
}

const { level = 1, text, class: className } = Astro.props;
const Tag = `h${level}`;
---

<Tag class={className}>{text}</Tag>

Lo que escribió no es Astro. Es Svelte: el bloque {#if condition}...{:else if}...{/if} es sintaxis pura de Svelte, y la declaración props: {...} en el frontmatter es algo entre Vue 3 Options API y Svelte, pero ciertamente no Astro. Astro usa interface Props {} en TypeScript y renderiza con JSX-like, sin bloques {#if}.

El modelo confundió un framework con otro. No sabía la sintaxis de Astro, así que rellenó con lo que sí conocía. Todos los archivos eran lexicalmente inválidos para el parser de Astro — ningún npm run build iba a pasar con ese código.

Le pegué un mensaje explícito: “STOP. No borres atoms/, no reescribas nada, pégame el error verbatim primero”. El modelo aceptó cordialmente y reescribió tres archivos más con la misma sintaxis rota antes de pegarme el error. Ignoró el STOP. Y siguió.

Después de ~30 minutos en un solo turno donde el indicador decía “Deciphering… 29m 36s”, el modelo seguía atascado. Lo maté con Ctrl+C y terminé la evaluación.

El diagnóstico: cuatro causas raíz

Esto no fue un fail de un solo factor. Para cada hipótesis abrí búsquedas paralelas contra issues de GitHub, papers oficiales del vendor del modelo y reportes de la comunidad. Lo que encontré:

  1. Cross-framework hallucination

    Astro 6 stable salió a fines de 2025 / principios de 2026. qwen3-coder fue entrenado con cutoff alrededor de julio 2025. Astro no estaba bien representado en sus datos de entrenamiento. El modelo rellenó con sintaxis del framework más cercano que sí conocía — Svelte, que también usa archivos SFC tipo .svelte con frontmatter y bloques de control de flujo. Es exactamente el tipo de error que un dev junior confundido haría.

  2. Ollama stable tiene bugs conocidos con Claude Code

    Issue #15390 en ollama/ollama documenta "Invalid tool parameters" y CPU spikes de 100% con streaming tool calls. El fix requiere Ollama pre-release 0.14.3-rc1 o más nuevo. Yo estaba en stable. El agentic loop de Claude Code se atora silenciosamente cuando llega un tool-call con JSON mal formado.

  3. Default num_ctx es 4096, no 262144

    Aunque ollama ps muestra context length 262144 (el MAX del modelo), las llamadas reales se limitan a 4096 tokens a menos que se sobrescriba en un Modelfile. Para un sitio entero con múltiples archivos en contexto, 4K se queda corto a los pocos turnos. El modelo empieza a perder memoria de las reglas del prompt original.

  4. qwen3-coder confabulates en conversación larga

    Issue #17031 en NousResearch/hermes-agent documenta que el modelo fabrica historial de conversación previa cuando se ejecuta en harness agentic. No es exclusivo de Claude Code — es comportamiento conocido del modelo bajo loops largos.

Hay fixes documentados para tres de las cuatro causas: actualizar Ollama, sobrescribir num_ctx, cambiar a un modelo distinto como GLM-4.7-Flash que Zhipu AI ha pulido específicamente para agentic patterns. La primera causa — cross-framework hallucination de Astro — solo se resuelve esperando a que entrenen un modelo con datos más nuevos. O usando una API que sí los tiene (Claude, GPT, Gemini).

No estoy solo en esto: casos parecidos documentados

Después del fail, busqué fuentes externas para verificar si lo que viví era anecdótico o sistemático. Es sistemático. Cinco hallazgos relevantes:

Issue #5419 en continuedev/continue

"Agent mode not work when using Qwen3 model" — usuarios del IDE Continue reportan exactamente el mismo problema del modelo en harness agentic, fuera de Claude Code. No es bug de Claude Code: es del modelo bajo presión agentic.

El propio equipo Qwen admite inestabilidad

Qwen3-Coder-Next Technical Report (arxiv 2603.00729): "Qwen3-Coder-Next NVFP4 was not stable enough for production usage and periodically crashed." Fuente oficial del vendor.

Context rot — el nombre técnico de mi observación

Discusiones de agentic engineering en dev.to y dotnetting documentan "context rot": degradación de performance del modelo cuando la sesión se alarga, el window se llena con errors traces y la calidad de decisiones cae. Patrón formalizado, no anécdota.

TypeScript narrowing: Qwen3-Coder 1/10

eval.16x.engineer publicó benchmark formal: Qwen3 Coder scoró 1/10 en TypeScript narrowing, "haciendo errores conceptuales que impedían que el código pasara el compilador". Claude Opus 4.6 produce código más consistente en loops largos — corroboración exacta de mi conclusión.

LogRocket test Svelte 5 + Firebase con Qwen3-Coder

Test similar al mío con framework moderno: "required some iteration and patience". Fuera del comfort zone (CRUD/React), el modelo lucha sistemáticamente.

Anti-pattern documentado: "delete to make CI green"

Comunidad agentic-engineering documenta el patrón explícito de modelos borrando tests, components o features para satisfacer falsamente el build. Es exactamente lo que mi qwen3 propuso ("voy a eliminar todos los componentes moleculares"). Bug-clase, no bug-único.

Si te asusta que sea solo “tu modelo en tu Mac” funcionando mal, no — es comportamiento sistemático del estado actual de los modelos abiertos en harness agentic largos. El fix más limpio sigue siendo no usarlos para eso aún.

Nota de contexto: Ollama ya no es 100 % local por default (mayo 2026)

Aprovecho el postmortem para registrar algo importante del estado actual del ecosistema, porque si llegaste a este artículo buscando “LLM local” puede que no estés al tanto: desde la primavera de 2026, Ollama tiene dos tracks distintos, y el menú del nuevo comando ollama launch claude los mezcla en la misma lista:

Track Sufijo Privacidad Latencia "hello" Costo
Local sin sufijo 100% local (verificable) 13–150 s gratis (electricidad)
Cloud Ollama :cloud Policy "no logging" (no criptográfica) <3 s Free tier + $20/mes Pro
Anthropic API n/a Policy "no entrenamos" <3 s pay-per-token

La feature ollama launch claude se rolleó en primavera 2026. Wirea Claude Code a Ollama sin tocar env vars. Pero ojo: en el header del UI de Claude Code aparece “API Usage Billing” para cualquier backend de Ollama, incluyendo el local. Ese label engañoso me hizo dudar durante un rato si mi modelo realmente estaba corriendo en mi Mac. Lo confirmé con ollama ps (PROCESSOR: 100% GPU durante toda la sesión). El label es genérico; no significa que estés pagando a Anthropic.

Si tu prioridad es privacidad verificable, elige solo modelos sin sufijo :cloud. Si tu prioridad es velocidad y aceptas la policy de “no logging” de Ollama, el cloud track tiene SOTA models (kimi-k2.6, glm-5.1) a precios razonables. No son opciones equivalentes — son trade-offs distintos.

Veredicto: cuándo SÍ vale qwen3-coder local

Resumiendo el aprendizaje en tabla:

Tarea qwen3-coder local M4 Max Veredicto
Responder "hello" 13–150 s Lento pero acepta
Refactorizar una función aislada OK
Doc-gen sobre código pegado en el prompt OK
Smoke test de 8 tareas aisladas 88% pass
Buscar archivos sin tool-use Alucina paths No
Construir un sitio Astro 6 sin supervisión Cross-framework hallucination + degrada scope No (mayo 2026)
Mantener coherencia en loop agéntic de 30+ turnos Pierde contexto, ignora instrucciones No (mayo 2026)

Funciona como pair-programmer aislado. Dale una función, devuélvela refactorizada. Dale un párrafo, devuélvelo documentado. Para eso 88% pass rate es genuino y suficiente.

NO funciona como agente autónomo many-turn. Al menos no en mi setup de mayo 2026. Si tu workflow real necesita builds o refactors de proyectos enteros sin supervisión, sigues necesitando la API. O achicas el scope drásticamente — un archivo a la vez, con verificación humana entre cada uno.

Conclusión: el M4 Max no es el bottleneck

64 GB de RAM unificada, 40 GPU cores, memoria de sobra para correr modelos enormes — y el experimento falló igual. No por el hardware. Por capacidad del modelo y madurez del harness.

Para el hardware que tengo, qwen3-coder local sirve para tareas micro. No reemplaza a Claude Opus en builds reales. Y eso está bien — son herramientas distintas para problemas distintos.

Si inviertes en un Mac con RAM suficiente para LLMs locales, hazlo sabiendo que el ceiling lo pone el ecosistema de modelos abiertos + el tooling. NO el hardware. El M4 Max te da margen para los próximos 2–3 años. Lo que falta es que los modelos abiertos alcancen a los propietarios en capacidad agentic. Hoy no están ahí. En 2027, probablemente.


Sources verificadas durante este postmortem:

  • github.com/ollama/ollama/issues/15390 — Invalid tool parameters + CPU fallback con Claude Code
  • github.com/NousResearch/hermes-agent/issues/17031 — Qwen3.6 confabulating session history
  • docs.ollama.com/integrations/claude-code — integración oficial
  • ollama.com/library/qwen3-coder — model card
  • ollama.com/blog/launchollama launch rollout primavera 2026
  • ollama.com/cloud — pricing + policy del track cloud
  • github.com/continuedev/continue/issues/5419 — Agent mode no funciona con Qwen3 (caso análogo en otro IDE)
  • arxiv.org/abs/2603.00729 — Qwen3-Coder-Next Technical Report (admisión oficial de inestabilidad en producción)
  • eval.16x.engineer/blog/qwen3-coder-evaluation-results — benchmark formal: Qwen3 Coder 1/10 en TypeScript narrowing
  • blog.logrocket.com/qwen-3-coder-agentic-cli/ — LogRocket Svelte 5 + Firebase test con Qwen3-Coder