🤖
LocalAI — Servidor de IA Local
Plan de migración para NexusData SL · Junio 2026 · CULTIVA IA
Apache-2.0 CPU-only compatible Sin GPU requerida OpenAI SDK drop-in Ubuntu 22.04 · Docker
📊
Impacto de la migración
Ahorro mensual estimado
~290 €
De 320 €/mes (OpenAI) a ~30 €/mes (VPS existente)
Privacidad
100%
Datos procesados en servidor propio, sin envíos externos
Reescritura de código
0 líneas
Compatible con openai SDK npm — solo cambiar baseURL
Tiempo de migración
5 días
Setup + tests + switch en producción
Hardware requerido
16 cores
64 GB RAM · Sin GPU · VPS existente
Modelos a desplegar
4
Chat · Embeddings · Whisper · Código (opcional)
💰
Comparativa de costes mensual
OpenAI GPT-4o
185 €
OpenAI Embeddings
88 €
OpenAI Whisper
47 €

Total OpenAI
320 €
Total LocalAI
~30 €
ROI positivo desde el mes 1. El VPS de NexusData (ya pagado) tiene capacidad suficiente. LocalAI + modelos GGUF son gratuitos y de licencia comercial. Ahorro anual estimado: ~3.480 €.
🏗️
Arquitectura propuesta
Aplicación NexusData SL
Frontend
Next.js
Chat UI
Backend
Node.js API
openai SDK npm
Base de datos
PostgreSQL
+ pgvector
↓ baseURL: http://localai:8080/v1
LocalAI Gateway
localhost:8080
API compatible OpenAI
↓ rutas por modelo
Chat
Mistral 7B
Q5_K_M · 5 GB
Embeddings
all-MiniLM-L6
SentenceTransformers
Transcripción
Whisper Base
148 MB · offline
Todo en VPS Ubuntu 22.04 · Docker Compose · Sin GPU
🧠
Selección de modelos para NexusData
Modelo Propósito Cuantización RAM uso Velocidad CPU Calidad vs GPT
Mistral-7B-Instruct v0.2
TheBloke/GGUF
Chat soporte Q5_K_M ~5.1 GB ~8 tok/s ~GPT-3.5
all-MiniLM-L6-v2
SentenceTransformers
Embeddings F32 nativo ~90 MB <1 ms/req Comparable
Whisper Base
OpenAI Whisper
Transcripción INT8 ~148 MB ~5x RT ~Whisper API
CodeLlama-7B-Instruct
Meta · opcional
SQL / código Q4_K_M ~4.2 GB ~10 tok/s Especializado
Gestión de RAM: Con 64 GB disponibles, ejecutar Mistral (5 GB) + MiniLM (90 MB) + Whisper (150 MB) simultáneamente ocupa ~6 GB — muy holgado. El contexto de cada llamada usa adicionalmente ~(context_size × 2 MB). Con CONTEXT_SIZE=4096 eso es 8 MB por sesión activa.
🐳
docker-compose.yml — Producción NexusData
YAML docker-compose.yml
version: "3.8"
services:
  localai:
    image: localai/localai:latest-cpu
    container_name: nexusdata-localai
    ports:
      - "8080:8080"
    volumes:
      - ./models:/build/models
      - ./models-cache:/build/.cache
    environment:
      - THREADS=14            # 16 cores físicos - 2 para OS
      - CONTEXT_SIZE=4096    # ~8MB RAM por sesión
      - PARALLEL_REQUESTS=4  # Hasta 4 inferencias simultáneas
      - GALLERIES='[{"name":"model-gallery","url":"github:mudler/LocalAI/gallery/index.yaml@master"}]'
      - MODELS_PATH=/build/models
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/readyz"]
      interval: 30s
      timeout: 10s
      retries: 3
    mem_limit: 32g       # Límite 32 GB (la mitad del VPS)
    cpus: "14"
⚙️
Configuración YAML de modelos
YAML models/mistral.yaml
name: mistral
backend: llama-cpp
parameters:
  model: mistral-7b-instruct-v0.2.Q5_K_M.gguf
  temperature: 0.7
  top_p: 0.9
  top_k: 40
  context_size: 8192
  threads: 14
  gpu_layers: 0       # CPU-only
template:
  chat_message: |
    {{.RoleName}}: {{.Content}}
  chat: |
    [INST] {{.Input}} [/INST]
YAML models/embedding.yaml
name: text-embedding-ada-002
backend: sentencetransformers
parameters:
  model: all-MiniLM-L6-v2
# Mapea el nombre openai original
# para que el código existente
# funcione sin cambios

---
name: whisper-1
backend: whisper
parameters:
  model: whisper-base.bin
  language: es     # Reuniones en español
  translate: false
🔧
Integración TypeScript — zero cambios al código existente
TypeScript src/lib/ai-client.ts
import OpenAI from 'openai';

// ⚡ ÚNICO CAMBIO: baseURL apunta a LocalAI en lugar de api.openai.com
const ai = new OpenAI({
  apiKey: 'not-needed',              // LocalAI no requiere API key
  baseURL: 'http://localai:8080/v1', // Docker service name = localai
});

// ✅ Chat de soporte — funciona exactamente igual que antes
export async function chatSoporte(pregunta: string): Promise<string> {
  const resp = await ai.chat.completions.create({
    model: 'mistral',
    messages: [
      { role: 'system', content: 'Eres asistente de análisis de datos industriales de NexusData. Responde en español.' },
      { role: 'user',   content: pregunta },
    ],
    temperature: 0.6,
  });
  return resp.choices[0].message.content ?? '';
}

// ✅ Embeddings para búsqueda semántica en documentos técnicos
export async function embedDocumento(texto: string): Promise<number[]> {
  const resp = await ai.embeddings.create({
    model: 'text-embedding-ada-002', // Mismo nombre → mapeado a all-MiniLM
    input: texto,
  });
  return resp.data[0].embedding;
}

// ✅ Transcripción de reuniones
export async function transcribirReunion(audio: File): Promise<string> {
  const resp = await ai.audio.transcriptions.create({
    model: 'whisper-1',  // Mismo nombre → mapeado a whisper-base
    file: audio,
    language: 'es',
  });
  return resp.text;
}
Tip de migración: Configura LOCALAI_BASE_URL como variable de entorno en .env.local y úsala como baseURL. En desarrollo apunta a http://localhost:8080/v1, en producción a http://localai:8080/v1. Sin tocar código.
📅
Plan de migración — 5 días
1
Lunes · Día 1
Setup infraestructura
  • Docker + Docker Compose en VPS Ubuntu 22.04
  • docker pull localai/localai:latest-cpu (~2 GB)
  • Crear directorios ./models y ./models-cache
  • Arrancar contenedor con docker-compose.yml base
  • Verificar GET /readyz devuelve 200
2
Martes · Día 2
Descargar e instalar modelos
  • Mistral-7B-Instruct Q5_K_M (~5.1 GB desde HuggingFace)
  • all-MiniLM-L6-v2 vía gallery API (automático)
  • Whisper Base (~148 MB)
  • Crear YAML de configuración para cada modelo
  • Probar cada endpoint con curl desde el VPS
3
Miércoles · Día 3
Integración con la app NexusData
  • Añadir LOCALAI_BASE_URL a variables de entorno
  • Crear cliente ai-client.ts con baseURL dinámica
  • Tests unitarios: chat, embeddings, transcripción
  • Validar que los embeddings generan vectores de dimensión correcta
  • Probar streaming en el chat de soporte
4
Jueves · Día 4
QA y benchmark de calidad
  • 100 preguntas de soporte reales → comparar respuestas Mistral vs GPT-4o
  • Benchmark embeddings: cosine similarity en documentos de referencia
  • Test transcripción: 10 fragmentos de audio de reuniones
  • Medir latencia P50/P95 bajo carga concurrente (4 requests)
  • Ajustar THREADS y CONTEXT_SIZE según resultados
5
Viernes · Día 5
Switch a producción
  • Cambiar variable de entorno producción a http://localai:8080/v1
  • Deploy gradual: 10% tráfico → 50% → 100%
  • Monitorizar logs LocalAI y métricas de la app
  • Cancelar suscripción OpenAI al confirmar estabilidad
  • Documentar runbook de operaciones (restart, logs, actualizar modelos)
Checklist de operaciones recomendadas
Configuración
  • THREADS = núcleos físicos (no lógicos)
    Hyperthreading no mejora inferencia LLM
  • Cuantización Q5_K_M como default
    Mejor equilibrio calidad/velocidad
  • Un modelo por propósito (chat / embed / audio)
    Evitar sobrecargar un solo modelo
  • health check en docker-compose
    Reinicio automático si falla
  • Nginx reverse proxy con rate limiting
    Opcional si exponer al exterior
Mantenimiento
  • Versionar YAMLs de modelos en git
    Reproducibilidad del setup
  • Backup del directorio ./models
    Los modelos son voluminosos, no re-descargar innecesariamente
  • Alertas en logs de errores de inferencia
    Grafana + Loki sobre Docker logs
  • Probar nueva versión de LocalAI en staging
    Antes de actualizar imagen en producción
  • Evaluar Llama 3.1 8B como reemplazo de Mistral
    Mayor calidad, misma RAM aprox.
⬇️
Comandos de instalación y verificación
Bash setup.sh — Script de instalación completo
#!/bin/bash
# NexusData SL — LocalAI Setup Script
# VPS: Ubuntu 22.04, 16 cores, 64 GB RAM

# 1. Crear directorios
mkdir -p ./localai/{models,models-cache}
cd ./localai

# 2. Descargar modelos
wget -P ./models/ \
  https://huggingface.co/TheBloke/Mistral-7B-Instruct-v0.2-GGUF/resolve/main/mistral-7b-instruct-v0.2.Q5_K_M.gguf

wget -P ./models/ \
  https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin
mv ./models/ggml-base.bin ./models/whisper-base.bin

# 3. Arrancar LocalAI
docker compose up -d

# 4. Esperar hasta que esté listo
until curl -sf http://localhost:8080/readyz; do sleep 5; done
echo "✓ LocalAI listo"

# 5. Instalar all-MiniLM-L6-v2 via gallery
curl -X POST http://localhost:8080/models/apply \
  -H "Content-Type: application/json" \
  -d '{"id": "huggingface://mudler/all-MiniLM-L6-v2-gguf/all-MiniLM-L6-v2.gguf"}'

# 6. Verificar modelos disponibles
curl -s http://localhost:8080/v1/models | jq '.data[].id'

# Output esperado:
# "mistral"
# "text-embedding-ada-002"
# "whisper-1"