No Necesitas una GPU de 3000€ para IA Local

Vistas: 1
0:00 / 0:00
No Necesitas una GPU de 3000€ para IA Local

Llevo varios meses hablándote de IA local. Empecé con un laboratorio modesto que cualquiera podía montar en casa, y poco a poco me fui creciendo. Primero RAG, luego agentes, después skills, MCPs, asistentes… hasta que terminé recomendando clústeres con varias Nvidia y servicios en la nube como OpenRouter. Y claro, en ese proceso me he dado cuenta de algo: me he dejado llevar. No todo el mundo tiene un equipo con recursos ilimitados, ni una GPU de 3000 euros, ni necesita montar un datacenter en el salón. Con el equipo que ya tienes, un portátil con 8 o 16 GB de RAM y un SSD, puedes montar un laboratorio de IA local perfectamente funcional. Solo se trata de elegir bien las herramientas y, sobre todo, los modelos adecuados. Así que he decidido darle la vuelta a la tortilla y dedicar este episodio, el último de la temporada 8, a lo básico: el botiquín del explorador, los recursos mínimos para empezar a trastear con IA local sin arruinarte.

El hardware que realmente necesitas

Vamos a empezar por lo que duele: el hardware. Si has estado siguiendo el podcast estos meses, igual te ha quedado la sensación de que sin una RTX 4090 no vales para nada. Y nada más lejos de la realidad.

Te puedo asegurar que con 8 GB de RAM ya puedes empezar. Con 16 GB tienes el punto dulce, lo que yo llamo el caballo de batalla. Y si tienes 32 GB, pues oye, ya estás en modo premium sin haber gastado un euro en una gráfica dedicada.

La clave está en el SSD. Esto no es negociable. Los modelos ocupan espacio — un modelo de 7B cuantizado ronda los 4-5 GB — y si usas un disco duro mecánico, la carga de modelos va a ser una tortura. Con un NVMe, los modelos se cargan en segundos. Con un SATA SSD, en unos pocos segundos más. Con un HDD… mejor ni lo intentes.

Y mira que lo he probado, por cabezonería. Puse un modelo de 7B en un disco duro mecánico externo, solo para ver qué tal. Tardó más de dos minutos en cargar el modelo en memoria, y cada vez que hacías una consulta, el tiempo de respuesta se disparaba. El cuello de botella del HDD es brutal: los modelos se cargan por fragmentos, y un disco giratorio busca esos fragmentos a 5400 revoluciones por minuto. Un NVMe moderno, en cambio, lee secuencialmente a varios GB por segundo. La diferencia entre esperar dos minutos y esperar cinco segundos. Tú eliges.

¿Y la GPU? Aquí viene la buena noticia: no la necesitas. Los modelos pequeños (1.5B a 7B) funcionan perfectamente en CPU. Eso sí, si tienes una GPU aunque sea modesta — una GTX 1650 con 4 GB, una RTX 2060 — puedes acelerar bastante la generación. Pero no es un requisito, es un extra.

En mi caso particular, he estado probando todo esto en un portátil con 16 GB de RAM y un Ryzen 5, sin GPU dedicada. Y funciona. No es ultrarrápido, pero es perfectamente usable. Hablamos de 25-40 tokens por segundo con un modelo como phi3.5, que para una conversación es más que suficiente.

Ollama: el corazón del laboratorio

Si hay una herramienta que ha democratizado la IA local, esa es Ollama. Y no es una exageración. Este gestor de modelos de lenguaje, escrito en Go, te permite descargar, ejecutar y usar modelos locales con una API compatible con OpenAI. Vamos, que si ya has usado ChatGPT, sabes usar Ollama. La diferencia es que aquí todo corre en tu máquina.

La instalación no puede ser más sencilla. Un solo comando y ya tienes Ollama funcionando:

curl -fsSL https://ollama.com/install.sh | sh

Ese comando descarga el binario estático, lo instala en /usr/local/bin y crea un servicio systemd que arranca automáticamente. En cuestión de segundos tienes la API REST escuchando en el puerto 11434.

Si eres de los que prefieren no hacer un curl | sh — y te entiendo, a mí también me escuece un poco —, tienes alternativas. En Debian y Ubuntu puedes añadir el repositorio oficial:

echo "deb [trusted=yes] https://ollama.com/apt ollama main" | sudo tee /etc/apt/sources.list.d/ollama.list
sudo apt update && sudo apt install ollama

En Fedora, el script oficial también funciona, pero si prefieres el paquete RPM, puedes descargarlo directamente desde el repositorio de Ollama. Y en openSUSE, el método más limpio es usar el script de instalación estático, que detecta tu distribución y se adapta. En Arch Linux, claro, lo tienes en AUR: yay -S ollama-bin.

Una vez instalado, te recomiendo verificar que el servicio está corriendo antes de hacer nada más:

systemctl status ollama
journalctl -u ollama --no-pager -n 20

Si ves que está activo y sin errores, ya puedes empezar a descargar modelos.

Una vez instalado, puedes descargar modelos con ollama pull:

# Modelo pequeño y rápido
ollama pull phi3.5

# Modelo de código
ollama pull qwen2.5-coder:1.5b

# Modelo mediano, el caballo de batalla
ollama pull qwen2.5:7b

# Modelo de embeddings para RAG
ollama pull nomic-embed-text

Y para probar que todo funciona, nada como hacer una consulta directamente desde el terminal:

ollama run qwen2.5:7b "Explícame qué es un modelo de lenguaje cuantizado como si tuviera 12 años"

La primera vez que ves a tu máquina respondiendo con inteligencia, sin pasar por ningún servidor externo, sin enviar tus datos a nadie… esa sensación no se olvida. 🎉

Trucos para sacarle partido a Ollama

Ollama tiene un montón de opciones que merece la pena conocer. Aquí van las que más uso:

  • ollama run --verbose — te muestra métricas de rendimiento, tokens por segundo, velocidad de generación.
  • OLLAMA_KEEP_ALIVE=0 — descarga el modelo de memoria inmediatamente después de usarlo. Ahorra RAM cuando tienes varios modelos.
  • OLLAMA_CONTEXT_LENGTH=4096 — reduce la ventana de contexto para hardware limitado. Con 8 GB de RAM, 4096 tokens es un buen compromiso.
  • ollama ps — te muestra qué modelos están cargados en memoria en ese momento.
  • ollama rm <modelo> — para liberar espacio cuando ya no necesitas un modelo.

Modelos pequeños, grandes resultados

Aquí está el meollo del asunto. La clave para tener un laboratorio funcional con hardware modesto es elegir bien los modelos. No necesitas un Llama 3.1 70B para resumir un artículo ni para traducir un párrafo. De hecho, para la mayoría de las tareas diarias, un modelo de 3B o 7B es más que suficiente.

Te he preparado una tabla con los modelos que recomiendo según tu hardware:

ModeloParamsTamaño (Q4)RAM mínimaIdeal para
Phi-3.5-mini (Microsoft)3.8B~2.5 GB8 GBRazonamiento, código, conversación
Qwen2.5-Coder-1.5B (Alibaba)1.5B~1 GB8 GBCódigo, tareas concretas
Llama 3.2 3B (Meta)3B~2 GB8-16 GBPropósito general
Qwen2.5 7B (Alibaba)7B~4.5 GB16 GBCaballo de batalla
DeepSeek-Coder-V2-Lite16B~9 GB16 GBProgramación avanzada
Llama 3.1 8B (Meta)8B~5 GB16-32 GBPropósito general

¿Ves? Con 8 GB de RAM puedes usar phi3.5 o qwen2.5-coder:1.5b. Con 16 GB, qwen2.5:7b es tu modelo para todo. Y solo cuando tienes 32 GB o más empiezas a mirar modelos de 8B o superiores.

Déjame darte un poco más de contexto sobre cada uno, porque no todos los modelos son iguales ni sirven para lo mismo:

  • Phi-3.5-mini de Microsoft es una bestia para su tamaño. Con solo 3.8B parámetros, rinde a 25-40 tokens por segundo en CPU y es sorprendentemente bueno en razonamiento lógico. Microsoft lo entrenó con datos sintéticos generados por modelos más grandes, y se nota: entiende instrucciones complejas, sigue cadenas de pensamiento, y genera código decente. Es mi recomendación número uno si tienes 8 GB de RAM o quieres algo que vaya rápido en una Raspberry Pi.
  • Qwen2.5-Coder-1.5B de Alibaba es especialista en código. Si tu día a día es programar — escribir funciones, refactorizar, documentar — este modelo te va a sorprender. No esperes que razone sobre filosofía, pero para tareas concretas de desarrollo va como un tiro. Y ocupa solo 1 GB.
  • Qwen2.5 7B es el caballo de batalla por una razón. Con 7B parámetros y cuantizado a 4 bits, ocupa unos 4.5 GB y corre en 16 GB de RAM a 8-15 tokens por segundo en CPU. La calidad de sus respuestas es excelente: entiende matices, sigue instrucciones complejas, genera código útil, y razona bien. Si solo pudiera tener un modelo en mi laboratorio, sería este. Para que te hagas una idea, phi3.5 es más rápido pero menos profundo — ideal para respuestas rápidas de una línea. qwen2.5:7b tarda más en responder, pero sus respuestas son más elaboradas y precisas. La diferencia se nota cuando le pides un análisis detallado o que resuelva un problema complejo.
  • Llama 3.2 3B de Meta es el todoterreno. Rápido, solvente, buena opción si quieres un modelo generalista sin complicarte.
  • DeepSeek-Coder-V2-Lite es para cuando el código se vuelve serio. 16B parámetros, ocupa unos 9 GB cuantizado, y necesita 16 GB de RAM. Pero para tareas de programación avanzada — generar APIs completas, refactorizar código legacy, escribir tests — es otro nivel.

La cuantización, ese gran invento

La cuantización es lo que hace posible que modelos grandes quepan en hardware modesto. Básicamente, reduces la precisión numérica de los pesos del modelo. En lugar de usar números de 16 bits (FP16), usas de 4 u 8 bits. El modelo ocupa menos y va más rápido, a costa de una pérdida de calidad mínima.

PrecisiónBitsTamaño relativoCalidad
FP1616100%Referencia
Q8_08~53%Casi imperceptible
Q4_K_M4~28%Muy buena — recomendado
Q3_K_S3~21%Aceptable
Q2_K2~15%Degradación notable

Mi recomendación: Q4_K_M es el punto dulce. Calidad casi idéntica al modelo original, pero ocupando una cuarta parte. Para 8 GB de RAM, puedes bajar a Q3_K_S si necesitas un modelo más grande.

Cuándo usar cada tamaño de modelo

Modelo pequeño (1.5B-3B) cuando tienes 8 GB de RAM o menos, necesitas respuestas rápidas (menos de un segundo), o la tarea es simple: traducción, resumen corto, clasificación. También son ideales para Raspberry Pi o equipos muy limitados.

Modelo mediano (7B) para el 80% de los casos de uso. Con 16 GB de RAM, qwen2.5:7b te da razonamiento de calidad, genera código útil y análisis detallados. Es el modelo que uso el 90% del tiempo.

Modelo grande (12B+) solo cuando tienes 32 GB+ de RAM o una GPU con VRAM. Para razonamiento multi-paso complejo o cuando la precisión es crítica. Y para esos casos, a veces merece más la pena tirar de nube.

Contenedores para aislar servicios

Una vez que tienes Ollama funcionando, el siguiente paso es montar una interfaz gráfica y organizar los servicios. Y aquí los contenedores son tus mejores amigos.

Docker o Podman, da igual. Ambos funcionan. Yo uso Docker por costumbre, pero Podman tiene la ventaja de ser rootless por defecto, lo que le da un plus de seguridad. La estructura es la misma.

La diferencia práctica entre ambos es sutil pero importante. Docker arranca un daemon como root que gestiona todos los contenedores. Podman, en cambio, no necesita daemon y puede ejecutar contenedores con tu usuario sin privilegios. Si ya tienes Docker instalado, quédate con él. Si empiezas de cero, Podman es más seguro y cada vez tiene mejor soporte. Migrar de uno a otro es trivial: los comandos son prácticamente idénticos (dockerpodman, docker-composepodman-compose), y los docker-compose.yml funcionan sin cambios en ambos.

¿Problemas comunes? El más frecuente es el puerto ocupado. Si ya tienes algo corriendo en el puerto 11434 o 3000, el contenedor fallará al arrancar. La solución es cambiar el mapeo de puertos en el compose o parar el servicio que lo ocupa. Otro clásico: los permisos de los volúmenes. Si el contenedor no puede escribir en el directorio que montas, prueba con chown -R 1000:1000 (el UID por defecto dentro del contenedor) o ajusta los permisos. Y el tercero en discordia: la falta de memoria. Si el contenedor se muere sin más, revisa los logs con docker compose logs y mira si hay errores de OOM (Out of Memory). Ahí es donde entran los límites de recursos que te cuento más abajo.

El docker-compose.yml básico tiene dos servicios: Ollama como gestor de modelos y Open WebUI como interfaz gráfica. Te dejo el compose completo que uso:

Fíjate en los detalles importantes:

  1. Healthchecks — Open WebUI espera a que Ollama esté sano antes de arrancar. Sin esto, la interfaz web se lanza antes de tiempo y da error.
  2. Red separadaia-local-net aísla los servicios entre sí. No están expuestos en la red del host más que por los puertos que abres.
  3. Volúmenes persistentes — Los modelos y los datos de las conversaciones están fuera del contenedor. Si borras el contenedor, no pierdes nada.
  4. Variables de entornoOLLAMA_KEEP_ALIVE=5m mantiene el modelo en memoria 5 minutos después del último uso. OLLAMA_NUM_PARALLEL=1 evita que se carguen varios modelos a la vez.

Para levantar todo el ecosistema, un solo comando:

docker compose up -d

Y ya tienes tu interfaz web en http://localhost:3000. Crea tu cuenta (el primer usuario es administrador), selecciona el modelo que hayas descargado y empieza a chatear.

Límites de recursos

Algo que aprendí a base de quemarme: pon límites a los contenedores. Si no lo haces, un solo servicio puede acaparar toda la RAM y dejar tu sistema hecho un Cristo:

docker run -d --memory="4g" --cpus="2" ollama/ollama:latest

Con Podman es igual de sencillo:

podman run -d --memory="4g" --cpus="2" ollama/ollama:latest

Herramientas complementarias: Whisper, TTS y SQLite Vec

Ollama y Open WebUI son la base, pero un laboratorio de IA local completo necesita algo más. Aquí entran las herramientas complementarias: transcripción de voz, texto a voz y búsqueda semántica.

Whisper: transcripción local

Whisper es el sistema de reconocimiento de voz de OpenAI, y lo mejor es que funciona completamente en local. No necesitas conexión a internet, no envías tus audios a ningún servidor.

Los modelos de Whisper van desde tiny (39M parámetros, 75 MB) hasta large (1.55B parámetros, 2.9 GB). Para hardware modesto, tiny o base funcionan sorprendentemente bien incluso en CPU:

# Instalar Whisper
pip install openai-whisper

# Transcribir un audio
whisper mi_audio.mp3 --model tiny --language Spanish

# Transcribir desde diferentes formatos
whisper mi_audio.wav --model base --language es
whisper mi_audio.m4a --model small --language es
whisper mi_audio.ogg --model tiny --language es

# Exportar a diferentes formatos de salida
whisper mi_audio.mp3 --model tiny --language es --output_format txt
whisper mi_audio.mp3 --model tiny --language es --output_format srt  # Subtítulos
whisper mi_audio.mp3 --model tiny --language es --output_format vtt  # WebVTT

Whisper acepta prácticamente cualquier formato de audio que le eches: MP3, WAV, M4A, OGG, FLAC, incluso WebM. Y la salida la puedes obtener como texto plano, SRT para subtítulos, VTT para web, TSV para hojas de cálculo, o JSON para procesar con scripts. Una maravilla.

Si quieres algo más rápido, faster-whisper es hasta 4 veces más rápido que el original:

pip install faster-whisper
whisper-ctranslate2 mi_audio.mp3 --model tiny --language es

Y para la cadena completa — descargar audio de YouTube y transcribirlo — aquí tienes el combo:

# Descargar audio
yt-dlp -x --audio-format mp3 "URL_DEL_VIDEO" -o "audio.mp3"

# Transcribir
whisper audio.mp3 --model tiny --language es

TTS local: texto a voz

Para la parte de síntesis de voz, el panorama ha cambiado bastante. Piper fue durante mucho tiempo la opción recomendada, pero está archivado desde octubre de 2025. El relevo lo ha cogido Kokoro-82M, un modelo de 82 millones de parámetros que produce audio de alta calidad incluso en CPU.

Kokoro tiene varias voces disponibles, cada una con un código de dos partes: el idioma (af para inglés americano, am para inglés británico, jf para japonés, etc.) y el nombre de la voz. Por ejemplo, af_bella es una voz femenina americana, am_adam es una voz masculina americana. Para español, tienes voces como es_garazi (femenino) y es_xabi (masculino). Puedes probarlas todas cambiando el parámetro voice en la llamada a la API.

Lo mejor es que Kokoro tiene una API compatible con OpenAI, así que integrarlo con Open WebUI es trivial:

# Con Docker (CPU)
docker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:latest

Y desde Python:

from openai import OpenAI
client = OpenAI(base_url="http://localhost:8880/v1", api_key="not-needed")
response = client.audio.speech.create(
    model="kokoro",
    voice="af_bella",
    input="Hola, soy tu asistente local"
)
response.stream_to_file("output.mp3")

SQLite Vec: búsqueda semántica local

Aquí llegamos a una de las piezas más interesantes: sqlite-vec. Es una extensión de SQLite para almacenar y buscar vectores de embeddings. Está escrito en C puro, sin dependencias, y funciona en cualquier sitio donde corra SQLite. Es el sucesor de sqlite-vss y está sponsorizado por Mozilla, nada menos.

sqlite-vec te permite hacer RAG (Retrieval Augmented Generation) sin montar un servidor de base de datos vectorial. No necesitas ChromaDB, ni Qdrant, ni Milvus. Con SQLite y esta extensión tienes búsqueda semántica en tu propia máquina.

El flujo es sencillo:

  1. Cargas la extensión vec0 en SQLite
  2. Creas una tabla virtual para vectores de 768 dimensiones (las que usa nomic-embed-text)
  3. Generas embeddings con Ollama
  4. Insertas documentos con sus vectores
  5. Buscas por similitud semántica

La demo que te he preparado indexa cuatro documentos de ejemplo y luego busca el más relevante para una consulta. Cuando ejecutas el script y ves que te devuelve el documento correcto basándose en el significado, no en las palabras exactas… eso es magia pura.

Organización del laboratorio: estructura y scripts

Una de las cosas que más me ha costado aprender es que la organización importa. Si empiezas a descargar modelos aquí y allá, a crear scripts en cualquier sitio, a los dos días tienes un desastre que no hay quien lo entienda.

Por eso he creado una estructura de directorios que uso como plantilla para cualquier laboratorio de IA local:

~/lab-ia/
├── docker/
│   ├── ollama/
│   │   └── models/         # Modelos descargados por Ollama
│   ├── open-webui/
│   │   └── data/           # Conversaciones, configuraciones
│   └── docker-compose.yml  # Orquestación de servicios
├── modelos/                 # GGUF descargados manualmente
├── datos/
│   ├── notas/              # Notas personales para indexar
│   ├── transcripciones/    # Audios transcritos
│   └── embeddings/         # Vectores generados
├── scripts/
│   ├── backup.sh           # Respaldo del laboratorio
│   ├── transcribir.sh      # yt-dlp + Whisper pipeline
│   └── consultar.sh        # Consulta rápida a Ollama
└── config/
    ├── ollama/
    └── open-webui/

Para no tener que crear esto a mano cada vez, he preparado un script de inicialización que hace todo el trabajo:

init-lab.sh
#!/bin/bash
# =============================================================================
# init-lab.sh — Inicialización del laboratorio de IA local
# =============================================================================
#
# Uso:
#   chmod +x init-lab.sh
#   ./init-lab.sh
#
# Descripción:
#   Script de inicialización rápida del laboratorio. Crea la estructura de
#   directorios recomendada en el episodio, descarga los modelos esenciales
#   y genera un docker-compose.yml básico para empezar.
#
#   Este script está diseñado para que el oyente pueda poner en marcha su
#   laboratorio con un solo comando después de instalar Ollama y Docker.
#
#   Estructura que crea:
#     ~/lab-ia/
#     ├── docker/
#     │   ├── ollama/
#     │   ├── open-webui/
#     │   └── docker-compose.yml
#     ├── modelos/
#     ├── datos/
#     │   ├── notas/
#     │   ├── transcripciones/
#     │   └── embeddings/
#     ├── scripts/
#     └── config/
#         ├── ollama/
#         └── open-webui/
#
# Requisitos:
#   - Ollama instalado (ver 02-instalacion/setup-ollama.sh)
#   - Docker instalado (para el docker-compose.yml)
#   - Conexión a internet (para descargar modelos)
#   - ~5 GB de espacio libre
#
# =============================================================================

set -euo pipefail

# Colores
VERDE='\033[0;32m'
AMARILLO='\033[1;33m'
AZUL='\033[0;34m'
ROJO='\033[0;31m'
RESET='\033[0m'

# Directorio base del laboratorio
LAB_DIR="$HOME/lab-ia"

echo -e "${AZUL}╔══════════════════════════════════════════════════════╗${RESET}"
echo -e "${AZUL}║  🔬  Inicialización del Laboratorio de IA Local     ║${RESET}"
echo -e "${AZUL}║  Episodio 827 — Recursos IA: el botiquín del        ║${RESET}"
echo -e "${AZUL}║                 explorador                          ║${RESET}"
echo -e "${AZUL}╚══════════════════════════════════════════════════════╝${RESET}"
echo ""

# =============================================================================
# PASO 1: Comprobar requisitos
# =============================================================================
echo -e "${AZUL}[1/5] Comprobando requisitos...${RESET}"
echo ""

REQUISITOS_OK=true

# Ollama
echo -n "  Ollama: "
if command -v ollama &>/dev/null; then
    echo -e "${VERDE}✓${RESET}"
else
    echo -e "${ROJO}✗${RESET}"
    echo "    → Instala Ollama primero: curl -fsSL https://ollama.com/install.sh | sh"
    REQUISITOS_OK=false
fi

# Docker
echo -n "  Docker: "
if command -v docker &>/dev/null; then
    echo -e "${VERDE}✓${RESET}"
else
    echo -e "${AMARILLO}⚠${RESET}"
    echo "    → Docker no está instalado. El docker-compose.yml se creará igual."
    echo "    → Instálalo con: curl -fsSL https://get.docker.com | sh"
fi

# Espacio en disco (~5 GB necesarios)
echo -n "  Espacio en disco: "
ESPACIO_LIBRE=$(df -BG "$HOME" | tail -1 | awk '{print $4}' | sed 's/G//')
if [ "$ESPACIO_LIBRE" -ge 10 ]; then
    echo -e "${VERDE}✓${RESET} ($ESPACIO_LIBRE GB libres)"
elif [ "$ESPACIO_LIBRE" -ge 5 ]; then
    echo -e "${AMARILLO}⚠${RESET} ($ESPACIO_LIBRE GB libres — suficiente pero justo)"
else
    echo -e "${ROJO}✗${RESET} ($ESPACIO_LIBRE GB libres — necesitas al menos 5 GB)"
    REQUISITOS_OK=false
fi

echo ""

if [ "$REQUISITOS_OK" = false ]; then
    echo "  ERROR: Requisitos insuficientes. Revisa los mensajes anteriores."
    exit 1
fi

# =============================================================================
# PASO 2: Crear estructura de directorios
# =============================================================================
echo -e "${AZUL}[2/5] Creando estructura de directorios...${RESET}"
echo ""

mkdir -p "$LAB_DIR"
mkdir -p "$LAB_DIR/docker/ollama"
mkdir -p "$LAB_DIR/docker/open-webui"
mkdir -p "$LAB_DIR/modelos"
mkdir -p "$LAB_DIR/datos/notas"
mkdir -p "$LAB_DIR/datos/transcripciones"
mkdir -p "$LAB_DIR/datos/embeddings"
mkdir -p "$LAB_DIR/scripts"
mkdir -p "$LAB_DIR/config/ollama"
mkdir -p "$LAB_DIR/config/open-webui"

echo "  Estructura creada en: ${AZUL}${LAB_DIR}${RESET}"
echo ""
echo "  ${LAB_DIR}/"
echo "  ├── docker/"
echo "  │   ├── ollama/"
echo "  │   ├── open-webui/"
echo "  │   └── docker-compose.yml  (se crea a continuación)"
echo "  ├── modelos/"
echo "  ├── datos/"
echo "  │   ├── notas/"
echo "  │   ├── transcripciones/"
echo "  │   └── embeddings/"
echo "  ├── scripts/"
echo "  └── config/"
echo "      ├── ollama/"
echo "      └── open-webui/"
echo ""

# =============================================================================
# PASO 3: Crear docker-compose.yml básico
# =============================================================================
echo -e "${AZUL}[3/5] Creando docker-compose.yml básico...${RESET}"
echo ""

COMPOSE_FILE="$LAB_DIR/docker/docker-compose.yml"

cat > "$COMPOSE_FILE" << 'COMPOSE_EOF'
# =============================================================================
# docker-compose.yml — Laboratorio IA Local
# Generado por init-lab.sh del Episodio 827
# =============================================================================
#
# Uso:
#   docker compose up -d          # Levantar todos los servicios
#   docker compose down           # Detener todos los servicios
#   docker compose logs -f        # Ver logs en tiempo real
#
# Servicios:
#   - ollama:     Gestor de modelos de lenguaje (API en :11434)
#   - open-webui: Interfaz gráfica web (web en :3000)
#
# Requisitos:
#   - Docker Engine 24+ o Podman 4+
#   - Docker Compose v2+
#
# =============================================================================

services:
  # ---------------------------------------------------------------------------
  # Ollama — Gestor de modelos de lenguaje
  # ---------------------------------------------------------------------------
  ollama:
    image: ollama/ollama:latest
    container_name: ollama-server
    hostname: ollama
    volumes:
      # Persistencia de modelos descargados
      - ./ollama/models:/root/.ollama
    ports:
      # API REST de Ollama
      - "11434:11434"
    environment:
      # Mantener el modelo en memoria para respuestas más rápidas
      - OLLAMA_KEEP_ALIVE=5m
      # Número máximo de modelos cargados simultáneamente
      - OLLAMA_NUM_PARALLEL=1
      # Límite de contexto (ventana de tokens)
      - OLLAMA_CONTEXT_LENGTH=8192
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - ia-local-net

  # ---------------------------------------------------------------------------
  # Open WebUI — Interfaz gráfica para chatear con modelos
  # ---------------------------------------------------------------------------
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    depends_on:
      ollama:
        condition: service_healthy
    volumes:
      # Persistencia de conversaciones, configuraciones, archivos subidos
      - ./open-webui/data:/app/backend/data
    ports:
      # Interfaz web
      - "3000:8080"
    environment:
      # URL del servicio Ollama (nombre del contenedor, no localhost)
      - OLLAMA_BASE_URL=http://ollama:11434
      # Tiempo máximo de espera para respuestas largas
      - WEBUI_TIMEOUT=300
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    networks:
      - ia-local-net

# ---------------------------------------------------------------------------
# Redes
# ---------------------------------------------------------------------------
networks:
  ia-local-net:
    name: ia-local-network
    driver: bridge
COMPOSE_EOF

echo -e "  ${VERDE}✓${RESET} docker-compose.yml creado en:"
echo "    $COMPOSE_FILE"
echo ""

# =============================================================================
# PASO 4: Descargar modelos esenciales
# =============================================================================
echo -e "${AZUL}[4/5] Descargando modelos esenciales...${RESET}"
echo ""

# Modelo de lenguaje pequeño (ideal para empezar)
MODELO_LENGUAJE="qwen2.5:3b"
echo "  Modelo de lenguaje: ${AMARILLO}${MODELO_LENGUAJE}${RESET}"
echo "  (~2 GB — recomendado para empezar)"
echo ""

if ollama list 2>/dev/null | grep -q "^${MODELO_LENGUAJE}\s"; then
    echo -e "  ${VERDE}✓${RESET} ${MODELO_LENGUAJE} ya está descargado."
else
    echo "  Descargando ${MODELO_LENGUAJE}..."
    echo "  (Esto puede tardar según tu conexión)"
    ollama pull "$MODELO_LENGUAJE"
    echo -e "  ${VERDE}✓${RESET} ${MODELO_LENGUAJE} descargado."
fi
echo ""

# Modelo de embeddings (para RAG y búsqueda semántica)
MODELO_EMBED="nomic-embed-text"
echo "  Modelo de embeddings: ${AMARILLO}${MODELO_EMBED}${RESET}"
echo "  (~0.3 GB — necesario para RAG y búsqueda semántica)"
echo ""

if ollama list 2>/dev/null | grep -q "^${MODELO_EMBED}\s"; then
    echo -e "  ${VERDE}✓${RESET} ${MODELO_EMBED} ya está descargado."
else
    echo "  Descargando ${MODELO_EMBED}..."
    ollama pull "$MODELO_EMBED"
    echo -e "  ${VERDE}✓${RESET} ${MODELO_EMBED} descargado."
fi
echo ""

# =============================================================================
# PASO 5: Crear script de respaldo básico
# =============================================================================
echo -e "${AZUL}[5/5] Creando script de respaldo básico...${RESET}"
echo ""

BACKUP_SCRIPT="$LAB_DIR/scripts/backup.sh"

cat > "$BACKUP_SCRIPT" << 'BACKUP_EOF'
#!/bin/bash
# backup.sh — Respaldo del laboratorio de IA local
#
# Uso:
#   ./backup.sh                    # Respaldo completo
#   ./backup.sh --modelos          # Solo respaldo de modelos
#   ./backup.sh --datos            # Solo respaldo de datos
#
# Descripción:
#   Crea una copia de seguridad de los elementos esenciales del laboratorio:
#   modelos descargados, configuraciones y datos generados.

set -euo pipefail

BACKUP_DIR="$HOME/lab-ia-backup-$(date +%Y%m%d)"
LAB_DIR="$HOME/lab-ia"

echo "=============================================="
echo "  Respaldo del Laboratorio IA Local"
echo "=============================================="
echo ""
echo "  Directorio de respaldo: $BACKUP_DIR"
echo ""

mkdir -p "$BACKUP_DIR"

# Respaldar configuración de Ollama
if [ -d "$HOME/.ollama" ]; then
    echo "  Respaldando modelos de Ollama..."
    cp -r "$HOME/.ollama" "$BACKUP_DIR/ollama"
    echo "  ✓ Modelos respaldados"
fi

# Respaldar docker-compose y configuraciones
if [ -d "$LAB_DIR/docker" ]; then
    echo "  Respaldando docker-compose..."
    cp -r "$LAB_DIR/docker" "$BACKUP_DIR/docker"
    echo "  ✓ Configuración Docker respaldada"
fi

# Respaldar scripts personalizados
if [ -d "$LAB_DIR/scripts" ]; then
    echo "  Respaldando scripts..."
    cp -r "$LAB_DIR/scripts" "$BACKUP_DIR/scripts"
    echo "  ✓ Scripts respaldados"
fi

echo ""
echo "  Respaldo completado en: $BACKUP_DIR"
echo "  Para restaurar: cp -r $BACKUP_DIR/* $HOME/"
echo "=============================================="
BACKUP_EOF

chmod +x "$BACKUP_SCRIPT"

echo -e "  ${VERDE}✓${RESET} Script de respaldo creado: $BACKUP_SCRIPT"
echo ""

# =============================================================================
# CIERRE
# =============================================================================
echo -e "${VERDE}╔══════════════════════════════════════════════════════╗${RESET}"
echo -e "${VERDE}║  ✅  Laboratorio inicializado con éxito             ║${RESET}"
echo -e "${VERDE}╚══════════════════════════════════════════════════════╝${RESET}"
echo ""
echo "  Tu laboratorio está listo en: ${AZUL}${LAB_DIR}${RESET}"
echo ""
echo "  Modelos descargados:"
ollama list 2>/dev/null | tail -n +2 | while read -r line; do
    echo "    • $line"
done
echo ""
echo "  Para empezar a usar tu laboratorio:"
echo ""
echo "    1. Abre la interfaz web (si tienes Docker):"
echo "       cd ${LAB_DIR}/docker"
echo "       docker compose up -d"
echo "       → http://localhost:3000"
echo ""
echo "    2. Chatea desde terminal:"
echo "       ollama run qwen2.5:3b \"¿Qué puedo hacer con IA local?\""
echo ""
echo "    3. Prueba la API REST:"
echo "       curl http://localhost:11434/api/tags"
echo ""
echo "    4. Haz un respaldo:"
echo "       ${LAB_DIR}/scripts/backup.sh"
echo ""
echo "  📖  Guía completa en: atareao.es/827"
echo ""
echo "  Próximo episodio (829): Skills imprescindibles para tu agente IA"
echo "  Vamos a enseñar al modelo a usar herramientas."
echo ""
echo "  Nos oímos el jueves. Sed felices. Y seguid trasteando."
echo ""

El script hace cinco cosas:

  1. Comprueba requisitos — verifica que Ollama y Docker están instalados, y que tienes al menos 5 GB de espacio libre.
  2. Crea la estructura de directorios — genera toda la jerarquía que te he mostrado.
  3. Genera el docker-compose.yml — con healthchecks y todo lo necesario.
  4. Descarga modelos esencialesqwen2.5:3b para lenguaje y nomic-embed-text para embeddings.
  5. Crea un script de respaldo — porque hacer backup de tu laboratorio es importante.

Y para la descarga de modelos por niveles, tengo otro script que te permite elegir según tu hardware. Además, para el respaldo automático, puedes programar el script de backup con cron. Un ejemplo: para hacer una copia de seguridad todos los domingos a las 2 de la madrugada, añades esta línea a tu crontab:

crontab -e
# Añadir:
0 2 * * 7 $HOME/lab-ia/scripts/backup.sh

Y si quieres llevar un control de versiones de tu configuración — los docker-compose, los scripts, las configuraciones —, nada como un repositorio git:

cd ~/lab-ia
git init
git add docker/ scripts/ config/
git commit -m "Laboratorio IA: configuración inicial"

Así, cualquier cambio que hagas en los scripts o en el compose queda registrado. Puedes experimentar sin miedo a romper nada, porque siempre puedes volver atrás con git checkout.

Los niveles son:

  • Nivel 1 (8 GB RAM): phi3.5 + qwen2.5-coder:1.5b (~3.5 GB)
  • Nivel 2 (16 GB RAM): llama3.2:3b + qwen2.5:7b + deepseek-coder (~15 GB)
  • Nivel 3 (32 GB+ RAM): llama3.1:8b + mistral:7b (~10 GB)
  • Embeddings: nomic-embed-text (~0.3 GB)

Tu primera consulta a un modelo local

Llegamos al momento estrella del episodio. El momento en que tu máquina cobra vida y empieza a responder con inteligencia, sin internet, sin enviar datos a nadie.

El script primera-consulta.sh te guía paso a paso:

Paso 1 — Verificar que Ollama está instalado y corriendo. Comprueba que el binario existe y que la API REST responde en http://localhost:11434.

Paso 2 — Descargar un modelo pequeño. Usamos qwen2.5:3b, que ocupa unos 2 GB y funciona en cualquier equipo con 8 GB de RAM.

Paso 3 — Hacer una consulta desde terminal. Aquí es donde ocurre la magia. El script lanza una pregunta y el modelo responde. La primera vez que ves eso en tu pantalla, créeme, se te queda una sonrisa tonta.

Paso 4 — Abrir Open WebUI. Si tienes Docker funcionando, te lleva a la interfaz gráfica en http://localhost:3000. Ahí puedes chatear, subir PDFs, pedir resúmenes…

Y si prefieres hacerlo a mano, los comandos básicos son:

# Descargar modelo
ollama pull qwen2.5:3b

# Consultar
ollama run qwen2.5:3b "Explícame qué es un modelo de lenguaje cuantizado"

# Ver API
curl http://localhost:11434/api/tags

# Ver logs
systemctl status ollama
journalctl -u ollama

Estrategia híbrida: local + nube

Aquí viene la reflexión final. Se trata de usar cada uno donde mejor encaja, local o nube según el caso.

En febrero de 2025, el Stanford Hazy Research Lab publicó un paper llamado Minions que describe exactamente este enfoque: un modelo local pequeño hace el trabajo pesado de procesamiento, y solo consulta al modelo grande en la nube para tareas complejas. El resultado es que el 90% de las consultas se resuelven localmente, y solo el 10% restante va a la nube. El ahorro en costes de API es brutal: si cada consulta a GPT-4o te cuesta unos céntimos, y el 90% de tus consultas diarias las resuelves con un modelo local gratuito, la factura mensual se desploma. Para un uso intensivo — digamos 500 consultas al día —, pasar de todo en la nube a un modelo híbrido puede reducir los costes de 150 euros al mes a menos de 15. Y encima ganas en privacidad.

Para configurar este enfoque híbrido en Open WebUI, es tan sencillo como añadir un proveedor externo en la configuración. Dentro de Open WebUI, vas a Admin Panel > Connections y añades un endpoint de OpenRouter o directamente de OpenAI. Luego, en cada conversación, puedes elegir qué modelo usar: qwen2.5:7b para el día a día, y si necesitas algo más potente, cambias a gpt-4o o claude-sonnet desde el mismo selector de modelos. Sin salir de la interfaz.

Cuándo usar local

  • Para el 80% de las tareas diarias: resúmenes, traducciones, clasificación, generación de ideas.
  • Con datos sensibles o privados: facturas, correos personales, documentación interna.
  • Cuando no hay conexión a internet: en un avión, en el campo, en una cafetería sin WiFi.
  • Para experimentar y aprender: porque romper cosas en local no cuesta dinero.

Cuándo usar nube

  • Modelos que no caben localmente: 70B, 120B, o los modelos de pago como GPT-4o o Claude.
  • Tareas que requieren razonamiento multi-paso complejo: análisis jurídicos, planificación estratégica.
  • Cuando la velocidad de generación es crítica: porque una GPU en la nube siempre va más rápido que tu CPU.
  • Para fine-tuning o entrenamiento.

Herramientas para el enfoque híbrido

HerramientaFunción
OpenRouterGateway único para múltiples modelos cloud
LiteLLMAPI unificada para 100+ proveedores
Ollama Cloud ModelsModelos cloud directamente desde Ollama (desde sept 2025)
Secure MinionsProtocolo de comunicación cifrada local-nube (Stanford)

En mi caso, tengo configurado Open WebUI para que use qwen2.5:7b por defecto (local), pero si necesito algo más potente, puedo cambiar a un modelo cloud desde el mismo interfaz. Lo mejor de ambos mundos.

Conclusión

Llegados a este punto, la conclusión después de esta temporada es que no necesitas una GPU de 3000 euros para tener un laboratorio de IA local funcional. Con un portátil de 16 GB de RAM, un SSD y ganas de trastear, tienes más que suficiente.

Ollama te da el motor, Open WebUI la interfaz, Whisper la transcripción, Kokoro la voz, y sqlite-vec la memoria semántica. Todo corre en tu máquina, todo es gratis, todo es privado.

Y cuando necesites más potencia, siempre puedes tirar de la nube. Pero para el día a día, para experimentar, para aprender, para tener un asistente que realmente es tuyo… lo local es el camino.

Este es el último episodio de la temporada 8. El próximo, el 828, ya es el primero de la novena temporada. Y te avanzo que vamos a seguir profundizando: skills para agentes, herramientas, automatizaciones… pero siempre desde la base que hemos construido hoy.


Más información

Deja una respuesta