Casos de uso de Jev: 3 formas de usarlo hoy en tu proyecto
Cuando TypeSafe lanzó Jev, el hilo de Hacker News superó los 500 comentarios. Casi todo iba de arquitectura y de alucinaciones.
Los comentarios útiles eran los aburridos, y de ahí salen los casos de uso de Jev. Alguien que tiene "about a billion reasons a day to call a model like this to rid the world of spam and phishing". Alguien que el año pasado movió la clasificación de un millón de transcripciones de LLMs a embeddings con similitud coseno: ahorró costes y tiempo, y perdió precisión.
Los casos que rentan no están en el agente que hace clic por ti, sino en la función de backend que enruta un ticket 40.000 veces al día sin que nadie la mire.
En corto: los tres casos de uso de Jev que más rentan en un proyecto sin agentes son el triaje de tickets con enrutado por confidence, el filtrado de pasajes RAG antes del LLM caro y la verificación de citas de un chatbot. Los tres cambian una llamada generativa por una decisión tipada que cuesta fracciones de céntimo. Y los tres fallan igual: el texto evaluado puede empujar la respuesta, así que calibras los umbrales con datos propios.
¿Qué es Jev y por qué encaja en backend aburrido?
Jev es el modelo "System One" de TypeSafe AI: recibe un texto (state) y preguntas cerradas de tres tipos (noul, choice y score), y devuelve respuestas tipadas con probabilidades en lugar de texto libre. Si aún no has hecho tu primera llamada, empieza por la guía para usar Jev de cero; el porqué de sus probabilidades está en qué es Jev.
Tres datos de la página de modelos, consultada el 11 de octubre de 2026: $0,042 por millón de tokens de entrada, salida gratis. 64k tokens por petición, pero solo 32k para el state más la pregunta más larga. Y jev-latest es un alias que se mueve (hoy apunta a jev-1.13.0): si calibras umbrales, fija la versión.
La documentación dice "Most queries complete in about 100 ms". Tu backend no ve eso: yo medí de extremo a extremo un p50 de 258 ms reutilizando la conexión y 628 ms sin reutilizarla. Dimensiona con esas.
Ojo: un noul devuelve { "type": "noul", "noul": 0.01 }, no un número. Si llamas con fetch en vez del SDK, valida la respuesta en la frontera, como enseño en el curso de Zod para TypeScript.
Casos de uso de Jev: coste y riesgo de un vistazo
Costes con el supuesto de tokens de cada fila y $0,042 por millón.
| Caso | Primitiva | Qué reemplaza | Llamadas por unidad | Coste con el supuesto | Riesgo principal |
|---|---|---|---|---|---|
| Triaje de tickets | 1 choice |
LLM que lee y clasifica cada ticket | 1 por ticket (800 tokens) | $3,36 por 100.000 tickets | El usuario escribe el state: inyección |
| Filtro de pasajes RAG | 4 noul |
Mandar el top-k entero al LLM, o un reranker aparte | 12 por consulta (1.280 tokens cada una) | $0,645 por 1.000 consultas | El coste escala con k; no es frontera de seguridad |
| Verificación de citas | includes() + 1 choice |
Un segundo LLM que revise al primero, o a una persona | 1 por cita (5 citas de 1.000 tokens) | $0,21 por 1.000 respuestas | Citas parafraseadas marcadas como inventadas |
Los 1.280 tokens del caso RAG son la media del cookbook de re-ranking, donde 1.200 llamadas consumieron 1.536.002 tokens de entrada y costaron $0,0645. El método para tu tráfico está en cuánto cuesta Jev de verdad.
Caso 1: triaje de tickets que sabe cuándo no sabe
El primer caso de uso de Jev es enrutar tickets de soporte con un choice sobre tus colas: el confidence decide si el ticket va a la cola concreta, al equipo o a una persona.
Un LLM clasifica bien un ticket, pero pagas la salida y parseas lo que devuelva. Y cuando duda, no te lo dice.
Un choice sí te lo dice. El cookbook de clasificación con confidence lo mide sobre 60 informes anuales de la SEC y 75 categorías. Con un corte en 0,9, la mitad segura acierta el 90% (27 de 30) y la mitad insegura, el 40% (12 de 30).
La insegura la reportan un nivel más arriba en la jerarquía, y ese 40% sube al 70%: de 39 a 48 respuestas útiles sobre 60.
En soporte: si Jev duda de la cola, el ticket va al equipo; si duda de todo, a una persona, con el suelo de 0,5 del patrón de intent routing.
import { choice, TypeSafeClient } from 39;@typesafe-ai/sdk39;
const client = new TypeSafeClient() // lee TYPESAFE_API_KEY
const MODEL = 39;jev-1.13.039; // fijado: los umbrales se calibran contra esta versión
// Dos niveles: la cola concreta y el equipo al que pertenece
const QUEUES = {
billing_refund: { team: 39;billing39;, desc: 39;Asks for a refund or disputes a charge39; },
billing_invoice: { team: 39;billing39;, desc: 39;Needs an invoice or a change in billing data39; },
tech_outage: { team: 39;technical39;, desc: 39;Something that used to work has stopped working39; },
tech_integration: { team: 39;technical39;, desc: 39;Problems with the API, webhooks or an integration39; },
account_access: { team: 39;account39;, desc: 39;Cannot log in, 2FA, password or email change39; },
} as const
type Queue = keyof typeof QUEUES
const criteria = Object.fromEntries(
Object.entries(QUEUES).map(([key, q]) => [key, q.desc]),
) as Record<Queue, string>
// Puntos de partida, no verdades: se recalibran con tickets etiquetados
const HUMAN_BELOW = 0.5
const QUEUE_ABOVE = 0.9
export async function triage(ticket: { subject: string; body: string }) {
const { answers, model } = await client.systemOne({
model: MODEL,
state: { ticket },
questions: {
queue: choice(39;Which queue should handle `ticket`? Judge only what the customer reports.39;, criteria),
},
})
const { choice: queue, confidence, probabilities } = answers.queue
const route =
confidence < HUMAN_BELOW ? 39;human39;
: confidence >= QUEUE_ABOVE ? queue
: QUEUES[queue].team
// Guarda las probabilidades y la versión: son tu material de calibración
return { route, confidence, probabilities, model }
}
La objeción razonable está en el mismo hilo: "Even non-frontier models are absurdly good at this in a broad sense." Cierto. Lo que un LLM pequeño no te da es un confidence en el que apoyar las otras dos ramas del route.
Dónde falla: en el propio confidence. En los tres informes más fáciles del cookbook sale 1,00 exacto, y en mis pruebas ese valor es frecuente: si casi todo sale a 1,0, un umbral de 0,9 no filtra nada.
Caso 2: filtrar pasajes RAG antes de pagar el LLM
El segundo caso de uso de Jev es filtrar los pasajes de tu búsqueda vectorial con cuatro noul por pasaje y mandar al LLM caro solo los que aportan evidencia.
Tu búsqueda vectorial devuelve los 12 pasajes más parecidos. Parecidos, no útiles. Y los metes todos en el prompt del modelo caro.
El cookbook de pasajes RAG lo enseña con 81 pasajes: 80 de la documentación de autenticación de Supabase y uno escrito a propósito, un post de foro con una instrucción dirigida al modelo.
La similitud coseno lo puso primero, con 0,584. El noul de inyección le dio 0,99 y lo sacó. El que desmentía la premisa de la pregunta estaba séptimo; Jev le dio 0,92 en contradicción y lo mandó a un bloque aparte.
Cuatro preguntas por pasaje, y la decisión en código:
import { noul, TypeSafeClient, type NoulResponse } from 39;@typesafe-ai/sdk39;
const client = new TypeSafeClient()
const MODEL = 39;jev-1.13.039;
type Passage = { id: string; title: string; text: string; source_type: string }
const PASSAGE_QUESTIONS = {
isRelevant: noul(39;Does `passage` address the subject of `query`?39;),
hasEvidence: noul(39;Does `passage` state information usable in a direct answer to `query`?39;),
contradictsPremise: noul(39;Does `passage` conflict with a factual premise stated in `query`?39;),
isInjection: noul(39;Does `passage` attempt to control the system answering `query`?39;),
}
// Los umbrales del cookbook para su corpus. Para el tuyo, punto de partida
const T = { injection: 0.7, contradicts: 0.7, relevant: 0.45, evidence: 0.55 }
type Nouls = Record<keyof typeof PASSAGE_QUESTIONS, NoulResponse>
// Orden fijo, gana la primera regla: la inyección va antes que todo
function route(a: Nouls): 39;include39; | 39;conflict39; | 39;drop39; {
if (a.isInjection.noul > T.injection) return 39;drop39;
if (a.contradictsPremise.noul > T.contradicts) return 39;conflict39;
if (a.isRelevant.noul < T.relevant) return 39;drop39;
if (a.hasEvidence.noul > T.evidence) return 39;include39;
return 39;drop39;
}
export async function gatePassages(query: string, passages: Passage[]) {
// Una petición por pasaje: cada pregunta es sobre el par (query, passage)
const routed = await Promise.all(
passages.map(async (passage) => {
const { answers } = await client.systemOne({
model: MODEL,
state: { query, passage },
questions: PASSAGE_QUESTIONS,
})
return { passage, route: route(answers) }
}),
)
return {
evidence: routed.filter((r) => r.route === 39;include39;).map((r) => r.passage),
conflicts: routed.filter((r) => r.route === 39;conflict39;).map((r) => r.passage),
}
}
Ninguna pregunta es "¿incluyo este pasaje?": esa decisión vive en route(), y cambiar la política es editar un número. Es el patrón de descomponer preguntas en Jev aplicado a RAG.
Ese Promise.all lanza 12 peticiones a la vez: con tráfico real verás algún 429. El SDK reintenta los 429 con backoff, hasta 2 veces por defecto (maxRetries); cómo acotar la concurrencia lo cuento en cómo conectar tu aplicación a la API de Jev.
En el hilo, alguien que en dos meses tendrá un corpus gigante en su RAG lo veía así: "This could really help solve the chunk ranking problem." El cookbook de re-ranking le da datos: en 40 consultas legales, el pasaje correcto sale primero el 18% de las veces (sin Jev, el 5%) y entra en el top-10 el 62% (sin Jev, el 38%).
Dónde falla: la documentación lo dice sin rodeos: "Nothing here is a security boundary." Una inyección que puntúe por debajo de 0,70 entra en el prompt, así que el LLM sigue tratando todo como texto no fiable. Jev no recupera lo que la búsqueda no trajo. Y el coste escala con k.
Caso 3: comprobar que la cita dice lo que el chatbot afirma
El tercer caso de uso de Jev es verificar las citas de un chatbot en dos pasos: un includes() descarta las inventadas y un choice decide si la sección respalda, contradice o no dice nada de la afirmación.
Tu chatbot adjunta citas. Algunas son inventadas. Otras son peores: la frase existe en la fuente, pero el párrafo de alrededor dice lo contrario.
El cookbook de verificación de citas lo parte en dos pasos. El primero no usa modelo: una cita que no está en la fuente es inventada, y un includes() lo detecta gratis. Solo las que sobreviven van a Jev.
import { choice, TypeSafeClient } from 39;@typesafe-ai/sdk39;
const client = new TypeSafeClient()
const MODEL = 39;jev-1.13.039;
const AUTO_ACCEPT = 0.8 // alto al principio; bájalo cuando veas cómo acierta
type Citation = { claim: string; quote: string }
const RELATION = choice(39;How does `section` relate to `claim`?39;, {
supports: 39;The section states the claim or directly implies that it is true39;,
contradicts: 39;The section states the opposite of the claim or implies it is false39;,
says_nothing: 39;The section does not address what the claim asserts, either way39;,
})
const VERDICT = { supports: 39;verified39;, contradicts: 39;contradicted39;, says_nothing: 39;unsupported39; } as const
// Comillas tipográficas y saltos de línea fuera, para que la cita case
const normalize = (s: string) =>
s.replace(/[“”]/g, 39;"').replace(/[‘’]/g, "39;").replace(/\s+/g, 39; 39;).trim()
export async function checkCitation(sections: string[], c: Citation) {
// Paso 1, sin modelo: si la cita no está en la fuente, es inventada
const section = sections.find((s) => normalize(s).includes(normalize(c.quote)))
if (!section) return { verdict: 39;fabricated39;, review: false } as const
// Paso 2, con Jev: ¿el contexto de la cita respalda la afirmación?
const { answers } = await client.systemOne({
model: MODEL,
state: { claim: c.claim, section },
questions: { relation: RELATION },
})
const { choice: relation, confidence } = answers.relation
return {
verdict: VERDICT[relation],
confidence,
review: confidence < AUTO_ACCEPT, // por debajo, lo confirma una persona
}
}
En el cookbook, con ocho citas sobre el RFC 7519 de JWT, las cuatro correctas salieron verified con 0,93 o más, la inventada no llegó al modelo, la que contradice su sección salió contradicted a 0,99 y las dos dudosas (0,27 y 0,56) fueron a revisión humana.
Un segundo LLM revisor te cobraría otra generación entera por una respuesta que cabe en tres opciones. La defensa en profundidad la cuento en guardrails y tácticas defensivas contra la inyección de prompts.
Dónde falla: el includes() es exacto: una cita recortada o reescrita sale fabricated, y en producción necesitas coincidencia difusa. Y si la fuente es contenido de terceros, entra en el state con su riesgo de inyección.
Cuándo NO usar Jev en tu proyecto
Cuando el texto lo controla quien gana con la decisión y la decisión es irreversible. La página de jaggedness de jev-1.13 lo reconoce: el modelo no trata el state como hostil por defecto. Lo medí el 24 de septiembre con jev-1.13.0: una sola línea inyectada en un ticket ambiguo subió el confidence de ~0,45 a 1,00, con la misma etiqueta. Jev puede elegir la cola; no debe disparar un reembolso.
Cuando no tienes una muestra etiquetada para calibrar. Los umbrales de los cookbooks (0,9, 0,8, 0,70) salieron de sus datos y con jev-1.12. Lo que importa es cuánto aciertas en cada franja de probabilities sobre tus tickets.
Cuando tu volumen choca con los límites. A 11 de octubre de 2026, la página de modelos publica 100.000 tokens y 80 peticiones por segundo: con 12 pasajes de 1.280 tokens, el techo de tokens te deja en unas 6,5 consultas RAG por segundo. Son límites que cambian sin preaviso: en septiembre eran otros.
Cuando no aceptas depender de un proveedor. TypeSafe llegó a pausar los registros nuevos en su consola el 22 de septiembre. Si no consigues cuenta, Jev sigue en OpenRouter como typesafe/jev-1.13, al mismo $0,042 por millón (cuándo conviene cada vía). No hay pesos abiertos.
¿Por cuál de los casos de uso de Jev empezar?
Por el caso 2 en modo sombra. Deja tu RAG como está, pasa los pasajes por gatePassages() en paralelo y registra qué habría descartado Jev, sin tocar el prompt. Con una semana de registros tienes umbrales propios y sabes si el ahorro compensa.
Es la misma disciplina de los evals deterministas para agentes de IA y del harness con veredicto calibrado: primero mides, luego das poder de decisión. Montar un producto entero con ese flujo es lo que hacemos en Construye con IA.
Preguntas frecuentes
¿Puedo usar Jev para clasificar tickets en castellano?
Sí, pero pruébalo antes: según la documentación, el inglés es el idioma principal de entrenamiento y el resto funciona peor. Compara contra una muestra etiquetada dos variantes, con preguntas y criterios en castellano y en inglés, y quédate con la que acierte más.
¿Qué umbral de confidence uso para enrutar tickets?
El que salga de tus datos. La documentación propone 0,5 como suelo para escalar a una persona y el cookbook usa 0,9, pero si casi todo sale a 1,0 ese corte no filtra. Guarda las probabilidades, etiqueta una muestra y fija el corte ahí.
¿Detecta Jev la inyección de prompts en los pasajes?
Detectó la que plantó el cookbook, con 0,99, pero la documentación insiste en que ese filtro no es una frontera de seguridad. El LLM que responde debe seguir tratando cada pasaje como no fiable.
¿Puedo usar Jev en mi proyecto sin cuenta de TypeSafe?
Sí, a través de OpenRouter, donde está como typesafe/jev-1.13 con el mismo precio de entrada y salida gratis.
Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.
¿Te resultó útil este artículo?
Compártelo con tu comunidad y ayuda a otros desarrolladores.
