Qué es servir un modelo de IA (y por qué existe vLLM)
Servir un modelo de IA es convertir un archivo de pesos en una API que responde preguntas. Ese es el oficio completo de un inference engineer, y esta guía te lleva de cero a tu propio servidor con vLLM por menos de $5.
Este es el capítulo 1 de la guía. No instalamos nada todavía: primero el mapa completo, porque cada error que vas a cometer más adelante vive en una de estas piezas.
Un modelo es un archivo, no un programa
Cuando descargas "un modelo de 7B", descargas una carpeta con miles de millones de números: los pesos. Vienen en archivos safetensors de varios GB, junto con el tokenizador y un config.json que describe la arquitectura.
Ese archivo no hace nada solo. Es más parecido a una librería (.dll / .so) que a un ejecutable: necesita un programa que lo cargue en memoria, le pase texto y lea lo que devuelve. Ese programa puede ser un script de 20 líneas o un servidor de producción.
Y aquí el primer malentendido clásico: "7B" no son 7 GB. Son ~7.000 millones de parámetros. En FP16, cada parámetro ocupa 2 bytes, así que ese modelo ocupa ~14 GB solo de pesos. La cuenta exacta la veremos en el capítulo 5, pero quédate con una idea: la memoria manda.
Tokens: la única moneda
El modelo no ve palabras. Ve tokens: trozos de texto de 3-4 caracteres en promedio. "inference" pueden ser 2 tokens; "Tlaxcala", 4. Y el modelo hace una sola cosa, millones de veces:
Dado este texto, ¿cuál es el siguiente token más probable?
Generar una respuesta es un bucle: predice un token, lo añade al contexto, predice el siguiente. Todo lo que mediremos en esta guía (TTFT, tokens/s) son medidas de ese bucle.
Cargar pesos en un script no es servir
El primer intento de todo el mundo se ve así:
# naive_serve.py
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
while True:
prompt = input("> ")
inputs = tokenizer(prompt, return_tensors="pt")
output = model.generate(**inputs, max_new_tokens=200)
print(tokenizer.decode(output[0]))
Funciona. Y sirve exactamente para aprender por qué no sirve:
- Un request a la vez. Mientras uno genera, los demás esperan con la GPU pagada y ociosa.
- Sin API estándar. Cada cliente tendría que hablar "mi protocolo casero por stdin".
- Sin streaming. El usuario mira una pantalla vacía 20 segundos.
- Sin gestión de memoria. Cada request re-procesa su prompt completo desde cero.
Un servidor de inferencia resuelve esas cuatro cosas: cola de requests, batching (meter varios prompts en el mismo pase de GPU), streaming token a token, y una API que ya es un estándar de facto.
La API que lo cambió todo: OpenAI-compatible
En 2023 OpenAI definió el formato de su API (/v1/chat/completions, messages, choices, usage) y el resto del mundo lo adoptó. Hoy casi todo habla ese idioma: opencode, Cursor, LangChain, tu script de 10 líneas.
Y esto tiene una consecuencia muy práctica: si tu servidor habla OpenAI-compatible, puedes cambiar el modelo o el backend y ningún cliente se entera. El ejemplo del capítulo 7 (un gateway con llaves por persona) existe gracias a esto.
Por qué vLLM y no otro
Un servidor de inferencia gestiona el recurso más caro del sistema: la memoria que ocupa el contexto de cada request. vLLM (nacido en UC Berkeley en 2023) ganó esa partida con una técnica llamada PagedAttention: trata la memoria del contexto como páginas, igual que un sistema operativo maneja la RAM, en vez de reservar bloques contiguos gigantes que se desperdician.
"vLLM achieves near-zero waste in KV cache memory... and delivers up to 24x higher throughput compared to HuggingFace Transformers." - Source: vLLM blog, PagedAttention
El 24× era el número del paper original; hoy nadie promete eso contra todo, pero la idea ganó: vLLM es el estándar de facto open source para servir LLMs, con licencia Apache-2.0 y soporte para MoE, FP8 y los modelos que usaremos en esta guía.
"vLLM is a fast and extensible library for LLM inference and decoding." - Source: Documentación de vLLM
Existen alternativas serias (TensorRT-LLM, SGLang, llama.cpp, Ollama). En el capítulo 2 usaremos Ollama precisamente porque es la puerta de entrada más fácil; vLLM es el siguiente nivel: el que dimensionas, mides y pones en producción.
Tu primer contacto: leer una respuesta de verdad
Sin instalar nada. Solo necesitas una cuenta gratuita en OpenRouter (agrega tu tarjeta NO, es opcional para modelos :free), una API key y curl. Cualquier modelo con sufijo :free de openrouter.ai/models sirve:
# terminal
curl https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/llama-3.2-3b-instruct:free",
"messages": [
{ "role": "user", "content": "Explica en una frase que es un servidor de inferencia" }
],
"max_tokens": 60,
"temperature": 0.2
}'
La respuesta (recortada) se ve así:
{
"model": "meta-llama/llama-3.2-3b-instruct:free",
"choices": [
{
"message": {
"role": "assistant",
"content": "Un servidor de inferencia expone un modelo de IA mediante una API para procesar solicitudes y devolver predicciones."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 24,
"completion_tokens": 21,
"total_tokens": 45
}
}
Nota que uso max_tokens y no max_input_tokens: no existe. Ese parámetro limita la salida; la entrada la limita la ventana de contexto del modelo. Y temperature: 0.2 hace la respuesta casi determinista, ideal para pruebas.
Los campos que de verdad importan:
choices[0].message.content: la respuesta. Obvio, pero es el único campo que la mayoría lee.finish_reason:"stop"significa que el modelo terminó solo;"length"significa que lo cortaste conmax_tokens. Diferenciar esto te ahorra horas de "el modelo se corta solo".usage: la factura.prompt_tokenses lo que costó tu pregunta;completion_tokens, la respuesta.
Checkpoint y el error típico
Checkpoint: ~10 minutos, $0. Ya sabes leer un response OpenAI-compatible campo por campo, que es el 80% de lo que haremos con vLLM en los capítulos 4-7.
El error típico de este nivel: no mirar usage. Cada turno de una conversación reenvía todo el historial, así que prompt_tokens crece con cada pregunta. En una API esto se paga token a token; en tu propio servidor será memoria. El mecanismo de fondo lo desarmé en 48 KB por token y es la razón técnica de medio capítulo de esta guía.
Si algún término se te escapó (KV cache, MoE, FP8), tengo un diccionario de AI en español para eso. Y si quieres la vista de disciplina completa, qué es Inference Engineering es la lectura profunda.
Siguiente capítulo
Instalamos Ollama, corremos tu primer modelo en tu máquina, gratis, y medimos tus primeros tiempos de respuesta. Por primera vez vas a ver con tus propios ojos cuánto tarda un token.
¿Ya conocías el formato OpenAI-compatible, o lo habías usado sin saber que era un estándar? Cuéntame en los comentarios.