Laya: decisiones tipadas estilo Jev con encoders que corren en local
En pocas palabras
Sección titulada «En pocas palabras»Laya es una alternativa de código abierto a Jev, el modelo System One de TypeSafe. Igual que Jev y que Kev, no genera texto: recibe un estado (un ticket, un correo, un JSON) y un conjunto de preguntas tipadas, y devuelve para cada una un valor estructurado con su distribución de probabilidad.
- Corre en tu máquina: los datos no salen de ahí.
- Tres tipos de pregunta:
choice(elegir una opción),score(una nota en una escala) ynoul(probabilidad de que la respuesta sea “sí”). - Una sola pasada hacia delante para todas las preguntas: unos 33 ms por pregunta en una GPU T4, según el README.
- Más de 100 idiomas, con un router que elige el checkpoint según el idioma del texto.
- Se puede entrenar con tus datos: el fine-tuning es donde está la mayor parte de la precisión.
- Servidor HTTP compatible con Jev: un cliente que ya habla con Jev solo necesita cambiar la URL base.
Un detalle de nombre: la pregunta de sí/no se llama noul, no null. Es el término que usa la API de Jev y Laya lo conserva para ser compatible.
Qué es un modelo “no autorregresivo”
Sección titulada «Qué es un modelo “no autorregresivo”»Un LLM normal es autorregresivo: genera la respuesta token a token, y cada token depende de los anteriores. Para clasificar algo hay que pedirle “devuelve JSON”, esperar el texto y parsearlo, con el riesgo de que invente una categoría o rompa el formato.
Laya es un encoder (de la familia de BERT): lee todo el texto de una vez y una cabeza de decisión puntúa cada opción en la misma pasada. Como no escribe nada, no hay texto que parsear ni categorías inventadas: la respuesta siempre es una de las opciones que tú definiste.
Los checkpoints
Sección titulada «Los checkpoints»| Checkpoint | Encoder | Parámetros | Contexto | Para qué |
|---|---|---|---|---|
laya | ModernBERT-large | 421M | 512 tokens | Inglés |
laya-multilingual | mmBERT-base | 322M | 1.024 (hasta 8.192) | Más de 100 idiomas, unas 2 veces más rápido |
laya-typed-decisions | ModernBERT-large | 421M | 1.024 tokens | Ajustado a los flujos del benchmark de decisiones tipadas |
El Router detecta el alfabeto y el idioma del estado y envía el texto en inglés a laya y el resto a laya-multilingual. Se puede forzar un checkpoint con model= o dar una pista de idioma con lang= / lang_guess=.
Los modelos se entrenan con RLCD: aprendizaje por refuerzo con recompensas basadas en reglas de puntuación estrictamente propias, la técnica que busca que una probabilidad de 0,8 signifique de verdad “acierta 8 de cada 10 veces”.
Instalación
Sección titulada «Instalación»pip install layaExtras opcionales según lo que necesites:
pip install "laya[serve]" # servidor HTTP compatible con Jevpip install "laya[mcp]" # servidor MCP para agentespip install "laya[langchain]" # integración con LangChain y LangGraphTambién hay extras para LlamaIndex, CrewAI, ONNX Runtime y una ruta rápida en GPU con TileLang, y un paquete para Node.js y navegador (laya-ts).
Uso desde Python
Sección titulada «Uso desde Python»from laya import Router
router = Router() # descarga el checkpoint la primera vez
state = "Hi, we were billed twice for March. Please refund the duplicate today or we will cancel our plan."questions = { "department": {"type": "choice", "instructions": "Which department should handle this?", "criteria": {"billing": "invoices, payments, refunds", "technical": "bugs, outages, system errors", "other": "everything else"}}, "urgency": {"type": "score", "instructions": "How urgent is this?", "criteria": ["not urgent", "soon", "blocking"]}, "churn_risk": {"type": "noul", "instructions": "Does the user threaten to cancel or leave?"},}
result = router.predict(state, questions)print(result["answers"]["department"]["choice"]) # billingprint(result["answers"]["churn_risk"]["noul"]) # probabilidad de "sí"print(result["routing"]["model"]) # englishLa misma llamada con un texto en español o en hindi se enruta sola a laya-multilingual.
Desde la terminal
Sección titulada «Desde la terminal»El paquete instala un comando laya:
laya "I was charged twice, please refund" # solo la decisión de enrutado, sin descargar nadalaya "My payment failed twice" --predict # respuestas completaslaya "My payment failed twice" --preset triage # conjunto de preguntas predefinidolaya "Where is my card" --questions intents.json # tus propias preguntas en un JSONcat tickets.txt | laya --batch - --predict --json # un lote desde stdinLos presets incluidos cubren cinco casos: router entre modelos pequeños y frontera, guardarraíles de prompts (jailbreaks e inyecciones), moderación de contenido, triaje de tickets y triaje de correo.
Servidor compatible con Jev
Sección titulada «Servidor compatible con Jev»pip install "laya[serve]"LAYA_DEVICE=cuda LAYA_PRELOAD=1 laya-serve # escucha en 0.0.0.0:8000curl -s localhost:8000/v1/systemone -H 'content-type: application/json' -d '{ "state": {"body": "billed twice, refund please or we cancel"}, "questions": {"dept": {"type": "choice", "instructions": "which team?", "criteria": {"billing": "refunds", "tech": "bugs"}}}}'Expone POST /v1/systemone y /v1/systemone/batch (hasta 64 estados) con el mismo protocolo que la API alojada de Jev. Con LAYA_API_KEY exige un token bearer. Al migrar un cliente de Jev cambian tres cosas:
- Opciones por pregunta: Jev acepta hasta 255; Laya reparte un presupuesto de tokens entre ellas y el servidor corta en 100.
- Niveles de
score: todos necesitan descripción; un nivelnulldevuelve 422. confidencese calcula con otra fórmula (1 menos la entropía normalizada), así que un umbral que funcionaba en Jev no sirve tal cual. Para un número calibrado en todos los tipos, usaanswer_confidence.
Para agentes, laya-mcp-server expone las mismas decisiones como herramientas MCP (laya_predict, laya_route, laya_preset…).
Entrenarlo con tus datos
Sección titulada «Entrenarlo con tus datos»Este es el punto que más pesa. En el benchmark de decisiones tipadas (2.000 decisiones en cuatro flujos de trabajo):
| Modelo | Precisión |
|---|---|
| Elección aleatoria | 0,318 |
laya base, sin ajustar | 0,362 |
| Clase mayoritaria | 0,461 |
| Jev (cifra publicada por TypeSafe) | 0,727 |
laya-typed-decisions (ajustado) | 0,766 |
Es decir: los modelos base no aportan casi nada en ese benchmark; toda la capacidad viene del ajuste. El repositorio trae un notebook para Kaggle (dos T4 gratuitas, unas 4 a 5 horas para 4 épocas sobre ~30.000 preguntas) y un script para Apple Silicon que hace todo el ciclo: preparar el dataset, entrenar con RLCD, calibrar temperaturas, evaluar y exportar.
python notebooks/laya_finetune_typed_decisions_mps.py --micro-batch 1 --grad-accum 32Laya, Kev y Jev
Sección titulada «Laya, Kev y Jev»| Jev | Kev | Laya | |
|---|---|---|---|
| Tipo | API alojada, sin pesos abiertos | LoRA sobre Qwen (0.8B a 27B) | Encoders BERT (322M y 421M) |
| Dónde corre | Servidores de TypeSafe | Tu GPU o Mac | CPU, GPU o Mac |
| Latencia | 236–276 ms p50 medidos por terceros | Depende del tamaño | ~33 ms por pregunta en T4 |
| Opciones por pregunta | Hasta 255 | Hasta 255 | Bien hasta ~20; tope de 100 en el servidor |
| Sin ajustar | Listo para usar | Bastante preciso desde 4B | Cerca del azar en decisiones tipadas |
Laya gana en coste de hardware y velocidad; a cambio, exige entrenarlo para tu dominio. Si no quieres entrenar nada, Jev alojado o un Kev de 4B en adelante son mejores puntos de partida.
Límites
Sección titulada «Límites»- Etiquetas booleanas: en preguntas
choice, claves comotrue/falseoyes/nopueden confundir al modelo. Usa etiquetas con significado o neutras (A/B). - Negaciones: en los ejemplos del issue #377, “no quiero cancelar” se clasificó como
cancel_accounten varios casos, incluso con probabilidad 0,9998. Valida frases negadas con tus datos. - Calibración: el README reconoce que ambos checkpoints salen demasiado seguros de sí mismos y que
laya-multilingualno trae temperaturas ajustadas. Calibra antes de fijar umbrales. - Documentos largos:
laya-multilinguallee hasta 8.192 tokens conmax_len=8192, pero por encima de ~4.000 tokens la precisión varía mucho.