PRODPlataforma BaaS soberana europeaAbrir panel →

IA nativa · 10 lectura mínima

Tutorial: tubería RAG con Postgres y pgvector

Affane Daylami · Fondateur · 13 de abril de 2026

volver al blog

Construir una canalización RAG en Postgres generalmente requiere ensamblar varias piezas usted mismo: una segmentación de texto, una llamada de incrustación, una tabla pgvector, una consulta de similitud. Este es el clásico LangChain + PGVectorchain. En Aurabase, este canal ya existe en el lado del servidor: dos llamadas, ragIngest() y rag(), lo reemplazan, mientras se basa en PostgreSQL estándar y pgvector, sin una base vectorial patentada para agregar.

Este texto en inglés se generó automáticamente a partir del original en francés y aún no ha sido revisado.
Esta página fue traducida automáticamente. La versión en inglés es autorizada.

El RAG nativo es parte de la IA nativaintegrada en el backend de Aurabase, junto con NL2SQL: no es un servicio de terceros que se ensambla sobre una base general. Requisitos previos para seguir este tutorial: un proyecto de Aurabase existente, un proveedor LLM configurado (OpenAI o Google Gemini para incrustaciones, uno de los tres proveedores nativos para generación) y una clave API del proyecto.

Lo esencial
  • Una canalización RAG de Aurabase consta de dos llamadas: ragIngest() para indexar un documento, rag() para consultar y generar una respuesta. La fragmentación, las incrustaciones y la búsqueda de vectores se gestionan en el lado del servidor.
  • En el fondo, son PostgreSQL y pgvector estándar: una tabla embeddings con una columna de vector por clase de dimensión (768, 1536, 3072) y un índice HNSW parcial por clase.
  • Chunking utiliza un tokenizador real (tiktoken o200k), con superposición configurable y protección antiexplosión en documentos grandes.
  • El contenido recuperado se neutraliza antes de ser inyectado en el mensaje: un documento que intenta escapar de su etiqueta para falsificar las instrucciones del sistema se desactiva explícitamente.
  • Solo OpenAI y Gemini generan incrustaciones en el lado de Aurabase. Anthropic/Claude no tiene una API de incrustaciones pública, permanece reservada para generar la respuesta final.
#
Concepto

Cómo funciona este oleoducto RAG

El oleoducto se desarrolla en cinco etapas. Tras la ingestión, el texto se corta, cada fragmento se vectoriza en lotes y luego se almacena. Al realizar la consulta, la pregunta se vectoriza a su vez, en comparación con los fragmentos almacenados por similitud de coseno, y los fragmentos más cercanos se inyectan en el mensaje enviado al modelo de generación.

Chunking (tiktoken) → Incrustaciones (por lotes) → almacenamiento pgvector (HNSW) → Búsqueda de similitudes → Generación aumentada

Un detalle arquitectónico que importa en producción: no se mantiene ninguna conexión Postgres durante las llamadas de red al proveedor de integración o generación. La transacción de base de datos se cierra antes de la llamada externa y se vuelve a abrir después, para nunca bloquear un backend PgBouncer compartido para la latencia de la red de terceros.

#
Paso 1

Crear el proyecto

A diferencia de un pgvector típico autohospedado, no necesita ejecutar CREATE EXTENSION vector ni crear una tabla usted mismo para esta canalización. El esquema embeddings del proyecto, con sus columnas vectoriales e índices HNSW, se aprovisiona automáticamente cuando se crea el proyecto.

terminalbash
# Cuenta Aurabase + CLI
npm i -g @aurabase/cli
aura login

# Crea el proyecto y vincúlalo a esta carpeta.
aura projects create mon-assistant --engine postgres
aura link --project-id <uuid-du-projet>

# Recuperar las claves API del proyecto vinculado
aura projects api-keys

El proveedor de integración se configura una vez, en el lado del proyecto (Estudio → IA → Proveedores). Es este proveedor el que determina la dimensión efectiva de sus vectores, por lo tanto, la columna utilizada en la tabla embeddings.

#
Paso 2

Indexe sus documentos con ragIngest()

Una llamada es suficiente para indexar un documento: el texto se divide en fragmentos, cada fragmento se vectoriza y luego se almacena en el espacio de nombres solicitado. La división utiliza un tokenizador real (tiktoken, codificación o200k_base), no una simple división por espacios, que sigue siendo correcta en texto sin espacios como algunos idiomas asiáticos.

app/api/ingest/route.tstypescript
import { aura } from '@/lib/aurabase'

export async function POST(req: Request) {
  const { content, metadata } = await req.json()

  const { data, error } = await aura.ai.ragIngest({
    namespace: 'docs-produit',
    content,
    metadata,
  })

  if (error) return Response.json({ error }, { status: 400 })
  return Response.json(data) // {identificación, trozos}
}

En HTTP sin formato, la ruta equivalente es POST /v1/ai/{project_id}/rag/ingest, autenticada mediante la clave API del proyecto.

terminalbash
curl -X POST https://<votre-gateway>/v1/ai/<project_id>/rag/ingest \
  -H "apikey: <votre-cle-api>" \
  -H "Content-Type: application/json" \
  -d '{
    "namespace": "docs-produit",
    "content": "Le texte complet de votre document ici...",
    "metadata": { "source": "guide-utilisateur.pdf" }
  }'

La ingesta es idempotente de forma predeterminada: un identificador de documento se deriva automáticamente (hash SHA-256 del contenido, o metadata.document_id si lo proporciona). La reingestión del mismo contenido reemplaza los fragmentos existentes en lugar de duplicarlos, lo que hace que sea seguro reproducir un trabajo de sincronización periódica.

#
Paso 3

Lo que realmente llega a Postgres

Aquí no hay magia patentada: la tabla que recibe sus vectores es una tabla Postgres ordinaria, con una columna vector por clase de dimensión y un índice HNSW parcial por columna (activo solo en las filas que la pueblan). Aquí está su definición real, simplificada:

diagrama de plataforma del proyecto (simplificado)sql
create table embeddings (
  id               uuid primary key default gen_random_uuid(),
  project_id       text not null,
  namespace        text not null,
  content          text not null,
  embedding_768    vector(768),
  embedding_1536   vector(1536),
  embedding_3072   vector(3072),
  embedding_model  text,
  dims             int,
  metadata         jsonb default '{}'::jsonb,
  created_at       timestamptz not null default now()
);

-- Un índice HNSW parcial por clase de dimensión
create index idx_embeddings_vec_1536 on embeddings
  using hnsw (embedding_1536 vector_cosine_ops)
  where embedding_1536 is not null;

-- Más allá de 2000 dimensiones, HNSW no admite el tipo de vector:
-- fundido en halfvec para la clase 3072
create index idx_embeddings_vec_3072 on embeddings
  using hnsw ((embedding_3072::halfvec(3072)) halfvec_cosine_ops)
  where embedding_3072 is not null;

Cada búsqueda compara los vectores con el operador de distancia coseno (<=>), el objetivo de las clases de operador vector_cosine_ops y halfvec_cosine_ops del índice. Para obtener detalles sobre las compensaciones de recuperación/latencia de HNSW frente a IVFFlat, consulte el artículo dedicado a la indexación de HNSW. Para elegir la dimensión de incrustación en sí, consulte la comparación 768 vs 1536 vs 3072.

#
Paso 4

Consultar y generar respuesta con rag()

Del lado de la consulta, rag() encadena la vectorización de la pregunta, la búsqueda por similitud en el espacio de nombres, la construcción del aviso aumentado y la llamada al modelo de generación, en un único viaje de ida y vuelta de red del lado del cliente.

app/api/ask/route.tstypescript
import { aura } from '@/lib/aurabase'

export async function POST(req: Request) {
  const { question } = await req.json()

  const { data, error } = await aura.ai.rag({
    question,
    namespace: 'docs-produit',
  })

  if (error) return Response.json({ error }, { status: 400 })
  return Response.json(data)
}

La respuesta lleva la respuesta generada y sus fuentes, con el proveedor realmente utilizado:

respuesta (extracto)json
{
  "data": {
    "answer": "D'après la documentation, ...",
    "sources": [
      { "id": "...", "content": "...", "similarity": 0.87 }
    ],
    "model": "claude-3-5-sonnet",
    "tokens": 412,
    "provider": "anthropic",
    "fallback_used": false
  }
}

El contenido recuperado nunca se inyecta sin formato en el indicador del sistema. Se trata de datos poco fiables (carga de usuario, página indexada): un documento que contiene, por ejemplo, una etiqueta de sección de cierre seguida de instrucciones falsas es neutralizado antes del montaje, sus corchetes angulares reemplazados por corchetes, el texto se conserva pero la estructura se desactiva.

Corpus indexado bajo otro modelo

Si rag() no encuentra nada aunque su espacio de nombres no esté vacío, la respuesta conlleva una advertencia explícita en lugar de un silencio engañoso: su corpus probablemente esté indexado bajo otro modelo u otra dimensión de incrustación. Vuelva a indexarlo a través de POST /v1/ai/{project_id}/rag/{namespace}/reindex.

#
Comparación

De LangChain y un pgvector hecho a mano: lo que cambia

Si ya ha creado un chatbot RAG en PostgreSQL con LangChain, cada bloque manual tiene aquí un equivalente administrado del lado del servidor, sin cambiar la base de datos subyacente.

Rompiendo el textoRecursiveCharacterTextSplitter para configurarlo usted mismoragIngest(): fragmentación integrada de tiktoken, 512 tokens / 64 superpuestos de forma predeterminada
IncrustacionesLlamada manual a OpenAIEmbeddings, gestión de límites de lotesReintento transitorio integrado y por lotes automáticamente
Almacenamiento de vectorestabla pgvector + índice HNSW para crear y migrar usted mismoEsquema e índices aprovisionados por proyecto.
Buscar + mensajePGVector.similarity_search() y luego ensamblar manualmente el mensajerag(): búsqueda y generación aumentada en una sola llamada
Contenido recuperadoInyectado como está en el mensaje.Neutralización automática de etiquetas de estructura antes del montaje.

¿Está migrando un proyecto existente construido en Supabase con este tipo de ensamblaje manual? La lógica de migración para el resto del backend (esquema, políticas RLS, SDK) se trata en nuestra guía de migración de Supabase a Aurabase.

#
Producción

Configuraciones y límites a tener en cuenta antes de entrar en producción

Tres configuraciones afectan directamente el costo y la latencia, todas verificadas en el código de servicio aura-ai. El número de intentos en una llamada de incorporación transitoria (fallo de red, error de proveedor 429) es 3 de forma predeterminada, con un retroceso base de 100 ms. Los documentos grandes se integran en sublotes limitados a 2048 fragmentos de forma predeterminada, para respetar los límites API de los proveedores sin fallar en un solo documento masivo. Una barrera de seguridad (10.000 fragmentos de forma predeterminada, configurable) rechaza explícitamente la ingesta de un documento que produciría una cantidad aberrante de fragmentos.

En el lado de la búsqueda, el tamaño de la lista de candidatos HNSW se ajusta automáticamente a max(64, top_k × 4): cuantos más resultados solicite, más candidatos explorará el índice para preservar la recuperación. Un valor fijo sigue siendo posible a través de una variable de entorno si su corpus tiene un perfil particular.

Los espacios vectoriales de diferentes modelos nunca se comparan entre sí: cada búsqueda permanece limitada al modelo de incorporación actual, y cambiar de modelo requiere una reindexación explícita en lugar de un cambio silencioso que rompería la coherencia de los resultados.

#
honestidad

Límites actuales a tener en cuenta

La dimensión de inserción debe pertenecer a una de las tres clases admitidas: 768, 1536 o 3072. Un proveedor que devuelve otra dimensión se rechaza con un error explícito, nunca se trunca ni se emite de forma silenciosa.

La generación de incrustaciones solo está disponible a través de OpenAI o Google Gemini entre los tres proveedores nativos: Anthropic/Claude no expone una API de incrustaciones pública, por lo que solo se usa para generar la respuesta final en este canal, nunca para la vectorización.

El SDK de JavaScript aún no expone las anulaciones de top_k y threshold en la llamada rag(): permanecen accesibles en HTTP directo, limitado respectivamente a [1, 50] y [0, 1], pero no desde aura.ai.rag() como lo está hoy. El umbral de similitud predeterminado (0,3) es deliberadamente permisivo; redúzcalo a un corpus denso para evitar fuentes irrelevantes en el mensaje.

#
ir más lejos

para ir más lejos

El RAG cubre preguntas sobre contenidos no estructurados (documentos, notas, tickets). Para preguntas sobre sus datos relacionales, el NL2SQL nativo de Aurabase traduce directamente una pregunta a SQL validado. Para obtener detalles sobre los parámetros HNSW y las clases de dimensiones mencionados anteriormente, consulte el artículo sobre indexación HNSW y la comparación de dimensiones de incrustación. La referencia completa de la API sigue siendo la documentación RAG & pgvector y la documentación AI Gateway.

#
Preguntas frecuentes

Preguntas frecuentes

¿Tiene que administrar usted mismo la extensión pgvector y el índice HNSW?+
No. El esquema de incrustaciones, la extensión pgvector y los índices HNSW parciales (uno por clase de dimensión: 768, 1536, 3072) se aprovisionan automáticamente cuando se crea el proyecto. Llamas directamente a ragIngest() y luego a rag(); el ajuste fino del índice (ef_search, modo iterative_scan) permanece accesible en el lado del servidor si su corpus tiene un perfil particular.
¿Qué dimensión de incrustación debo elegir para mi caso de uso?+
Aurabase valida la dimensión devuelta por su proveedor frente a tres clases admitidas: 768, 1536 y 3072. La elección depende del modelo de incrustación configurado (por ejemplo, text-embedding-3-small en OpenAI produce 1536 dimensiones) e implica un compromiso entre recuperación, costo y tamaño de almacenamiento detallado en nuestra comparación dedicada a las dimensiones de incrustación.

¿LISTO PARA IMPLEMENTAR?

Tu backend en cinco minutos.

No se requiere tarjeta de crédito · 500 MB gratis · 50,000 MAU