RAG desde Cero hasta Producción
Embeddings · Vector DB · Chunking · el sistema que elimina alucinaciones
Mapa rápido · salta al paso que te interese
Qué es RAG y cuándo usarlo
RAG = Retrieval Augmented Generation. En lugar de confiar en que el modelo "sabe" la respuesta, le das contexto relevante antes de que responda. El resultado: respuestas basadas en tus datos, no en alucinaciones.
| Sin RAG | Con RAG |
|---|---|
| Modelo inventa datos | Modelo cita tus documentos |
| Desactualizado (training cutoff) | Siempre actualizado |
| Genérico | Específico a tu dominio |
| No sabes de dónde viene | Trazabilidad completa |
El flujo de RAG
- Ingesta: Tus documentos se procesan y se guardan como embeddings
- Query: La pregunta del usuario se convierte a embedding
- Retrieval: Se buscan los documentos más similares (cosine similarity)
- Generation: Claude recibe la pregunta + los documentos como contexto
- Response: La respuesta cita directamente tus fuentes
⚠ Advertencia
Pipeline completo: cada componente
Un pipeline de RAG tiene 4 componentes principales. Cada uno tiene trade-offs que afectan calidad y costo.
No puedes meter un PDF de 500 páginas como contexto. Hay que partirlo en chunks que quepan y que tengan sentido semántico.
| Estrategia | Cuándo usarla | Chunk size |
|---|---|---|
| Por párrafos | Texto narrativo | 500-1000 chars |
| Por secciones (headers) | Docs técnicos | 1000-3000 chars |
| Sliding window | Cuando contexto importa | 500 chars, 100 overlap |
| Semántico | Docs complejos | Variable, por tema |
function chunkText(text: string, chunkSize = 1000, overlap = 200): string[] {
const chunks: string[] = []
let start = 0
while (start < text.length) {
const end = Math.min(start + chunkSize, text.length)
chunks.push(text.slice(start, end))
start = end - overlap
}
return chunks
}
// Chunking semántico por headers
function chunkByHeaders(markdown: string): { title: string; content: string }[] {
const sections = markdown.split(/^## /gm)
return sections.map((section, i) => {
const lines = section.split('\n')
return {
title: i === 0 ? 'Intro' : lines[0],
content: lines.slice(1).join('\n').trim(),
}
}).filter(s => s.content.length > 50)
}Tip Pro
Código de producción listo para deploy
Un endpoint completo que puedes deployar en Vercel ahora mismo.
import { NextRequest } from 'next/server'
import Anthropic from '@anthropic-ai/sdk'
import OpenAI from 'openai'
import { createClient } from '@supabase/supabase-js'
const anthropic = new Anthropic()
const openai = new OpenAI()
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_KEY!
)
export async function POST(req: NextRequest) {
const { question } = await req.json()
if (!question || question.length > 1000) {
return Response.json({ error: 'Invalid question' }, { status: 400 })
}
// 1. Embed question
const embeddingRes = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: question,
})
const queryEmbedding = embeddingRes.data[0].embedding
// 2. Search similar docs
const { data: docs, error } = await supabase.rpc('match_documents', {
query_embedding: queryEmbedding,
match_count: 5,
match_threshold: 0.7,
})
if (error) {
return Response.json({ error: 'Search failed' }, { status: 500 })
}
if (!docs || docs.length === 0) {
return Response.json({
answer: 'No encontré información relevante en la base de conocimiento.',
sources: [],
})
}
// 3. Build context
const context = docs
.map((d: { content: string }, i: number) => `[Fuente ${i + 1}]: ${d.content}`)
.join('\n\n')
// 4. Generate answer
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 2048,
system: `Eres un asistente experto. Responde ÚNICAMENTE basándote en el contexto.
Si la información no está, di que no tienes esa información.
Citá las fuentes usando [Fuente N].`,
messages: [{
role: 'user',
content: `Contexto:\n${context}\n\nPregunta: ${question}`
}]
})
return Response.json({
answer: response.content[0].text,
sources: docs.map((d: { id: string; metadata: Record<string, unknown>; similarity: number }) => ({
id: d.id,
metadata: d.metadata,
similarity: d.similarity,
})),
tokens_used: response.usage.input_tokens + response.usage.output_tokens,
})
}Tip Pro
"RAG es la diferencia entre un chatbot que inventa y uno que sabe. Configura bien el chunking, elige el modelo de embedding correcto, y vas a tener un asistente que responde con TUS datos, no con alucinaciones."
— El Arsenal, Renderz Studio
¿Quieres el sistema completo?
500+ páginas, 25 capítulos, código de producción, acceso a todas las guías del Arsenal.
Curso Completo · €29PayPal · Acceso inmediato