Todos los recursos

RAG no viene por defecto. Es una concesión a la que recurres cuando el conocimiento no cabe, cambia seguido o tiene que citarse. Esta guía te lleva primero por la decisión y después por el build: un stack real de chunk-embed-retrieve en Supabase, un ejemplo paso a paso y los detalles que, sin que te des cuenta, te dañan la recuperación.

RAG o meterlo directo en el prompt

En resumen

  • RAG es una concesión, no la opción por defecto. Recurre a él solo cuando el corpus no cabe, cambia todo el tiempo o necesitas citar las fuentes.
  • Las ventanas de contexto grandes hacen que "métele todo al prompt" sea viable más seguido de lo que la gente cree, y de paso te ahorra el peor problema de la recuperación: traer los fragmentos equivocados.
  • La calidad de RAG está en el chunking y la recuperación, no en el modelo. El mejor modelo, alimentado con malos chunks, responde con toda confianza a partir de algo apenas parecido, no de lo relevante.
  • A la búsqueda vectorial pura se le escapan los términos exactos: códigos de producto, nombres, mensajes de error. La recuperación híbrida (vector más keyword) es la solución, no un extra opcional.
  • Un stack que funciona en Supabase: fragmenta por límites semánticos, genera los embeddings, guárdalos en pgvector con su metadata, recupera las mejores coincidencias y cita lo que devolviste.

RAG se volvió un acto reflejo. Alguien tiene documentos y de una vez echa mano de embeddings, un vector store y una estrategia de chunking antes de preguntarse si de verdad los necesita. Siendo honestos, RAG es una concesión a la que recurres cuando el conocimiento no cabe en el contexto, cambia seguido o tiene que citarse. No es la opción por defecto para "el modelo necesita saber cosas". Esta guía te da una decisión que puedas defender y después recorre el build real en Supabase: schema, chunking, recuperación y un ejemplo paso a paso. Vas a terminar pudiendo explicar por qué usaste o no usaste recuperación, y cómo evitar que, sin que te enteres, te termine devolviendo lo que no es.

Nota

Requisitos: una llamada a un modelo que ya funcione (aquí Claude), una base Supabase/Postgres donde puedas correr migrations, y un modelo de embeddings que puedas llamar. No necesitas RAG para arrancar. La sección 01 existe justamente para convencerte de que no lo uses cuando el camino simple gana. Si ya decidiste que necesitas recuperación y solo quieres el build, pásate directo a la sección 04.

01 · Cuándo NO usar RAG

Empieza por aquí, porque el pipeline de RAG más barato es el que nunca llegas a construir. Olvídate de la recuperación y mete el contenido directo en el prompt cuando se cumpla cualquiera de estas:

  • Todo el corpus cabe holgado en la ventana de contexto. Con las ventanas grandes de hoy, un manual de políticas, un spec de producto o unas cuantas docenas de páginas de docs caben de sobra muchas veces. Si cabe, meterlo en el prompt es más simple, no hay un paso de recuperación que pueda salir mal, y el modelo ve todo en lugar de una tajada que alguien adivinó.
  • El contenido es estable. Un documento de políticas que actualizas una vez al mes no justifica un pipeline de embeddings que reindexa en cada cambio. Pégalo y listo, y actualizas el pegado cuando cambie el documento.
  • No te quieres echar encima toda la maquinaria. RAG no es una sola cosa. Es un chunker, una llamada de embeddings, un vector store, un índice, una función de recuperación y un harness de evals para saber que funciona. Cada pieza es un rincón donde se puede esconder una respuesta equivocada.

Lo que la gente subestima: meter el documento en el contexto te ahorra el problema más difícil de la recuperación, que es traer los fragmentos equivocados. Cuando todo está en el prompt, no existe tal cosa como el "fragmento equivocado". El trabajo del modelo se vuelve más fácil, no más difícil.

Consejo

Antes de construir RAG, haz la prueba floja: pega el corpus entero en el prompt y mide calidad y costo con preguntas reales. Si alcanza y la factura es aceptable, ya terminaste. Ese resultado negativo también es un resultado. Te ahorró un pipeline entero.

02 · Cuándo RAG sí se gana su complejidad

Recurre a la recuperación cuando al menos una de estas sea cierta, y sé honesto sobre cuál:

  • El corpus es demasiado grande para caber, o lo bastante grande como para que meterlo entero en cada request sea un desperdicio. Estás pagando tokens de entrada por miles de páginas solo para responder una pregunta sobre un párrafo.
  • Cambia todo el tiempo. Una base de conocimiento, un historial de tickets o un catálogo de producto que se actualiza cada hora pide un índice donde puedas hacer upsert, no un pegado que tengas que volver a pegar.
  • Necesitas atribuir la fuente. Si la respuesta tiene que citar de cuál documento salió, por normativa, por confianza, por un enlace de vuelta al origen, la recuperación te da esa procedencia gratis, porque sabes exactamente qué fragmentos le entregaste al modelo.

Aun así, "RAG" y "métele todo" no son cosas que se excluyan. Un camino intermedio bastante común y poco aprovechado es recupera amplio y luego mete: trae con recuperación un conjunto generoso de fragmentos candidatos, pero mete en el prompt más de los que dejaría pasar un top-k apretado, y deja que el modelo haga el juicio final de relevancia ahí, en contexto. Consigues la escala de la recuperación con la tolerancia del stuffing ante un ranking imperfecto.

03 · El modelo mental: la calidad está en la recuperación, no en el modelo

El error más caro es creer que un mejor modelo arregla un RAG malo. No lo arregla. Si la recuperación le entrega al modelo los cinco fragmentos equivocados, un modelo más potente solo produce una respuesta equivocada mejor redactada. No tiene cómo saber que el pasaje relevante nunca estuvo frente a él. Basura entra, basura segura de sí misma sale.

Así que la calidad de RAG se reduce sobre todo a dos decisiones que ocurren antes, río arriba.

Chunking

  • Fragmenta por límites semánticos como secciones, párrafos, unidades lógicas, no por un conteo ciego de caracteres que parte una oración a la mitad de una cláusula.
  • Deja algo de solape entre fragmentos vecinos para que una idea que cae justo en un límite sobreviva en al menos uno de ellos.
  • No fragmentes de más. Los fragmentos diminutos recuperan con precisión, pero pierden el contexto de alrededor que el modelo necesita para usarlos de verdad. Los fragmentos grandes cargan contexto, pero diluyen el foco del embedding. Ajusta esto contra preguntas reales, no a ojo.

Recuperación

  • Guarda metadata (fuente, sección, fecha, tipo) para poder filtrar antes de rankear. Acotar al conjunto correcto de documentos antes de la búsqueda por similitud rinde mucho más que andar ajustando el modelo.
  • Devuelve suficiente, no todo. El top-5 es un punto de partida. Mide si la respuesta correcta suele estar dentro del conjunto que devuelves.

04 · Construye el stack en Supabase

Aquí va un pipeline que sí funciona. Activa pgvector, diseña una tabla que guarde el contenido junto con su embedding y su metadata filtrable, y luego fragmenta, genera los embeddings e inserta.

-- Activa la extensión y crea una tabla que guarde chunks + embeddings.
create extension if not exists vector;

create table docs (
  id          bigint generated always as identity primary key,
  source      text not null,        -- de qué documento salió
  section     text,                 -- objetivo para filtrar/citar
  content     text not null,        -- el texto del fragmento
  embedding   vector(1536),         -- igual a la dimensión de tu modelo de embeddings
  updated_at  timestamptz default now()
);

-- Índice aproximado para que la recuperación siga rápida al crecer la tabla.
create index on docs using ivfflat (embedding vector_cosine_ops) with (lists = 100);

Tu código de aplicación fragmenta cada documento por límites semánticos, llama al modelo de embeddings por cada fragmento e inserta el texto, el vector y la metadata. Al actualizar, haz upsert por source para que un documento que cambió se reindexe sin duplicar los fragmentos viejos.

Para recuperar, genera el embedding de la pregunta que entra con el mismo modelo y luego ordena por distancia vectorial, opcionalmente filtrando antes por metadata:

select content, source, section
from docs
where source = any($2)            -- opcional: prefiltra a un conjunto de docs
order by embedding <=> $1          -- $1 = el embedding de la consulta
limit 5;

El operador «<=>» es la distancia coseno, y mientras más chico, más cerca. Pásale esas filas al modelo como contexto y dile que cite el «source»/«section» que usó, para que la respuesta venga con su procedencia.

Atención

Usa el mismo modelo de embeddings para los documentos y para las consultas. Los vectores de dos modelos distintos viven en espacios distintos y comparar sus distancias no significa nada, así que vas a tener una recuperación que parece razonable pero en el fondo es aleatoria. Vuelve a generar los embeddings de todo el corpus cada vez que cambies de modelo de embeddings.

05 · El detalle que le pega a todo el mundo: la búsqueda vectorial pura no atrapa los términos exactos

La búsqueda vectorial encuentra cosas que son semánticamente parecidas. Y eso es justo lo que no te sirve con los identificadores exactos: un código de producto como SKU-4471, el nombre de una persona, un mensaje de error como ECONNREFUSED. El embedding de una consulta que contiene ECONNREFUSED queda "cerca" de un montón de prosa sobre manejo de errores, así que la búsqueda vectorial tan campante devuelve un fragmento que habla de errores pero nunca menciona el que preguntaste. El modelo entonces responde, con toda confianza, a partir de un fragmento que era apenas parecido, no relevante.

La solución es la recuperación híbrida: corre la búsqueda vectorial y una búsqueda por keyword/texto completo, y luego mezcla los resultados. En Postgres ya tienes la búsqueda de texto completo incorporada.

-- Brazo de keyword: atrapa términos exactos que la búsqueda vectorial pasa por alto.
select content, source, section
from docs
where to_tsvector('spanish', content) @@ plainto_tsquery('spanish', $1)
limit 5;

Combina los dos conjuntos de resultados. Lo más simple es tomar la unión y volver a rankear, o usar reciprocal rank fusion para mezclar los dos órdenes. La búsqueda vectorial te da "es del mismo tema". La de keyword te garantiza que el término literal está ahí. Juntas se cubren los puntos ciegos una a la otra, y el fallo de esta sección prácticamente desaparece.

Importante

Si llevas RAG a producción sin un set de evals, estás volando a ciegas. Mantén un archivo chiquito de preguntas reales con los documentos fuente que las deberían responder y mide dos cosas: ¿la recuperación devolvió siquiera el fragmento correcto (recall)?, y ¿el modelo respondió bien con esos fragmentos? La mayoría de los reportes de "el modelo se equivoca" son en realidad "la recuperación nunca le dio la respuesta", y solo los distingues midiendo.

06 · Ejemplo paso a paso: respuestas de soporte sobre una base que cambia

Pongamos que estás respondiendo preguntas de soporte sobre una base de conocimiento que se actualiza a diario y donde las respuestas tienen que enlazar al artículo del que salieron. Recorramos primero la decisión y después el build.

  1. ¿Cabe? ¿Es estable? No, ninguna de las dos. Cientos de artículos, actualizados a diario. Meter la base entera en cada request es un desperdicio, y un pegado queda viejo para mañana. Aquí RAG sí vale la pena.
  2. ¿Necesitas citar? Sí. Las respuestas tienen que enlazar al artículo fuente. Eso solo ya es un voto fuerte a favor de la recuperación, porque vas a saber exactamente qué fragmentos devolviste.
  3. Constrúyelo. Fragmenta cada artículo por sección, genera los embeddings y guarda en pgvector con «source» = slug del artículo y «section» = encabezado. En el sync nocturno, haz upsert por artículo para que las ediciones reindexen limpio.
  4. Haz híbrida la recuperación. Las preguntas de soporte están llenas de términos exactos: códigos de error, nombres de plan, etiquetas de botones. Corre vector más keyword, mezcla, toma los mejores resultados y entrégaselos al modelo con la instrucción de citar «source».
  5. Pon el filtro en la calidad de la recuperación, no en el tamaño del modelo. Mide el recall en tu set de evals. Si el artículo correcto no aparece en el conjunto devuelto, ningún cambio de modelo va a ayudar. Arregla el chunking o agrega el brazo de keyword. Si está y la respuesta sigue mal, entonces sí mira el prompt o el modelo.

Lo importante del recorrido es el orden. Primero decidiste que RAG estaba justificado (no cabe, cambia, hay que citar), y después gastaste tu esfuerzo donde de verdad está la calidad (chunking, recuperación híbrida y un set de evals), y dejaste el modelo como la última perilla, no la primera.

RAG vale toda esa complejidad justo cuando el corpus no cabe, no se queda quieto o tiene que citarse. No vale nada cuando un simple pegado habría resuelto. Decide eso primero, con honestidad, antes de escribir una sola línea de pipeline. Después gasta tu esfuerzo en chunking y recuperación híbrida, demuéstralo con un set chico de evals, y deja que el modelo sea esa parte fácil que se supone que debe ser.

Puntos clave

  • Trata RAG como una concesión, no como la opción por defecto: pega el corpus y mide primero. Esa prueba floja muchas veces cierra el proyecto ahí mismo.
  • Recurre a la recuperación solo cuando el corpus no cabe, cambia todo el tiempo o tiene que citarse.
  • La calidad de RAG está en el chunking y la recuperación, no en el modelo. Con malos chunks, hasta un modelo potente se equivoca con toda fluidez.
  • Combina siempre la búsqueda vectorial con keyword/texto completo (recuperación híbrida), o se te van a escapar los términos exactos como códigos y mensajes de error.
  • Lleva a producción un set de evals que separe el recall de la recuperación de la corrección de la respuesta, para que arregles la capa correcta en vez de cambiar de modelo por puro reflejo.

Preguntas frecuentes

Si las ventanas de contexto siguen creciendo, ¿RAG va a desaparecer?

Las ventanas más grandes reducen los casos donde *necesitas* RAG, pero no lo eliminan. Tres cosas todavía te obligan a recuperar: un corpus demasiado grande para caber incluso en una ventana enorme, contenido que cambia todo el tiempo (no vas a querer volver a meter miles de páginas en cada request), y un requisito firme de citar de qué documento salió la respuesta. Lo que sí cambia es la opción por defecto. "Métele todo al prompt" debería ser tu primera prueba mucho más seguido de lo que lo es hoy.

Mis respuestas de RAG salen mal. ¿Debería cambiar a un modelo mejor?

Casi nunca es lo primero que debes hacer. Mide el recall: ¿la recuperación realmente devolvió el fragmento que contiene la respuesta? Si no lo hizo, ningún cambio de modelo va a ayudar, porque el modelo no puede leer lo que nunca le mostraron. Arregla el chunking, agrega filtrado por metadata, o agrega el brazo de keyword de la recuperación híbrida. Solo si el fragmento correcto *sí* estaba en el conjunto devuelto y la respuesta sigue mal deberías ponerte a mirar el prompt o el modelo.

¿Por qué no alcanza con la búsqueda vectorial pura?

Porque la búsqueda vectorial encuentra lo semánticamente parecido, y eso es justo lo que no te sirve con los identificadores exactos como códigos de producto, nombres y mensajes de error. Una consulta con "ECONNREFUSED" queda cerca de un montón de prosa sobre manejo de errores, así que la búsqueda vectorial devuelve un fragmento que habla de errores pero nunca menciona el que preguntaste. Combínala con búsqueda por keyword/texto completo (recuperación híbrida) para garantizar que el término literal está presente en al menos uno de los dos brazos de los resultados.

¿Cómo debería fragmentar mis documentos?

Por límites semánticos como secciones, párrafos y unidades lógicas, no por un conteo ciego de caracteres que parte las oraciones a la mitad. Deja un poco de solape entre fragmentos vecinos para que las ideas que caen en un límite sobrevivan en algún lado. Evita los extremos: los fragmentos diminutos recuperan con precisión pero pierden el contexto que el modelo necesita para usarlos; los enormes cargan contexto pero diluyen el foco del embedding. No hay un número mágico que sirva para todo, así que ajústalo contra tus propias preguntas de evals.

¿Necesito una base de datos vectorial dedicada, o alcanza con Postgres/Supabase?

Para la mayoría de los proyectos, pgvector en Supabase sobra, y tiene una ventaja real: tus vectores viven al lado de tus datos relacionales y de tu búsqueda de texto completo, así que el filtrado por metadata y la recuperación híbrida son puro SQL, sin un segundo sistema que andar sincronizando. Pásate a un vector store dedicado cuando la escala, los tipos de índice especializados o la latencia de consulta de verdad se te queden cortos con lo que te da Postgres. No pagues ese costo operativo antes de comprobar que lo necesitas.

¿Cómo sé que mi RAG de verdad funciona?

Con un set chico de evals, no a ojo. Mantén un archivo de preguntas reales emparejadas con los documentos fuente que deberían responderlas, y mide dos cosas por separado: el recall de la recuperación (¿volvió el fragmento correcto?) y la corrección de la respuesta dados esos fragmentos. Separar las dos es justamente la clave. Te dice si lo que toca arreglar es la recuperación o el prompt. Sin eso, cada regresión parece que "el modelo empeoró" cuando casi siempre es la recuperación trayendo, sin que te enteres, lo que no es.

¿Prefieres que lo hagamos por ti?

Esto mismo lo construimos para negocios como el tuyo. La primera conversación es gratis y sin compromiso.

Escríbenos por WhatsApp

Escríbenos por WhatsApp

Escanéalo con tu teléfono para escribirnos por WhatsApp.

Escanéalo con tu teléfono para escribirnos por WhatsApp.

¿Estás desde el teléfono y no puedes escanear? Escríbenos a info@ilustrari.com

Primera conversación gratis. Te responde el fundador.

Recursos relacionados