AJUAREZ93
WorkTech ExplicadoNewsletterExperienceStackContact
Hire me
Tech Explicado

27 de julio de 2026

Qdrant: cuándo tu RAG ya se le quedó chico a Postgres

DatabaseAI / ML

El problema que Qdrant resuelve

En el artículo pasado sobre llegué a una conclusión específica: la mayoría de los sistemas de RAG nunca tienen un problema de escala vectorial real, tienen un problema de mantener sincronizados datos relacionales y vectoriales. Ese artículo termina justo donde empieza este: ¿qué pasa cuando sí llegas a ese problema de escala?

Postgres con pgvector es, en el fondo, una base de datos relacional a la que le agregaste un tipo de columna especial. Eso es una ventaja hasta que deja de serlo: el motor de almacenamiento, el planificador de queries, el sistema de locks, todo fue diseñado para filas y transacciones, no para grafos de vecinos más cercanos. Cuando el volumen de vectores crece a decenas de millones, cuando necesitas escalar la búsqueda horizontalmente entre varios nodos, o cuando el patrón de acceso es casi siempre "dame vecinos cercanos con estos filtros" y casi nunca un join relacional complejo, seguir forzando ese patrón dentro de Postgres empieza a costar más de lo que ahorra.

Qdrant no es "pgvector pero más rápido". Es un motor de almacenamiento escrito desde cero (en Rust) alrededor de una sola pregunta: cómo indexar y buscar vectores de la forma más eficiente posible, con metadata como ciudadano de primera clase en esa búsqueda, no como un filtro que se aplica después. Esa diferencia de diseño —optimizar para vectores desde la capa de almacenamiento, no agregarlos encima de un motor relacional— es lo que este artículo desarrolla bloque por bloque.

El flujo real: qué hace distinto el índice de Qdrant

La infografía muestra el mismo flujo de alto nivel que cualquier sistema de embeddings: documento → vector → índice → búsqueda. La diferencia está en el "lo indexa con HNSW" — Qdrant y pgvector usan el mismo algoritmo de base, pero lo que rodea a ese algoritmo es distinto por completo.

Qdrant organiza los datos en segmentos: bloques de almacenamiento que se pueden construir, optimizar y consultar de forma independiente. Cada segmento mantiene su propio grafo HNSW y su propio almacén de payloads, memory-mapped a disco para que el sistema operativo maneje qué páginas viven en RAM sin que Qdrant tenga que administrar eso a mano. Cuando insertas puntos nuevos, van a un segmento "en construcción"; en background, un proceso de optimización los fusiona con segmentos más grandes y reconstruye el índice de forma incremental —sin bloquear las búsquedas que están corriendo al mismo tiempo, algo que un índice HNSW monolítico no puede hacer sin degradar el servicio durante la reconstrucción.

La otra pieza que la infografía resume en una flecha es la cuantización: Qdrant puede comprimir los vectores de precisión completa (float32) a representaciones más compactas —escalar (int8), producto, o binaria— para reducir el uso de memoria varias veces, mientras mantiene los vectores originales en disco para re-rankear los candidatos finales con precisión completa. Es un trade-off explícito de memoria contra recall que tú controlas, no una decisión que el motor toma por ti.

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, ScalarQuantization, ScalarQuantizationConfig, ScalarType

client = QdrantClient(url="http://localhost:6333")

client.create_collection(
    collection_name="documentos",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
    quantization_config=ScalarQuantization(
        scalar=ScalarQuantizationConfig(type=ScalarType.INT8, quantile=0.99)
    ),
)

Las cuatro piezas detrás de las feature cards

Diseñada específicamente para vectores. No es una frase de marketing — se traduce en decisiones concretas: el formato de almacenamiento en disco está optimizado para lectura de vectores densos, el motor no carga el overhead de un parser SQL ni de un sistema de transacciones ACID completo, y cada operación (insertar, buscar, filtrar) está diseñada alrededor de un único tipo de dato en vez de ser genérica para cualquier esquema relacional.

Filtrado combinado (vector + metadata). Esta es, en mi experiencia, la razón real por la que un equipo migra de pgvector a Qdrant, más que la velocidad pura. El problema con filtrar "después" de la búsqueda vectorial (traer 100 candidatos y descartar los que no cumplen el filtro) es que si el filtro es restrictivo, puedes terminar con menos de los K resultados que pediste, o ninguno. Qdrant resuelve esto con filtrado durante el recorrido del grafo HNSW: el algoritmo de búsqueda considera el filtro mientras navega el grafo, no después, así que los K resultados que regresa ya cumplen el filtro garantizado.

from qdrant_client.models import Filter, FieldCondition, MatchValue

resultados = client.search(
    collection_name="documentos",
    query_vector=embedding,
    query_filter=Filter(
        must=[FieldCondition(key="proyecto", match=MatchValue(value="studioengine"))]
    ),
    limit=5,
)

Para que ese filtrado sea rápido a escala, los campos de payload que filtras seguido necesitan un índice de payload explícito (client.create_payload_index(...)) — sin eso, Qdrant sigue funcionando, pero el filtro degrada a un escaneo lineal sobre el payload de cada candidato.

Escala horizontal nativa. Una colección se puede dividir en shards distribuidos entre varios nodos, con réplicas por shard para tolerancia a fallos. A diferencia de particionar una tabla de Postgres a mano, Qdrant enruta las queries a los shards correctos automáticamente y agrega los resultados — el cliente no necesita saber en qué nodo vive cada vector.

API simple vía REST o gRPC. El punto no es "tiene una API HTTP", es que esa API está modelada alrededor de los conceptos nativos del dominio —points, collections, payloads— en vez de traducir esos conceptos a sentencias SQL genéricas. Eso simplifica tanto el cliente como el lado del servidor, porque no hay una capa de traducción entre "lo que quieres hacer" y "cómo el motor lo representa internamente".

Casos de uso reales

Sistemas RAG en producción —no un prototipo con cien documentos, sino un sistema con tráfico real, actualizaciones constantes del corpus, y requisitos de latencia bajo carga concurrente. Es exactamente el caso de StudioEngine.ai: el corpus crece continuamente y las consultas de recuperación tienen que responder en el mismo ciclo de request-response que ve el usuario final, no en un batch job nocturno.

Búsqueda semántica a gran escala, donde el volumen de documentos ya no cabe cómodo en un índice que se reconstruye por completo con cada actualización mayor.

Recomendaciones personalizadas, combinando el vector de preferencias de un usuario con filtros de negocio (inventario disponible, región, categoría) en la misma query — el mismo patrón de filtrado combinado que RAG, aplicado a otro dominio.

Detección de similitud de imágenes, donde el "documento" es un embedding visual en vez de texto —el mecanismo de indexación y búsqueda es idéntico, Qdrant es agnóstico al origen del vector.

Buenas prácticas que casi nadie configura bien la primera vez

Define bien tus payloads y filtros desde el diseño, no después. Qué campos vas a filtrar seguido determina qué necesita índice de payload — decidirlo tarde significa reindexar una colección ya poblada, que en volúmenes grandes no es instantáneo.

Ajusta los parámetros de HNSW a tu caso, no uses los defaults a ciegas. m (cuántas conexiones por nodo en el grafo) sube el recall y la memoria usada; ef_construct sube la calidad del índice a costa de tiempo de construcción; ef (en tiempo de búsqueda) es el dial más directo entre velocidad y recall — súbelo si te falta precisión, bájalo si te sobra latencia. No hay un valor correcto universal, depende de tu distribución de datos y tu presupuesto de latencia real.

Usa colecciones separadas por dominio. Mezclar embeddings de dominios distintos (o de modelos distintos, con dimensiones o distribuciones diferentes) en la misma colección degrada la calidad del grafo HNSW, porque el algoritmo asume que el espacio vectorial es razonablemente homogéneo. Colecciones separadas también te dan aislamiento operativo: puedes reindexar o escalar una sin tocar las demás.

Monitorea latencia bajo carga real, no en pruebas con tráfico sintético de un solo cliente. El recall que mides en desarrollo con ef alto y sin concurrencia no es el que vas a tener en producción con cientos de queries paralelas compitiendo por el mismo grafo en memoria. Vale la pena medir p95/p99 de latencia con el nivel de concurrencia real esperado antes de fijar los parámetros de HNSW en definitivo.

Cuándo NO usar Qdrant

  • Cuando tu volumen todavía cabe cómodo en pgvector. Si ya tienes Postgres, tus datos vectoriales están fuertemente relacionados con datos transaccionales, y el volumen no exige sharding, operar un segundo sistema (backups, monitoreo, upgrades) es complejidad que no estás cobrando en beneficio real.
  • Cuando necesitas transacciones ACID cruzando datos vectoriales y relacionales. Qdrant no te da la garantía de "esta escritura vectorial y esta escritura relacional suceden juntas o ninguna sucede" — si esa consistencia es un requisito real de negocio, pgvector dentro de tu base transaccional existente es la opción más simple.
  • Cuando estás en etapa de prototipo. Levantar y operar un servicio adicional —aunque sea con Docker en un solo comando— es fricción que no vale la pena pagar antes de validar que el producto necesita RAG en primer lugar.
  • Cuando no tienes el volumen ni la concurrencia que justifique el tuning. Los parámetros de HNSW, cuantización, y sharding solo importan cuando hay suficiente escala para que el trade-off sea real — por debajo de eso, es complejidad de configuración sin beneficio medible.

Ejemplo práctico: RAG con filtrado por proyecto

Este es el patrón real detrás de StudioEngine.ai: cada consulta de recuperación no solo busca por similitud, la restringe al proyecto correcto —sin eso, un usuario podría recibir contexto de documentos que no le pertenecen.

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PayloadSchemaType

client = QdrantClient(url="http://localhost:6333")

client.create_collection(
    collection_name="documentos",
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)
client.create_payload_index(
    collection_name="documentos",
    field_name="proyecto",
    field_schema=PayloadSchemaType.KEYWORD,
)

def indexar(id, texto, embedding, proyecto, tags):
    client.upsert(
        collection_name="documentos",
        points=[{
            "id": id,
            "vector": embedding,
            "payload": {"proyecto": proyecto, "tags": tags, "texto": texto},
        }],
    )

def recuperar_contexto(embedding, proyecto, k=5):
    return client.search(
        collection_name="documentos",
        query_vector=embedding,
        query_filter=Filter(
            must=[FieldCondition(key="proyecto", match=MatchValue(value=proyecto))]
        ),
        limit=k,
    )

El índice de payload sobre proyecto es lo que hace que ese filtro sea rápido incluso con millones de documentos de proyectos distintos mezclados en la misma colección —sin él, cada búsqueda tendría que evaluar el filtro punto por punto sobre los candidatos del grafo.

Mi conclusión

Migrar de pgvector a Qdrant no fue una decisión de "esto es más nuevo" — fue una decisión de que el patrón de acceso real (búsqueda vectorial con filtros de metadata, corpus creciendo constantemente, latencia bajo carga concurrente real) ya no encajaba bien en un motor pensado primero para filas y transacciones. Lo que más valoro de Qdrant en producción no es la velocidad en el caso ideal, es que el filtrado combinado funciona como se espera incluso cuando el filtro es restrictivo, y que la reconstrucción incremental del índice no me obliga a elegir entre mantener el corpus actualizado y mantener las búsquedas rápidas. Si tu RAG todavía no siente ese dolor, no lo adoptes por adelantado — pero cuando lo sientas, vas a reconocerlo.


Este artículo acompaña la infografía de Qdrant — parte de la serie Tecnolog-IA con Inteligenc-IA.

Dónde lo he usado

En producción

  • StudioEngine.ai — Qdrant es la base de datos vectorial detrás del sistema de RAG que usa el proyecto actualmente. Los embeddings se generan con la API de y se indexan en Qdrant junto con payloads de metadata (proyecto, tipo de asset, tags), así que la recuperación no es solo "los K vecinos más cercanos" — es "los K vecinos más cercanos que además cumplen estos filtros", resuelto en una sola consulta en vez de traer de más y filtrar después en la aplicación.
Qdrant
TrabajoIntercoProyectoStudioEngine.ai

Temas relacionados

RDS + pgvector: búsqueda semántica sin salir de Postgres (y cuándo no conviene)

RDS + pgvector: búsqueda semántica sin salir de Postgres (y cuándo no conviene)

Redis: ¿por qué es tan rápido? (y cuándo no usarlo)

Redis: ¿por qué es tan rápido? (y cuándo no usarlo)

Qdrant: cuándo tu RAG ya se le quedó chico a Postgres

Infografía generada con ChatGPT usando un prompt personalizado. —

AJUAREZ93

© 2026 Jose Alfonso Guerrero Juarez · Built from Guanajuato, México

github.com/ajuarez93 ↗