El resultado que no esperaba: Gemma 4 le ganó a Claude Haiku 4.5 escribiendo DAX
En el artículo anterior conté cómo armé un evaluador automático para medir qué tan bien escriben medidas DAX tres modelos LLM: dos locales (qwen3.5:9b y gemma4:e4b corriendo en Ollama) y uno en la nube (claude-haiku-4-5 vía la API de Anthropic). Las 13 preguntas, distribuidas en 5 niveles de dificultad, fueron calificadas por un cuarto modelo —claude-sonnet-4-6— actuando como juez.
Tenía una hipótesis bastante firme antes de correrlo: Haiku 4.5 iba a ganar cómodo. Es Anthropic, está pulido, tiene atrás un equipo de seguridad y benchmarks privados. Los modelos locales son simpáticos pero no juegan en la misma liga.
Me equivoqué. Esta es la tabla final:
| Modelo | Puntaje total | % del máximo | Tiempo total | Costo |
|---|---|---|---|---|
gemma4:e4b |
303.3 / 360 | 84.2% | 10 min | $0 |
claude-haiku-4-5 |
268.2 / 360 | 74.5% | 30 seg | ~$0.005 |
qwen3.5:9b |
48.1 / 360 | 13.4% | 64 min | $0 |
Sí, leíste bien. Un modelo local de ~4B parámetros efectivos, corriendo en mi GPU casera, calificó mejor que Haiku 4.5 en una tarea técnica especializada. Y el modelo de 9B que tenía como favorito local quedó casi por fuera del experimento.
Vamos por partes, porque cada modelo tiene una historia distinta y los detalles importan.
Cómo leer estos números
Antes de entrar al detalle, dos aclaraciones:
- Los puntajes vienen de un juez automático (Sonnet 4.6) calificando contra una rúbrica de 4 dimensiones (sintaxis, lógica, contexto/filtros, calidad de código). No es perfecto, pero es consistente y no tiene preferencia por ningún modelo evaluado.
- La rúbrica es estricta para tareas DAX, no para “código que más o menos funciona”. Una medida que devuelve un valor pero usa la función equivocada pierde puntos aunque “ande”.
Y el sesgo más importante: esto mide DAX, nada más. No estoy diciendo que Gemma sea mejor que Haiku para escribir Python, traducir documentos o resumir noticias. Estoy diciendo que para esta tarea específica, en este experimento, el resultado fue ese.
Qwen 3.5:9b — el favorito que se autodestruyó
Tenía expectativas altas con Qwen 3.5. 9B parámetros, soporte para razonamiento extendido, buen rendimiento en benchmarks públicos. Empezó bien:
[GEN OK] qwen3.5:9b 1A en 64.3s — Score: 9.8/10
[GEN OK] qwen3.5:9b 1B en 46.6s — Score: 9.8/10
Las dos primeras preguntas (nivel básico) las resolvió perfecto. Pero a partir de 1C empezó esto:
[GEN ERROR] qwen3.5:9b 1C: El modelo penso pero no produjo respuesta final (thinking de 18337 chars)
[GEN ERROR] qwen3.5:9b 2A: ... thinking de 19795 chars
[GEN ERROR] qwen3.5:9b 2B: ... thinking de 14759 chars
[GEN ERROR] qwen3.5:9b 2C: ... thinking de 20422 chars
[GEN ERROR] qwen3.5:9b 3A: ... thinking de 12925 chars
[GEN ERROR] qwen3.5:9b 3B: ... thinking de 19659 chars
[GEN OK] qwen3.5:9b 3C en 198.5s — Score: 28.5/30
[GEN ERROR] qwen3.5:9b 4A: ... thinking de 20316 chars
[GEN ERROR] qwen3.5:9b 4B: ... thinking de 31393 chars
[GEN ERROR] qwen3.5:9b 5A: ... thinking de 23950 chars
[GEN ERROR] qwen3.5:9b 5B: ... thinking de 22179 chars
Esto requiere explicación. Qwen 3.5 emite el razonamiento en un campo thinking separado del campo response final. En 11 de las 13 preguntas, el modelo pensó entre 12.000 y 31.000 caracteres —páginas y páginas de razonamiento interno— y nunca cerró el pensamiento ni emitió la medida DAX. El stream terminaba con done: true pero response vacío.
Cuando finalmente emitió respuesta (preguntas 1A, 1B, 3C), las medidas fueron excelentes: 9.8/10, 9.8/10, 28.5/30. La calidad estaba ahí. El problema era llegar al output.
Hipótesis sobre la causa:
- Loop de auto-duda infinito: el razonamiento extendido entró en un bucle donde el modelo cuestionaba sus propias respuestas hasta agotarse.
- Bug de Ollama con thinking: posiblemente la implementación de Ollama no maneja bien el cierre del bloque thinking en este modelo.
- Configuración inadecuada del thinking budget: el modelo no tenía un límite explícito de razonamiento.
Todavía no sé cuál de las tres es. Lo que sí sé es que Qwen 3.5:9b en su configuración default de Ollama es inutilizable para tareas de generación que necesitan output determinístico. Si pensás usarlo, necesitás controlar el thinking — y eso es harina de otro costal.
Veredicto Qwen 3.5:9b: cuando produce, produce muy bien. Pero produce menos del 25% del tiempo. Inservible en este estado.
Gemma 4:e4b — la sorpresa del experimento
Gemma 4 es un modelo más chico (~4B parámetros efectivos en la variante e4b) y no soporta thinking explícito. Esperaba que quedara último. Quedó primero.
| Pregunta | Score Gemma | Comentario del juez (resumido) |
|---|---|---|
| 1A Total Ventas | 9.8/10 | “Usa SUMX correctamente para iterar y multiplicar Cantidad × PrecioUnitario” |
| 1B Nro Transacciones | 9.8/10 | “Correcta, concisa, COUNTROWS sobre la tabla Ventas” |
| 1C Precio Promedio | 9.8/10 | “AVERAGE aplicado correctamente sobre la columna” |
| 2A Margen Bruto % | 16.6/20 | “SUMX correcto, pero podría usar DIVIDE en vez de IF para división por cero” |
| 2B Ventas Electrónica | 17.8/20 | “CALCULATE con SUMX y filtro de categoría correctos” |
| 2C Clientes Únicos | 19.0/20 | “DISTINCTCOUNT óptimo” |
| 3A Ventas Año Anterior | 27.6/30 | “CALCULATE + SAMEPERIODLASTYEAR correctos” |
| 3B Ventas YTD | 26.7/30 | “TOTALYTD con SUMX, válido y resuelve correctamente” |
| 3C Crecimiento % YoY | 27.0/30 | “VAR/RETURN bien estructurado, maneja BLANK y división por cero” |
| 4A Ranking Producto | 36.0/40 | “RANKX con ALLSELECTED, DENSE y DESC correctos” |
| 4B Media Móvil 3M | 29.2/40 | “DATESINPERIOD bien usado, pero divide entre 3 fijo en lugar de contar meses con datos” |
| 5A Clasificación ABC | 24.5/50 | “Estructura y comentarios buenos, pero la lógica central está rota” |
| 5B Métrica Dinámica | 49.5/50 | “Medida ejemplar: VAR/RETURN, SWITCH con SELECTEDVALUE sobre tabla desconectada” |
Lo que más me sorprendió: 49.5/50 en la pregunta más difícil del set (5B, Métrica Dinámica con SWITCH y SELECTEDVALUE sobre una tabla desconectada). Es exactamente el patrón complejo que en mi experiencia rompe a la mayoría de los junior DAX devs.
¿Dónde sí flaqueó?
- 5A Clasificación ABC (24.5/50, 49%): la estructura general estaba bien (variables, comentarios, RANKX), pero la lógica de cálculo del porcentaje acumulado tenía un error que invalidaba la clasificación. Es el tipo de error que un dashboard “muestra números” sin que nadie note hasta meses después.
- 4B Media Móvil 3M (29.2/40, 73%): usó
DATESINPERIODcorrectamente pero dividió entre 3 fijo. El detalle de “¿qué pasa si el período tiene menos de 3 meses con ventas?” no lo consideró.
Veredicto Gemma 4:e4b: sólido, completo, muy buen ratio precio/performance. Para la mayoría de medidas DAX que se escriben en el día a día, este modelo alcanza. Su debilidad aparece en las medidas con lógica multi-paso donde un sub-cálculo intermedio puede salir mal sin romper la sintaxis.
Claude Haiku 4.5 — el favorito que dio sorpresas malas
Haiku resolvió las 13 preguntas en 30 segundos totales. Velocidad impecable. Sintaxis siempre limpia. Estructura de respuestas profesional. Y aun así sacó 268/360.
¿Qué pasó? Tres patrones repetidos:
Patrón 1: errores DAX-específicos que un experto pillaría al instante
4A Ranking Producto: 10.8/40 — DENSEDRANK no es una función DAX válida
Sí, Haiku inventó una función. DENSEDRANK no existe en DAX. La función correcta para ranking denso es RANKX con el parámetro Dense. Es un error sutil pero costoso —Gemma sacó 36/40 en la misma pregunta usando el patrón correcto.
Otros errores similares:
- 1B: usó
COUNTA(Ventas[FechaVenta])en lugar deCOUNTROWS(Ventas).COUNTAcuenta valores no nulos en una columna; si hubiera fechas nulas el resultado sería distinto. - 2B / 3A / 3B: pasó
SUM(Ventas[Cantidad] * Ventas[PrecioUnitario])como argumento.SUMno acepta multiplicación de columnas directamente —se necesitaSUMX(Ventas, Ventas[Cantidad] * Ventas[PrecioUnitario]). Es probablemente el error más común de los principiantes en DAX, y Haiku lo cometió.
Patrón 2: el modelo “casi tiene razón” pero con un detalle invalidante
3C Crecimiento % YoY: 25.5/30 — usar Calendario[Anio] = AñoActual - 1 directamente
La lógica era casi correcta, pero el patrón usado para acceder al año anterior no respetaba el contexto de filtro como SAMEPERIODLASTYEAR lo haría. En un reporte por trimestre o mes, los números van a salir raros.
Patrón 3: las preguntas más difíciles —donde Haiku brilló
5B Métrica Dinámica: 48.0/50 — Implementación excelente y completa
4B Media Móvil 3M: 32.8/40 — Buen uso de VAR/RETURN, DATESINPERIOD correcto
Acá Haiku jugó al nivel que esperaba. El SWITCH con SELECTEDVALUE de la 5B salió impecable, casi idéntico al de Gemma. Y la 4B le salió mejor que a Gemma (32.8 vs 29.2).
Veredicto Claude Haiku 4.5: rápido, completo, sintaxis siempre legible. Pero para DAX específicamente tiene lagunas en funciones del idioma que un modelo más especializado o con mejor cobertura de documentación de Power BI no tendría. La velocidad y consistencia de output son su gran ventaja; la profundidad técnica es donde lo superó Gemma.
Cuatro patrones que aparecieron en el análisis
1. La complejidad alta no necesariamente penaliza al modelo más chico
Habría apostado que las preguntas de nivel 5 (50 pts c/u) iban a ser donde Haiku se despegara. Fue al revés en una de las dos:
| Pregunta | Gemma | Haiku | Diferencia |
|---|---|---|---|
| 5A Clasificación ABC | 24.5 | 28.0 | -3.5 |
| 5B Métrica Dinámica | 49.5 | 48.0 | +1.5 |
Empate técnico en las preguntas más difíciles. Donde Haiku se diferenció (negativamente) fue en preguntas medias que tienen funciones DAX específicas con nombres que se pueden confundir.
2. La velocidad tiene un costo
Haiku terminó en 30 segundos. Gemma tardó 10 minutos. Pero la diferencia de tiempo se traduce —en este caso— en una diferencia de precisión técnica. Modelos más rápidos parecen estar invirtiendo menos “tokens internos” en validar funciones contra el corpus de DAX que conocen.
3. El razonamiento extendido sin control es un riesgo
Qwen es el caso extremo, pero el principio es general: si un modelo tiene thinking habilitado y no le ponés un techo, puede gastar todo su presupuesto razonando y nunca llegar a producir. Para tareas de generación, el output importa más que la elegancia del razonamiento que llevó a él.
4. La rúbrica de 4 dimensiones revela cosas que un test “anda/no anda” no muestra
Si hubiera medido solo “el código corre sin error”, Gemma y Haiku habrían empatado en casi todo. La rúbrica diferenciada (sintaxis, lógica, contexto, calidad) es lo que hizo visible que Haiku usaba DENSEDRANK o SUM con multiplicación de columnas: el código parece correcto y posiblemente corre, pero la lógica subyacente está rota o usa una función inventada.
Recomendaciones prácticas
Si estás eligiendo un modelo para asistirte con DAX en Power BI:
Para uso diario en un equipo:
- Primera opción: Gemma 4:e4b corriendo local. Mejor balance de los tres en este test, costo cero, privacidad total.
- Segunda opción: Haiku 4.5 vía API. Si necesitás velocidad y querés ahorrarte el setup de Ollama, va a hacer buen trabajo el 80% del tiempo. Tené presentes las lagunas en funciones DAX-específicas y validá las medidas más críticas antes de pegarlas al modelo.
Para medidas críticas (ej. KPIs financieros, métricas regulatorias):
- No confíes en ningún modelo solo. Generá con local o Haiku, validá con Sonnet/Opus, y siempre testeá con datos reales antes de producción.
Para Qwen 3.5:9b:
- Hasta que el problema del thinking se resuelva (configurando el budget, cambiando la versión, o moviendo a otra runtime), no lo recomiendo para esta tarea. Si querés un modelo local de ~9B, Gemma o Llama van a darte mejores resultados garantizados.
Lo que este experimento NO prueba
Para no exagerar las conclusiones:
- No probé los modelos top de Claude (Sonnet 4.6, Opus 4.7). Esos casi seguro habrían superado a Gemma. Pero su costo es 6x-25x el de Haiku, y la pregunta era específicamente “¿vale la pena pagar la API si tengo modelos locales decentes?”.
- Solo evalué DAX. Para SQL, Python o tareas de chat general los resultados pueden ser totalmente distintos.
- El juez también es un LLM. Sonnet 4.6 puede estar perdiendo errores sutiles que un experto humano notaría. Lo más probable es que las posiciones relativas se mantengan, pero los puntajes absolutos podrían correrse 5-10 puntos.
- Una sola corrida. Para un benchmark riguroso querría ver 5 corridas con
temperature0.2 y reportar promedios + desviación estándar. Acá tenés un punto de datos, no un estudio.
El takeaway que me llevo
Gasto cada vez menos tiempo en Twitter/Reddit leyendo “este modelo es mejor que el otro”. Los benchmarks públicos miden tareas que casi nunca son las que vos hacés. Tu tarea específica —escribir DAX, queries SQL para tu schema particular, código en tu framework con tus convenciones— necesita su propio benchmark.
Y armarlo no es tan caro. Este experimento me costó:
- 2 días de trabajo de desarrollo del evaluador.
- ~$0.05 en llamadas a Claude (juez + uno de los modelos evaluados).
- ~75 minutos de cómputo local.
A cambio tengo data dura sobre cuál modelo conviene para mi caso de uso específico, y un evaluador reusable que puedo correr cada vez que sale una versión nueva de cualquier modelo.
Si trabajás con LLMs en serio para una tarea repetitiva: armate tu propio benchmark. Vas a sorprenderte de cuánto cambia cuando ves los números en lugar de leer opiniones.

Deja una respuesta