Jev TypeSafe AI + LangChain + Vercel AI Gateway

Artículo

Jev TypeSafe AI + LangChain + Vercel AI Gateway

La mayoría de sistemas usan un LLM para decidir cosas como clasificar, priorizar o enrutar.

jev está diseñado específicamente para tomar esas decisiones. En lugar de generar texto, evalúa un estado y devuelve una decisión tipada junto con las probabilidades de cada opción.

En las evaluaciones publicadas por TypeSafe, jev alcanzó hasta 193,6× mayor velocidad y 444,6× menor costo que los modelos de referencia.

En este artículo vas a ver cómo usar jev con LangChain a través de Vercel AI Gateway.

Dale esta skill a tu agente de código

Si quieres que tu agente de código conozca esta integración, puedes instalar la skill jev-ai-gateway-langchain.

La skill incluye la configuración de AI Gateway y ejemplos de los primitives y middlewares utilizados en este artículo.

En Claude Code:

claude plugin marketplace add dcastillogi/skills
claude plugin install jev-ai-gateway-langchain@dcastillogi
Enter fullscreen mode Exit fullscreen mode

Para otros agentes compatibles con skills.sh:

npx skills add dcastillogi/skills --skill jev-ai-gateway-langchain
Enter fullscreen mode Exit fullscreen mode

Después de instalarla, puedes pedirle al agente que configure la conexión, cree classifiers con los primitives adecuados o incorpore los middlewares en tu aplicación de LangChain.

Paso 1: entender la entrada y la salida

Una solicitud a jev tiene dos partes principales:

  • State: los datos que quieres evaluar. Puede ser un ticket, un documento, un registro o el historial de un agente.
  • Questions: las decisiones que jev debe tomar sobre esos datos.
state + questions → jev → respuestas tipadas + probabilidades
Enter fullscreen mode Exit fullscreen mode

Puedes enviar varias preguntas sobre el mismo estado. Jev las evalúa en paralelo y devuelve cada respuesta bajo el identificador que definiste.

Las preguntas de una misma solicitud son independientes. Si una decisión necesita el resultado de otra para formularse, debes hacer una segunda solicitud.

Paso 2: elegir el primitive adecuado

Antes de construir tu classifier, define qué forma debe tener cada respuesta. TypeSafe ofrece tres primitives: Noul, Choice y Score.

Primitive Tipo de decisión Resultado
Noul Evaluar una condición binaria Probabilidad entre 0 y 1
Choice Elegir una categoría Opción, distribución y confianza
Score Medir una escala ordenada Puntaje, distribución, leyenda y confianza

Noul: evaluar una condición

Noul responde qué tan probable es que una condición sea verdadera. En un sistema de soporte puedes utilizarlo para representar la urgencia de un ticket:

from langchain_typesafe import Noul, NoulCriteria

urgent_question = Noul(
    instructions="¿El ticket requiere atención inmediata?",
    criteria=NoulCriteria(
        true="El cliente está bloqueado o existe una interrupción crítica.",
        false="La solicitud puede esperar sin producir un impacto importante.",
    ),
)
Enter fullscreen mode Exit fullscreen mode

La respuesta contiene el campo noul:

probability = result.nouls["urgent"].noul
Enter fullscreen mode Exit fullscreen mode

Un valor cercano a 1 favorece true; uno cercano a 0, false. Los valores próximos a 0.5 no favorecen claramente ninguna respuesta.

Noul no incluye un campo confidence separado.

NoulCriteria es opcional. Puedes utilizarlo cuando la condición necesita límites más precisos que una pregunta breve.

Choice: elegir una opción

Choice clasifica el estado dentro de un conjunto definido por tu aplicación. Las claves son los valores que utilizará tu código y las descripciones explican cuándo corresponde cada opción.

from langchain_typesafe import Choice

department_question = Choice(
    instructions="¿Qué equipo debe atender el ticket?",
    criteria={
        "billing": "Cobros, facturas y reembolsos.",
        "technical": "Errores e interrupciones del servicio.",
        "account": "Acceso, permisos y datos de la cuenta.",
        "other": "Ninguna de las categorías anteriores.",
    },
)
Enter fullscreen mode Exit fullscreen mode

La respuesta contiene tres campos principales:

answer = result.choices["department"]

answer.choice
answer.probabilities
answer.confidence
Enter fullscreen mode Exit fullscreen mode
  • choice es la opción seleccionada.
  • probabilities contiene la distribución entre todas las opciones.
  • confidence resume qué tan concentrada está esa distribución.

Si tu lista no cubre todos los casos posibles, conviene incluir una opción como other o none_of_the_above.

Score: medir una escala

Score se utiliza cuando las respuestas tienen un orden. Por ejemplo, puedes utilizarlo para medir la frustración de un cliente:

from langchain_typesafe import Score

frustration_question = Score(
    instructions="¿Cuál es el nivel de frustración del cliente?",
    criteria=[
        "Tono neutral.",
        "Molestia expresada de forma moderada.",
        "Enojo explícito o lenguaje hostil.",
    ],
)
Enter fullscreen mode Exit fullscreen mode

La respuesta incluye:

answer = result.scores["frustration"]

answer.score
answer.probabilities
answer.legend
answer.confidence
Enter fullscreen mode Exit fullscreen mode

Los niveles se numeran desde 0. score es una media ponderada de sus probabilidades, por lo que puede ser fraccionario.

En una escala de 0 a 2, un resultado de 1.4 representa una distribución entre los niveles existentes.

Las descripciones deberían representar situaciones observables. Una escala como “sin impacto”, “función degradada” y “operación bloqueada” aporta más información que “bajo”, “medio” y “alto”.

Paso 3: conocer los cinco límites principales

Estos son los límites relevantes que publica actualmente la documentación de TypeSafe:

Área Límite
Opciones de Choice Máximo de 255
Niveles de Score Entre 2 y 10
Espacio de respuesta de Noul Binario; devuelve P(verdadero) entre 0 y 1
Contexto total 64k tokens por solicitud
state + pregunta más larga Máximo de 32k tokens

Un Choice puede manejar taxonomías amplias. Si tienes más de 255 categorías, puedes dividir la clasificación en niveles o resolverla mediante una jerarquía.

Un Score debería incluir únicamente niveles que puedan distinguirse mediante sus descripciones. Agregar niveles similares puede distribuir la probabilidad entre alternativas poco claras.

Los 64k tokens cubren el estado y todas las preguntas de la solicitud. Además, el estado combinado con la pregunta individual más larga debe permanecer dentro de 32k tokens.

Estos valores pueden cambiar, así que si vas a diseñar cargas cercanas al límite, revisa la documentación antes de hacerlo.

Paso 4: instalar la integración de LangChain

pip install "langchain-typesafe[experimental]" python-dotenv
Enter fullscreen mode Exit fullscreen mode

langchain-typesafe incluye TypeSafeClassifier. El extra experimental incorpora ModelRouterMiddleware y AutoModeMiddleware.

La versión utilizada en este proyecto es alfa. El classifier está marcado como beta y los middlewares son experimentales.

Paso 5: conectar LangChain con jev mediante AI Gateway

Por defecto, langchain-typesafe utiliza la API de TypeSafe y busca una credencial en TYPESAFE_API_KEY.

En este proyecto queremos consumir jev mediante Vercel AI Gateway. Vercel expone una API compatible con el formato de TypeSafe:

https://ai-gateway.vercel.sh/typesafe/v1/systemone
Enter fullscreen mode Exit fullscreen mode

Solo necesitas indicarle al paquete dos datos:

  1. Utilizar la credencial de Vercel como TYPESAFE_API_KEY.
  2. Utilizar https://ai-gateway.vercel.sh/typesafe como TYPESAFE_BASE_URL.

El cliente añade /v1/systemone a esa URL base. No necesitas transformar preguntas o respuestas ni intervenir el transporte HTTP.

Creamos gateway.py:

import os

from dotenv import load_dotenv

load_dotenv()

token = os.getenv("AI_GATEWAY_API_KEY") or os.getenv("VERCEL_OIDC_TOKEN")
if not token:
    raise RuntimeError("No se encontró una credencial para Vercel AI Gateway.")

os.environ["TYPESAFE_API_KEY"] = token
os.environ["TYPESAFE_BASE_URL"] = "https://ai-gateway.vercel.sh/typesafe"
Enter fullscreen mode Exit fullscreen mode

La credencial local se guarda en .env:

AI_GATEWAY_API_KEY=...
Enter fullscreen mode Exit fullscreen mode

Esta capa de configuración también se aplica a los classifiers que los middlewares construyen internamente.

Con esta configuración, langchain-typesafe queda listo para enviar solicitudes a jev mediante AI Gateway.

Ejemplo: clasificar un ticket

Ahora agrupamos los tres primitives definidos anteriormente:

import gateway  # Configura la API TypeSafe-compatible
from langchain_typesafe import TypeSafeClassifier

classifier = TypeSafeClassifier(
    questions={
        "urgent": urgent_question,
        "department": department_question,
        "frustration": frustration_question,
    }
)
Enter fullscreen mode Exit fullscreen mode

Luego enviamos el ticket como estado:

ticket = {
    "message": (
        "El sistema de pagos sigue fallando y no podemos completar ventas. "
        "Necesitamos una solución hoy."
    ),
    "customer_plan": "enterprise",
}

result = classifier.invoke(ticket)
Enter fullscreen mode Exit fullscreen mode

Las tres preguntas se envían en una sola solicitud. Puedes incorporar el resultado a una regla de negocio común:

department = result.choices["department"]
urgent = result.nouls["urgent"].noul
frustration = result.scores["frustration"]

if department.confidence < 0.5:
    route_to_manual_triage(ticket)
elif urgent >= 0.8 or frustration.score >= 1.5:
    route_to_senior_agent(ticket, department.choice)
else:
    assign(ticket, department.choice)
Enter fullscreen mode Exit fullscreen mode

Los umbrales son ilustrativos. Deberías evaluarlos con tickets representativos y definir qué hacer cuando el resultado sea incierto.

TypeSafeClassifier implementa la interfaz Runnable, por lo que también puedes utilizarlo con ainvoke, batch y dentro de otros flujos de LangChain.

Más ejemplos con LangChain

Además del classifier general, langchain-typesafe incluye middlewares para decisiones frecuentes dentro de un agente.

Seleccionar un modelo

ModelRouterMiddleware utiliza un Choice para seleccionar entre modelos ya configurados. Evalúa el último mensaje humano al inicio de la ejecución y guarda la respuesta en model_route.

from langchain.agents import create_agent
from langchain_typesafe.experimental.middleware import (
    ModelChoice,
    ModelRouterMiddleware,
)

router = ModelRouterMiddleware(
    choices={
        "fast": ModelChoice(
            model=fast_model,
            criteria="Consultas directas y cambios acotados.",
        ),
        "capable": ModelChoice(
            model=capable_model,
            criteria="Arquitectura y decisiones de alto impacto.",
        ),
    },
    instructions="Seleccione el modelo adecuado para completar la tarea.",
)

agent = create_agent(fast_model, middleware=[router])
Enter fullscreen mode Exit fullscreen mode

Evaluar una herramienta

AutoModeMiddleware evalúa una llamada inmediatamente antes de ejecutar una herramienta.

from langchain_typesafe import NoulCriteria
from langchain_typesafe.experimental.middleware import AutoModeMiddleware

guardrail = AutoModeMiddleware(
    tools=["bash"],
    instructions=(
        "¿La llamada propuesta es destructiva, irreversible o excede la "
        "autorización concedida para este sistema?"
    ),
    criteria=NoulCriteria(
        true="Elimina datos, interrumpe servicios o produce un efecto no aprobado.",
        false="Consulta información o realiza un cambio reversible y autorizado.",
    ),
)
Enter fullscreen mode Exit fullscreen mode

La política debe indicar qué operaciones requieren aprobación. Esta evaluación complementa los controles de acceso y aislamiento de la aplicación.

Supervisar el ciclo del agente

También puedes ejecutar un classifier después de cada respuesta:

from langchain.agents.middleware import after_model

@after_model
def supervise(state, runtime) -> None:
    result = supervisor.invoke(state["messages"])

    if result.nouls["escalate"].noul >= 0.8:
        record_handoff(result)
Enter fullscreen mode Exit fullscreen mode

La integración acepta mensajes de LangChain como state, por lo que puedes evaluar el historial sin conversión manual.

Referencias

© 2026 Daniel Castillo. Todos los derechos reservados.
DC
Daniel Castillo
AI Solutions Engineer @Blend360