Correr el juez local con Laya
El slot del juez es un protocolo, no un proveedor. Desde la v0.6.27 un checkpoint de Laya en disco — ModernBERT, Apache 2.0 — responde whether, choose y rate sin red, sin secreto y sin costo por token, y el mismo bloque corre sin cambiar una línea.
Un bloque judge hace preguntas tipadas sobre un estado y recibe probabilidades calibradas: ¿es esto cierto? (0,86), ¿cuál de estas? (billing, y la distribución), ¿dónde en esta escala? (entre upset y furious). Hasta la v0.6.27 eso quería decir una cuenta en TypeSafe y una llamada a Jev, o el provider mock, que te da una forma sin significado.
Los binarios oficiales traen ahora un tercer backend. Un checkpoint de Laya en disco responde los mismos tres verbos sin red, sin secreto y sin costo por token:
SYNSEMA_JUDGE_PROVIDER=laya
SYNSEMA_JUDGE_MODEL=/models/laya # el directorio del checkpoint, o un org/repo ya en la caché de HF
Nada más cambia. El programa no sabe qué backend respondió, porque el slot del juez siempre fue un protocolo, no un proveedor.
Qué es Laya§
Laya es un modelo de decisión no autorregresivo de Convai Innovations, Apache 2.0: un backbone ModernBERT-large (421M de parámetros) con una cabeza de decisión, que toma un estado y preguntas tipadas y las responde en una sola pasada. Es lo mismo que es Jev — un modelo System One — desde otro lado, con pesos que podés poner en un disco tuyo.
Sus tres tipos de pregunta mapean a los tres verbos del bloque:
| Synsema | Laya | Qué leés |
|---|---|---|
whether "…" | noul | probability |
choose "…" between {…} [or nothing] | choice | choice, probabilities, confidence |
rate "…" across […] | score | score, level, levels, probabilities |
noul es el nombre que le da el upstream a una pregunta de sí o no; el lenguaje se queda con whether, porque adoptar el glosario de un proveedor es la forma en que un protocolo se convierte en una dependencia.
No se descarga nada por vos: el checkpoint pesa ~843 MB y bajarlo es una decisión, como un .gguf. Si no está, el error dice cómo conseguirlo.
El mismo bloque, sin clave§
Éste es el programa de la entrada de Jev — sin cambiar un byte — con dos variables de entorno distintas:
require judge
let ticket be {"subject": "Payouts failing", "messages": [
{"from": "customer", "text": "Help! My payouts have been failing for 3 days and nobody
answers. I want my money back NOW or I'm cancelling."}
]}
let v be judge ticket
refund: whether "The customer is asking for money back"
team: choose "Which team should handle this?" between {
"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations"
} or nothing
anger: rate "How frustrated is the customer?" across {
"calm": "Polite, no complaint",
"upset": "Repeat contact, asks for a fix soon",
"furious": "Caps, threats to cancel, demands immediate action"
}
when confidence of v.team < 0.8
print("gate: human")
otherwise
print("gate: auto")
En una laptop sin GPU:
available: true
refund.probability: 0.8596
team.choice: billing
team.probabilities: {billing: 0.7621, technical: 0.0850, none: 0.1529}
team.confidence: 0.3595
anger.level: furious
anger.score: 1.7154
model: laya:/models/laya
usage: 288
gate: human
Leé dos veces la cuarta línea desde abajo. El juez eligió billing y tiene razón, pero la masa está repartida — 0,36 de confianza — así que la compuerta que ya habías escrito manda el ticket a una persona. Ése es todo el motivo para querer una probabilidad en vez de un string, y funciona igual contra un checkpoint en un directorio tuyo.
Cinco preguntas en dos bloques tardaron 7,9 s de reloj, carga del modelo incluida, y una sola pregunta tarda 2,5 s de punta a punta en un proceso nuevo. Tres corridas dieron números idénticos. usage sigue contando tokens de entrada — sólo que no hay factura detrás.
decide también, y sin nada de red§
Si tu programa ya usa decide between […] given x, SYNSEMA_JUDGE_DECIDE=1 hace que cada decide del proceso lo responda el juez como un choose calibrado — una de tus opciones byte por byte, sin normalización y sin reintento. Con laya detrás, eso es una decisión que nunca sale de la máquina:
$ synsema run --cap-set judge,llm,stdout triage.syn
decide: refund
model: laya:/models/laya
available: true
Ningún net en el conjunto de capacidades. Nada que denegar, porque no hay nada a quien llamar.
Qué está verificado y qué no§
La secuencia que ve un modelo System One es la capa donde un error no falla: los marcadores corren igual, el modelo simplemente puntúa otra cosa, y la respuesta se ve tan verosímil como una correcta. Así que es la parte que tiene un test permanente.
[CLS] <tipo> question: <instrucciones> [SEP] [MASK] opción0 [MASK] opción1 … [SEP] <estado> [SEP]
Los [MASK] son marcadores — el modelo puntúa cada posición, y ese puntaje es la preferencia por esa opción — así que el orden y la posición exacta importan. Contra la implementación de referencia con el tokenizador real: 96 tokens idénticos y marcadores en [12, 22, 32, 39]. Eso descarta todo lo que escribimos alrededor del modelo: el tokenizador, el render de las opciones, la serialización del estado, los recortes de presupuesto.
Lo que no está cerrado, y hay que decirlo en voz alta: sobre el caso publicado en el README del upstream obtenemos el mismo argmax (billing) con confianza 0,864 donde el README reporta 0,94. Nuestra aritmética es correcta dada la distribución que observamos, y los tokens de entrada son idénticos, así que la diferencia está en los números que salen del encoder, o el valor publicado no es comparable (otra versión del checkpoint, u otro checkpoint servido por su Router). Cerrarlo exige correr la implementación del upstream, que pide PyTorch.
Lo que lleva a lo que hay que hacer antes de mover un umbral de un backend al otro: medirlo de nuevo. Una compuerta en 0,9 es un número ajustado contra un modelo concreto. Jev y Laya son modelos distintos; la forma de la respuesta es la misma, la calibración no necesariamente.
Los otros límites, todos juntos§
- 512 tokens de contexto (leídos de la propia config del checkpoint). El prefijo — tipo de
pregunta, instrucciones, marcadores de opción — se lleva hasta 192, y el estado se trunca a lo que queda. Un ticket largo pierde la cola, así que poné primero lo que importa, o juzgá un campo en vez del objeto entero.
- CPU, en segundos, no en milisegundos. Los ~33 ms de la tarjeta del modelo no son lo que hace
una laptop. La carga se paga una vez por proceso; bajo serve los requests después del primero la reusan.
- Corre en
f32aunque el checkpoint seaf16, porque en CPU la media precisión se emula y sale
más lenta. Cuesta ~1,7 GB de RAM con el modelo cargado.
- La cabeza de acción/escalamiento no está implementada a propósito. Es una rama paralela que no
toca los logits — la respuesta es idéntica con o sin ella — y el contrato de judge no expone una probabilidad de acción, así que sería aritmética que nadie puede leer.
Qué cambia sobre estar offline§
Cada respuesta de judge trae available, y sin provider vuelve con available: false, confidence: 0 y su valor principal en nothing — nunca un número inventado. Esa degradación es deliberada y se queda. Lo que cambia en la v0.6.27 es que estar offline pasa a ser una elección y no un destino: un programa con require judge, sin secreto y sin red obtiene probabilidades calibradas reales de un archivo en disco.
synsema judge status muestra sólo lo que le aplica al backend elegido — con laya, el checkpoint y nada más:
Provider laya (SYNSEMA_JUDGE_PROVIDER, environ)
Checkpoint /models/laya (SYNSEMA_JUDGE_MODEL, environ)
Budget (sin techo) (SYNSEMA_JUDGE_BUDGET, default)
decide LLM (default) (SYNSEMA_JUDGE_DECIDE)
Estado: ✅ VIVO — los bloques `judge` corren LOCAL contra el checkpoint: sin red, sin clave y sin
costo por token.
Exit 0 vivo, 1 offline, sin tocar la red — así que synsema judge status && synsema serve app.syn sigue siendo una compuerta de despliegue. Y judge_model() devuelve laya:<checkpoint>, así que dos corridas con pesos distintos nunca se parecen en un log.
Cuál usar§
typesafe (Jev) | laya | mock | |
|---|---|---|---|
| Dónde | la API del proveedor | un checkpoint en tu disco | en ningún lado |
| Necesita | una clave | ~843 MB y RAM | nada |
| Responde | juicios reales | juicios reales | una forma sin significado |
| Sirve para | producción a escala, estados largos | aislado de la red, on-prem, CI con números reales, sin proveedor | tests |
mock y laya responden preguntas distintas. mock va en CI, donde un checkpoint no tiene nada que hacer. laya es para cuando querés un juicio real y no podés — o no querés — mandar el estado a ningún lado.
La página del manual es Judge; el bloque en sí, verbo por verbo, está en Cómo usar Jev desde Synsema; y la mitad generativa del mismo release — un modelo en tu proceso, arquitecturas como archivos — está en Un modelo que ya tenés.