Saltar al contenido principal

LangChain / Vercel AI SDK

Cuando los proyectos crecen, llamar directamente a las APIs de los LLMs se vuelve repetitivo y difícil de mantener. Los frameworks de orquestación resuelven esto añadiendo abstracciones para encadenar llamadas, gestionar prompts, conectar con bases de datos vectoriales y más.

Veremos dos: Vercel AI SDK y LangChain.

Vercel AI SDK

El Vercel AI SDK es el framework más popular para Node.js y Next.js. Es ligero, con excelente soporte de streaming, y tiene una API unificada para múltiples proveedores.

npm install ai

# Instala el proveedor que necesites:
npm install @ai-sdk/openai
npm install @ai-sdk/anthropic
npm install @ai-sdk/google
npm install @ai-sdk/ollama # para Ollama local

Generación de texto básica

La función generateText es la forma más directa de obtener una respuesta completa de un LLM. Es ideal para tareas síncronas donde no necesitas mostrar la respuesta al usuario mientras se genera (como procesar datos en segundo plano o generar resúmenes cortos).

import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';

const { text } = await generateText({
model: openai('gpt-4o-mini'),
prompt: '¿Qué es el event loop de Node.js?'
});

console.log(text);

Con system prompt y mensajes

Para un control más preciso, podemos usar un system prompt (instrucciones que el modelo sigue pero el usuario no ve) y una lista de mensajes que representan el historial de la conversación. Esto permite que el modelo mantenga el contexto de turnos anteriores.

import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';

const { text } = await generateText({
model: anthropic('claude-haiku-4-5'),
system: 'Eres un asistente experto en Node.js. Responde en español.',
messages: [
{ role: 'user', content: '¿Cómo manejo errores asíncronos?' }
]
});

Streaming de texto

El streaming es fundamental para una buena experiencia de usuario, ya que permite mostrar la respuesta palabra por palabra conforme se genera, reduciendo la percepción de latencia. streamText devuelve un iterador asíncrono para consumir los fragmentos (chunks).

import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

const result = streamText({
model: openai('gpt-4o-mini'),
prompt: 'Escribe un tutorial corto sobre Express.js'
});

for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}

Generación de objetos (structured output)

Tradicionalmente, pedirle JSON a un LLM era arriesgado porque podía cometer errores de formato. La función generateObject utiliza el esquema de validación de Zod para forzar al modelo a devolver datos estructurados y tipados con total fiabilidad.

import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';

const { object } = await generateObject({
model: openai('gpt-4o-mini'),
schema: z.object({
nombre: z.string(),
descripcion: z.string(),
ejemplos: z.array(z.string()).max(3),
nivel: z.enum(['básico', 'intermedio', 'avanzado'])
}),
prompt: 'Define qué es el patrón Repository en Node.js'
});

console.log(object);

Con Ollama (local)

El AI SDK soporta Ollama de forma nativa, lo que facilita enormemente el testeo local sin necesidad de claves de API externas.

import { generateText } from 'ai';
import { ollama } from '@ai-sdk/ollama';

const { text } = await generateText({
model: ollama('llama4:8b'),
prompt: '¿Cómo funciona require() en Node.js?'
});

Integración en Express con streaming (SSE)

Para enviar el stream del LLM al navegador en aplicaciones Node.js clásicas, utilizamos Server-Sent Events (SSE). Esto mantiene una conexión abierta donde el servidor empuja cada fragmento de texto al cliente.

import express from 'express';
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

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

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

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

const result = streamText({
model: openai('gpt-4o-mini'),
messages
});

for await (const chunk of result.textStream) {
res.write(`data: ${JSON.stringify({ text: chunk })}\n\n`);
}

res.write('data: [DONE]\n\n');
res.end();
});

app.listen(3000);

LangChain

LangChain es un framework más completo y complejo que el AI SDK. Brilla en casos de uso avanzados: cadenas de razonamiento, RAG elaborado y agentes complejos.

npm install langchain @langchain/openai @langchain/core

Chat básico

A diferencia del AI SDK, LangChain utiliza clases orientadas a objetos e instancias de modelos que se invocan con el método .invoke().

import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';

const model = new ChatOpenAI({
model: 'gpt-4o-mini',
temperature: 0.5
});

const response = await model.invoke([
new SystemMessage('Eres un experto en Node.js.'),
new HumanMessage('¿Qué son los streams?')
]);

console.log(response.content);

Prompt Templates

Los prompt templates permiten desacoplar la lógica del texto. Definimos plantillas con variables (como {tecnologia}) que se rellenan dinámicamente. LangChain utiliza el método .pipe() para encadenar componentes de forma fluida (LCEL - LangChain Expression Language).

import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';

const prompt = ChatPromptTemplate.fromMessages([
['system', 'Eres un experto en {tecnologia}. Responde siempre en español.'],
['human', '{pregunta}']
]);

const model = new ChatOpenAI({ model: 'gpt-4o-mini' });

// Crear la cadena (chain)
const chain = prompt.pipe(model);

const response = await chain.invoke({
tecnologia: 'Node.js',
pregunta: '¿Cuál es la diferencia entre process.nextTick y setImmediate?'
});

console.log(response.content);

Output Parsers

Por defecto, los modelos devuelven objetos complejos con metadatos. Los parsers ayudan a extraer solo la información útil (como el texto limpio) o a transformar la salida en objetos JSON.

import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';
import { StringOutputParser } from '@langchain/core/output_parsers';

const prompt = ChatPromptTemplate.fromTemplate(
'Dame 5 casos de uso de {tecnologia} en formato lista numerada'
);

const model = new ChatOpenAI({ model: 'gpt-4o-mini' });
const parser = new StringOutputParser();

const chain = prompt.pipe(model).pipe(parser);

const result = await chain.invoke({ tecnologia: 'WebSockets' });
console.log(result); // String limpio (sin metadata)

Historial de conversación (memory)

A diferencia de las llamadas simples, el historial permite "recordar" lo dicho anteriormente. RunnableWithMessageHistory gestiona automáticamente la recuperación y guardado de los mensajes en un almacén de sesiones.

import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
import { RunnableWithMessageHistory } from '@langchain/core/runnables';
import { ChatMessageHistory } from '@langchain/community/stores/message/in_memory';

const model = new ChatOpenAI({ model: 'gpt-4o-mini' });

const prompt = ChatPromptTemplate.fromMessages([
['system', 'Eres un asistente de programación.'],
new MessagesPlaceholder('history'),
['human', '{input}']
]);

const chain = prompt.pipe(model);

// Almacén de sesiones (en producción: Redis, base de datos, etc.)
const store = {};

const chainWithHistory = new RunnableWithMessageHistory({
runnable: chain,
getMessageHistory: (sessionId) => {
if (!store[sessionId]) store[sessionId] = new ChatMessageHistory();
return store[sessionId];
},
inputMessagesKey: 'input',
historyMessagesKey: 'history'
});

// Uso
await chainWithHistory.invoke(
{ input: '¿Qué es Express.js?' },
{ configurable: { sessionId: 'user-123' } }
);

const response = await chainWithHistory.invoke(
{ input: '¿Y cómo añado middleware?' },
{ configurable: { sessionId: 'user-123' } }
);

console.log(response.content);

Vercel AI SDK vs LangChain

CriterioVercel AI SDKLangChain
SimplicidadMuy sencilloCurva de aprendizaje
StreamingExcelenteSoportado
Structured outputsCon ZodCon parsers
RAG avanzadoBásicoMuy completo
Agentes complejosLimitadoLangGraph
Next.js / FullstackIntegración nativaManual
MantenimientoActivo (Vercel)Cambia mucho la API
Recomendación

Empieza con el Vercel AI SDK para la mayoría de casos. Migra a LangChain solo si necesitas pipelines RAG complejos o agentes con LangGraph.