Inicio / Artículos / Arquitectura de servicio de modelos: por qué la API es solo la puerta de entrada

Arquitectura de servicio de modelos: por qué la API es solo la puerta de entrada

Una guía de la plataforma que hay detrás de un endpoint de inferencia: enrutamiento, escalado, versionado, observabilidad, seguridad y cómo llamar a modelos servidos desde Spring AI o LangChain4j.

2004 palabras

Piensa en lo que ve un comensal en un restaurante: alguien toma la comanda, desaparece hacia la cocina y vuelve con un plato. La parte que no se ve importa más. Hay que coordinar pedidos, revisar existencias, los cocineros preparan los platos, se atienden muchas mesas a la vez y cada plato tiene que llegar a la persona correcta.

Servir un modelo de machine learning tiene la misma forma. Desde fuera, una aplicación ve un endpoint HTTP que recibe una petición y devuelve una predicción. Detrás puede haber enrutamiento, autenticación, obtención de features, preprocesamiento, el propio runtime del modelo, gestión de recursos, selección de versión, monitorización y gestión de fallos. El endpoint es el camarero. La arquitectura de servicio es el restaurante.

Salir del notebook

Un modelo puede comportarse de maravilla en un notebook y aun así ser un quebradero de cabeza en operación en cuanto otros sistemas dependen de él. Durante el desarrollo, una científica de datos carga el modelo, prepara las entradas, ejecuta la inferencia y revisa los resultados, todo en un entorno controlado. Producción rompe cada una de esas comodidades. Varias aplicaciones piden predicciones al mismo tiempo, el tráfico sube y baja, hay que cambiar versiones sin interrumpir a quien llama, y una respuesta puede depender de datos que pertenecen a otros servicios.

En ese momento el modelo deja de ser un artefacto aislado y pasa a ser un componente de un sistema distribuido. La pregunta cambia de cómo llamarlo a cómo ofrecer la inferencia como un servicio de producción fiable.

La API es la puerta de entrada, nada más

Una API da a las aplicaciones una forma controlada de pedir predicciones, pero es una pequeña parte del conjunto. Imagina un pago que pasa por una app de banca online. Antes de que vuelva una decisión, puede que haya que autenticar la petición, enriquecerla con features del cliente, validarla, preprocesarla, asignarle una versión del modelo, puntuarla y después postprocesarla.

Cada uno de esos pasos puede cambiar el resultado. El modelo puede responder en milisegundos mientras la obtención de features se arrastra. Un modelo sano puede quedar inaccesible porque el cómputo que tiene debajo se ha agotado. Una versión nueva del modelo puede comportarse distinto sin que el contrato de la API cambie en absoluto.

Así que trata la API como el punto de entrada a un sistema de servicio, igual que el camarero representa la cara visible del restaurante.

Qué pasa en una sola inferencia

Una petición de producción suele atravesar varias responsabilidades antes de que la respuesta llegue a la aplicación de negocio. Un gateway puede encargarse de autenticación, autorización, enrutamiento y límites de tasa. Luego la capa de servicio obtiene features o contexto, ejecuta el preprocesamiento, elige una versión del modelo y llama al runtime. Después, puede que la salida necesite postprocesarse antes de devolverse.

El recorrido es este: aplicación cliente, luego API o gateway, luego la capa de servicio, luego obtención de features y contexto, luego el runtime del modelo, luego postprocesamiento y luego la respuesta.

Ver el recorrido completo ayuda a diagnosticar. Si una llamada tarda dos segundos cuando debería tardar doscientos milisegundos, puede que el modelo no tenga la culpa. Pueden ser la obtención de features, los saltos de red, la competencia por recursos o un worker sobrecargado. Por eso conviene tratar el servicio como un único sistema, del cliente al runtime, y medirlo así.

La arquitectura que hay detrás

Las configuraciones maduras reparten responsabilidades en vez de meterlo todo en un único servicio. La aplicación de negocio se ocupa de la interacción con el cliente o el proceso. La capa de servicio atiende las peticiones de inferencia. El runtime ejecuta el modelo. Alrededor hay capacidades de plataforma para despliegue, escalado, seguridad, observabilidad y gestión de recursos.

La clave es que el servicio de modelos pertenece a la plataforma, entre las aplicaciones y los runtimes. Puede conectarse a feature stores, registros de modelos, gateways e infraestructura sin que nada de eso pase a formar parte del modelo. Cuando se multiplican los modelos y los consumidores, esta separación compensa: los equipos de plataforma pueden cambiar detalles de despliegue y runtime sin obligar a cada equipo de aplicación a aprenderlos.

Un solo modelo rara vez basta

El primer despliegue puede tener un único modelo, pero las plataformas empresariales rara vez se mantienen tan ordenadas. Distintas funciones de negocio necesitan modelos distintos, y un mismo modelo puede existir en varias versiones mientras se evalúa y se despliega.

Eso convierte el enrutamiento en un asunto de diseño. La plataforma tiene que decidir qué modelo y qué versión responde a una petición y dónde se ejecuta, sobre todo cuando los modelos difieren en recursos o comparten hardware.

Un registro de modelos ofrece un catálogo fiable de lo que existe y de qué versiones hay disponibles. En lugar de esparcir detalles de despliegue por el código de las aplicaciones, la capa de servicio lee del registro la información del ciclo de vida. Así se separa construir modelos de consumirlos, y un modelo puede evolucionar sin obligar a quien lo llama a cambiar en cada versión.

Escalar la inferencia

Escalar la inferencia no siempre se parece a escalar un servicio web corriente. Una API sin estado suele crecer añadiendo instancias. Los servidores de modelos tienen límites adicionales, porque los modelos pueden consumir mucha memoria, capacidad de GPU o tiempo de arranque.

Un modelo grande puede necesitar hardware especializado, mientras que uno pequeño puede atender miles de peticiones por segundo. A algunas cargas les conviene agrupar peticiones en lotes para mantener ocupado el hardware. Otras no toleran ni un pequeño aumento del tiempo de respuesta.

Así que las decisiones de escalado deben sopesar a la vez el tamaño del modelo, el tráfico, la concurrencia, el hardware, los objetivos de latencia y el coste. Añadir instancias puede resolver un cuello de botella y crear otro si el tiempo de carga o la disponibilidad de GPU pasan a ser el límite.

Síncrono, asíncrono y por lotes

No todos los casos de uso exigen una respuesta inmediata. Una comprobación de fraude durante la autorización de un pago la necesita ya. El procesamiento de documentos puede aceptar un trabajo y devolver los resultados más tarde.

La inferencia por lotes es un tercer patrón. Puntuar a millones de clientes durante la noche va de rendimiento total, no de la latencia de una sola petición.

Cada patrón tiene sus propios rasgos arquitectónicos, así que parte del proceso de negocio y no de la costumbre de usar REST. Pregúntate cuándo se necesita la predicción y dónde encaja en el flujo que la rodea.

El versionado es un problema de producción

Desplegar un modelo no es lo mismo que publicar una versión de una aplicación. Un modelo nuevo puede alterar predicciones y rendimiento aunque la infraestructura de servicio no haya cambiado, así que importa un despliegue controlado.

Un enfoque habitual envía una pequeña parte del tráfico a la versión nueva mientras la antigua se ocupa del resto. La plataforma vigila señales técnicas y de negocio, aumenta la proporción paso a paso y puede dar marcha atrás si la recién llegada se porta mal.

Observabilidad

La monitorización clásica dice si la infraestructura está sana. El servicio de modelos necesita una mirada más amplia: latencia de inferencia, rendimiento, fallos, versiones de modelo, uso de recursos y el estado de dependencias como los servicios de features.

Un sistema puede parecer técnicamente sano y aun así dar resultados raros si el modelo recibe entradas distintas de aquellas con las que se construyó. Por eso la observabilidad debe unir métricas de infraestructura con señales adecuadas a nivel de modelo, respetando lo sensibles que sean los datos. El objetivo es tener la certeza de que se ejecuta el modelo correcto, y de forma fiable dentro de los límites esperados, más allá de que el endpoint conteste.

Seguridad y aislamiento

Los endpoints de servicio exponen capacidades de negocio valiosas y manejan datos sensibles, así que la seguridad debe estar en el diseño desde el principio. Autenticación, autorización, validación de peticiones, limitación de tasa, fronteras de red y control de acceso deberían rodear la inferencia en lugar de llegar después del despliegue.

El aislamiento importa igual cuando los modelos comparten infraestructura. Una sola carga de alto volumen no debería poder dejar sin cómputo o memoria a modelos ajenos, sobre todo cuando hay GPU de por medio. Tratar el servicio como una función de plataforma permite aplicar estos controles de forma coherente, en lugar de que cada equipo de aplicación resuelva solo los mismos problemas.

Usarlo desde Java

Desde una aplicación Java, llamar a un modelo puede parecer engañosamente fácil. Un framework como Spring AI ofrece una abstracción de programación limpia, y el trabajo de desplegar, escalar, enrutar y ejecutar los modelos queda fuera de la app. En una empresa esa separación vale mucho, porque los equipos de aplicación no deberían cargar con las decisiones de infraestructura necesarias para operar cada modelo.

Con Spring AI, una app usa un ChatClient para llamar a la capacidad de IA mientras el modelo en sí lo expone la infraestructura de servicio:

@Service
public class RiskAssessmentService {

    private final ChatClient chatClient;

    public RiskAssessmentService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    public String assess(String transactionContext) {
        return chatClient.prompt()
                .system("""
                        You are a transaction risk assessment assistant.
                        Analyse the supplied transaction context.
                        """)
                .user(transactionContext)
                .call()
                .content();
    }
}

El código no sabe si el modelo está en un contenedor local, en un clúster de Kubernetes, en un servidor de inferencia dedicado o en hardware con GPU. Eso es cosa de la plataforma. La aplicación consume la capacidad a través de la interfaz que se le da.

La idea se vuelve más útil cuando el modelo necesita contexto estructurado. En lugar de que cada aplicación obtenga y monte las entradas por su cuenta, lleva ese trabajo a un servicio de aplicación o de plataforma:

public String assess(String customerId, Transaction transaction) {

    String context = featureService
            .getCustomerContext(customerId);

    return chatClient.prompt()
            .system("Assess the transaction using the customer context.")
            .user("""
                    Customer context:
                    %s

                    Transaction:
                    %s
                    """.formatted(context, transaction))
            .call()
            .content();
}

La aplicación Java orquesta la interacción de negocio, un servicio de features aporta el contexto actual y la capa de servicio sigue siendo la responsable de la inferencia. Cuantas más aplicaciones usan el mismo modelo, más valiosa se vuelve esta división.

LangChain4j ofrece una abstracción parecida a nivel de aplicación. La app empresarial define una interfaz que representa la capacidad de IA, lo que mantiene la infraestructura específica del modelo fuera del código de negocio:

public interface RiskAssistant {

    @SystemMessage("""
        Assess the transaction risk using the supplied
        customer and transaction context.
        """)
    String assess(String context);
}

Después, la app usa esa capacidad a través del framework, y el endpoint que hay detrás se gestiona de forma independiente:

String context = featureService
        .getCustomerContext(customerId);

String assessment = riskAssistant.assess(context);

La frontera que cuenta no está entre Spring AI y LangChain4j. Cualquiera de los dos puede servir como capa de integración. Lo que importa es separar el código de negocio, la orquestación de IA y la infraestructura de servicio. Esa separación permite que la plataforma cambie por su cuenta: un modelo puede pasar a otro runtime, la capacidad puede crecer, puede desplegarse una versión nueva, y nada de eso obliga a rediseñar las aplicaciones Java que la consumen. Ese es el beneficio real de tratar el servicio como una capacidad de plataforma y no como un simple modelo escondido tras un endpoint HTTP.

Elegir una arquitectura

Ningún diseño sirve para todas las cargas. Un modelo pequeño usado por una herramienta interna con poco tráfico puede funcionar perfectamente dentro de la aplicación. Un modelo compartido por varias apps de negocio puede justificar una capa de servicio dedicada.

Los contenedores dan un despliegue y un escalado coherentes, y Kubernetes ofrece una orquestación más potente para carteras de modelos más grandes. Las plataformas de inferencia especializadas tienen sentido cuando necesitas GPU, un rendimiento muy alto, batching avanzado o un control más fino de los recursos.

Sopesa las características del modelo, el perfil de tráfico, los requisitos de latencia, lo crítico que es para el negocio, la madurez operativa y el coste. La plataforma más sofisticada no es automáticamente la adecuada. Apunta a la arquitectura más pequeña que cubra de forma fiable lo que necesita la carga y deje un camino razonable para crecer.

El endpoint es solo el comienzo

De vuelta en el restaurante, el cliente ve al camarero, pero esa es una capa fina de una operación grande. La comanda tiene que tomarse bien, ir a la estación correcta, cocinarse con los recursos disponibles, coordinarse con otras mesas y servirse sin paralizar la cocina.

El servicio de modelos funciona igual. Un endpoint hace que un modelo sea accesible, y la accesibilidad es solo el principio. Una inferencia fiable se apoya en lo que la rodea: enrutamiento, obtención de contexto, versiones, cómputo, escalado, gestión de fallos, observabilidad, seguridad y controles operativos.

Puede que el modelo siga en el centro de la capacidad, pero es la arquitectura de servicio la que decide si esa capacidad puede sostener decisiones de negocio reales. Servir no consiste en exponer un modelo. Consiste en construir el sistema que permite que un modelo respalde esas decisiones de forma fiable.

Para seguir leyendo

Para la parte de Java, consulta la documentación de LangChain4j y Spring AI. En el lado de la plataforma, la documentación de Kubernetes, KServe y Triton Inference Server de NVIDIA son buenos puntos de partida.