Los grandes modelos de idiomas (LLM) están en todas partes, desde aplicaciones cotidianas a herramientas avanzadas. Usarlos es fácil. Pero ¿qué pasa si necesita ejecutar su propio modelo? Ya sea que tenga uno ajustado o esté tratando con datos sensibles a la privacidad, la complejidad aumenta. En este post, compartiremos lo que aprendimos mientras construimos nuestro propio sistema de inferencia LLM. Cubriremos el almacenamiento y el despliegue de modelos, el diseño de la arquitectura del servicio y la resolución de problemas del mundo real como el enrutamiento, el streaming y la gestión de microservicios. Introducción Los LLM están alimentando una amplia gama de aplicaciones - desde chatbots y agentes de flujo de trabajo a herramientas de automatización inteligentes.Mientras que la generación aumentada por la recuperación, la llamada de herramientas y los protocolos multiagentes son importantes, operan a un nivel por encima del motor principal: un LLM fundamental. Muchos proyectos dependen de proveedores externos, como , de , o , lo cual es suficiente para la mayoría de los casos de uso. Pero para ciertas aplicaciones, rápidamente se convierte en un problema. ¿Y si el proveedor se cae? ¿Y si necesita un control total sobre la latencia, el precio o el tiempo de funcionamiento? Lo más importante, ¿y si le importa la privacidad y no puede permitirse enviar datos de usuario a un tercero? abiertas Gemini Anthropic abiertas Gemini Antropología Aquí es donde la auto-hosting se vuelve esencial. Servir un modelo pre-entrenado o ajustado proporciona control, seguridad y la capacidad de adaptar el modelo a necesidades empresariales específicas. Construir un sistema de este tipo no requiere un gran equipo o extensos recursos. Lo construimos con un presupuesto modesto, un pequeño equipo y sólo unos pocos nodos. Esta restricción influyó en nuestra decisión arquitectónica, requiriéndonos centrarnos en la practicidad y la eficiencia. En las siguientes secciones, cubriremos los desafíos enfrentados, las soluciones implementadas y las lecciones aprendidas a lo largo del camino. Vista general Estos son los componentes básicos que forman la columna vertebral del sistema. Formatos y codificación: un lenguaje compartido entre los servicios es esencial, lo que significa formatos consistentes de solicitud/respuesta, esquemas de parámetros de generación, estructuras de historial de diálogo y serialización que funcionan en todas partes, desde el frontend al backend hasta los modelos. El manejo de múltiples modelos, tipos de solicitudes y prioridades de anfitrión requiere decisiones de enrutamiento deliberadas. Describiremos cómo las solicitudes de usuario entrantes se enrutan a través del sistema - desde el punto de entrada inicial al nodo de trabajador apropiado - y cómo se transmiten las respuestas. Almacenamiento y despliegue de modelos ¿Dónde viven los modelos y cómo están preparados para su uso en la producción? Vamos a discutir las pruebas clave a realizar, incluyendo asegurar la fiabilidad del modelo. Observabilidad. ¿Cómo sabes que las cosas están funcionando? mostraremos qué métricas seguimos, cómo monitorizamos fallos y las sondas que utilizamos para garantizar la salud y la fiabilidad del sistema. Esquema y codificación de datos La elección del esquema correcto para la transferencia de datos es crucial.Un formato compartido entre los servicios simplifica la integración, reduce los errores y mejora la adaptabilidad.Nos esforzamos por diseñar el sistema para que funcione de forma suave tanto con modelos auto-hostados como con proveedores externos, sin exponer diferencias al usuario. Por qué es importante el diseño de esquemas No hay un estándar universal para el intercambio de datos LLM. Muchos proveedores siguen esquemas similares a mientras que otros - como o Muchos de estos proveedores ofrecen SDKs compatibles con OpenAI que mantienen el mismo esquema, aunque a menudo con limitaciones o conjuntos de características reducidos (por ejemplo, , de Otros proyectos como El objetivo es unificar estas variaciones envueltas en una interfaz compatible con OpenAI. La apertura de Claude Gemini SDK compatible con OpenAI de Anthropic La capa de compatibilidad OpenAI de Gemini OpenRouter La apertura de Claude Gemini SDK compatible con OpenAI de Anthropic La capa de compatibilidad OpenAI de Gemini OpenRouter Adherirse a un esquema de proveedor predefinido tiene sus ventajas: Obtén una API bien probada y estable. Puede confiar en los SDK y herramientas existentes. Pero también hay desventajas reales: Crea bloqueo de proveedores, lo que dificulta el soporte de múltiples proveedores. Limita la flexibilidad para ampliar el esquema con características personalizadas necesarias para las necesidades de los negocios o los requisitos del equipo de ciencia de datos. Estás expuesto a cambios o depreciaciones fuera de tu control. These schemas often carry that restrict fine-grained control. legacy constraints Para abordar este problema, hemos decidido definir nuestro - un esquema diseñado en torno a nuestras necesidades, que luego podemos mapear a varios formatos externos cuando sea necesario. own internal data model Diseño de esquemas internos Antes de abordar los desafíos, definamos el problema y delineemos nuestras expectativas para la solución: Conversión fácil a formatos requeridos por proveedores externos y viceversa. Soporte completo para funciones específicas para nuestros equipos de negocios y ciencia de datos. Asegúrese de que el esquema sea fácilmente extensible para adaptarse a las necesidades futuras. Comenzamos revisando los principales esquemas de LLM para comprender cómo los proveedores estructuran los mensajes, los parámetros y las salidas. común en la mayoría de los sistemas, incluyendo: core domain entities Mensajes (por ejemplo, prompt, historia) Parámetros de generación (por ejemplo, temperatura, top_p, beam_search) Hemos identificado ciertos parámetros, como , de , o , como ser específico a la configuración interna del proveedor y la lógica de negocio. Estos elementos se encuentran fuera del dominio del núcleo LLM y no son parte del esquema compartido. En cambio, se tratan como extensiones opcionales. Cuando una característica se convierte en ampliamente adoptada o necesaria para una interoperabilidad más amplia, evaluamos la integración en el esquema del núcleo. service_tier usage_metadata reasoning_mode A un nivel alto, nuestro esquema de entrada está estructurado con estos componentes clave: Modelo: Usado como una clave de enrutamiento, actúa como un identificador de enrutamiento, permitiendo al sistema dirigir la solicitud al nodo de trabajador apropiado. Parámetros de generación: configuración del modelo de núcleo (por ejemplo, temperatura, top_p, max_tokens). — Conversation history and prompt payloads. Messages Herramientas: Definiciones de herramientas que el modelo puede utilizar. Esto nos lleva al siguiente esquema, representado en un Ilustra la estructura y la intención del diseño, aunque algunos detalles de implementación se omiten por la simplicidad. Piñonero como Piñonero como class ChatCompletionRequest(BaseModel): model: str # Routing key to select the appropriate model or service messages: list[Message] # Prompt and dialogue history generation_parameters: GenerationParameters # Core generation settings tools: list[Tool] # Optional tool defenitions class GenerationParameters(BaseModel): temperature: float top_p: float max_tokens: int beam_search: BeamSearchParams # Optional, non-core fields specific to certain providers provider_extensions: dict[str, Any] = {} ... # Other parameters Movemos deliberadamente los parámetros de generación en un campo separado en lugar de colocarlos en el nivel de la raíz. los parámetros (por ejemplo, temperatura, top-p, configuración del modelo) y Muchos equipos en nuestro ecosistema almacenan estos parámetros constantes en sistemas de configuración externos, lo que hace que esta separación sea práctica y necesaria. constante variable Incluimos un campo adicional llamado Dentro de la Estos parámetros varían significativamente entre los diferentes proveedores de LLM, la validación e interpretación de estos campos es El componente que sabe cómo comunicarse con un proveedor de modelo específico.Así, evitamos el acoplamiento de paso innecesario causado por la validación redundante de datos en múltiples servicios. provider_extensions GenerationParameters delegated to the final module that handles model inference Para garantizar la compatibilidad retroactiva, se introducen nuevas características de esquema de salida como en el esquema de solicitud. Estos campos actúan como banderas de características – los usuarios deben configurarlos para optar a comportamientos específicos. Este enfoque mantiene el esquema central estable al tiempo que permite la evolución incremental. Por ejemplo, los rasgos de razonamiento sólo se incluirán en la salida si el campo correspondiente se establece en la solicitud. explicit, optional fields Estos esquemas se mantienen en una biblioteca Python compartida y se utilizan en los servicios para garantizar un manejo consistente de solicitudes y respuestas. Trabajar con proveedores de terceros Comenzamos describiendo cómo construimos nuestra propia plataforma, así que ¿por qué preocuparse por la compatibilidad entre proveedores externos?A pesar de confiar en nuestra infraestructura interna, todavía hay varios escenarios en los que los modelos externos juegan un papel: Generación de datos sintéticos para prototipos y experimentación por nuestros equipos de ciencia de datos. Tareas de propósito general donde algunos modelos propietarios funcionan mejor fuera de la caja. Casos de uso no sensibles donde la privacidad, la latencia o el control de la infraestructura son menos críticos. El flujo general de comunicación con proveedores externos se puede resumir de la siguiente manera: Este proceso implica los siguientes pasos: El servicio especial LLM-Gateway responsable de la comunicación con el proveedor recibe la solicitud del usuario en nuestro formato de esquema. The request is converted into the provider-specific format, including any . provide_extensions The external provider processes the request and returns a response. El servicio LLM-Gateway recibe la respuesta y la mapea de nuevo en nuestro esquema de respuesta estandarizada. Este es un esquema de alto nivel que abstrae algunos microservicios individuales.Los detalles sobre componentes específicos y el formato de respuesta de transmisión se cubrirán en las siguientes secciones. Formato de streaming Las respuestas de LLM se generan incrementalmente - token por token - y luego se agregan en Desde la perspectiva del usuario, ya sea a través de un navegador, aplicación móvil o terminal, la experiencia debe permanecer Esto requiere un mecanismo de transporte que apoye . chunks fluid and responsive low-latency, real-time streaming Hay dos opciones principales para lograr esto: WebSockets: Un canal de comunicación full-duplex que permite una interacción bidireccional continua entre el cliente y el servidor. Eventos de envío de servidor (SSE): Un protocolo de transmisión unidireccional basado en HTTP que se utiliza ampliamente para actualizaciones en tiempo real. WebSockets WebSockets Eventos de envío de servidor (SSE) Eventos de envío de servidor (SSE) ¿Por qué SSE sobre WebSockets? Si bien ambas opciones son viables, — en particular para APIs compatibles con OpenAI y sistemas similares. Esto se debe a varias ventajas prácticas: SSE is the more commonly used solution for standard LLM inference Simplicidad: SSE se ejecuta sobre HTTP estándar, no requiere actualizaciones especiales o negociación. Compatibilidad: Funciona nativamente en todos los navegadores principales sin bibliotecas adicionales. Flujo unidireccional: La mayoría de las respuestas LLM fluyen sólo de servidor a cliente, lo que se alinea con el diseño de SSE. Proxy-Friendliness: SSE juega bien con la infraestructura HTTP estándar, incluidos los proxy reversos. Actualizaciones o negociaciones Debido a estos beneficios, . SSE is typically chosen for text-only, prompt-response streaming use cases However, some emerging use cases require richer, low-latency, bidirectional communication — such as real-time transcription or speech-to-speech interactions. abordar estas necesidades mediante el uso Estos protocolos son más adecuados para la entrada y salida multimodales continuas. OpenAI’s Realtime API WebSockets OpenAI’s Realtime API Dado que nuestro sistema se centra exclusivamente en , we stick with por su simplicidad, compatibilidad y alineación con nuestro modelo de streaming. text-based interactions SSE Responder Stream Contenido con seleccionado como la capa de transporte, el siguiente paso era definir La transmisión efectiva requiere más que sólo texto en bruto: necesita proporcionar suficiente para apoyar a los consumidores a continuación, como las interfaces de usuario y las herramientas de automatización.El flujo debe incluir la siguiente información: SSE what structure, metadata, and context Metadatos de nivel de encabezado: información básica de identificación, como el ID de solicitud. La salida del núcleo - los tokens o cadenas generadas por el modelo - se entrega incrementalmente a medida que las secuencias (n) se transmiten de vuelta, chunk-by-chunk. Cada generación puede consistir en múltiples secuencias (por ejemplo, n = 2, n = 4) .Estas secuencias se generan de forma independiente y se transmiten en paralelo, cada una dividida en su propio conjunto de fragmentos incrementales. Metadatos de uso y Token-Level. Esto incluye el número de tokens generados, datos de timing y diagnósticos opcionales como logprobs o rastros de razonamiento. Después de definir la estructura de la respuesta transmitida, también consideramos varios requisitos no funcionales esenciales para la fiabilidad y la evolución futura. Our stream design is intended to be: — clearly distinguishing between content types and event boundaries. Structured — capable of carrying optional metadata without breaking existing clients. Extensible — resilient to malformed, delayed, or partial data. Robust En muchas aplicaciones, como o - Se generan múltiples secuencias (completiones) en paralelo como parte de una única solicitud de generación. side-by-side comparison diverse sampling El formato más completo para las respuestas de streaming está definido en el Según la especificación, un chunk de una sola generación puede incluir varias secuencias en el El array: OpenAI API de referencia choices OpenAI API de referencia elecciones Array Una lista de opciones de finalización de chat. Puede contener más de un elemento si n es mayor que 1. choices Array Una lista de opciones de finalización de chat. Puede contener más de un elemento si n es mayor que 1. Aunque, en la práctica, los fragmentos individuales suelen contener solo un único delta, el formato permite múltiples actualizaciones de secuencia por fragmentos.Es importante tener esto en cuenta, ya que las actualizaciones futuras podrían hacer un uso más amplio de esta capacidad. está diseñado para apoyar esta estructura. official Python SDK Aplicaciones Python SDK We chose to follow the same structure to ensure compatibility with a wide range of potential features. The diagram below illustrates an example from our implementation, where a single generation consists of three sequences, streamed in six chunks over time: Este chunk marca el comienzo de toda la generación. No contiene ningún contenido real, pero incluye metadatos compartidos, como el ID de generación, el timestamp y el rol (por ejemplo, asistente, etc.). Chunk 2 — Sequence Start (Green & Purple). Dos secuencias comienzan a transmitirse en paralelo. Cada una está etiquetada con un identificador único para distinguirla de las demás. The third sequence starts (blue), while the first two sequences (green and purple) stream incremental content via delta events. Chunk 3 — Sequence Start (Blue) & Sequence Delta. The green and blue sequences continue streaming deltas. The purple sequence finishes — this includes a structured finish_reason (like stop, length, etc.). Chunk 4 — Midstream Updates & Finish (Purple). Chunk 5 – Remaining Sequence Finishes.Tanto las secuencias verde y azul completas.El ciclo de vida de cada secuencia ahora está completamente encerrado entre sus marcadores de inicio y finalización respectivos. This chunk closes the generation and may include global usage statistics, final token counts, latency info, or other diagnostics. Chunk 6 — Generation Finish. As you see, to make the stream robust and easier to parse, we opted to , rather than relying on implicit mechanisms such as null checks, EOFs, or magic tokens. This structured approach simplifies downstream parsing, especially in environments where multiple completions are streamed in parallel, and also improves debuggability and fault isolation during development and runtime inspection. explicitly signal Start and Finish events for both the overall generation and each individual sequence Moreover, we introduce an additional un fragmento que lleva información estructurada sobre fallos. Algunos errores, como solicitudes malformadas o problemas de autorización, pueden aparecer directamente a través de los códigos de respuesta HTTP estándar. , we have two options: either abruptly terminate the HTTP stream or emit a well-formed SSE error event. We chose the latter. Abruptly closing the connection makes it hard for clients to distinguish between network issues and actual model/service failures. By using a dedicated error chunk, we enable more reliable detection and propagation of issues during streaming. Error during the generation process Backend Services and Request Flow En el centro del sistema se encuentra un único punto de entrada: . It handles basic concerns like authentication, usage tracking and quota enforcement, request formatting, and routing based on the specified model. While it may look like the Gateway carries a lot of responsibility, each task is intentionally simple and modular. For external providers, it adapts requests to their APIs and maps responses back into a unified format. For self-hosted models, requests are routed directly to internal systems using our own unified schema. This design allows seamless support for both external and internal models through a consistent interface. LLM-Gateway Modelos Autogestionados Como se mencionó anteriormente, es adecuado para la transmisión de respuestas a los usuarios finales, pero no es una opción práctica para Cuando llega una solicitud, debe ser redirigida a un nodo de trabajador adecuado para la inferencia del modelo, y el resultado se transmite de vuelta.Mientras que algunos sistemas manejan esto usando proxies HTTP en cadena y enrutamiento basado en encabezados, en nuestra experiencia, este enfoque se vuelve difícil de gestionar y evolucionar a medida que la lógica crece en complejidad. Server-Sent Events (SSE) internal backend communication Our internal infrastructure needs to support: Planificación consciente de la prioridad: las solicitudes pueden tener diferentes niveles de urgencia (por ejemplo, interactivo vs. batch), y las tareas de alta prioridad deben ser tratadas primero. — Certain nodes run on higher-performance GPUs and should be preferred; others serve as overflow capacity. Hardware-aware routing Despacho específico del modelo: Cada trabajador está configurado para soportar solo un subconjunto de modelos, basado en la compatibilidad del hardware y las restricciones de los recursos. To address these requirements, we use a para desconectar el enrutamiento de tareas de la entrega de resultados. Este diseño proporciona una mayor flexibilidad y resiliencia bajo diferentes condiciones de carga y enrutamiento. Para este propósito, aunque otros corredores también podrían ser viables dependiendo de su latencia, rendimiento y preferencias operativas. RabbitMQ fue un ajuste natural dado su madurez y alineación con nuestra herramienta existente. message broker conejito conejito RabbitMQ Ahora echemos un vistazo más detallado a cómo se implementa este sistema en la práctica: Nosotros usamos , allowing us to route requests based on model compatibility and node capabilities. The process is as follows: dedicated queues per model El servicio LLM-Gateway (representado como el usuario) inicia una solicitud HTTP para desencadenar una tarea de generación de texto. The request is handled by the , which selects the appropriate queue (marked in green on the image) based on the requested model and appends the message to it. Task Routing via Scheduler service. Scheduler An appropriate (only one worker is shown for simplicity, but there are many) subscribed to the queue picks up the task and begins processing. This worker runs the selected model locally. Worker Picks Up Task. Inference Worker El trabajador transmite la respuesta chunk-by-chunk a la Cuesta de Respuesta, a la que se suscribe la réplica del Planificador que maneja la solicitud. The Scheduler listens to the reply queue and receives the response chunks as they arrive. Receiving Response Chunks. SSE Streaming. Los fragmentos se convierten en formato SSE y se transmiten al cliente. To handle , evitamos abrumar el broker de mensajes: large payloads En lugar de incorporar grandes datos de entrada o salida directamente en la tarea, los subimos a una tienda externa compatible con S3. A reference (such as a URL or resource ID) is included in the task metadata, and the worker retrieves the actual content when needed. Aplicando el diseño con RabbitMQ Cuando se trata de Cada uno Es un RabbitMQ regular , dedicado a manejar un único tipo de modelo. que se puede lograr mediante el uso de . In this setup, messages with higher priority values are delivered and processed before lower priority ones. For , where messages should be directed to the most performant available nodes first, Los consumidores de mayor prioridad reciben mensajes mientras estén activos; los consumidores de menor prioridad sólo reciben mensajes cuando los de mayor prioridad están bloqueados o no están disponibles. routing and publishing messages Request Queue La cola priority-aware scheduling message priorities hardware-aware routing Prioridades del consumidor La cola Prioridades de mensaje Prioridades del consumidor If message loss is unacceptable, the following must be in place: El editor confirma para asegurarse de que el corredor ha recibido y almacenado el mensaje. and so data survives restarts. Durable queues persistent messages Quorum cuentas para una mayor durabilidad a través de la replicación. Estos también soportan mensajes simplificados y prioridades de consumidores a partir de RabbitMQ 4.0. La editorial confirma Publisher confirms Quórum de cuentas Quórum de cuentas Mensajes simplificados y prioridades para los consumidores a partir de RabbitMQ 4.0 Hasta ahora, hemos cubierto cómo se publican las tareas - pero cómo es la handled? The first step is to understand how trabajar en RabbitMQ. El corredor apoya un concepto llamado , which are bound to a single connection and automatically deleted when that connection closes. This makes them a natural fit for our setup. streamed response temporary queues exclusive queues Exclusivas cuentas exclusive queues Creamos , asegurándose de que se limpia automáticamente cuando se cierra la réplica. Sin embargo, esto introduce un desafío: mientras que cada réplica de servicio tiene una única cola RabbitMQ, debe manejar . one exclusive queue per Scheduler service replica many requests in parallel To address this, we treat the RabbitMQ queue as a , encaminando las respuestas a la réplica correcta de Scheduler. Cada solicitud de usuario se asigna una , que se incluye en cada respuesta. dentro de la , we maintain an additional con colas de memoria de corta duración — una por solicitud activa. Las colas de entrada se ajustan a estas colas basadas en el identificador y se transmiten en consecuencia. Estas colas de memoria se eliminan una vez que la solicitud se completa, mientras que la cola RabbitMQ persiste durante la vida de la réplica del servicio. transport layer unique identifier Scheduler in-memory routing layer Schematically this looks as follows: A central dispatcher within the Scheduler dispatches chunks to the appropriate in-memory queue, each managed by a dedicated handler. Handlers then stream the chunks to users using SSE-protocol. Inferencia Hay varios marcos maduros disponibles para la inferencia LLM eficiente, como y Estos sistemas están diseñados para y generar tokens de respuesta en tiempo real, a menudo con características como el batch continuo y la optimización de la memoria GPU. como el motor de inferencia del núcleo, con algunas modificaciones personalizadas: VLLM Escombros process multiple sequences in parallel vLLM VLLM VLLM Escombros SGLANG Implementación de búsqueda de haz personalizada para adaptarse mejor a nuestra lógica de generación y soportar restricciones estructuradas. Soporte para esquemas de salida estructurados, permitiendo que los modelos devuelvan salidas que se ajusten a formatos específicos para el negocio. A través de la experiencia, hemos aprendido que incluso las pequeñas actualizaciones de la biblioteca pueden — ya sea en calidad de salida, determinismo o comportamiento concurrente. Debido a esto, hemos establecido una tubería de pruebas robusta: significantly alter model behavior Test de estrés para descubrir problemas de concurrencia, fugas de memoria o regresiones de estabilidad. Pruebas de determinismo para garantizar resultados consistentes para semillas fijas y conjuntos de parámetros. Prueba de grilla de parámetros para cubrir una amplia gama de configuraciones de generación, sin pasar por alto. Almacenamiento y despliegue Most modern systems run in — either in the cloud or within Kubernetes (K8s). While this setup works well for typical backend services, it introduces challenges around Los modelos de LLM pueden ser , y el modelo de cocción pesa directamente en las imágenes de Docker - rápidamente se vuelve problemático: containerized environments model weight storage tens or even hundreds of gigabytes in size — Even with multi-stage builds and caching, transferring large model files during the build phase can dramatically increase CI time. Slow builds Despliegue lento: Cada despliegue requiere sacar imágenes masivas, lo que puede tomar varios minutos y causar interrupciones o retrasos. — Neither Docker registries nor Kubernetes nodes are optimized for handling extremely large images, resulting in bloated storage usage and bandwidth strain. Resource inefficiency To solve this, we separate from the Docker image lifecycle. Our models are stored in an , y recogido justo antes del inicio del servicio de inferencia. Para mejorar el tiempo de inicio y evitar descargas redundantes, también usamos cache de los pesos del modelo en cada nodo. model storage external S3-compatible object storage local persistent volumes (PVCs) Los volúmenes persistentes locales (PVC) local persistent volumes (PVCs) Observación Un sistema como este — construido en — requires to ensure reliability and performance at scale. streaming, message queues, and real-time token generation robust observability Además de las métricas estándar de nivel de servicio (CPU, memoria, tasas de error, etc.), encontramos esencial monitorear lo siguiente: Profundidad de la cola, retroceso de mensajes y número de consumidores: el seguimiento del número de mensajes pendiente, el tamaño de la cola actual y el número de consumidores activos ayuda a detectar las barreras en la distribución de tareas y los desequilibrios en la utilización de los trabajadores. — tracking the number of tokens or response chunks generated per second helps identify latency or throughput regressions. Token/chunk throughput — to pinpoint where requests fail or stall across components (gateway, broker, workers, etc.). El rastreo distribuido — since inference processes can crash under rare conditions (e.g., bad input or extreme parameter values), proactive monitoring of liveness and readiness is critical. Inference engine health checks El rastreo distribuido Distributed tracing Further Improvements Mientras que nuestro sistema está listo para la producción, todavía hay importantes retos y oportunidades para la optimización: Uso de un KV-cache distribuido para aumentar el rendimiento de inferencia. Soporte de cancelación de solicitud para conservar la computadora cuando ya no se necesitan puertas de salida. Creating a for data science teams. simple model delivery pipeline Conclusión While building a reliable and provider-independent LLM serving system can seem complex at first, it doesn’t require reinventing the wheel. Each component — streaming via SSE, task distribution through message brokers, and inference handled by runtimes like vLLM — serves a clear purpose and is grounded in existing, well-supported tools. With the right structure in place, it’s possible to create a maintainable and adaptable setup that meets production requirements without unnecessary complexity. In the next post, we’ll explore more advanced topics such as distributed KV-caching, handling multiple models across replicas, and deployment workflows suited to ML-oriented teams. Autores Tochka , por Stanislav Shimovolos por Stanislav Shimovolos por Stanislav Shimovolos Tochka , Maxim Afanasyev Máximo Afanasiev Maxim Afanasyev Reconocimientos Trabajos realizados en Tochka Dmitri Kryukov Dmitri Kryukov Dmitri Kryukov