Viernes 30 de octubre de 2026, 23:30. Se escribe el último registro de un archivo de 738 KB: ochocientos documentos etiquetados, ocho campos cada uno. Son las etiquetas que llevábamos cuatro días debiendo. No las generó un modelo. Las generaron las reglas deterministas que ya habíamos auditado una por una — las mismas que deciden qué es una anomalía en el proyecto. La idea era simple: si las reglas producen la verdad, las reglas pueden producir el ground truth de un set de evaluación.

Sábado 31 de octubre, 01:14:02. Se lanza el barrido de reasoning_effort: ochocientos documentos contra el modelo abierto de Meta, tres niveles de esfuerzo, la misma vara para todos. Nueve horas después, el log tenía exactamente una línea. Cero resultados. 2.248.855 tokens gastados y no un solo número que mostrar.

Sábado 31 de octubre, 14:52:45. Segunda corrida, con el harness arreglado. rc=0. Ocho minutos después teníamos la tabla completa —y un hallazgo que no buscábamos.

Este artículo es la bitácora de esas quince horas. Y la tesis que salió de ellas no es la que esperábamos escribir cuando el viernes entramos al workshop de Fireworks.

🔴 LA TESIS DE ESTE ARTÍCULO

Fuimos a reproducir el benchmark de un vendor sobre datos chilenos. Lo que se rompió primero no fue el modelo: fue nuestro harness. Y cuando por fin midió, el modelo nos dijo algo más útil que su porcentaje de acierto: copia perfecto y clasifica regular.

Los cuatro campos que se copian literalmente —monto total, monto neto, RUT, fecha— dieron 100,0% en 2.886 evaluaciones. Los dos campos que exigen decidir una categoría dieron 88,6%, y se llevaron 183 de los 210 fallos. El número global, 96,55%, es el que se presume. El que importa es 76,0%: la fracción de documentos que quedaron perfectos de punta a punta.

Y hay un detalle que cambia la conversación sobre modelos: el 91% de los documentos que fallaron fallaron un solo campo. No es un modelo impreciso. Es un modelo que acierta todo lo que se lee y se equivoca en lo que se decide — y el campo en el que más se equivoca (82,5%) es justamente el que define a dónde va el documento. Un error de copia se detecta. Un error de clasificación se propaga.

0. Nota de método: qué medimos, qué no, y qué intereses tenemos sobre la mesa

Esta casa tiene una regla y esta vez pesa: declarar los sesgos antes de dar los números, no después de que alguien los encuentre.

Interés declarado, primero. Estamos compitiendo en el hackatón donde este workshop se dictó. Fireworks nos seleccionó y nos dio 100 USD en créditos que estamos usando exactamente para esto. No somos observadores neutrales del partner cuya herramienta medimos: somos clientes agradecidos de ella. Cualquier conclusión favorable a Fireworks debe leerse con ese descuento aplicado.

Segundo interés, menos cómodo. El benchmark original del workshop comparaba el modelo abierto de Meta contra DeepSeek. Nuestro harness personal corre, entre otros, sobre DeepSeek. Y el resultado del workshop favoreció al modelo de Meta. No vamos a discutir ese resultado: no lo replicamos. Lo que sí hicimos fue correr el mismo tipo de instrumento sobre nuestro corpus, y ese instrumento no tiene proveedor favorito.

Qué mide el set de evaluación. Ochocientos ejemplos construidos desde registros reales de compras públicas chilenas: cada documento trae sus páginas con el texto renderizado desde el registro estructurado, y una etiqueta por campo. La etiqueta no salió de un modelo — salió de la misma regla determinista que después va a juzgar si el extractor acertó. Eso tiene un nombre técnico (weak supervision auditable) y una consecuencia práctica: el que etiqueta y el que gradúa no comparten modelo, que es la única razón por la que comparar dos backends acá significa algo.

🟡 EL TECHO QUE HAY QUE DECLARAR ANTES DE PRESUMIR

El texto de estos 800 documentos está renderizado desde registros estructurados, no extraído por OCR de un PDF escaneado. Eso significa que la tarea mide extracción de campos desde texto, no lectura de documentos degradados. Un PDF escaneado con un sello encima, una tabla torcida o una resolución de 2009 fotocopiada tres veces son un problema distinto y más difícil, y estos números no dicen nada sobre ese caso.

Y hay un segundo techo, más sutil: como el texto se generó desde la misma fuente que la etiqueta, el set mide recuperación de información que sabemos que está ahí. No mide si el documento real contiene el dato. Es la diferencia entre «¿lo puede leer?» y «¿está?». La primera pregunta es la que este harness responde.

1. Qué nos dio el workshop: tres notebooks y un número que no esperábamos

El viernes 30 de octubre, 65,7 minutos, 921 segmentos de transcripción. «Building Specialized Intelligence on Fireworks: From Inference to Training with Muse Open Models», con Sinan Ozdemir. No fue una presentación de producto: fue un repo abierto, tres notebooks y una sesión de preguntas donde el autor dijo en voz alta cosas que su propio README no dice.

Parte Notebook La pregunta que responde
I muse_vs_deepseek_demo ¿El modelo abierto de Meta le gana a DeepSeek en tareas cortas?
II muse_reasoning_sweep ¿Más esfuerzo de razonamiento produce mejores respuestas?
III muse_glimmer_train_router ¿Se puede entrenar un router que decida a qué modelo mandar cada consulta?

El tercero es el que se llevó los aplausos: 600 filas de un dataset público, un LoRA sobre Muse Glimmer 30B, y el route accuracy pasa de 22,5% a 90,0%; el exact match, de 2,5% a 57,5%, con un McNemar de p = 4,8·10⁻⁷. Y el costo real del experimento, dicho en vivo y no en el slide: unos 10 minutos de entrenamiento, 35 pasos, cero dólares (el SFT de LoRA está gratis hasta el 1 de diciembre).

Ese es el dato que nos hizo abrir un archivo a las once y media de la noche. Si 600 filas mueven una aguja 67 puntos, el cuello de botella de un extractor especializado no es el cómputo: son las etiquetas. Y nosotros teníamos una manera de producirlas que no dependía de nadie.

Pero antes de entrenar nada había que hacer lo que casi nadie hace: medir la línea base. Y ahí el workshop tenía una segunda lección, más incómoda.

2. El hallazgo que nos ahorró dinero: el esfuerzo alto empeora la respuesta

El notebook II barre seis niveles de reasoning_effort sobre cuatro benchmarks. Los resultados, tal como están en el repo:

Benchmark minimal low medium high xhigh max
GSM8K989999949897
BoolQ939093868984
NQ-Open646967646665
ARC-Easy959692838586
Overall87,588,587,881,884,583,0

Leído de izquierda a derecha, el gráfico debería subir. Baja. El nivel con mejor promedio es low, y high cuesta 6,7 puntos contra low — casi el mismo margen con el que el modelo de Meta le gana a su competidor en el notebook I (+2,0). El nivel que más tokens consume es el que peor responde.

«Does higher effort always give better output? No.»

— Sinan Ozdemir, workshop de Fireworks, 30-oct-2026, [00:36:07]

Y la explicación que dio es la parte que nos importa, porque es una propiedad del modelo y no del proveedor: a mayor esfuerzo, el modelo relee su propia respuesta y se autoconvence de lo contrario. Hay un paper de Meta de hace año y medio sobre exactamente eso, que él citó en vivo con una frase que anotamos textual:

«They walk themselves into the wrong answer.»

— sobre el modo de falla del razonamiento extendido, [00:37:37]

Lo que esto cambió en nuestra arquitectura es permanente: nuestro verificador adversarial —la pieza que borra toda afirmación que no puede trazar a un documento— nunca le pide al modelo «reconsidera». Un segundo pase de razonamiento sobre una decisión ya tomada no es una revisión: es un generador de falsos negativos con costo doble. La verificación tiene que ser código, no otra vuelta del mismo motor.

Y una advertencia que él mismo puso y que conviene repetir porque se ignora: los niveles de esfuerzo no son comparables entre proveedores. Cada laboratorio define qué significa high, así que un high contra otro high no es una comparación: es una coincidencia de nombres.

3. El modo de falla que casi heredamos: la respuesta vacía que gradúa cero

De los cinco banderines de rigor que le encontramos al repo, hay uno que no es un detalle de estilo sino un problema de ingeniería, y ellos lo reportan bien:

«19 of 400 max answers were empty because reasoning used up the 2.048-token output limit.»

— muse_reasoning_sweep, notebook II del repo del workshop

Cuatro coma siete cinco por ciento de respuestas en blanco. No son errores: son respuestas vacías que el grader cuenta como incorrectas sin levantar una excepción. El modelo razonó tanto que se quedó sin tokens para contestar, y el harness —correctamente— lo anotó como un fallo de precisión en vez de lo que era: un fallo de presupuesto.

En un benchmark eso es un dato. En un motor de evidencia es el pecado capital. Porque una respuesta vacía, en un sistema que produce hallazgos sobre plata pública, se lee exactamente igual que «no hay nada que reportar». El silencio del modelo se convierte en una afirmación del sistema. Y esa afirmación es falsa.

⚙️ LA REGLA QUE ADOPTAMOS ANTES DE ESCRIBIR UNA LÍNEA

Vacío no es «no hay hallazgo». Vacío es unverified declarado, con causa y con nombre. Todo campo que el extractor no puede sostener con una cita vuelve como ausencia explícita — MISSING_FIELD, PAGE_OUT_OF_RANGE, NO_EVIDENCE — y esa ausencia se cuenta en el reporte. Un instrumento que no distingue «no lo encontré» de «no existe» no sirve para auditar nada.

El resultado de esa decisión, medido veinticuatro horas después: 0 documentos truncados en 800. La trampa del taller no apareció porque el diseño no la invitaba — corrimos a low, donde el razonamiento consume 296.118 tokens (el 53,4% de toda la salida) y aun así deja espacio para responder. Elegir el nivel de esfuerzo no fue una optimización de costo: fue una decisión de fiabilidad.

4. Las etiquetas: de reglas que se pueden auditar, no de un modelo

La parte que nadie filma. Ochocientos documentos, ocho campos: identificador de la orden, tipo de documento, procedencia, monto total, monto neto, RUT del proveedor, unidad compradora y fecha.

La decisión de diseño que hace que este set valga algo es de dónde salen las etiquetas. La respuesta honesta es casi decepcionante: de las reglas deterministas del proyecto, las mismas que ya tenían que pasar sus propios tests. Si la etiqueta dijera «esta orden es una resolución de adjudicación» porque un LLM lo dijo, el benchmark estaría midiendo el acuerdo entre dos modelos y llamándolo verdad. Con reglas, la etiqueta es un artefacto que un tercero puede leer, discutir y refutar.

Eso también tiene un costo, y lo asumimos: el set hereda los sesgos de las reglas. Donde la regla tiene un punto ciego, la etiqueta también. Pero un punto ciego escrito en código se encuentra leyendo; uno escondido en los pesos no se encuentra de ninguna manera.

Hubo además dos bugs reales en la construcción, que quedaron arreglados con sus tests:

Bug Qué rompía Cómo se detectó
El formato de fecha El 31% de las fechas se leía mal: un único parser no cubría los dos formatos del corpus. El test de cobertura de campos, que cuenta cuántos ejemplos tienen valor por campo.
La etiqueta atada al mecanismo La etiqueta de tipo_documento seguía al mecanismo de compra en vez de al layout del documento. Dos documentos con la misma forma y distinto procedimiento recibían la misma etiqueta siendo distintos. Lectura del set, no un test: apareció revisando veinte ejemplos a mano.

El segundo es el interesante para cualquiera que construya un set de evaluación: un conjunto de etiquetas mal construido no produce un número malo, produce un número plausible. Si no hubiéramos leído veinte ejemplos a mano, el tipo_documento habría dado un porcentaje creíble midiendo la cosa equivocada.

5. 01:14: el barrido que gastó 2,25 millones de tokens y no dejó un número

Acá está la parte de la noche que no es heroica. El harness se lanzó a la 01:14, escribió su línea de inicio y murió sin dejar un solo resultado. No hubo excepción visible. Los archivos de salida —sweep_low.json, sweep_medium.json, sweep_high.json— simplemente no existían. Y el proceso, tampoco.

La tabla de consumo de esa madrugada sí existe:

Corrida Prompt Completion Total Resultado
01:14 · la que murió 1.036.736 1.212.119 2.248.855 ❌ cero mediciones
14:28 · la que midió 624.245 554.084 1.178.329 ✅ 800 documentos

Lea eso dos veces. El intento que no produjo un solo dato consumió 1,9 veces más que la corrida completa que sí produjo todos. La razón es que la primera no estaba midiendo: estaba reintentando, y cada reintento se paga igual que un acierto.

La causa raíz no fue mala suerte. Fue una línea de código:

if args.json_out:
    args.json_out.write_text(json.dumps(report))   # única escritura, al terminar los 800

Toda la evidencia del trabajo se escribía al final. Si el proceso muere en el documento 780 —sesión cerrada, SSH cortado, terminal cerrada, kernel aburrido— se pierden los 800. Y el proceso corría sin nohup ni setsid: dependía de que la terminal siguiera viva. La misma terminal que yo estaba usando para conversar.

🔴 LA LECCIÓN QUE NO ES SOBRE BENCHMARKS

Trabajo que no deja rastro no es trabajo: es gasto. Un motor de evidencia que escribe sus hallazgos al final del recorrido no es un motor de evidencia — es una promesa con factura. Y lo incómodo es que nosotros construimos ese motor precisamente porque otros sistemas fallan así. Nos pasó a nosotros, en el harness de evaluación, a las dos horas de haber escrito el artículo que decía que no debía pasar.

El arreglo son dos commits y una idea:

Cambio Qué garantiza
Journal por documento (JSONL incremental) Si el proceso muere, se pierde un documento, no ochocientos. Y --resume retoma exactamente donde quedó: la corrida de las 14:28 arrancó con reanudado: 0 porque no había nada que reanudar.
Encadenar el siguiente esfuerzo al terminar el actual El barrido completo ya no depende de que alguien esté despierto para lanzar la etapa siguiente. El reloj no piensa — pero tampoco se queda dormido.
setsid nohup La corrida sobrevive a la terminal que la lanzó. Suena trivial hasta que la terminal se cierra sola.

La corrida de la tarde corrió con ocho workers, a 1,84 segundos por documento, y cerró en 24 minutos 15 segundos con código de salida 0. Y este es el detalle que vale más que todo lo anterior: cuando reescribimos el harness para que no pudiera perder trabajo, el número que salió ya no era el de un demo. Era el de una medición.

6. Los números: muse-spark-open a low sobre 800 documentos

Una corrida, ocho campos, 6.086 evaluaciones de campo. Grado determinista —reglas, no un juez-LLM— contra etiquetas producidas por reglas.

Campo Aciertos Evaluados Precisión Naturaleza
monto_total_clp800800100,0%copia
rut_proveedor800800100,0%copia
fecha_orden800800100,0%copia
monto_neto_clp486486100,0%copia
orden_compra_id79880099,8%copia
unidad_compradora77580096,9%copia normalizada
procedencia75780094,6%clasificación
tipo_documento66080082,5%clasificación
Total5.8766.08696,55%

Y el número que va debajo de la tabla, que es el que casi nadie publica:

Métrica Valor Qué significa
Documentos exactos 608 / 800 = 76,0% Todos los campos del documento, correctos, de punta a punta. Es el número que un auditor puede firmar.
Campos no verificados 2 / 6.086 = 0,03% Ausencias declaradas con causa (MISSING_FIELD), no inventadas.
Documentos truncados 0 La trampa del límite de tokens no apareció a low.
Documentos sin monto neto 314 / 800 El 39% de los documentos no trae monto neto: el sistema lo declara ausente en vez de calcularlo. No se rellena lo que no está.
Tokens por documento 1.473 624.245 prompt + 554.084 completion, de los cuales 296.118 son razonamiento (53,4%).

Las dos cifras —96,55% y 76,0%— describen el mismo sistema y no se contradicen. La primera responde «¿lee bien los campos?». La segunda responde «¿entrega documentos utilizables?». Cualquier producto que se venda con la primera y se juzgue con la segunda está construido sobre una diferencia de veinte puntos.

7. El hallazgo que no buscábamos: copia perfecto, clasifica regular

Con la tabla por campo en la mano hicimos lo que había que hacer y no habíamos planeado: mirar por documento, no por campo. Cómo se distribuyen los 192 documentos imperfectos.

Fallos en el documento Documentos Acumulado
0 — perfecto60876,0%
exactamente 117597,9%
21699,9%
31100%

El 91% de los documentos imperfectos falla un solo campo. No hay un modelo que se derrumbe: hay un modelo que casi siempre acierta todo y se equivoca en una cosa. Y cuando miramos cuál cosa, el patrón se vuelve evidente:

Grupo Campos Precisión Fallos % del total de fallos
Copia literal monto total, monto neto, RUT, fecha 2.886 / 2.886 = 100,0% 0 0,0%
Clasificación tipo de documento, procedencia 1.417 / 1.600 = 88,6% 183 87,1%
Identificador orden de compra 798 / 800 = 99,8% 2 1,0%
Copia normalizada unidad compradora 775 / 800 = 96,9% 25 11,9%

Cuatro campos de copia literal: 2.886 de 2.886. Cero fallos. Dos campos que exigen decidir una categoría: 88,6%, y concentran el 87% de todos los errores del sistema.

🧠 LA FRASE QUE NOS VAMOS A LLEVAR AL PITCH

Un modelo no es «preciso» o «impreciso» en abstracto. Copia perfecto y clasifica regular. Todo lo que está escrito en el documento y hay que trasladar, lo traslada sin error en casi tres mil intentos. Todo lo que exige interpretar qué clase de documento es, falla uno de cada seis.

Y el campo que más falla —tipo_documento, 82,5%— no es un campo más: es el que decide a dónde va el documento. Un error de copia se detecta comparando. Un error de clasificación se propaga en silencio a todo lo que venga después.

Esto reordena el diseño de todo el sistema, y no en la dirección que la gente espera. La intuición de la industria es «fine-tuneemos el extractor para que suba el número». Nuestros datos dicen algo más específico: el extractor no necesita ayuda para leer. Necesita ayuda para decidir. Fine-tunear un modelo para que copie mejor lo que ya copia al 100% es gastar cómputo en el problema resuelto.

Y valida, por la vía empírica, la razón por la que el fine-tuning es un nice-to-have y no una dependencia: el cuello de botella no es de capacidad del modelo. Es de arquitectura de decisión — y eso se arregla con confianza, umbrales y una cola de revisión humana, cosas que no necesitan entrenamiento.

8. Por qué entrenar un router sigue teniendo sentido (y dónde exactamente)

Volvamos al notebook III, porque después de mirar nuestros propios números su resultado se lee distinto. El 22,5% → 90,0% del router es real y es grande. Pero conviene ver qué mejoró: un clasificador de consultas, no un extractor. Y esa es exactamente la forma del hueco que medimos nosotros.

El router del workshop Nuestro resultado
Tarea: elegir una categoría para una entrada Nuestro único punto débil: elegir categorías (82,5% y 94,6%)
600 filas, ~10 minutos, SFT gratis 800 filas etiquetadas ya construidas, del mismo dominio
Resultado: +67,5 puntos en la tarea de clasificar Hipótesis a probar: subir tipo_documento de 82,5% sin tocar la copia

Ahí está el único lugar donde un fine-tune se justifica en nuestro sistema, y no es el que la intuición sugiere. No entrenaríamos al extractor: entrenaríamos al clasificador. La copia ya está resuelta al 100%; lo que falta es criterio de categoría. Y el entregable no sería el modelo, sería el harness otra vez: base contra ajustado, holdout fijo, grading determinista, test pareado — el mismo instrumento que acaba de producir la tabla de la sección 6.

Con una advertencia operativa que el taller dio y que conviene tener escrita antes de que sorprenda a alguien en plena demo: un LoRA no corre en serverless. Requiere un despliegue dedicado que cobra por GPU-hora y que tiene arranque en frío. Por eso el diseño del extractor tiene dos implementaciones con la misma interfaz, y la de cero-shot es la que responde si la ajustada no está desplegada. La demo se graba con la que siempre está.

9. Lo que no vamos a afirmar

Seis cosas, y las escribimos antes de que las pregunte un jurado, un cliente o un competidor.

1. Este 96,55% no es comparable con el 90,0% del router. Son tareas distintas, datos distintos y varas distintas. Ponerlos en la misma frase sería exactamente el tipo de comparación que criticamos.

2. Una sola semilla. Corrimos low una vez. No hay todavía test pareado contra medium ni high sobre este corpus — es lo próximo, y el harness ya quedó encadenado para hacerlo sin supervisión. Hasta entonces, la tabla de esfuerzo del taller es de ellos, no nuestra, y así hay que citarla.

3. El gold es nuestro. Etiquetas de nuestras reglas sobre documentos renderizados por nosotros. Es el mejor set que pudimos construir con lo que existe, y sigue siendo un set construido por la parte interesada. Un tercero con acceso a los PDFs originales puede encontrar discrepancias que nosotros no veremos.

4. No medimos el caso difícil. PDFs escaneados, sellos, tablas torcidas, documentos de 2009: nada de eso está en este set. Los números de arriba no se extrapolan ahí.

5. Fireworks nos dio los créditos. Dicho en la sección 0 y vale repetirlo: un cliente satisfecho midiendo la herramienta que le regalaron tiene un sesgo, y el sesgo va en la dirección de los resultados publicados.

6. Y una observación sobre el repo del taller que no es un ataque pero sí una diferencia. Su MUSE_AHEAD.md instruye borrar el panel donde su modelo pierde: «UiPad coordinates favor DeepSeek; drop that panel». Los notebooks lo repiten: «Skip UiPad coordinates». En vivo, en cambio, el autor dijo lo contrario y con más integridad que su documentación:

«These results are not statistically significant. I did not run a statistical test.»

— Sinan Ozdemir, en vivo, [00:24:00]. No está en MUSE_AHEAD.md.

Y también, sin que nadie se lo preguntara: «I did not compare this to Jev. Maybe I should. Maybe you should» — el autor admitiendo que falta el baseline obvio.

Esa distancia entre lo que un repo muestra y lo que su autor admite cuando le pones un micrófono no es hipocresía: es lo normal. Los repos son material de marketing y las charlas son ingeniería. Pero define el estándar al que queremos responder, y es el mismo que aplicamos en este artículo: el número que sobrevive no es el que gana el panel, es el que se puede defender cuando te preguntan por el test estadístico. Nosotros no tenemos el test todavía. Lo decimos.

Cierre: el modelo se cambia en una línea; el harness decidió qué se ve

Quince horas separan la primera línea del archivo de etiquetas del rc=0 del barrido. En el medio hubo un workshop, dos bugs de etiquetado, un harness que gastó 2,25 millones de tokens sin dejar un número, un arreglo de tres commits, y una tabla que no esperábamos.

El modelo no fue el protagonista de ninguna de esas horas. Lo que decidió el resultado fue el harness: el que eligió low y por eso no tuvo una sola respuesta truncada; el que declara las ausencias y por eso el 39% de los documentos sin monto neto no se convirtió en números inventados; el que escribe por documento y por eso hoy hay una medición donde ayer hubo una factura.

Y queda la lección más incómoda, que es sobre nosotros: llevábamos días diciendo en público que un sistema de evidencia tiene que dejar rastro de lo que hace. A la primera noche lo escribimos al final del archivo, como cualquiera.

Eso es lo que un harness es, en realidad: la parte de nosotros que no se olvida de lo que ya aprendimos.

¿Estás midiendo tu extractor o estás midiendo tu demo?

Publicamos el harness, las etiquetas y el repo — con los números incómodos adentro. Si estás construyendo agentes que tienen que sostener lo que afirman sobre datos reales, y quieres saber qué fracción de tus documentos sobrevive una revisión de punta a punta en vez de un promedio por campo, conversemos. La diferencia entre 96,55% y 76% es la diferencia entre un demo y algo que un humano firma.

Hablemos de tu harness →