AirLLM: Experimente el modelo Kimi K3 con solo 4 GB de VRAM

August 3, 2026

Introducción del proyecto

AirLLM es una herramienta en Python orientada a la inferencia de grandes modelos de lenguaje. Su idea central no consiste en cargar el modelo completo de una sola vez en la VRAM, sino en dividirlo en capas jerárquicas más finas y, durante la inferencia, cargar únicamente lo que se necesita. De este modo, podemos probar modelos como 70B, 235B, 671B e incluso modelos MoE aún más extravagantes como Kimi K3 en entornos con una VRAM muy limitada.

Kimi K3 ha estado muy en el foco recientemente por una razón bastante directa: se trata de un modelo MoE de escala enorme. Su número de parámetros es muy grande, pero en la práctica cada token solo se enruta a una parte de los expertos. AirLLM aprovecha exactamente esto mediante una carga en streaming a nivel de expertos, evitando tener que meter el modelo completo en la VRAM de una sola vez.

En este artículo armaremos juntos un proyecto mínimo: primero ejecutaremos la inferencia de Kimi K3 y luego lo encapsularemos en un sencillo API HTTP. Al final también comentaremos cuándo conviene la inferencia local con baja VRAM y cuándo es mejor usar directamente una interfaz compatible como Defapi para hacerlo más fácil.

Dificultad: Intermedia | Duración: 30-60 minutos | Resultados: Comprender el flujo de inferencia con baja VRAM de AirLLM y poner en marcha un servicio mínimo de inferencia para Kimi K3

Perfil de destinatarios

  • Desarrolladores que quieren experimentar con grandes modelos en una sola tarjeta y en máquinas con poca VRAM
  • Ingenieros que quieren evaluar el coste de inferencia de modelos como Kimi K3, DeepSeek-V3 y Qwen3-235B
  • Personas que están creando prototipos de aplicaciones de IA y quieren validar el rendimiento del modelo localmente primero
  • Desarrolladores que necesitan encapsular el modelo local como API y luego conectarlo a Agentes, RAG o sistemas de negocio

Dependencias clave y entorno

Kimi K3 es bastante exigente con el entorno. Primero aclaremos el límite de dependencias:

DependenciaVersión recomendadaDescripción
Python3.10+Se recomienda usar un entorno virtual para aislar dependencias
NVIDIA DriverCompatible con CUDA 12Debe poder ejecutar PyTorch en una versión que soporte CUDA 12
PyTorchConstruido para CUDA 12La cadena de dependencias de Kimi K3 suele encajar mejor con CUDA 12
transformers4.56.xEl código remoto del modelo Kimi K3 tiene requisitos sobre la versión
airllmÚltima versiónUsa AutoModel para cargar el modelo de forma unificada
compressed-tensorsÚltima versiónLos formatos de pesos relacionados con Kimi K3 se usarán aquí
flash-attnCompatible con CUDA 12El código del modelo Kimi K3 normalmente requiere flash attention

WARNING

AirLLM puede reducir el uso de VRAM, pero no hace que los archivos del modelo se vuelvan más pequeños. La primera ejecución descargará y dividirá el modelo; el espacio en disco y la calidad de la red siguen siendo cruciales. Los modelos como Kimi K3 son enormes: se recomienda preparar suficiente espacio de caché antes.

Estructura completa del proyecto

Primero prepararemos un proyecto mínimo, manteniendo la estructura simple:

airllm-kimi-k3-demo/
├── .env.example
├── requirements.txt
├── run_kimi_k3.py
├── server.py
└── README.md

1. Crear un entorno virtual de Python

Crea el directorio y el entorno virtual:

mkdir airllm-kimi-k3-demo
cd airllm-kimi-k3-demo

python -m venv .venv
source .venv/bin/activate

En Windows PowerShell, activa así:

mkdir airllm-kimi-k3-demo
cd airllm-kimi-k3-demo

python -m venv .venv
.venv\Scripts\Activate.ps1

Verifica la versión de Python:

python --version

Se recomienda que el resultado sea al menos Python 3.10.x. Si en tu máquina hay varias versiones de Python, puedes especificar explícitamente con py -3.10 -m venv .venv.

2. Instalar AirLLM y dependencias de Kimi K3

Crea requirements.txt:

airllm
accelerate
sentencepiece
safetensors
compressed-tensors
python-dotenv
fastapi
uvicorn[standard]

Instala primero PyTorch. Como ejemplo con CUDA 12.1:

pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

Luego instala las dependencias principales:

pip install -r requirements.txt
pip install "transformers>=4.56,<4.57"

Por último, instala flash-attn:

pip install flash-attn --no-build-isolation

TIP

Si flash-attn compila muy lento, primero confirma que el entorno actual puede hacer import torch correctamente y que torch.version.cuda muestra que es la familia de CUDA 12. Kimi K3 tiene dependencias más orientadas a un entorno de ingeniería; no se recomienda instalarlo directamente en un entorno global de Python desordenado.

3. Configurar permisos del modelo y directorio de caché

Algunos modelos de Hugging Face requieren inicio de sesión o autorización. Preparamos un .env.example:

HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
HF_HOME=/data/huggingface
AIRLLM_MODEL_ID=moonshotai/Kimi-K3

Al usarlo de verdad, copia una copia:

cp .env.example .env

En Windows PowerShell:

Copy-Item .env.example .env

Luego completa tu token de Hugging Face. El ID del modelo debe coincidir con el nombre del repositorio publicado oficialmente; si usas un repositorio espejo o uno sincronizado internamente, también puedes cambiarlo por el repo id correspondiente.

WARNING

No envíes .env a un repositorio Git. El token del modelo es información sensible; si se filtra, podría llevar a un abuso de los permisos del modelo privado.

4. Escribir el script mínimo de inferencia de Kimi K3

Crea run_kimi_k3.py:

import os

from dotenv import load_dotenv
from airllm import AutoModel


def main() -> None:
    """Carga Kimi K3 y ejecuta una generación mínima de texto."""
    load_dotenv()

    model_id = os.getenv("AIRLLM_MODEL_ID", "moonshotai/Kimi-K3")
    hf_token = os.getenv("HF_TOKEN")

    # Controla la longitud de entrada: primero usa un prompt corto para comprobar el flujo y evitar esperas largas en la validación inicial.
    max_length = 256

    # AutoModel detecta automáticamente el tipo de modelo; hf_token se usa para acceder a repositorios que requieren autorización.
    model = AutoModel.from_pretrained(
        model_id,
        hf_token=hf_token,
        profiling_mode=True,
    )

    prompt = "Explica en tres frases por qué AirLLM puede reducir la VRAM para la inferencia de grandes modelos."

    # El tokenizer sigue el estilo de Hugging Face, por lo que el coste de migración es bajo.
    input_tokens = model.tokenizer(
        [prompt],
        return_tensors="pt",
        return_attention_mask=False,
        truncation=True,
        max_length=max_length,
        padding=False,
    )

    # En la inferencia, AirLLM carga pesos por capas; establece max_new_tokens un poco más bajo para validar rápidamente.
    generation_output = model.generate(
        input_tokens["input_ids"].cuda(),
        max_new_tokens=128,
        use_cache=True,
        return_dict_in_generate=True,
    )

    output = model.tokenizer.decode(
        generation_output.sequences[0],
        skip_special_tokens=True,
    )

    print(output)


if __name__ == "__main__":
    main()

Ejecución:

python run_kimi_k3.py

La primera ejecución suele ser más lenta, porque AirLLM debe descargar el modelo, analizar los archivos de pesos y dividirlo en un formato que luego se pueda cargar por capas. Verás que la segunda ejecución será mucho más fluida, ya que la caché del modelo y los resultados de la división ya estarán listos.

5. Observar el uso de VRAM y el consumo de disco

Abre otra terminal para vigilar la VRAM:

nvidia-smi -l 1

Observa dos indicadores:

Memory-Usage
GPU-Util

Si todo va bien, la VRAM no se disparará instantáneamente como en transformers.from_pretrained() tradicional, donde el modelo completo se carga de golpe. El enfoque de AirLLM es más parecido a “ir tomando lo necesario a medida que avanza”: carga en memoria la capa y la parte de expertos correspondientes cuando se necesita y libera cuando ya no se usan.

Luego revisa el disco:

du -sh "$HF_HOME"

En Windows PowerShell puedes usar:

Get-ChildItem $env:HF_HOME -Recurse | Measure-Object Length -Sum

TIP

La inferencia con baja VRAM no es un almuerzo gratis. AirLLM traslada la carga de la VRAM hacia la E/S del disco, la memoria de CPU y la caché del modelo; por tanto, la velocidad del SSD impactará directamente la experiencia.

6. Habilitar la compresión de parámetros

AirLLM permite reducir el tamaño de la carga de pesos mediante el parámetro compression. Podemos añadir 4bit al inicializar el modelo:

model = AutoModel.from_pretrained(
    model_id,
    hf_token=hf_token,
    compression="4bit",  # Usa compresión de pesos en 4bit para reducir el volumen de carga y mejorar la velocidad en algunos casos
    profiling_mode=True,
)

Si quieres ser más conservador, prueba primero 8bit:

model = AutoModel.from_pretrained(
    model_id,
    hf_token=hf_token,
    compression="8bit",  # 8bit es más conservador; equilibra mejor velocidad y precisión
    profiling_mode=True,
)

Normalmente elegimos así:

EscenarioRecomendación
Solo probar si Kimi K3 puede ejecutarseSin compresión, primero confirma el flujo
La E/S de disco es un cuello de botellaPrueba 8bit
Prioridad en velocidad y validación barataPrueba 4bit
Hacer una evaluación seriaFija los parámetros y compara salidas en múltiples rondas

WARNING

La compresión cambia la forma en que se cargan los pesos. Aunque en muchos casos el impacto en la inferencia es pequeño, si estás haciendo evaluación de modelos, inferencia con textos largos o validación de negocio sensible, asegúrate de conservar la versión sin comprimir como referencia (baseline).

7. Encapsularlo en un sencillo API HTTP

Después de que el script local funcione, normalmente querrás integrarlo en una aplicación. A continuación usaremos FastAPI para construir un servicio mínimo.

Crea server.py:

import os
from typing import List, Literal

from dotenv import load_dotenv
from fastapi import FastAPI
from pydantic import BaseModel
from airllm import AutoModel


class ChatMessage(BaseModel):
    """Estructura de mensajes estilo OpenAI, útil para reemplazarlo por una API compatible más adelante."""
    role: Literal["system", "user", "assistant"]
    content: str


class ChatRequest(BaseModel):
    """Solicitud de chat mínima: conserva solo los campos necesarios para el tutorial."""
    messages: List[ChatMessage]
    max_tokens: int = 256


class ChatResponse(BaseModel):
    """Devuelve un resultado en texto; mantenlo simple para facilitar el debugging."""
    text: str


load_dotenv()

app = FastAPI(title="AirLLM Kimi K3 Demo")

model_id = os.getenv("AIRLLM_MODEL_ID", "moonshotai/Kimi-K3")
hf_token = os.getenv("HF_TOKEN")

# Carga el modelo al iniciar el servicio; en un entorno real de producción se puede añadir health check y carga perezosa.
model = AutoModel.from_pretrained(
    model_id,
    hf_token=hf_token,
    profiling_mode=False,
)


def build_prompt(messages: List[ChatMessage]) -> str:
    """Une mensajes en múltiples turnos en un prompt simple; luego se puede reemplazar por la plantilla oficial de chat del modelo."""
    lines: list[str] = []
    for message in messages:
        lines.append(f"{message.role}: {message.content}")
    lines.append("assistant:")
    return "\n".join(lines)


@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest) -> ChatResponse:
    """Ejecuta una generación de texto para Kimi K3."""
    prompt = build_prompt(request.messages)

    input_tokens = model.tokenizer(
        [prompt],
        return_tensors="pt",
        return_attention_mask=False,
        truncation=True,
        max_length=1024,
        padding=False,
    )

    generation_output = model.generate(
        input_tokens["input_ids"].cuda(),
        max_new_tokens=request.max_tokens,
        use_cache=True,
        return_dict_in_generate=True,
    )

    text = model.tokenizer.decode(
        generation_output.sequences[0],
        skip_special_tokens=True,
    )

    return ChatResponse(text=text)

Iniciar el servicio:

uvicorn server:app --host 0.0.0.0 --port 8000

Prueba de llamada:

curl -X POST http://localhost:8000/chat \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "role": "user",
        "content": "Explica en chino por qué los modelos MoE como Kimi K3 son adecuados para la carga en streaming por expertos."
      }
    ],
    "max_tokens": 200
  }'

Ahora ya tenemos un API mínimo. Aún no es de nivel producción, pero es suficiente para experimentos locales, prototipos de Agent, depuración de prompts y comparación del rendimiento del modelo.

8. Cómo elegir entre inferencia local y la interfaz de Defapi

El valor de AirLLM es muy claro: nos permite tocar los grandes modelos en máquinas con poca VRAM, especialmente para aprender, validar, evaluar y hacer experimentos offline. Pero en producción también hay que considerar el throughput, la latencia, la concurrencia, las actualizaciones del modelo, la recuperación ante fallos y el coste operativo.

Si solo quieres integrar rápidamente las capacidades del modelo en una aplicación, o necesitas una API unificada con estilo OpenAI/Anthropic/Gemini, Defapi encaja mejor. Su ventaja es que el precio suele ser aproximadamente la mitad del oficial y que los modelos comunes suelen ser compatibles con estos protocolos:

  • v1/chat/completions
  • v1/messages
  • v1beta/models/

Por ejemplo, podemos abstraer la llamada local /chat en un cliente unificado. En la fase de desarrollo, usamos AirLLM local; en el entorno online, cambiamos a Defapi:

import os
import requests


def ask_model(prompt: str) -> str:
    """Elige inferencia local o una interfaz compatible con Defapi según variables de entorno."""
    provider = os.getenv("MODEL_PROVIDER", "local")

    if provider == "defapi":
        response = requests.post(
            "https://api.defapi.org/api/v1/chat/completions",
            headers={
                "Authorization": f"Bearer {os.environ['DEFAPI_API_KEY']}",
                "Content-Type": "application/json",
            },
            json={
                "model": os.getenv("DEFAPI_MODEL", "anthropic/claude-sonnet-4.5"),
                "messages": [{"role": "user", "content": prompt}],
                "temperature": 0.7,
            },
            timeout=60,
        )
        response.raise_for_status()
        return response.json()["choices"][0]["message"]["content"]

    response = requests.post(
        "http://localhost:8000/chat",
        json={
            "messages": [{"role": "user", "content": prompt}],
            "max_tokens": 256,
        },
        timeout=300,
    )
    response.raise_for_status()
    return response.json()["text"]

La ventaja de este enfoque es que las fronteras quedan muy claras: el modelo local se encarga de explorar y validar, mientras que Defapi se encarga de integrar modelos online de forma estable, unificada y a bajo coste. Para OpenClaw, servicios RAG y Agentes automatizados, este esquema de doble modo es muy útil.

Solución de problemas frecuentes

1. ¿Qué hacer si falla la instalación de flash-attn?

Primero confirma que PyTorch reconozca CUDA correctamente:

python -c "import torch; print(torch.__version__); print(torch.version.cuda); print(torch.cuda.is_available())"

Si torch.cuda.is_available() es False, no te precipites a instalar flash-attn. Primero debes corregir PyTorch, el driver y las versiones de CUDA. Para modelos como Kimi K3, se recomienda usar una compilación para CUDA 12, para evitar fallos de compilación causados por la falta de paquetes precompilados adecuados en CUDA 13.

2. ¿Qué hacer si transformers da un error de versión?

Fija la versión en 4.56.x:

pip uninstall -y transformers
pip install "transformers>=4.56,<4.57"

Luego vuelve a ejecutar:

python run_kimi_k3.py

Si ves que falla la carga del código remoto del modelo, normalmente es porque la versión de transformers no coincide con el código del modelo.

3. ¿Qué hacer si falla la descarga desde Hugging Face?

Primero confirma si el token funciona:

huggingface-cli whoami

Si no estás logueado:

huggingface-cli login

También puedes usar solo HF_TOKEN dentro de .env. Si el modelo requiere permisos de acceso, primero debes aceptar los términos en la página del modelo.

4. ¿Qué hacer si el espacio en disco es insuficiente?

La primera ejecución de AirLLM descargará los pesos originales y también generará los archivos del modelo dividido. Puedes mover el directorio de caché a un disco más grande:

export HF_HOME=/mnt/models/huggingface

O configurarlo en .env:

HF_HOME=/mnt/models/huggingface

Si el disco sigue justo, puedes probar delete_original=True de AirLLM para conservar solo los archivos convertidos:

model = AutoModel.from_pretrained(
    model_id,
    hf_token=hf_token,
    delete_original=True,  # Elimina los pesos originales tras la conversión para ahorrar espacio en disco
)

5. ¿Por qué la primera ejecución es especialmente lenta?

Es un comportamiento normal. La primera ejecución incluye pasos como descargar pesos, leer índices, dividir el modelo y escribir en la caché. La segunda ejecución reutiliza la caché, por lo que será mucho más rápida.

Puedes habilitar el profiling:

model = AutoModel.from_pretrained(
    model_id,
    hf_token=hf_token,
    profiling_mode=True,  # Muestra el tiempo de carga e inferencia para ayudar a localizar cuellos de botella
)

Si el tiempo se gasta principalmente en lectura de disco, cambiar a un SSD NVMe más rápido suele ser más efectivo que aumentar la VRAM.

6. ¿La VRAM no baja a lo esperado?

Primero confirma que tu código no mezcla el método tradicional de carga:

from airllm import AutoModel

model = AutoModel.from_pretrained(model_id)

No mezcles con el método tradicional de carga:

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained(model_id)

Este último seguirá la ruta de carga tradicional y es fácil que llene la VRAM de inmediato.

7. ¿Por qué el servicio de API se bloquea cuando aumentan las concurrencias?

La inferencia local con poca VRAM es más adecuada para experimentos en serie y no para asumir directamente llamadas de alta concurrencia. La solución más simple es limitar la concurrencia:

uvicorn server:app --host 0.0.0.0 --port 8000 --workers 1

Si necesitas llamadas online más estables, puedes derivar las solicitudes a Defapi en línea, dejando AirLLM local para evaluación y depuración.

Lecturas adicionales / Direcciones avanzadas

  • Comparar el tiempo de carga, la VRAM y la calidad de salida de Kimi K3, DeepSeek-V3 y Qwen3-235B bajo AirLLM.
  • Convertir el servicio FastAPI en una interfaz compatible con OpenAI tipo /v1/chat/completions.
  • Integrar Defapi y usar v1/chat/completions o v1/messages para gestionar llamadas a modelos online de forma unificada.
  • Añadir una cola al servicio de AirLLM para evitar que varias solicitudes compitan a la vez por el disco y la GPU.
  • En OpenClaw u otros marcos de Agent, configurar modelos locales y modelos en la nube para seleccionar dinámicamente según la complejidad de la tarea.
Updated August 3, 2026