Renderz Studio
Academia
Proyectos
Servicios
Arsenal
Repo
Herramientas
Skills
El Oráculo
Guía Gratis
← El Arsenal
ArquitecturaAvanzado50 min

RAG desde Cero hasta Producción

Embeddings · Vector DB · Chunking · el sistema que elimina alucinaciones

RAGEmbeddingsVector DB
✓ Guía gratuita · Acceso completo

Mapa rápido · salta al paso que te interese

Qué es RAG y cuándo usarlo

El patrón explicado sin bullshit

Pipeline completo

Ingesta, chunking, embeddings, retrieval

Código de producción

Con Supabase pgvector

01

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 RAGCon RAG
Modelo inventa datosModelo cita tus documentos
Desactualizado (training cutoff)Siempre actualizado
GenéricoEspecífico a tu dominio
No sabes de dónde vieneTrazabilidad completa

El flujo de RAG

  1. Ingesta: Tus documentos se procesan y se guardan como embeddings
  2. Query: La pregunta del usuario se convierte a embedding
  3. Retrieval: Se buscan los documentos más similares (cosine similarity)
  4. Generation: Claude recibe la pregunta + los documentos como contexto
  5. Response: La respuesta cita directamente tus fuentes

⚠ Advertencia

RAG no es magia. Si tus documentos son basura, el output va a ser basura con citas. Garbage in, garbage out — pero ahora con referencias.
02

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.

EstrategiaCuándo usarlaChunk size
Por párrafosTexto narrativo500-1000 chars
Por secciones (headers)Docs técnicos1000-3000 chars
Sliding windowCuando contexto importa500 chars, 100 overlap
SemánticoDocs complejosVariable, por tema
Chunking con overlap
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

Overlap es clave. Sin overlap, perdés contexto en los bordes. Con 10-20% de overlap, los chunks se solapan y no perdés información en los cortes.
03

Código de producción listo para deploy

Un endpoint completo que puedes deployar en Vercel ahora mismo.

app/api/rag/query/route.ts
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

Cacheá embeddings de queries comunes. Si la misma pregunta llega 100 veces, no pagues 100 veces el embedding. Redis o Supabase cache.

"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

Guías relacionadas

Supabase + IA

pgvector para tu RAG

Optimización de Costes

Cacheá embeddings y ahorrá

¿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 · €29

PayPal · Acceso inmediato

¿Hablamos?