Laya en el vigía: un modelo de decisión de 322M reemplaza al LLM, y lo que aprendí probándolo
Mi agente que vigila la infra decidía todo con un LLM, entre 10 y 40 segundos por decisión. Ahora decide Laya, un modelo de decisión tipada open de 322M en CPU, en menos de un segundo. Nueve experimentos pre-registrados, cuatro negativos, y una conclusión: el modelo chico llegó al nivel del LLM que lo entrenó y ahí se quedó.
- ai
- llm
- devops
- alerting
- fine-tuning
- open-source
- homelab
- agents

TL;DR
Mi agente que vigila la infra (clau-vigía, lo conté acá) decidía todo con un solo LLM: si avisarme, por qué canal y qué decir. Entre 10 y 40 segundos por decisión. Desde esta semana la decisión la toma Laya, un modelo de decisión tipada de 322 millones de parámetros, open, corriendo en CPU, en menos de un segundo por señal. El LLM solo escribe el mensaje.
Lo que vale de este post no es el modelo: es el método para saber si servía. Nueve experimentos pre-registrados, cuatro con resultado negativo, y una conclusión que no esperaba: el modelo chico llegó exactamente al nivel del LLM que lo entrenó, y ahí se quedó. Para pasar de ahí, las etiquetas tienen que ser mías.
El problema: un LLM tomando una decisión de tres opciones
clau-vigía corre cada 15 minutos y cuando hay una señal nueva (un servicio caído, un pod en CrashLoop, una alerta de Grafana, un meeting inminente) le pregunta al LLM: ¿le aviso a Carlos? ¿Por llamada, por mensaje o por mensaje con botones “tomo / descarto”? ¿Qué le digo?
Las dos primeras preguntas son una clasificación. La tercera es una redacción. Usar un LLM de frontera para clasificar en tres opciones es caro y lento, y la latencia se nota: entre que salta la alerta y suena el teléfono pasaban 30 segundos.
En septiembre salieron dos modelos pensados para exactamente esto, con una semana de diferencia:
- Jev, de TypeSafe AI: API cerrada, devuelve decisiones tipadas con confianza calibrada, aguanta 255 opciones, no se puede fine-tunear.
- Laya, de Convai: Apache 2.0, pesos abiertos, ModernBERT-large 421M para inglés o mmBERT-base 322M multilingüe, corre en CPU, se fine-tunea.
Los dos responden preguntas tipadas sobre un estado: noul (sí/no con probabilidad), choice (una opción entre varias) y score (un nivel ordinal). No generan texto, así que no hay nada que parsear ni que alucinar.
Quería probar Jev. Cerró las suscripciones nuevas por demanda. Buscando alternativa encontré Laya, y que sea abierta cambió todo lo que sigue.
Lo que Laya sabe hacer sin entrenar: nada útil
El model card de Laya es honesto y conviene leerlo antes que la prensa: los checkpoints base dan 0,36 en su propio benchmark de decisiones tipadas, con random en 0,32. “Laya es una base rápida para especializar, no un motor de decisión zero-shot”.
Lo confirmé en el primer experimento. Le di la señal “servicio caído: qwen, crit, nuevo” y las tres preguntas. Respondió “avisar” con probabilidad 1,00. Y también con 1,00 a “scrub del NAS terminado sin errores”, a “3 mails importantes sin leer” y a todo lo demás. El base dice que sí a todo.
Eso no es un problema, es el punto de partida. Pero define el piso: un modelo que siempre dice “avisar” acierta exactamente la proporción de positivos del test. Cualquier versión tiene que superar eso, no el 50%.
El método, antes que el modelo
Vengo de un hackathon donde lo que mejor funcionó no fue el agente sino la disciplina para medirlo. Apliqué lo mismo:
- Ground truth propio. Escribí 128 casos a mano, con las formas reales de las señales de vigía y la respuesta correcta según mi criterio: servicios caídos, nodos NotReady, pools ZFS degradados, backups fallidos, demos en vivo, mails urgentes, meetings a distintos minutos, y sus versiones repetidas y recuperadas. 70 para entrenar, 58 congelados como test. El test no se tocó más.
- Control. Laya sin entrenar, con las mismas preguntas. Se re-corre cada vez que cambia algo del instrumento.
- Pre-registro. Cada versión con su hipótesis y su resultado esperado escritos antes de correrla. Los negativos se guardan igual que los positivos.
- Métricas por clase. Nunca accuracy sola: recall de positivos, recall de negativos, falsos negativos en severidad crítica, calibración. Con clases desbalanceadas, accuracy miente.
El state que ve el modelo es la señal más un contexto en palabras:
{
"contexto": "SEÑAL REPETIDA: ya avisada 2 veces, cambió el detalle.",
"severidad": "crit",
"titulo": "Servicio caído: whisper-tts",
"detalle": "whisper-tts (http://ai-server:8000/health) no responde OK — timeout.",
"fuente": "collector.infra",
"key": "service-down/whisper-tts",
"cambio": "modificada",
"veces_avisada": 2,
"otras_senales_activas": 1
}
Y las preguntas, en castellano, con los criterios de cada opción:
{
"avisar": {"type": "noul", "instructions": "¿Esta señal amerita avisarle a Carlos ahora por Telegram? ...",
"criteria": {"false": "no avisar: ruido, informativo, ya avisado, o sin acción posible",
"true": "avisar ahora: caída, incidente, demo en vivo, recuperación de algo ya avisado, meeting inminente"}},
"canal": {"type": "choice", "instructions": "Si se avisa, ¿por qué canal?",
"criteria": {"voice": "llamada telefónica: grave y urgente, servicio core caído, pod en CrashLoop, demo crit",
"text": "mensaje de Telegram: el default, dato accionable, recuperaciones, meetings, avisos informativos",
"actionable": "mensaje con botones tomo/descarto: incidente operativo que alguien debe tomar"}},
"urgencia": {"type": "score", "instructions": "¿Qué tan urgente es esta señal para Carlos?",
"criteria": ["low: puede esperar horas", "med: conviene mirarlo en la próxima hora", "high: ahora mismo, interrumpir"]}
}
Laya corre en un servidor FastAPI en mi AI Server, en CPU, con torch sin CUDA a propósito: la GPU la tiene Qwen y no hay VRAM para los dos. Unos 600 ms por señal con las tres preguntas.
Siete versiones, cuatro negativos

Todo sobre el mismo test de 58 casos: 37 positivos, 21 negativos, 19 críticos. Mayoría de clase 0,638.
| versión | train | avisar acc | recall+ | recall- | FN crit | canal | qué cambió |
|---|---|---|---|---|---|---|---|
| base | 0 | 0,638 | 1,00 | 0,00 | 0 | 0,34 | control: avisa todo |
| v1 | 22 | 0,42 | 0,54 | 0,17 | 3 | 0,23 | receta del notebook, 18 pasos |
| v2 | 22 | 0,71 | 0,88 | 0,22 | 0 | 0,72 | 260 pasos |
| v3 | 39 | 0,655 | 0,84 | 0,33 | 3 | 0,54 | más casos a mano |
| v4 | 39 | 0,603 | 0,65 | 0,52 | 3 | 0,30 | contexto adelante del state |
| v5 | 70 | 0,741 | 0,81 | 0,62 | 1 | 0,57 | casos apuntados a las fallas |
| v6 | 493 | 0,810 | 0,92 | 0,62 | 0 | 0,65 | + 423 generados, LLM etiqueta |
| v7 | 889 | 0,810 | 0,87 | 0,71 | 0 | 0,60 | + 396 generados apuntados |
Cada fila enseñó algo distinto.
v1 salió peor que el control. Porté el notebook de fine-tuning de Convai, pensado para 2 GPUs y 1200 casos, y copié sus hiperparámetros: 16 ejemplos por actualización. Con 50 ejemplos y 6 vueltas eso son 18 correcciones en total. El modelo no se equivocaba, estaba indeciso: casi todas las probabilidades pegadas a 0,5. Lección: cuando portás una receta, portá la cantidad de actualizaciones, no los números sueltos.
v3 memorizó. Con la receta corregida y 39 casos, train 1,00 en todo y test exactamente en la mayoría de clase. El alumno que estudia las respuestas del examen viejo y en el nuevo no sabe qué hacer. Un modelo de 322M con 39 ejemplos no necesita entender nada.
v4 encontró un atajo. Los fallos de v3 eran pares gemelos: mismo título, mismo detalle, etiqueta opuesta, y lo único distinto era veces_avisada al final de un JSON de 500 tokens. Puse el contexto adelante y en palabras para ayudarlo. Salió peor: aprendió “nueva y crit, sí; warn, no; repetida, no” y dejó de leer el detalle. Hacerle fácil una cosa al modelo le da permiso para ignorar el resto, cuando los datos son pocos. Con 70 casos (v5) el formato empató contra el viejo; lo dejé porque las reglas que acierta son las que menos toleran error.
v6 es donde cambió la escala. Dejé de escribir casos a mano. Mi Qwen local generó 423 señales variadas, apuntadas a los 12 patrones que fallaban, y el LLM de producción de vigía las etiquetó con su prompt real, en lotes de 6, como si fueran alertas de verdad. Antes de entrenar revisé el 10% y encontré un sesgo del teacher: callaba las recuperaciones (3 de 37 con aviso previo) y filtraba demos repetidas. Eso lo corregí por criterio antes de entrenar, con la etiqueta original guardada. Resultado: 0,81, cero críticas calladas, calibración a la mitad, y todas las reglas aprendidas (DEMO, repetida, recuperada) sin una línea de código.
v7 movió el umbral, no el criterio. 396 más, desbalanceadas hacia lo que fallaba. Recall de negativos subió de 0,62 a 0,71 y el de positivos bajó lo mismo. Accuracy clavada en 0,81 por segunda vez.
Medir al maestro
Cuando dos versiones con 1,8x de datos dan el mismo número, el problema no son los datos. Hice lo que tendría que haber hecho antes: le mandé los 58 casos de test al LLM de producción dos veces, en distinto orden.
| avisar | canal | |
|---|---|---|
| LLM contra sí mismo (dos corridas) | 0,89 | 0,83 |
| LLM contra mi criterio | 0,80 a 0,84 | 0,67 a 0,70 |
| Laya v7 contra mi criterio | 0,81 | 0,73 |
Laya es un aprendiz de ese maestro: puede ser tan buena como él en lo que le mostró, no mejor, y si el maestro se equivoca de forma sistemática aprende el error como verdad. Llegó exactamente al mismo lugar. Más rondas con este teacher no van a subir el número, van a mover errores de un lado al otro.
La prueba más clara está en el canal. De mis 13 casos que piden llamada, el LLM llama en 4. Laya llama en 3, y en los mismos. No es que Laya no aprenda a llamar: es que el prompt que tenía en producción casi nunca llamaba, y nadie se lo había enseñado. Un fallo del modelo chico que resultó ser un fallo del sistema grande, y que llevaba semanas sin que lo viera.
Los negativos que fallan en todas las versiones son también los que falla el LLM: un meeting a 40 minutos, mails mundanos, Qwen apagado por el propio switchboard con restauración automática. Casos donde hay que leer el detalle y entender que el estado es esperado.
Producción: Laya decide, el LLM redacta
Con 0,81 y cero críticas calladas, el riesgo de ponerlo a decidir era el mismo que el del LLM, y la latencia bajaba veinte veces. Lo puse en producción con tres redes:
- Fallback. Si Laya falla en cualquier señal de un tick, ese tick lo decide el LLM entero, como antes.
- Redacción con respaldo. Laya devuelve qué señales avisar, por qué canal y con qué urgencia; el LLM recibe esa lista en “modo redacción” y escribe un mensaje por aviso respetando lo decidido. Si omite alguno, sale un mensaje armado del título y el detalle. Un aviso nunca se pierde por la redacción.
- Sombra. El LLM sigue decidiendo en segundo plano, sin que nadie lo espere, y su decisión se registra al lado de la de Laya. Es el dataset de comparación que se arma solo.
Así quedó el corazón del cambio en el daemon:
decision = None
if config.laya_url and config.laya_mode == "decide":
if config.laya_shadow_llm:
asyncio.create_task(laya.sombra_llm(config, prompt))
decision = await laya.decidir_y_redactar(config, signals, new, changed, resolved, state)
if decision is None:
decision = await decide(config, prompt)
Y el primer tick real con una alerta warn por webhook:
laya: decide key=nas/zfs/snapshot-nocturno cambio=nueva veces=0 -> avisar=True (p=0.90) canal=text urgencia=med (598 ms)
dispatcher: delegado por canal=text
laya: sombra del LLM registrada (1 aviso(s), 13009 ms)
Laya decidió en 598 ms. El LLM, en sombra, llegó 13 segundos después a la misma conclusión.
Lo que sigue
- Etiquetas mías. El techo del teacher se pasa con el criterio del que sabe. Unas 100 señales etiquetadas por mí por Telegram, con tres botones, valen más que 400 del LLM. Y de paso miden el acuerdo entre el LLM y yo, que es el techo real de producción.
- Jev. Cuando abra suscripciones, entra al mismo test, con la misma tabla. Zero-shot ya funciona y aguanta 255 opciones; lo que no se puede es entrenarlo. Va a ser una comparación interesante justamente por eso.
- El prompt del LLM. Si quiero llamada para “servicio core caído” o “afecta a un cliente ahora”, eso se escribe en las reglas del decider, y el teacher cambia.
Lo honesto: 58 casos escritos por mí son direccionales, no significativos. Las señales reales pueden tener formas que ni Qwen ni yo imaginamos. Por eso la sombra.
Referencias: model card de Laya · Jev, TypeSafe AI · comparativa Laya vs Jev de Artifilog · benchmark pareado de Anthus · el post corto en LinkedIn