Bases de Datos Vectoriales en 2026: Cuál Elegir para RAG
Una guía práctica de 2026 para elegir una base de datos vectorial para RAG y embeddings. Comparación a fondo de pgvector, Qdrant, Pinecone, Weaviate, Milvus y Chroma en algoritmos de indexación (HNSW, IVFFlat, IVF-PQ, DiskANN), métricas de distancia, búsqueda filtrada e híbrida, cuantización, tradeoffs de escala y latencia, self-host vs gestionado y costo real -- con una tabla de decisión por caso de uso, operación en producción y código de cliente ejecutable.
Índice de Contenidos
- Bases de Datos Vectoriales: Visión General
- Algoritmos de Indexación (HNSW, IVFFlat, DiskANN)
- Métricas de Distancia y Similaridad
- Búsqueda Filtrada e Híbrida
- Las Bases de Datos Comparadas
- Cuantización y Memoria
- Escala, Latencia y Rendimiento
- Self-Host vs Gestionado y Costo
- Guía de Decisión por Caso de Uso
- Operación en Producción
- Ejemplos de Código de Cliente
1. Bases de Datos Vectoriales: Visión General
Una base de datos vectorial almacena vectores de embedding de alta dimensión y responde rápido a una pregunta: "¿cuáles de los vectores almacenados están más cerca de este vector de consulta?" Esa búsqueda del vecino más cercano es el motor de recuperación detrás de RAG, la búsqueda semántica, las recomendaciones, la deduplicación y la búsqueda de imágenes o audio. La base de datos no es lo que hace buenos tus resultados -- tu modelo de embedding fija el techo de calidad -- pero es lo que hace la recuperación rápida, filtrable, durable y económica a escala. Para RAG en concreto, es el componente que convierte un montón de embeddings en contexto fundamentado en menos de 100ms.
Una base de datos vectorial hace tres tareas: almacenar vectores junto con su metadata (payload) de forma durable, indexarlos con una estructura de vecino más cercano aproximado (ANN) para que la búsqueda no escanee cada fila, y buscar con una métrica de distancia mientras aplica filtros de metadata. En 2026 la verdadera decisión no es "qué base de datos tiene búsqueda vectorial" -- casi todas la tienen -- sino qué tradeoff entre recall, latencia, poder de filtrado, carga operativa y costo encaja con tu carga de RAG. Esta guía compara las seis que importan: pgvector, Qdrant, Pinecone, Weaviate, Milvus y Chroma.
Embeddings como Vectores
Cada chunk de texto (o imagen, o audio) se convierte en un arreglo de floats de longitud fija -- típicamente de 384 a 3072 dimensiones -- mediante un modelo de embedding. El contenido semánticamente similar se mapea a puntos cercanos en ese espacio. La base de datos vectorial almacena estos arreglos como una columna vector(N) o un campo vectorial con nombre. La dimensión es fija por colección y debe coincidir exactamente con tu modelo de embedding; cambiar de modelo implica re-embeber todo el corpus. Almacena el texto fuente y la metadata junto al vector para que la recuperación pueda retornar contexto usable, no solo IDs.
Vecino Más Cercano Aproximado (ANN)
La búsqueda exacta del vecino más cercano es O(n) por consulta -- bien para 10K vectores, imposible a 10M. Los índices ANN (HNSW, IVF, DiskANN) sacrifican un poco de recall por una aceleración de órdenes de magnitud al buscar solo un subconjunto prometedor del espacio. Ajustas una perilla (ef_search en HNSW, nprobe en IVF) que se mueve a lo largo de la curva recall-vs-latencia. "Recall@10 = 0.98" significa que el índice ANN retornó el 98% de los verdaderos top-10 vecinos. Toda base de datos vectorial de producción es, en su núcleo, un índice ANN con almacenamiento y filtrado encima.
El Camino de la Consulta
Una consulta RAG hace: (1) embeber la pregunta del usuario con el mismo modelo usado en la ingestión, (2) enviar el vector de consulta más cualquier filtro de metadata a la base de datos, (3) el índice ANN retorna los top-k vectores más cercanos con scores y payloads, (4) opcionalmente reranquear o fusionar con resultados por keywords, (5) entregar el texto recuperado al LLM. La base de datos es dueña de los pasos 2-3. Un índice bien ajustado mantiene eso en 5-50ms incluso sobre millones de vectores, dejando tu presupuesto de latencia para el embedding y la generación.
Tradeoff Recall vs Latencia
No hay almuerzo gratis: más recall cuesta más latencia y memoria. Subir ef_search de HNSW de 40 a 200 aumenta el recall pero multiplica el tiempo de consulta; un m más grande mejora el recall pero agranda el índice. El punto de operación correcto es específico de la carga -- una herramienta de búsqueda legal necesita recall@10 cerca de 1.0 y tolera 100ms, mientras que un autocompletado necesita 5ms y tolera 0.9. Mide el recall contra un ground truth de fuerza bruta sobre una muestra, luego ajusta la perilla a tu SLO de latencia en vez de adivinar.
Metadata y Payloads
El RAG real casi nunca hace búsqueda vectorial pura. Filtras: solo este tenant, solo docs de 2025+, solo contenido que el usuario puede ver, solo este tipo de documento. La base de datos almacena metadata estructurada (payload JSON, o columnas tipadas en pgvector) junto a cada vector y te deja restringir la búsqueda por ella. Qué tan bien una base de datos combina el filtrado con la búsqueda ANN -- pre-filtro vs post-filtro, y si sigue retornando el top-k completo bajo filtros estrictos -- es uno de los mayores diferenciadores prácticos entre motores.
Dónde Encaja en RAG
En un stack RAG la base de datos vectorial se sitúa entre la ingestión (chunk -> embeber -> upsert) y la generación (recuperar -> reranquear -> prompt). Es una dependencia entre varias: un modelo de embedding, un reranker opcional, un LLM y a menudo Redis para caché. Elegirla bien significa emparejar su techo de escala, su modelo de filtrado y su historia de hosting con el resto de tu arquitectura -- no perseguir el número de benchmark más rápido. Para el panorama completo de punta a punta, mira la guía de pipelines RAG.
2. Algoritmos de Indexación (HNSW, IVFFlat, DiskANN)
El índice es el mayor determinante de la velocidad, el recall, el uso de memoria y el tiempo de construcción de una base de datos vectorial. Casi todo motor usa HNSW por defecto hoy, pero las variantes de IVF y DiskANN importan para corpus grandes o con memoria limitada. Entender qué hace cada uno -- y qué perillas mueven el recall vs la latencia -- te permite ajustar cualquier base de datos en vez de confiar en los defaults. Las familias de abajo son las que realmente vas a encontrar en pgvector, Qdrant, Pinecone, Weaviate y Milvus.
HNSW (Grafo)
Hierarchical Navigable Small World construye un grafo multicapa donde cada vector se enlaza con sus vecinos más cercanos; la búsqueda camina el grafo de forma voraz desde un punto de entrada en la capa superior hasta la capa base. Es el default en Qdrant, Weaviate, Milvus y pgvector porque da el mejor tradeoff recall-latencia para cargas en memoria. Perillas clave: m (vecinos por nodo, 16-64; mayor = mejor recall, más memoria), ef_construction (lista de candidatos en construcción, 100-500; mayor = mejor grafo, construcción más lenta) y ef_search en tiempo de consulta (mayor = más recall, más latencia). Desventaja: todo el grafo vive en RAM, así que la memoria escala con vectores x dimensiones.
IVFFlat (Archivo Invertido)
IVFFlat particiona el espacio en lists (celdas de Voronoi) vía k-means, luego en tiempo de consulta escanea solo las nprobe celdas más cercanas en vez del conjunto completo. Construye mucho más rápido que HNSW y usa menos memoria, pero el recall es menor y necesita datos representativos para construir buenos centroides -- crea el índice después de cargar los datos, no antes. En pgvector, fija lists ~= filas/1000 (hasta ~sqrt(filas) para tablas muy grandes) y sube probes en tiempo de consulta para cambiar latencia por recall. Una elección pragmática cuando el tiempo de construcción o la memoria importan más que el recall máximo.
IVF-PQ (Cuantización de Producto)
IVF combinado con Product Quantization comprime cada vector en un código corto (por ejemplo, 64 bytes) dividiéndolo en sub-vectores y cuantizando cada uno contra un pequeño codebook. Esto reduce la memoria 10-50x, permitiendo que un solo nodo albergue cientos de millones de vectores, a costa de distancias aproximadas. Estándar en Milvus (IVF_PQ) y Faiss para conjuntos a escala de miles de millones. Combínalo con una pasada de re-scoring sobre vectores de precisión completa para el top-k final para recuperar la mayor parte del recall perdido.
DiskANN (Residente en SSD)
DiskANN (el grafo Vamana de Microsoft) mantiene el grueso del índice en un SSD NVMe con una copia comprimida en RAM, así que puedes servir datasets mucho más grandes que la memoria a una fracción del costo de un HNSW totalmente en RAM. Milvus ofrece DISKANN; Qdrant soporta HNSW y payloads en disco; la extensión pgvectorscale de pgvector (Timescale) agrega un índice StreamingDiskANN estilo DiskANN. El tradeoff es mayor latencia de cola por las lecturas de SSD, mitigada al mantener los vectores cuantizados en RAM. El recurso ideal cuando tu corpus supera la memoria asequible pero aún necesitas buen recall.
Flat (Fuerza Bruta / Exacto)
Un índice flat no hace aproximación -- computa la distancia exacta a cada vector. El recall es un perfecto 1.0 y no hay nada que ajustar, pero la latencia crece linealmente con el número de filas. Es la elección correcta por debajo de aproximadamente 50K-100K vectores (donde un escaneo ya es sub-10ms), para casos de uso críticos en corrección y como el ground truth contra el que mides el recall ANN. pgvector sin índice, la bandera de búsqueda exacta de Qdrant y FLAT de Milvus lo proveen. Siempre haz benchmark del recall ANN contra un baseline flat antes de lanzar.
Ajustar las Perillas
Cualquiera sea el índice, el flujo es el mismo: fija los parámetros de construcción para tu presupuesto de memoria, luego mueve la perilla de consulta para alcanzar tu SLO de recall con la menor latencia. HNSW: sube ef_search hasta que el recall se estabilice. IVF: sube nprobe/probes. Reconstruye con un m/ef_construction mayor o más lists solo si la perilla de consulta no alcanza el recall objetivo. Mide sobre tus propios datos con tus propios filtros -- los benchmarks publicados usan datasets limpios y sin filtrar que rara vez coinciden con producción, donde el filtrado puede colapsar el recall de HNSW si el motor post-filtra.
3. Métricas de Distancia y Similaridad
La métrica de distancia define qué significa "más cercano", y debe coincidir con cómo fue entrenado tu modelo de embedding. Elige la equivocada y el recall colapsa en silencio -- el índice funciona, pero retorna los vecinos incorrectos. Cada métrica también mapea a una clase de operador de índice específica (pgvector) o a un campo de configuración (Qdrant, Milvus, Weaviate, Pinecone). La regla práctica: lee la ficha de tu modelo de embedding y usa la métrica que recomienda, luego normaliza si eso te permite usar el operador más rápido.
Similaridad Coseno
Mide el ángulo entre dos vectores, ignorando la magnitud, así que captura la dirección (orientación semántica) en vez de la longitud. Es el default para la mayoría de modelos de embedding de texto (OpenAI, Cohere, BGE, E5) y la elección segura ante la duda. pgvector la expone como vector_cosine_ops y el operador <=>; Qdrant/Milvus/Weaviate la llaman Cosine. Nota que el coseno sobre vectores normalizados equivale al producto punto -- por eso muchos motores normalizan internamente y ejecutan el kernel de producto interno, más rápido.
Producto Punto (Producto Interno)
El producto interno crudo de dos vectores; mayor significa más similar. Es el más barato de computar y la elección correcta para modelos entrenados con él (muchos modelos de recuperación y recomendación, y configuraciones estilo MIPS). En pgvector es vector_ip_ops con el operador <#> (que retorna el producto interno negativo, así que menor es más cercano). Si tus vectores están normalizados con L2, el producto punto y el coseno ordenan los resultados de forma idéntica -- así que normaliza una vez en la ingestión y usa producto interno por velocidad.
Distancia Euclidiana (L2)
Distancia en línea recta en el espacio de embeddings; menor significa más similar. Tiene en cuenta la magnitud, lo que importa para algunos embeddings de imagen, audio y clustering donde la longitud del vector lleva información. pgvector usa vector_l2_ops y el operador <->; también existe L2 al cuadrado para un ranking más barato. Usa L2 cuando la ficha del modelo lo especifique -- forzar coseno sobre un modelo entrenado con L2 degrada los resultados.
Normalización
Normalizar los vectores con L2 (escalar cada uno a longitud unitaria) es el paso de preprocesamiento más común. Hace que el producto punto iguale al coseno, estabiliza las distancias y te permite usar el kernel de producto interno más rápido y la cuantización binaria. Algunos modelos ya retornan vectores normalizados (revisa la ficha); si no, normaliza una vez en la ingestión y de nuevo para cada vector de consulta. Sé consistente -- mezclar documentos normalizados con consultas sin normalizar produce rankings basura.
Elegir para Tu Modelo
Empareja la métrica con el modelo, no con la intuición: OpenAI text-embedding-3 y la mayoría de modelos BGE/E5/GTE usan coseno; algunos modelos de rerank/recomendación usan producto punto; ciertos modelos de visión usan L2. Ante la duda, normaliza y usa coseno -- rara vez se equivoca para texto. Fija la métrica al crear la colección; cambiarla después implica reconstruir el índice. Una métrica mal emparejada es una de las causas silenciosas más comunes de mal recall en RAG.
Hamming y Manhattan
Para vectores cuantizados en binario (cada dimensión reducida a un bit), la similaridad se computa con la distancia de Hamming -- un XOR-y-popcount que es extraordinariamente rápido y amigable con la caché, habilitando ahorros de memoria de 32x. La distancia de Manhattan (L1) la ofrecen algunos motores (Qdrant, Milvus) para modelos entrenados con ella. Son especializadas: usa Hamming como la primera pasada gruesa en un pipeline de cuantización binaria + rescoring, y L1 solo cuando un modelo lo pida explícitamente.
MaxSim y Multi-Vector
Los modelos de interacción tardía (ColBERT, ColPali) representan cada documento como muchos vectores a nivel de token y puntúan con MaxSim -- sumando, sobre los tokens de la consulta, la máxima similaridad con cualquier token del documento. Esto captura coincidencias de grano fino que un solo vector agrupado pierde. Qdrant y Weaviate ahora soportan campos multi-vector de forma nativa, y Milvus soporta múltiples campos vectoriales por entidad. Cuesta más almacenamiento pero mejora el recall en consultas difíciles; trátalo como una opción avanzada una vez que la recuperación de un solo vector se estanca.
4. Búsqueda Filtrada e Híbrida
La búsqueda vectorial pura es rara en RAG de producción. Casi siempre restringes los resultados por metadata (tenant, permisos, fecha, tipo de documento) y a menudo mezclas la similaridad semántica con la coincidencia por keywords. Cómo maneja una base de datos la búsqueda filtrada e híbrida -- correctamente y sin arruinar el recall -- es uno de los mayores diferenciadores prácticos entre motores. Aquí es donde el pre-filtrado de Qdrant y la búsqueda híbrida nativa de Weaviate se ganan su reputación, y donde el post-filtrado ingenuo retorna en silencio muy pocos resultados.
Filtrado por Metadata
Toda consulta RAG seria lleva un filtro: solo este tenant_id, solo year >= 2025, solo docs que el usuario puede leer. Las bases de datos almacenan metadata estructurada (payload JSON en Qdrant/Weaviate/Pinecone/Milvus, columnas tipadas o JSONB en pgvector) y te dejan expresar condiciones booleanas, de rango y de conjunto. La parte sutil no es la sintaxis sino cómo el filtro interactúa con el índice ANN -- hazlo mal y o pierdes recall o escaneas de más. Diseña tus campos filtrables por adelantado e indéxalos.
Pre-filtro vs Post-filtro
El post-filtrado ejecuta la búsqueda ANN primero, luego descarta los resultados que no coinciden -- rápido, pero si el filtro es selectivo puedes obtener muchos menos de k resultados (o cero). El pre-filtrado restringe el conjunto de candidatos antes/dentro del recorrido del grafo, garantizando un top-k completo pero exigiendo que el motor integre el filtrado con el índice. Qdrant está construido en torno al pre-filtrado rápido vía índices de payload; Milvus y Weaviate soportan búsqueda filtrada con particiones/índices; pgvector aplica cláusulas WHERE que el planner puede o no empujar dentro del escaneo HNSW. Filtros estrictos + post-filtrado es un clásico bug de recall silencioso.
Índices de Payload y Campos
Filtrar rápido requiere que la metadata misma esté indexada, no escaneada. Qdrant te deja crear índices de payload (keyword, entero, float, geo, datetime, bool) para que los filtros se resuelvan en un bitmap antes del recorrido vectorial. Los filtros de pgvector se apoyan en índices ordinarios B-tree/GIN de PostgreSQL sobre tus columnas o campos JSONB -- una de sus fortalezas subestimadas, ya que obtienes toda la caja de herramientas de indexación relacional. Milvus construye índices escalares; Weaviate indexa propiedades. Siempre indexa los campos por los que filtras, o el filtrado se vuelve el cuello de botella.
Búsqueda Híbrida (Densa + Sparse)
La búsqueda híbrida fusiona la similaridad vectorial densa con el scoring sparse/por keywords. Los vectores clavan las coincidencias semánticas ("costo" ~ "precios"); las keywords clavan los términos exactos que los vectores pierden (códigos de error, SKUs de producto, acrónimos, nombres raros). Weaviate, Qdrant, Milvus y Pinecone soportan híbrida de forma nativa; pgvector combina la búsqueda vectorial con la búsqueda de texto completo tsvector de PostgreSQL en SQL. Para RAG sobre docs técnicos, código o catálogos, la híbrida supera consistentemente a la búsqueda vectorial pura -- es la característica de mayor valor después del filtrado básico.
Vectores Sparse (BM25 / SPLADE)
La mitad por keywords de la búsqueda híbrida es un vector sparse -- casi todo ceros, con pesos en los términos que aparecen. El BM25 clásico pondera términos crudos; los modelos sparse aprendidos (SPLADE, la salida sparse de BGE-M3) predicen pesos de términos incluyendo expansiones, capturando algo de semántica mientras se mantienen precisos por keywords. Qdrant y Milvus almacenan vectores sparse como campos de primera clase; Weaviate computa BM25 internamente; Pinecone acepta registros sparse-dense. Los vectores sparse son baratos de almacenar e inmunes a la falla de "redacción diferente" de la búsqueda solo densa.
Fusión de Scores (RRF)
Los scores densos y sparse viven en escalas diferentes, así que no puedes solo sumarlos. Reciprocal Rank Fusion combina las dos listas ranqueadas por rango, no por score crudo: RRF(d) = sum(1 / (k + rank_i)) con k alrededor de 60. Es liviana en parámetros, robusta y la fusión por defecto en los endpoints híbridos de Weaviate, Qdrant y Milvus; Weaviate también ofrece fusión de score relativo con un peso alpha. Prefiere RRF a menos que tengas datos etiquetados para ajustar una fusión ponderada o aprendida.
| Database | Filtering model | Payload/field indexes | Hybrid (dense+sparse) | Fusion |
|---|---|---|---|---|
| pgvector | SQL WHERE (B-tree/GIN) | Full PostgreSQL indexes | Via tsvector FTS in SQL | Manual / RRF in SQL |
| Qdrant | Pre-filter (payload index) | Yes (typed payload idx) | Native sparse + dense | RRF / DBSF |
| Pinecone | Metadata filter | Metadata indexed | Sparse-dense records | Weighted / RRF |
| Weaviate | Filtered vector search | Property indexes | Native BM25 + vector | RRF / relative score |
| Milvus | Filtered search + partitions | Scalar indexes | Native sparse + dense | RRF / weighted |
| Chroma | Metadata where filter | Basic metadata index | No native (full-text add-on) | Manual |
5. Las Bases de Datos Comparadas
Estas seis cubren esencialmente todo escenario de RAG en 2026, desde un proyecto paralelo en una caja Postgres existente hasta una plataforma de búsqueda de mil millones de vectores. Todas hacen HNSW, filtrado por metadata y consultas sub-100ms a escala de millones -- así que la decisión se reduce al poder de filtrado, la búsqueda híbrida, el techo de escala y cuánta superficie operativa quieres poseer. Lee cada una con tu propia carga en mente; la tabla de decisión en la sección 8 mapea casos de uso a elecciones.
pgvector
Una extensión de PostgreSQL que agrega un tipo vector más índices HNSW e IVFFlat a la base de datos que ya corres. Su superpoder no es la velocidad cruda sino cero infraestructura nueva: los embeddings viven junto a tus datos relacionales, así que obtienes transacciones ACID, joins, llaves foráneas, filtrado por cláusula WHERE y tu backup/monitoreo/replicación existentes gratis. La extensión pgvectorscale (Timescale) agrega un índice estilo DiskANN y filtrado basado en etiquetas que la empujan mucho más allá de sus límites viejos. El default pragmático para equipos de hasta unos pocos millones de vectores que ya tienen PostgreSQL -- y a menudo la respuesta correcta incluso cuando un motor dedicado sería marginalmente más rápido.
Qdrant
Una base de datos vectorial nativa en Rust, construida para el propósito, que consistentemente lidera los benchmarks de búsqueda filtrada. Su fortaleza distintiva es el pre-filtrado rápido: los índices de payload resuelven las condiciones de metadata antes del recorrido vectorial, así que los filtros estrictos aún retornan un top-k completo sin el colapso de recall que causa el post-filtrado. Soporta cuantización escalar, binaria y de producto (ahorros de memoria 4-32x), vectores sparse y puntos multi-vector para búsqueda híbrida y de interacción tardía, y almacenamiento en disco para conjuntos grandes. Córrelo self-hosted vía Docker/Kubernetes o en Qdrant Cloud. La elección orientada a rendimiento cuando el RAG filtrado de baja latencia a escala es la prioridad.
Pinecone
Completamente gestionada y serverless -- no hay clúster que dimensionar, parchar ni escalar. Crea un índice, haz upsert de vectores, consulta; el almacenamiento y el cómputo escalan automáticamente y pagas por uso. Soporta namespaces para aislamiento multi-tenant, búsqueda híbrida sparse-dense, filtrado por metadata e inferencia integrada (embeber + reranquear sin salir de Pinecone). La opción de menor operación: ideal para equipos que quieren RAG de producción sin poseer una base de datos. Los tradeoffs son la dependencia del proveedor, la ausencia de self-hosting y costos que pueden trepar con alto volumen de consultas -- modela tu precio antes de comprometerte.
Weaviate
Open-source con la búsqueda híbrida más fuerte lista para usar: fusión nativa BM25 + vector (RRF o score relativo) en una sola consulta, así que la coincidencia semántica y por keywords se combinan sin plomería extra. Módulos opcionales de vectorizador y reranker embeben texto crudo por ti, y ofrece multi-tenancy, RBAC y campos con nombre/multi-vector. Disponible self-hosted o como Weaviate Cloud. La elección natural cuando la búsqueda híbrida es un requisito de primera clase -- docs técnicos, catálogos de producto, corpus mixtos de keyword/semántica -- y la quieres integrada en vez de ensamblada.
Milvus
Construido para escala de mil millones de vectores con una arquitectura distribuida y con almacenamiento-cómputo separados. Ofrece el menú de índices más amplio (HNSW, variantes IVF, IVF-PQ, DISKANN, CAGRA/GPU_IVF acelerados por GPU), búsqueda híbrida sparse+dense nativa y escalado horizontal en Kubernetes. Córrelo como Milvus Lite (embebido, instalable con pip para dev), Standalone (nodo único) o Distributed (clúster de producción); Zilliz Cloud es la versión gestionada. La elección para 100M+ de vectores, indexación acelerada por GPU o cuando necesitas escalar una sola colección mucho más allá de un nodo.
Chroma
La opción más amigable para desarrolladores para empezar. pip install chromadb y corre en-proceso con almacenamiento persistente y cero configuración; existen un modo cliente-servidor y la gestionada Chroma Cloud para cuando superes el modo embebido. Las funciones de embedding integradas (OpenAI, Cohere, sentence-transformers) hacen triviales los demos y notebooks. Es excelente para prototipado, RAG local y apps de una sola máquina, pero no apunta a producción a escala de miles de millones ni a cargas pesadas de híbrida/filtrado -- gradúate a pgvector, Qdrant o Milvus cuando la superes. Para configuraciones totalmente locales, combínala con inferencia local.
| Database | Deployment | Indexes | Hybrid search | Practical scale | Best for |
|---|---|---|---|---|---|
| pgvector | PostgreSQL ext (self / any managed PG) | HNSW, IVFFlat (+DiskANN via pgvectorscale) | Via SQL + tsvector | ~1-5M/node (more with pgvectorscale) | Existing Postgres stacks |
| Qdrant | Self-hosted + Qdrant Cloud | HNSW (on-disk), quantization | Native sparse + dense | Billions (sharded) | Low-latency filtered RAG |
| Pinecone | Managed serverless only | Proprietary (managed) | Sparse-dense | Billions | Zero-ops teams |
| Weaviate | Self-hosted + Weaviate Cloud | HNSW, flat, quantization | Native BM25 + vector | Billions | Built-in hybrid + vectorizers |
| Milvus | Self-hosted + Zilliz Cloud | HNSW, IVF*, IVF-PQ, DISKANN, GPU | Native sparse + dense | 10B+ (distributed) | Massive / GPU scale |
| Chroma | Embedded + server + Chroma Cloud | HNSW | No native (add-on) | ~1M (single machine) | Prototyping, local RAG |
6. Cuantización y Memoria
La memoria es el costo dominante de la búsqueda vectorial. Un millón de vectores float32 de 1536 dimensiones son ~6 GB solo para los arreglos crudos, antes de que el grafo HNSW lo duplique -- y HNSW lo quiere todo en RAM. La cuantización comprime los vectores para caber más por nodo y recortar costo, sacrificando un poco de exactitud que una pasada de rescoring usualmente recupera. Entender las cuentas de memoria y la escalera de cuantización es cómo mantienes un índice RAG grande económico sin arruinar el recall.
Las Cuentas de Memoria
Almacenamiento crudo = vectores x dimensiones x bytes-por-componente. float32 = 4 bytes, así que 1M x 1536 x 4 = ~6.1 GB; HNSW agrega aproximadamente m x 8-16 bytes por vector para los enlaces del grafo encima. Por esto 10M+ de vectores de precisión completa necesitan decenas de GB de RAM. Dos palancas lo reducen: menos dimensiones (truncamiento Matryoshka) y menos bytes por dimensión (cuantización). Computa este número primero -- decide el tamaño de tu nodo, tu elección de índice y a menudo toda tu arquitectura.
Cuantización Escalar (int8)
Mapea cada componente float32 a un entero de 8 bits usando rangos min/max por dimensión, recortando la memoria ~4x con típicamente menos de 1% de pérdida de recall -- el primer paso más seguro. Qdrant, Milvus y Weaviate ofrecen cuantización escalar int8 como una bandera de configuración; Cohere y Voyage incluso emiten embeddings int8 directamente. Mantén los vectores originales en disco para rescoring opcional. Si haces una sola cuantización, haz esta: es casi gratis en exactitud y divide en cuatro tu factura de RAM.
Cuantización Binaria
Reduce cada dimensión a un solo bit (el signo), dando un brutal recorte de memoria de ~32x y comparaciones por distancia de Hamming que son extremadamente rápidas. Funciona sorprendentemente bien para embeddings de alta dimensión y normalizados (1024+ dims de OpenAI, Cohere, Voyage) pero pierde demasiado en vectores de baja dimensión. Siempre combínala con oversampling + rescoring: recupera un conjunto amplio de candidatos con distancia binaria, luego re-ranquea los primeros cientos con vectores de precisión completa. Qdrant popularizó este patrón; Milvus y Weaviate también lo soportan.
Cuantización de Producto (PQ)
Divide cada vector en sub-vectores y codifica cada uno contra un codebook aprendido, comprimiendo a un puñado de bytes por vector (10-50x). Sustenta IVF_PQ de Milvus y Faiss para conjuntos a escala de miles de millones donde hasta int8 es demasiado grande. Las distancias PQ son más aproximadas que la cuantización escalar, así que una etapa de rescoring de precisión completa es esencial para un buen top-k. Elige PQ cuando tu corpus es tan grande que caber en memoria es la restricción vinculante, no cuando una cuantización escalar más simple bastaría.
Reducción de Dimensiones Matryoshka
Los modelos entrenados con Matryoshka (OpenAI text-embedding-3, Gemini, Voyage, Nomic) te dejan simplemente truncar un vector a menos dimensiones -- 3072 a 1536 a 768 a 256 -- con una pérdida de calidad gradual, no catastrófica. Reducir a la mitad las dimensiones reduce a la mitad el almacenamiento y acelera las cuentas de distancia, de forma ortogonal a la cuantización (puedes hacer ambas). Es la palanca más barata: sin cambio de índice, solo almacena menos componentes. Haz benchmark de la caída de recall sobre tus datos y quédate con la dimensión más pequeña que aún cumpla tu objetivo.
Oversampling y Rescoring
El truco que hace segura la cuantización agresiva: busca en el índice comprimido más candidatos de los que necesitas (por ejemplo, top-200 con binaria), luego recomputa las distancias exactas sobre los vectores de precisión completa solo para esos candidatos para producir el top-10 final. Conservas la mayor parte del ahorro de memoria mientras recuperas la mayor parte del recall. Qdrant, Milvus y Weaviate lo exponen como una opción de rescore/refine -- mantén los originales en disco (no están en el camino caliente) para que el rescoring pueda leerlos.
float16 y Almacenamiento en Disco
Entre float32 e int8 está float16/bfloat16 -- un simple recorte de 2x con pérdida de exactitud insignificante y sin codebook. Más allá de los trucos en memoria, la mayoría de motores puede mantener vectores y/o payloads en disco (HNSW en disco de Qdrant, MMAP/DISKANN de Milvus, pgvector en el almacenamiento de tabla estándar con el buffer cache) para que la RAM sostenga solo el grafo y los datos calientes. Combina reducción de dimensiones, cuantización y almacenamiento en disco para servir corpus grandes en hardware modesto y asequible.
7. Escala, Latencia y Rendimiento
"Rápido" no significa nada sin decir a qué recall, a qué tamaño de dataset y bajo qué filtros. Los benchmarks de proveedores escogen a dedo datasets limpios y sin filtrar al recall que los favorece. Para elegir bien, aprende a leer la curva recall-vs-QPS, dimensiona la memoria con honestidad y ten en cuenta el tiempo de construcción y los cold starts. Esta sección trata de medir el rendimiento sobre tu carga en vez de confiar en un número de marketing.
Cómo Leer Benchmarks
Las referencias neutrales son ANN-Benchmarks (recall vs QPS a nivel de algoritmo) y VectorDBBench (de punta a punta, incluye filtrado y tiempo de construcción). Cualquier número de latencia solo tiene sentido emparejado con un número de recall -- 5ms a recall 0.80 no es comparable con 20ms a 0.99. Cuidado con las comparaciones de peras con manzanas: un hilo vs concurrente, en memoria vs en disco, sin filtrar vs filtrado. Siempre reproduce sobre tus propios vectores, dimensiones, filtros y hardware antes de creer un ranking.
Ajuste de Recall@k
Recall@k es la fracción de los verdaderos top-k vecinos que tu índice ANN realmente retorna, medida contra un baseline flat/de fuerza bruta. Construye un conjunto de ground-truth a partir de una muestra de consultas, luego sube la perilla de consulta (ef_search para HNSW, nprobe para IVF) hasta que el recall alcance tu objetivo -- usualmente 0.95-0.99 para RAG. Ir más alto cuesta latencia por ganancias que se encogen. Vuelve a medir después de agregar filtros: los motores que post-filtran pueden caer muy por debajo de su recall de titular una vez que se aplica un filtro selectivo.
Throughput y Concurrencia
La latencia de una sola consulta y el QPS sostenido son métricas diferentes. La búsqueda HNSW está limitada por CPU y se paraleliza entre núcleos, así que el throughput escala con las vCPUs hasta que el ancho de banda de memoria se satura. Mide p50/p95/p99 bajo concurrencia realista, no una consulta a la vez -- la latencia de cola es lo que los usuarios sienten. Los motores en Rust (Qdrant) y los núcleos en C++ (Milvus) tienden a sostener colas más bajas bajo carga; pgvector comparte CPU con el resto de tu base de datos, así que aísla o descarga en réplicas las consultas vectoriales si compiten con el tráfico OLTP.
Sharding y Escala Horizontal
Cuando un nodo no puede albergar el índice ni servir el QPS, haces sharding: particionas los vectores entre nodos, distribuyes las consultas y fusionas el top-k. Milvus y Qdrant lo hacen de forma nativa; Pinecone lo oculta por completo detrás de serverless; pgvector escala lecturas con réplicas pero una sola colección generalmente vive en un primario. Las réplicas agregan throughput de lectura y HA; los shards agregan capacidad. Conoce tu curva de crecimiento -- adaptar sharding a un diseño de un solo nodo es doloroso.
Dimensionar Memoria por Vector
Presupuesta la RAM como: (dimensiones x bytes-por-componente) + overhead del grafo HNSW (~m x 8-16 bytes) + payload, por el número de vectores, más margen. Un punto HNSW float32 de 1536 dims cuesta aproximadamente 6-8 KB en total; la cuantización int8 lo baja hacia ~1.5-2 KB. Multiplica por tu conteo objetivo y sabes si cabe en un nodo o fuerza cuantización/DiskANN/sharding. Este único cálculo impulsa la mayoría de las decisiones de arquitectura -- hazlo antes de elegir una base de datos.
Tiempo de Construcción y Cold Start
Construir el índice no es gratis. La construcción de HNSW sobre millones de vectores puede tardar de minutos a horas y es intensiva en CPU; IVF construye más rápido pero necesita una muestra de entrenamiento representativa para sus centroides. pgvector puede construir HNSW de forma concurrente y con workers en paralelo; Milvus construye índices como jobs en segundo plano. Planea también los cold starts: los índices mapeados en memoria/en disco deben calentar su caché antes de alcanzar la latencia de estado estable. Ten en cuenta el tiempo de construcción y calentamiento en los deploys, la re-indexación y el failover -- no solo la velocidad de consulta en estado estable.
8. Self-Host vs Gestionado y Costo
El modelo de despliegue moldea el costo, el control y cuánto tiempo de tu equipo consume la base de datos. El espectro va desde serverless completamente gestionado (problema de otro, precio medido) pasando por clústeres gestionados y motores self-hosted hasta una librería embebida dentro de tu app. No hay una respuesta universalmente correcta -- solo el ajuste correcto para tu escala, necesidades de cumplimiento, infraestructura existente y apetito por la operación. Modela el costo total, no solo el precio de lista.
Serverless Gestionado
Pinecone es el arquetipo: sin clústeres que dimensionar, parchar ni escalar, y pagas por lo que usas (almacenamiento + lecturas/escrituras). Es el camino más rápido a RAG de producción y la menor carga operativa -- ideal para equipos pequeños y tráfico impredecible y con picos. Los costos son un precio medido que puede sorprenderte con alto volumen de consultas, sin control de infraestructura y dependencia del proveedor en una API propietaria. Modela un mes realista de lecturas y escrituras antes de comprometerte; serverless es más barato a volumen bajo-a-medio y estable.
Clústeres Gestionados
Qdrant Cloud, Weaviate Cloud y Zilliz Cloud (Milvus gestionado) corren el motor open-source por ti sobre nodos aprovisionados que tú dimensionas. Obtienes la mayor parte del alivio operativo de serverless mientras conservas el conjunto completo de características del motor y una salida para self-hostear el mismo software. El precio es típicamente por-nodo/por-hora más almacenamiento, lo cual es más predecible que el puro medido por uso a escala. El punto medio para equipos que quieren las capacidades del motor sin correr Kubernetes ellos mismos.
Self-Hosted
Corre Qdrant, Weaviate o Milvus tú mismo en Docker o Kubernetes. Obtienes máximo control, residencia de datos, sin tarifas por consulta y el menor costo por vector a gran escala -- a cambio de poseer las actualizaciones, backups, monitoreo, escalado y guardias. Vale la pena cuando el volumen es alto y estable, cuando el cumplimiento exige que los datos permanezcan en tu entorno o cuando ya operas infraestructura. Presupuesta tiempo real de ingeniería; el software "gratis" tiene un costo operativo muy real.
pgvector: Sin Base de Datos Nueva
El despliegue más barato es a menudo el que ya corres. Si tienes PostgreSQL -- autogestionado o en RDS/Cloud SQL/Supabase/Neon -- pgvector agrega búsqueda vectorial sin un servicio nuevo que desplegar, asegurar, respaldar o pagar por separado. Un sistema que operar, un backup, un conjunto de credenciales, consistencia transaccional entre vectores y datos relacionales. Para una gran parte de las apps de RAG bajo unos pocos millones de vectores, esto elimina toda la decisión de "qué base de datos vectorial".
Modelo de Costo Gestionado
El precio gestionado tiene tres impulsores: vectores almacenados (GB, función de conteo x dimensiones x precisión), volumen de consultas (lecturas) y escrituras/actualizaciones. Dos grandes palancas lo recortan: la cuantización y el truncamiento Matryoshka reducen el almacenamiento; el caché semántico recorta las lecturas. Vigila las réplicas (costo multiplicado por HA/throughput) y el egress. El modo de falla es una app parlanchina haciendo millones de consultas pequeñas -- agrupa en lotes, cachea y pre-filtra para mantener bajo el volumen de lectura. Siempre proyecta un mes pico, no uno promedio.
TCO de Self-Host
El costo self-hosted es mayormente RAM (HNSW quiere los vectores en memoria) más CPU para el throughput de consultas, más disco para los índices en disco y los originales -- y luego el rubro oculto: el tiempo de ingeniería para actualizaciones, backups, monitoreo e incidentes. Las cuentas de memoria de la sección 6 dimensionan la caja; la cuantización y DiskANN la encogen. A escala grande y estable, self-hostear es dramáticamente más barato por vector que lo gestionado; a escala pequeña el overhead operativo usualmente no vale la pena frente a serverless o pgvector.
Embebido / En-Proceso
Para apps locales, edge, pruebas y prototipos, una librería en-proceso no necesita servidor alguno: Chroma (modo embebido), Milvus Lite, sqlite-vec, LanceDB y FAISS corren dentro de tu proceso con los datos en archivos locales. Cero salto de red, cero despliegue, trivial de enviar en una CLI o app de escritorio. El techo es una sola máquina y concurrencia limitada -- perfecto para empezar, y fácil de graduar a un motor cliente-servidor cuando necesites escala multi-nodo o acceso compartido.
Residencia, Cumplimiento y Lock-In
Más allá del costo, sopesa dónde pueden vivir los datos y qué tan difícil es irse. Los datos regulados (salud, finanzas, datos personales de la UE) pueden prohibir una nube gestionada de terceros, empujándote a self-hostear o a pgvector en tu propia región. El lock-in varía: los motores open-source (Qdrant, Weaviate, Milvus, Chroma, pgvector) te dejan migrar o self-hostear el mismo software; una API gestionada propietaria es más difícil de abandonar. Conserva los embeddings crudos y el texto fuente para que siempre puedas re-indexar en otro motor -- tu corpus, no el almacén de vectores, es el activo.
9. Guía de Decisión por Caso de Uso
No hay una única mejor base de datos vectorial -- solo el mejor ajuste para tus restricciones. Parte de lo que ya corres y de cuántos vectores tienes, luego deja que las necesidades de filtrado, la escala y el apetito operativo desempaten. El default que sorprende a la gente: si ya corres PostgreSQL y tienes menos de unos pocos millones de vectores, pgvector es muy a menudo la respuesta correcta, y elimina toda la decisión de "qué base de datos vectorial". Los escenarios de abajo mapean situaciones comunes de RAG a una elección.
Ya en Postgres, menos de 5M de vectores
Si ya operas PostgreSQL y tu corpus está por debajo de unos pocos millones de vectores, usa pgvector. Obtienes búsqueda vectorial con cero infraestructura nueva, consistencia transaccional entre embeddings y filas relacionales, filtrado SQL con índices reales y tus backups y monitoreo existentes. No es el motor más rápido a escala extrema, pero para la mayoría de las apps de RAG es lo suficientemente rápido y por mucho el más simple. Agrega pgvectorscale para DiskANN y mayor escala antes de recurrir a un motor dedicado.
Búsqueda filtrada más rápida
Cuando la baja latencia bajo filtrado pesado por metadata es la prioridad -- RAG multi-tenant, búsqueda acotada por permisos, restricciones estrictas de fecha/tipo -- elige Qdrant. Su núcleo en Rust y el pre-filtrado vía índices de payload mantienen fuertes el recall y la latencia justo donde los motores que post-filtran se desmoronan, y sus opciones de cuantización mantienen asequibles los índices grandes. Self-hostéalo o usa Qdrant Cloud. La elección orientada a rendimiento cuando la búsqueda vectorial filtrada es tu camino caliente.
Cero operación, serverless
Si no tienes apetito para correr una base de datos y quieres lanzar RAG ahora, elige Pinecone. Serverless significa sin dimensionar, parchar ni escalar, y la inferencia integrada puede embeber y reranquear por ti. Mejor para equipos pequeños, tráfico con picos y volumen de consultas bajo-a-medio y estable. Modela tus costos de lectura/escritura primero y acepta el tradeoff del lock-in -- conserva tus embeddings crudos para poder migrar después si el volumen hace más barato el self-hosting.
Búsqueda híbrida de primera clase
Cuando la precisión por keywords importa tanto como la semántica -- docs técnicos, código, catálogos de producto, corpus cargados de acrónimos -- Weaviate te da fusión nativa BM25 + vector en una consulta, más módulos opcionales de vectorizador y reranker para que puedas pasar texto crudo. Escala a miles de millones y corre self-hosted o en Weaviate Cloud. La elección cuando quieres recuperación híbrida integrada en vez de ensamblada a partir de piezas.
Escala de miles de millones o GPU
Para 100M+ de vectores, indexación acelerada por GPU o una sola colección que debe escalar mucho más allá de un nodo, elige Milvus. Su arquitectura distribuida y con almacenamiento-cómputo separados, su amplio menú de índices (IVF-PQ, DISKANN, GPU CAGRA) y su búsqueda híbrida nativa están construidos para escala masiva. Córrelo self-hosted en Kubernetes o como Zilliz Cloud gestionado. Excesivo para corpus pequeños -- pero la herramienta correcta cuando la escala es la restricción que define.
Prototipo, local o edge
Para notebooks, demos, pruebas, apps de escritorio y edge, o RAG totalmente local, empieza con Chroma (o Milvus Lite / sqlite-vec / LanceDB). Corre en-proceso con un solo pip install, no necesita servidor y tiene funciones de embedding integradas. Prototipa rápido, luego gradúate a pgvector, Qdrant o Milvus cuando necesites escala multi-nodo, filtrado pesado o acceso compartido de producción. Genial para empezar; no un motor de producción a escala de miles de millones.
| Your situation | Pick | Why |
|---|---|---|
| Already run PostgreSQL, <5M vectors | pgvector | No new infra, SQL filtering, transactional |
| Low latency under strict filters, multi-tenant | Qdrant | Rust + pre-filtering keep recall & speed |
| Want zero database operations, ship now | Pinecone | Serverless, no sizing or scaling |
| Hybrid (keyword + semantic) is essential | Weaviate | Native BM25 + vector fusion built in |
| 100M+ vectors, GPU indexing, sharding | Milvus | Distributed, widest index menu |
| Prototype, notebook, local/edge app | Chroma | In-process, zero config, pip install |
# Quick RAM sizing to decide "one node vs quantize/shard"
def ram_gb(n_vectors, dims, bytes_per_component=4, hnsw_m=16):
vec_bytes = n_vectors * dims * bytes_per_component # raw vectors
graph_bytes = n_vectors * hnsw_m * 12 # ~HNSW links
total = (vec_bytes + graph_bytes) * 1.3 # +30% headroom
return round(total / 1e9, 2)
# 5M vectors, 1536 dims, HNSW m=16
print("float32:", ram_gb(5_000_000, 1536), "GB") # ~46 GB -> big node
print("int8 :", ram_gb(5_000_000, 1536, bytes_per_component=1), "GB") # ~12 GB
print("MRL-768 int8:", ram_gb(5_000_000, 768, bytes_per_component=1), "GB") # ~6 GB
# Rule of thumb:
# fits comfortably in one node's RAM -> pgvector / single Qdrant / Chroma
# too big for RAM but budget matters -> quantize (int8/binary) or DiskANN
# still too big or needs > 1 node of QPS -> Milvus / sharded Qdrant / Pinecone
10. Operación en Producción
Llevar una base de datos vectorial a producción es más que una consulta que funciona. Necesitas actualizaciones incrementales, backups, aislamiento de tenants, monitoreo y una historia de costo que sobreviva al crecimiento. El algoritmo de recuperación es la parte fácil; la superficie operativa -- mantener el índice fresco, durable, aislado y observable -- es lo que separa un demo de un sistema que puedes operar a las 3am.
Upserts e Indexación Incremental
Nunca re-embebas todo el corpus en cada cambio. Identifica cada vector por un ID estable de documento/chunk, rastrea los checksums de contenido y re-embebe solo lo que cambió -- luego haz upsert (insertar-o-reemplazar) por ID. Maneja los borrados explícitamente: un vector huérfano de un documento removido sigue apareciendo en los resultados. Para fuentes como Confluence o Notion, dispara la re-ingestión con webhooks. HNSW maneja inserciones en línea pero el borrado pesado fragmenta el grafo con el tiempo, así que programa mantenimiento/reconstrucciones periódicas del índice.
Backups y Snapshots
Tus vectores son costosos de recomputar, así que respáldalos. pgvector se apoya en tus backups existentes de PostgreSQL (pg_dump, WAL, PITR) -- una ventaja real. Qdrant y Milvus proveen herramientas de snapshot/backup; los servicios gestionados lo manejan pero verifica el RPO/RTO y que realmente puedas restaurar. Mantén el texto fuente crudo y los embeddings en almacenamiento de objetos barato como el camino de recuperación definitivo: siempre puedes reconstruir cualquier índice a partir de ellos, y también te libera para migrar de motor.
Multi-Tenancy
Aísla los datos de cada cliente para prevenir fugas. Opciones: colecciones separadas por tenant (aislamiento más fuerte, más overhead), multi-tenancy nativo (tenants de Weaviate, namespaces de Pinecone, Qdrant con un índice de payload de tenant), o un filtro tenant_id aplicado del lado del servidor (pgvector con seguridad a nivel de fila). Siempre aplica el filtro de tenant como un pre-filtro obligatorio, nunca confíes en el cliente. Prueba el aislamiento consultando como tenant A y afirmando cero filas del tenant B.
Monitoreo y Observabilidad
Rastrea la latencia de consulta (p50/p95/p99), el QPS, el recall sobre una muestra etiquetada, el uso de memoria y disco, la duración de construcción/refresco del índice y las tasas de error. Alerta sobre la escalada de la latencia p99 (fragmentación del índice o presión de memoria) y sobre la deriva de recall (una métrica o modelo mal emparejados tras un cambio). Vigila específicamente el camino de consulta filtrada -- se degrada distinto que la no filtrada. Para pgvector, reutiliza tu monitoreo existente de PostgreSQL; los motores dedicados exponen métricas de Prometheus.
Optimización de Costos
Las grandes palancas, en orden: cuantiza (int8 es ~4x, binaria+rescore ~32x), trunca dimensiones con modelos Matryoshka, mueve los vectores fríos a disco/DiskANN y cachea las consultas repetidas en Redis. En servicios gestionados, agrupa las escrituras en lotes y pre-filtra para recortar las lecturas medidas, y piensa bien antes de agregar réplicas (multiplican el costo). Dimensiona el nodo justo a partir de las cuentas de memoria de la sección 6 en vez de sobre-aprovisionar RAM que nunca usas.
Ingestión Masiva y Batching
Las cargas iniciales de millones de vectores necesitan batching: embebe en lotes (respeta los límites de tasa del proveedor y usa workers en paralelo), luego haz upsert en lotes de cientos a miles por request. Para cargas muy grandes, inserta los datos primero y construye el índice después -- IVF necesita los datos para entrenar los centroides, y masivo-luego-índice es mucho más rápido que uno a la vez. En pgvector, usa COPY y construye HNSW con workers en paralelo; en Milvus/Qdrant, usa sus caminos de importación masiva. Haz la ingestión idempotente para que un reintento no duplique vectores.
Seguridad y Control de Acceso
Una base de datos vectorial puede filtrar datos sensibles a través de la recuperación. Almacena las reglas de acceso por documento como metadata y aplícalas como un pre-filtro del lado del servidor en tiempo de consulta -- nunca confíes en que el LLM respete los límites, ya que cualquier cosa en el contexto puede aparecer en la respuesta. Cifra en reposo y en tránsito, acota las API keys y bloquea el puerto de admin/dashboard (no expongas Qdrant/Milvus al internet público). Audita las consultas que tocan datos restringidos, y recuerda que los embeddings pueden filtrar información sobre su texto fuente.
11. Ejemplos de Código de Cliente
La misma tarea de RAG -- crear una colección, hacer upsert de embeddings con metadata, ejecutar una búsqueda de similaridad filtrada -- a través de los cuatro motores que es más probable que elijas. Cada uno usa el cliente actual de 2026 y muestra una configuración HNSW/coseno más filtrado por metadata. Los embeddings vienen de cualquier modelo (se muestra OpenAI text-embedding-3); cambia por el tuyo. Estas son las 20 líneas que cargan el peso; envuélvelas en tu código de ingestión y generación.
pgvector (Python + SQL, HNSW)
import psycopg2, json, openai
client = openai.OpenAI()
conn = psycopg2.connect("postgresql://user:pass@localhost:5432/ragdb")
def embed(text: str) -> list[float]:
r = client.embeddings.create(model="text-embedding-3-small",
input=text, dimensions=1536)
return r.data[0].embedding
# Setup: extension, table, and an HNSW cosine index
with conn.cursor() as cur:
cur.execute("CREATE EXTENSION IF NOT EXISTS vector")
cur.execute("""
CREATE TABLE IF NOT EXISTS documents (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
content TEXT NOT NULL,
tenant_id TEXT,
year INT,
embedding vector(1536)
)""")
cur.execute("""
CREATE INDEX IF NOT EXISTS docs_emb_idx ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200)""")
# index the columns you filter on
cur.execute("CREATE INDEX IF NOT EXISTS docs_tenant_idx ON documents (tenant_id, year)")
conn.commit()
def upsert(content, tenant_id, year):
with conn.cursor() as cur:
cur.execute(
"INSERT INTO documents (content, tenant_id, year, embedding) "
"VALUES (%s, %s, %s, %s::vector)",
(content, tenant_id, year, str(embed(content))))
conn.commit()
def search(query, tenant_id, k=5):
qv = str(embed(query))
with conn.cursor() as cur:
cur.execute("SET LOCAL hnsw.ef_search = 100") # recall/latency knob
cur.execute("""
SELECT content, 1 - (embedding <=> %s::vector) AS score
FROM documents
WHERE tenant_id = %s AND year >= 2025 -- pre-filter
ORDER BY embedding <=> %s::vector
LIMIT %s""", (qv, tenant_id, qv, k))
return cur.fetchall()
print(search("how do I rotate API keys?", tenant_id="acme"))
Qdrant (búsqueda filtrada, cliente Python)
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance, VectorParams, PointStruct, Filter, FieldCondition,
MatchValue, Range, PayloadSchemaType,
)
import openai
oai = openai.OpenAI()
client = QdrantClient(url="http://localhost:6333") # or QdrantClient(url=..., api_key=...)
def embed(text: str) -> list[float]:
return oai.embeddings.create(model="text-embedding-3-small",
input=text, dimensions=1536).data[0].embedding
# 1. Create a collection (HNSW + cosine are the defaults)
# recreate_collection is deprecated -- check existence, then create
if not client.collection_exists("docs"):
client.create_collection(
collection_name="docs",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)
# index the payload fields you filter on -> fast pre-filtering
client.create_payload_index("docs", "tenant_id", PayloadSchemaType.KEYWORD)
client.create_payload_index("docs", "year", PayloadSchemaType.INTEGER)
# 2. Upsert vectors with metadata payloads
def upsert(idx, content, tenant_id, year):
client.upsert("docs", points=[PointStruct(
id=idx, vector=embed(content),
payload={"content": content, "tenant_id": tenant_id, "year": year})])
# 3. Filtered similarity search (filter applied BEFORE the vector walk)
def search(query, tenant_id, k=5):
hits = client.query_points(
collection_name="docs",
query=embed(query),
query_filter=Filter(must=[
FieldCondition(key="tenant_id", match=MatchValue(value=tenant_id)),
FieldCondition(key="year", range=Range(gte=2025)),
]),
limit=k, with_payload=True,
search_params={"hnsw_ef": 100}, # recall/latency knob
).points
return [(h.payload["content"], h.score) for h in hits]
print(search("how do I rotate API keys?", tenant_id="acme"))
Pinecone (serverless)
from pinecone import Pinecone, ServerlessSpec
import openai
oai = openai.OpenAI()
pc = Pinecone(api_key="your-api-key")
def embed(text: str) -> list[float]:
return oai.embeddings.create(model="text-embedding-3-small",
input=text, dimensions=1536).data[0].embedding
# 1. Create a serverless index (nothing to size or scale)
if not pc.has_index("docs"):
pc.create_index(
name="docs", dimension=1536, metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"))
index = pc.Index("docs")
# 2. Upsert vectors with metadata; namespaces isolate tenants
def upsert(idx, content, tenant_id, year):
index.upsert(namespace=tenant_id, vectors=[{
"id": idx, "values": embed(content),
"metadata": {"content": content, "year": year}}])
# 3. Filtered query within a tenant namespace
def search(query, tenant_id, k=5):
res = index.query(
namespace=tenant_id,
vector=embed(query),
top_k=k, include_metadata=True,
filter={"year": {"$gte": 2025}})
return [(m["metadata"]["content"], m["score"]) for m in res["matches"]]
print(search("how do I rotate API keys?", tenant_id="acme"))
Weaviate (búsqueda híbrida nativa)
import weaviate
from weaviate.classes.config import Configure, Property, DataType
from weaviate.classes.query import Filter
import openai
oai = openai.OpenAI()
client = weaviate.connect_to_local() # or connect_to_weaviate_cloud(...)
def embed(text: str) -> list[float]:
return oai.embeddings.create(model="text-embedding-3-small",
input=text, dimensions=1536).data[0].embedding
# 1. Create a collection; we supply our own vectors
if not client.collections.exists("Docs"):
client.collections.create(
"Docs",
vector_config=Configure.Vectors.self_provided(),
properties=[
Property(name="content", data_type=DataType.TEXT),
Property(name="tenant_id", data_type=DataType.TEXT),
Property(name="year", data_type=DataType.INT),
])
docs = client.collections.get("Docs")
# 2. Insert objects with their vectors
def upsert(content, tenant_id, year):
docs.data.insert(
properties={"content": content, "tenant_id": tenant_id, "year": year},
vector=embed(content))
# 3. Hybrid search: BM25 keywords + vector, fused by RRF, with a filter
def search(query, tenant_id, k=5):
res = docs.query.hybrid(
query=query, # keyword side (BM25)
vector=embed(query), # semantic side
alpha=0.5, # 0 = keyword only, 1 = vector only
filters=Filter.by_property("tenant_id").equal(tenant_id)
& Filter.by_property("year").greater_or_equal(2025),
limit=k)
return [(o.properties["content"], o.metadata.score) for o in res.objects]
print(search("how do I rotate API keys?", tenant_id="acme"))
client.close()