---
title: "Consejos concretos para pasar de un RAG simple a uno mejor"
description: "La configuración de los tutoriales no te da control sobre la consulta, hace una sola consulta por llamada y devuelve cuatro fragmentos. Seis cambios para solucionarlo, desde multi-query hasta un umbral de relevancia, nodo a nodo en n8n."
date: 2025-08-24
language: es
canonical: https://gduv.club/es/articles/better-rag
source: gduv.club
---
Casi todos los tutoriales de RAG en n8n terminan en el mismo lienzo: un agente de IA y, conectado a él, un nodo de vector store usado directamente como herramienta. He montado esa configuración muchas veces. Funciona, y durante un tiempo no profundicé más.

Luego empecé a hacerle preguntas más difíciles y surgieron tres problemas que tienen la misma causa: el agente es el dueño de la recuperación, y tú no controlas nada.

Esta es la versión escrita de un vídeo, con el mismo curso de biología y las mismas cifras.

[Mejora tus agentes RAG en n8n: Multi-Query inteligente y razonamiento (con Supabase + GPT-5)](https://www.youtube.com/watch?v=rKTM_SWLHLI)

## El lienzo por el que todos empiezan

Mi base de conocimientos para esto es un curso de biología, vectorizado en Supabase. Si le preguntas "qué es una célula", responde bien. Esa es la demo que todo el mundo muestra, y no es mentira: las preguntas sencillas funcionan.

**Agente de IA**

**Cuando se recibe mensaje de chat** → **Agente de IA** (Modelo de Chat: Modelo de chat de OpenAI; Memoria: Memoria simple; Herramienta: Vector Store de Supabase (El agente escribe la consulta, se devuelven cuatro fragmentos))

_Tres subnodos, y la recuperación es uno de ellos. Todo lo que ocurre dentro de esa tercera caja lo decide el agente en tiempo de ejecución._

Esto es lo que controlas en esa configuración: una descripción de la herramienta que indica al agente cuándo usarla. Esa es toda la superficie de control.

**Lo que defines** (Una sola vez, al compilar)

- Una descripción de la herramienta
- Cuántos fragmentos devolver

**Lo que decide el agente** (En cada llamada)

- El texto exacto de la consulta enviada al almacén de vectores
- Que haya una sola consulta, nunca dos
- Nada sobre qué tan cercano debe estar un fragmento

_La columna de la izquierda es toda la superficie de configuración. Todo lo de la derecha se decide automáticamente._

### Nunca ves la consulta

El agente redacta la consulta y la envía. Puedes intentar guiarlo con la descripción de la herramienta y esperar que funcione. Si se trata de una pregunta que requería reformularse, o donde el fraseo del usuario no se parece en nada al de tus documentos, te enterarás revisando el registro de ejecución después.

### Una consulta por llamada

Una pregunta real a menudo no es una sola pregunta. Si pides algo que requiere definir un término y luego relacionar ese término con otro, una única búsqueda de similitud debe cubrir ambos a la vez. El resultado son fragmentos mediocres para ambos casos en lugar de buenos para cualquiera de los dos.

El agente puede llamar a la herramienta dos veces, pero eso implica dos turnos completos del modelo y, por lo general, no se molesta en hacerlo.

### Devuelve cuatro fragmentos sin importar lo que pidas

Esto fue lo que me hizo cambiar la configuración. El almacén de vectores devuelve los cuatro fragmentos más cercanos. Los más cercanos, no necesariamente cercanos.

Pregunta a mi curso de biología a qué hora cenamos hoy y seguirás recibiendo cuatro fragmentos. Están lejos, pero son los cuatro menos lejanos, así que aparecen. El agente los recibe sin indicación alguna de que son basura, y como los modelos son complacientes, los trata como material relevante.

**Cuatro fragmentos devueltos para una pregunta no relacionada, con puntuaciones de similitud entre 0,11 y 0,19, todos pasados al agente como si fueran relevantes.**

  <div class="dg-json">
    <p class="dg-label">Consulta: "cuándo cenamos hoy"</p>
    <div class="dg-box dg-box--bad">
      <span class="dg-box__title">4 fragmentos devueltos</span>
      <span class="dg-box__note">Transporte de la membrana celular <span class="dg-json__score">0.19</span></span>
      <span class="dg-box__note">Mitosis, cronología de fases <span class="dg-json__score">0.16</span></span>
      <span class="dg-box__note">Cinética enzimática <span class="dg-json__score">0.13</span></span>
      <span class="dg-box__note">Fotosíntesis <span class="dg-json__score">0.11</span></span>
    </div>
    <p class="dg-note">Las puntuaciones existen dentro del almacén de vectores. La herramienta no te las entrega.</p>
  </div>

## Sustituye la herramienta por un subflujo de trabajo

La solución es dejar de darle al agente el almacén de vectores y darle, en su lugar, un subflujo de trabajo. En n8n, esa es la herramienta **Call n8n Workflow**: el agente la llama, se ejecuta un flujo de trabajo en su propio lienzo y el agente recibe lo que el último nodo devuelva.

La parte del agente apenas cambia. El mismo activador, el mismo agente y el almacén de vectores sustituido por dos herramientas.

**Agente de IA**

**Cuando se recibe mensaje de chat** → **Agente de IA** (gpt-5-mini, Modelo de chat: OpenAI Chat Model (gpt-5-mini); Memoria: Memoria simple (Los últimos 8 turnos); Herramienta: Consultar base de conocimientos (El subflujo. Acepta una matriz de 1 a 5 consultas); Herramienta: Pensar (Usado justo después de la recuperación, máx. 50 palabras))

_Aquí el agente se ejecuta en gpt-5-mini, no en un modelo frontera. Una vez que la recuperación hace la clasificación, la tarea del agente es redactar una respuesta con lo que ha recibido, y eso no es la parte difícil._

El subflujo de trabajo es donde reside toda la nueva superficie de control. Cada recuadro a continuación es un paso que has escrito y que puedes abrir en el registro de ejecución posteriormente.

**El subflujo de recuperación: un activador que acepta una matriz de consultas, un divisor (Split Out), un bucle, un nodo de Supabase Vector Store con embeddings adjuntos, un paso de limpieza, un filtro a 0,4 y un IF que agrega los fragmentos o escribe una frase indicando que nada coincidió, antes de volver al bucle.**
 0,4',
      note: 'Filtro. Siempre devuelve datos, por lo que una consulta sin coincidencia sigue adelante',
      tone: 'good',
      mark: true,
    },
    { label: '¿Algún fragmento?', note: 'IF, sobre si algo sobrevivió' },
  ]}
  branches={[
    { route: 'true, algo pasó', tone: 'good', nodes: [{ label: 'Agregar fragmentos' }] },
    {
      route: 'false, nada pasó',
      tone: 'warn',
      nodes: [
        {
          label: 'Indicar que no hay coincidencia',
          note: '"Ningún fragmento alcanzó el umbral de relevancia, la base de conocimientos no pudo proporcionar información"',
        },
      ],
    },
  ]}
  tail={[{ label: 'Preparar salida del bucle', note: 'La consulta emparejada con lo que devolvió' }]}
  loop="Preparar salida del bucle vuelve a Bucle sobre elementos. Cuando cada consulta se haya ejecutado, la salida final del bucle va a un nodo de Agregación y el agente recibe un único campo: Recuperación de la base de conocimientos."
/>

### Una matriz de consultas en una sola llamada a la herramienta

El activador del subflujo acepta un campo llamado `queries`. La descripción de la herramienta indica al agente que es una matriz de una a cinco, y el prompt del sistema dice lo mismo en la otra dirección: cuanto más compleja sea la pregunta del usuario, más debes dividirla en subconsultas, hasta un máximo de cinco.

Esa es la parte de la multiconsulta, y cuesta una sola llamada a la herramienta en lugar de tres.

| Step | What happens | Cost |
| :--- | :--- | ---: |
| El usuario hace una pregunta compleja | Dos términos y cómo interactúan |  |
| El agente escribe tres consultas | Definir la primera, definir la segunda y cómo se relacionan | 1 llamada a herramienta |
| El subflujo ejecuta las tres | Un bucle y una puntuación para cada resultado |  |
| Se devuelve un elemento agregado | Agrupado por consulta |  |

_Aquí el agente decidió el desglose por sí mismo. Yo solo le indiqué que el campo admite hasta cinco._

**Define el parámetro como JSON, o llegará como una cadena**

Cuando el modelo rellena un parámetro de herramienta mediante `$fromAI`, el tipo predeterminado es string, y un array de un solo elemento se devuelve como algo que no se puede analizar. Establece el tipo en `json` y especifícalo en la descripción: usa un array incluso cuando solo haya una pregunta. Mi descripción termina con un ejemplo de array, porque es lo que el modelo copia.

### La puntuación es la clave

Dentro del subflujo, Supabase es un nodo normal, por lo que yo mismo mapeo la consulta y la respuesta incluye una puntuación de similitud. El filtro descarta cualquier cosa que esté en 0.4 o por debajo.

Dos detalles que me costaron tiempo. El filtro debe configurarse para emitir siempre datos; de lo contrario, una consulta en la que nada superó el filtro no produce ningún elemento y la rama siguiente nunca se ejecuta. Y el IF posterior comprueba si sobrevivió algún fragmento, ya que es el caso que quieres gestionar explícitamente en lugar de devolver silencio: el agente recibe una frase indicando que la base de conocimientos no tenía nada útil para esa consulta.

**0.4 es mi cifra, no una universal**

Es lo que funcionó en esta base de conocimientos con estos tamaños de fragmentos y este modelo de embedding. Si cambias cualquiera de los tres, el valor varía. Haz algunas preguntas reales y otras deliberadamente inconexas, observa las puntuaciones que recibes y elige el límite entre ambas.

### Indica al agente de dónde proviene el fragmento

El paso de limpieza es más importante de lo que parece. Supabase devuelve metadatos que no me sirven, como el tipo de contenido del archivo original, y eso supone tokens en cada fragmento de cada consulta.

Lo que conservo es el texto del fragmento más dos campos: el capítulo del que proviene y su puntuación de relevancia redondeada a dos decimales. Ambos pasan al agente. El nombre del capítulo le permite indicar de dónde proviene la información, y pasar la puntuación permite que el agente vea que una de sus cinco consultas alcanzó el 0.42 mientras que otra llegó al 0.81, y pueda ponderarlas en consecuencia.

### Nombra lo que devuelves

El agregado final no produce un bloque anónimo. Produce un campo llamado `Knowledge base retrieval` y, dentro de él, cada entrada empareja `Query to the knowledge base` con `Chunks returned`.

Ese emparejamiento es fundamental. El agente envió tres consultas, recibe tres grupos etiquetados y puede saber qué fragmentos responden a qué parte de su propia pregunta. Si le entregas un array plano de doce fragmentos, tendrá que inferir el mapeo, algo que hará mal en algunas ocasiones.

Nombrar los campos requiere un nodo Set. Es lo más barato de esta lista y lo que más tiempo tardé en implementar.

## Un paso de reflexión antes de la respuesta

El otro cambio es en el propio agente. Le he dado una herramienta de reflexión y el prompt del sistema le indica que la use justo después de recibir la recuperación: analizar la pregunta frente a lo obtenido y cuestionar si realmente tiene lo necesario para responder.

La descripción de la herramienta lo limita a 50 palabras. Sin esto, escribe párrafos enteros, y reflexionar en voz alta sobre cuatro fragmentos no merece gastar una página de tokens.

Puedes leer esas notas en el registro de ejecución, que es la parte que no esperaba que me gustara tanto. Cuando una respuesta es incorrecta, las notas suelen indicarte si el fallo estuvo en la recuperación o en el razonamiento.

Las últimas líneas del prompt del sistema son las que conservaría si solo pudiera mantener dos. Responder únicamente basándose en el contenido del curso y, si una pregunta queda fuera de este, redirigir en lugar de responder. Y si no tienes lo necesario, dilo en lugar de responder usando conocimientos generales.

Ambas instrucciones solo funcionan porque la recuperación inferior es honesta cuando vuelve vacía. Una instrucción para admitir ignorancia no sirve de nada cuando cada consulta devuelve cuatro fragmentos que parecen evidencias.

## Lo que esto enseña realmente

Cada corrección aquí es la misma corrección. Algo se decidía por mí, así que lo moví a un paso que yo controlo.

Eso vale más que el flujo de trabajo. Ahora, cuando trabajo en Claude Code, las preguntas que hago son las que esta configuración me obligó a plantear: qué se buscó exactamente, cuánto se recuperó, cuánto valía la pena leer, qué hay en la ventana de contexto que nadie necesitaba. Un agente genérico oculta todo esto por defecto y leerá encantado veinte archivos para responder algo que solo requería dos.

Construir la recuperación a mano una vez es la forma de aprender a fijarse en los detalles.

## Fuentes

- [n8n: Call n8n Workflow tool](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolworkflow/)
- [n8n: Supabase vector store node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstoresupabase/)
- [n8n: `$fromAI` in tool parameters](https://docs.n8n.io/advanced-ai/examples/using-the-fromai-function/)
- [Supabase: pgvector and similarity search](https://supabase.com/docs/guides/ai/vector-columns)