¿Qué es RAG?
Retrieval Augmented Generation (RAG) es una técnica que mejora las respuestas de los LLMs recuperando información relevante de fuentes de conocimiento externas. En lugar de depender solo de los datos de entrenamiento del modelo, RAG permite que tu IA acceda y use tus propios documentos, bases de datos o APIs.
📚 Pipeline de RAG
- Cargar: Importa documentos desde varias fuentes
- Dividir: Parte documentos en fragmentos más pequeños
- Incrustar: Convierte texto en representaciones vectoriales
- Almacenar: Guarda embeddings en una base de datos vectorial
- Recuperar: Encuentra fragmentos relevantes para una consulta
- Generar: Usa el contexto recuperado para generar respuestas
Cargadores de documentos
LangChain ofrece loaders para muchos tipos de documentos:
Los loaders normalizan archivos y páginas web en un formato de documento consistente para los pasos posteriores.
// Text files
import { TextLoader } from "langchain/document_loaders/fs/text";
const textLoader = new TextLoader("./docs/readme.txt");
// PDF files
import { PDFLoader } from "langchain/document_loaders/fs/pdf";
const pdfLoader = new PDFLoader("./docs/manual.pdf");
// Web pages
import { CheerioWebBaseLoader } from "langchain/document_loaders/web/cheerio";
const webLoader = new CheerioWebBaseLoader("https://example.com/docs");
// JSON files
import { JSONLoader } from "langchain/document_loaders/fs/json";
const jsonLoader = new JSONLoader("./data/products.json");
// Load documents
const docs = await pdfLoader.load();
console.log(docs.length, "documents loaded");
División de texto
Divide documentos en fragmentos que quepan dentro de los límites de contexto del modelo:
Fragmentos más pequeños mejoran la precisión de recuperación y mantienen los prompts dentro del límite de tokens.
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000, // Max characters per chunk
chunkOverlap: 200, // Overlap between chunks
separators: ["\n\n", "\n", " ", ""], // Split priorities
});
const docs = await loader.load();
const splitDocs = await splitter.splitDocuments(docs);
console.log(`Split into ${splitDocs.length} chunks`);
Embeddings
Convierte texto a vectores para búsqueda semántica:
Los embeddings convierten texto en vectores numéricos para comparar similitud y recuperar contexto relevante.
import { OpenAIEmbeddings } from "@langchain/openai";
const embeddings = new OpenAIEmbeddings({
modelName: "text-embedding-3-small",
});
// Embed a single text
const vector = await embeddings.embedQuery("What is LangChain?");
console.log(vector.length); // 1536 dimensions
// Embed multiple texts
const vectors = await embeddings.embedDocuments([
"LangChain is a framework",
"It helps build AI apps",
]);
Vector stores
Almacena y busca embeddings de forma eficiente:
Vector store en memoria
El store en memoria es ideal para prototipos y datasets pequeños sin infraestructura externa.
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
// Create vector store from documents
const vectorStore = await MemoryVectorStore.fromDocuments(
splitDocs,
new OpenAIEmbeddings()
);
// Search for similar documents
const results = await vectorStore.similaritySearch(
"How do I use chains?",
4 // Return top 4 results
);
console.log(results);
Usar Pinecone
Usa una base de datos vectorial gestionada como Pinecone para escalabilidad y persistencia.
import { Pinecone } from "@pinecone-database/pinecone";
import { PineconeStore } from "@langchain/pinecone";
const pinecone = new Pinecone();
const index = pinecone.index("langchain-docs");
// Create store
const vectorStore = await PineconeStore.fromDocuments(
splitDocs,
new OpenAIEmbeddings(),
{ pineconeIndex: index }
);
// Or connect to existing
const existingStore = await PineconeStore.fromExistingIndex(
new OpenAIEmbeddings(),
{ pineconeIndex: index }
);
Cadena RAG completa
Este ejemplo completo conecta embeddings, un retriever y un prompt para responder preguntas usando tus datos.
import { ChatOpenAI } from "@langchain/openai";
import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnableSequence, RunnablePassthrough } from "@langchain/core/runnables";
// Setup
const model = new ChatOpenAI({ modelName: "gpt-4" });
const embeddings = new OpenAIEmbeddings();
// Create vector store from your documents
const vectorStore = await MemoryVectorStore.fromDocuments(
splitDocs,
embeddings
);
// Create retriever
const retriever = vectorStore.asRetriever({
k: 4, // Number of documents to retrieve
});
// RAG prompt
const ragPrompt = ChatPromptTemplate.fromTemplate(`
Answer the question based only on the following context:
{context}
Question: {question}
If the answer is not in the context, say "I don't have enough information to answer that."
`);
// Helper to format documents
const formatDocs = (docs: Document[]) =>
docs.map(doc => doc.pageContent).join("\n\n");
// Create RAG chain
const ragChain = RunnableSequence.from([
{
context: retriever.pipe(formatDocs),
question: new RunnablePassthrough(),
},
ragPrompt,
model,
new StringOutputParser(),
]);
// Ask questions
const answer = await ragChain.invoke(
"How do I create a chain in LangChain?"
);
console.log(answer);
Avanzado: RAG conversacional
RAG conversacional reescribe las preguntas del usuario usando el historial y luego recupera y responde con contexto.
import { createHistoryAwareRetriever } from "langchain/chains/history_aware_retriever";
import { createRetrievalChain } from "langchain/chains/retrieval";
import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
// Contextualize question based on chat history
const contextualizePrompt = ChatPromptTemplate.fromMessages([
["system", "Given chat history and a question, reformulate it to be standalone."],
new MessagesPlaceholder("chat_history"),
["human", "{input}"],
]);
const historyAwareRetriever = await createHistoryAwareRetriever({
llm: model,
retriever,
rephrasePrompt: contextualizePrompt,
});
// Answer with context
const answerPrompt = ChatPromptTemplate.fromMessages([
["system", "Answer based on context: {context}"],
new MessagesPlaceholder("chat_history"),
["human", "{input}"],
]);
const documentChain = await createStuffDocumentsChain({
llm: model,
prompt: answerPrompt,
});
const conversationalRagChain = await createRetrievalChain({
retriever: historyAwareRetriever,
combineDocsChain: documentChain,
});
// Use with history
const response = await conversationalRagChain.invoke({
chat_history: [],
input: "What is LangChain?",
});
💡 Puntos clave
- • RAG conecta LLMs con tu propia base de conocimiento
- • Los loaders de documentos soportan PDFs, páginas web, JSON y más
- • Los text splitters crean tamaños óptimos para la recuperación
- • Los vector stores habilitan búsqueda semántica sobre embeddings
- • Combina recuperación con prompts para respuestas con contexto
📚 Más recursos
-
Tutorial de RAG →
Guía paso a paso para crear aplicaciones RAG.
-
Documentación de recuperación →
Todos los loaders, splitters y vector stores.