Software 📅 17/04/2026

Node.js + LLM en Producción: Ollama y APIs IA sin Dependencias

Node.js + LLM en Producción: Ollama y APIs IA sin Dependencias

De prototipos con ChatGPT a sistemas productivos con control total: cómo una startup puede ahorrar 90% en costos de IA corriendo modelos locales


El Problema Que Nadie Quiere Admitir

Tu startup construye un producto con la API de OpenAI. Funciona. Los primeros clientes lo aman. Pero en Q3 te enfrentas a la realidad:

Y ahí comienza la realidad: prototipar con APIs propietarias es fácil. Pasar a producción sin ellas es donde los desarrolladores desaparecen por una semana.

En 2026, esa semana no es necesaria.

NodeLLM, Ollama y el ecosistema de JavaScript para LLMs han madurado lo suficiente para que una startup de 3 personas despliegue un sistema de IA robusto sin tocar Python, sin usar GPUs de lujo, y sin pagar €8.000/mes a OpenAI.

Esta guía es para developers que quieren construir APIs de IA que funcionen en producción, con código que puedan mantener, sin que cada feature request signifique una llamada a OpenAI.


¿Por Qué Node.js Para LLMs?

La pregunta es legítima. ¿No debería hacerse esto en Python?

Respuesta corta: Sí, pero no siempre.

Respuesta larga:

JavaScript domina el backend moderno en startups. Si tu arquitectura ya es Node.js (Express, NestJS, Fastify), agregar IA localmente significa:

Además, en 2026 las librerías de Node.js para LLMs son production-ready:


El Stack 2026: De Prototipos a Producción

Nivel 1: Prototipos Rápidos (Semana 1)

npm install openai # o anthropic, o cohere

Nivel 2: Self-Hosted Local (Semana 2-3)

Herramientas: Ollama (servidor local, UI opcional), Ollama Node SDK o node-llama-cpp (conexión desde Node), Express (tu API).

Nivel 3: Híbrido + Observabilidad (Mes 2)

Stack: NodeLLM (orquestación multi-provider), Vector DB (Pinecone, Weaviate o local con Qdrant), Redis (caching de embeddings), Prometheus + Grafana (monitoreo).


Implementación Real: Chatbot en 2 Horas

Opción 1: Local + Ollama (Sin Dinero)

Paso 1: Instala Ollama

# macOS/Linux: descarga de ollama.ai
# Windows: WSL2 + Ollama

# Descarga un modelo
ollama pull llama2
ollama pull mistral

Paso 2: Node.js Server

// server.js
const express = require('express');
const { Ollama } = require('ollama');

const app = express();
app.use(express.json());

const ollama = new Ollama({ base_url: 'http://localhost:11434' });

app.post('/api/chat', async (req, res) => {
  const { message } = req.body;

  try {
    const response = await ollama.generate({
      model: 'mistral', // cambia según tu modelo
      prompt: message,
      stream: false,
    });

    res.json({ reply: response.response });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000, () => console.log('API en puerto 3000'));

Paso 3: Test

curl -X POST http://localhost:3000/api/chat \
  -H "Content-Type: application/json" \
  -d '{"message":"¿Qué es la IA?"}'

Costo: €0. Tiempo: 30 minutos.
Limitación: Llama responde lento (5-15s dependiendo del modelo y GPU).

Opción 2: NodeLLM + OpenAI (Control + APIs)

// server.js (usando NodeLLM)
const { NodeLLM } = require('@node-llm/core');

const llm = NodeLLM.chat('gpt-4o'); // zero-config: lee API key de ENV

app.post('/api/chat', async (req, res) => {
  const { message } = req.body;

  try {
    const response = await llm.ask(message);
    res.json({ reply: response.content });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Ventaja: El mismo código funciona con OpenAI, Anthropic, Gemini, Ollama local...

// Cambias el provider en una línea
const llm = NodeLLM.chat('claude-3.5-sonnet'); // Ahora es Anthropic
const llm = NodeLLM.chat('mistral-large'); // O Mistral

Costo: Pay-per-token con APIs. Control total de modelo.

Opción 3: Streaming (Respuestas en Tiempo Real)

El cliente ve el texto generarse carácter por carácter (como ChatGPT).

app.post('/api/chat-stream', async (req, res) => {
  const { message } = req.body;

  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  try {
    for await (const chunk of llm.stream(message)) {
      res.write(`data: ${JSON.stringify({ content: chunk.content })}\n\n`);
    }
    res.write('data: [DONE]\n\n');
    res.end();
  } catch (error) {
    res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
    res.end();
  }
});

Frontend (JavaScript vanilla):

const eventSource = new EventSource('/api/chat-stream');
let text = '';

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.content) {
    text += data.content;
    document.getElementById('response').textContent = text;
  }
};

eventSource.onerror = () => {
  eventSource.close();
};

Costo: Mismo costo. UX: 10x mejor.


Costos Reales: Local vs OpenAI API

Caso: SaaS con 1.000 usuarios activos/mes

Asunción: 5 llamadas promedio por usuario, 500 tokens/llamada.

Opción 1: OpenAI API (GPT-4)

Opción 2: Ollama + M2 Mac

Opción 3: Híbrido (Smart Routing)

// Si la pregunta es simple → Mistral local (gratis)
// Si necesita razonamiento complejo → GPT-4 (pago)

const selectModel = async (message) => {
  const complexity = await estimateComplexity(message); // heurística simple

  if (complexity < 3) {
    return 'mistral-local'; // < 1ms latencia, costo 0
  } else {
    return 'gpt-4o'; // 200ms latencia, costo $0.003
  }
};

Resultado:


Patrones de Producción

1. Caching de Respuestas

const redis = require('redis');
const client = redis.createClient();

app.post('/api/chat', async (req, res) => {
  const { message } = req.body;
  const hash = crypto.createHash('sha256').update(message).digest('hex');

  // Intenta leer del cache
  const cached = await client.get(`chat:${hash}`);
  if (cached) {
    return res.json(JSON.parse(cached));
  }

  // Si no está en cache, genera respuesta
  const response = await llm.ask(message);

  // Guarda en cache por 24 horas
  await client.setex(`chat:${hash}`, 86400, JSON.stringify(response));

  res.json(response);
});

Impacto: Elimina 60-70% de latencia para preguntas recurrentes.

2. Rate Limiting

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 minuto
  max: 10, // 10 requests/minuto por IP
  message: 'Demasiadas solicitudes, intenta más tarde',
});

app.post('/api/chat', limiter, async (req, res) => {
  // ... tu lógica
});

Protege contra: Spam, abuso, costos explosivos con APIs.

3. Versionado de Modelos

const MODELS = {
  'v1': 'gpt-4o',      // 2026-01
  'v2': 'gpt-4.5',     // 2026-03
  'experimental': 'o1', // Nuevo
};

app.post('/api/chat', async (req, res) => {
  const { message, version = 'v1' } = req.body;
  const model = MODELS[version] || MODELS['v1'];

  const response = await NodeLLM.chat(model).ask(message);
  res.json(response);
});

Ventaja: Testing A/B de modelos sin downtime.

4. Error Handling + Fallback

const chatWithFallback = async (message) => {
  try {
    // Intenta con OpenAI primero (mejor calidad)
    return await NodeLLM.chat('gpt-4o').ask(message);
  } catch (error) {
    console.log('OpenAI falló, usando local...');

    try {
      // Fallback a Mistral local
      return await NodeLLM.chat('mistral').ask(message);
    } catch (innerError) {
      // Última opción: respuesta cacheada o predefinida
      return { content: 'Lo siento, no puedo responder ahora. Intenta más tarde.' };
    }
  }
};

Beneficio: Confiabilidad sin depender de un solo provider.


Debugging + Observabilidad

Logging de Prompts y Costos

const winston = require('winston');

const logger = winston.createLogger({
  transports: [new winston.transports.File({ filename: 'ai-calls.log' })],
});

app.post('/api/chat', async (req, res) => {
  const { message } = req.body;
  const startTime = Date.now();

  const response = await llm.ask(message);

  const latency = Date.now() - startTime;
  const estimatedCost = calculateCost(response.model, response.tokens);

  logger.info({
    timestamp: new Date(),
    message: message.substring(0, 100),
    model: response.model,
    tokens: response.tokens,
    latency_ms: latency,
    cost_usd: estimatedCost,
  });

  res.json(response);
});

Output esperado:

{
  "timestamp": "2026-04-16T10:30:00Z",
  "message": "¿Qué es un API?",
  "model": "gpt-4o",
  "tokens": 145,
  "latency_ms": 890,
  "cost_usd": 0.0043
}

Analítica: Promedio latencia: 850ms | Costo total/mes: $230 | Modelos más usados: GPT-4 (60%), Mistral (40%).


Errores Comunes en Producción

Error 1: Confundir Prototipos con Sistemas

Peligro: El código funciona en tu laptop pero explota con 100 usuarios concurrentes.

// ❌ MALO: Sin control de concurrencia
app.post('/api/chat', async (req, res) => {
  const response = await ollama.generate({ ... });
  res.json(response);
});

Problema: Si Ollama procesa 1 request a la vez y llegan 50 simultáneos, todos esperan 50x el tiempo.

// ✅ BUENO: Queue + Pool
const pLimit = require('p-limit');
const limit = pLimit(4); // máximo 4 requests concurrentes a Ollama

app.post('/api/chat', async (req, res) => {
  const response = await limit(() => ollama.generate({ ... }));
  res.json(response);
});

Error 2: No Monitorear Costos

Peligro: Una llamada recursiva accidental genera €2.000 en factura de OpenAI.

// ❌ MALO: Sin límite de tokens
const response = await llm.ask(userInput);

// ✅ BUENO: Con límite
const response = await llm.ask(userInput, {
  max_tokens: 500, // límite estricto
  temperature: 0.7,
});

Error 3: Token Injection Attacks

Peligro: Un usuario malicioso inyecta instrucciones en el prompt.

// ❌ MALO: Concatenación directa
const prompt = `Responde: ${userInput}`;
const response = await llm.ask(prompt);

// ✅ BUENO: Sanitización + System Prompt
const systemPrompt = `Eres un asistente amable. 
Tu rol es responder preguntas sobre productos.
NUNCA divulges información interna o de clientes.`;

const response = await llm.ask(userInput, {
  system: systemPrompt,
  // userInput está aislado, no puede romper el context
});

Roadmap 2026


Conclusión: El Stack Ganador

No necesitas Python. No necesitas datacenters. No necesitas pagar €8.000/mes en tu factura de OpenAI. En 2026, un stack de IA productivo cabe en un MacBook y cuesta €5/mes en electricidad.

El código que escribas hoy seguirá corriendo en 2028. El provider que elijas hoy podría dejar de existir mañana. Elige sabiamente.


Recursos

También te puede interesar

PHP 8.5 y la IA - lo nuevo
Software

PHP 8.5 y la IA - lo nuevo

Cómo Programar con Agentes IA Autónomos en 2026: Guía Práctica para Desarrolladores
Software

Cómo Programar con Agentes IA Autónomos en 2026: Guía Práctica para Desarrolladores