15 · Bases de datos vectoriales
Una lista de embeddings en memoria sirve para una demo. Una base de datos vectorial añade las cuatro cosas que producción necesita: persistencia, filtrado por metadatos, escala y una API limpia de upsert/query.
Una base de datos vectorial es la capa de almacenamiento de RAG: persiste registros {vector, text, metadata}, te deja pre-filtrar por metadatos antes de la búsqueda por similitud y escala la consulta de vecinos más cercanos que tu bucle en memoria hacía a mano — todo detrás de una API de upsert/query.
Sin esto:
Sin una base de datos vectorial tu recuperación vive en una lista de Python que muere cuando el proceso reinicia, re-embebe todo en cada arranque, no puede decir 'busca solo en los documentos de este usuario' y se arrastra pasados unos miles de vectores.
En las lecciones de RAG embebiste un puñado de documentos en vectores y los buscaste con un bucle de similitud coseno sobre una lista de Python. Ese es el modelo mental correcto — y es exactamente lo que hace una base de datos vectorial — pero una lista en memoria carece de cuatro cosas que toda app real necesita:
-
Persistencia. Una lista desaparece cuando el proceso reinicia. Re-embeber todo tu corpus en cada arranque es lento y cuesta dinero. Una BD vectorial guarda los vectores en disco para que embebas una sola vez.
-
Filtrado por metadatos. Los registros reales no son solo un vector y un string. Llevan metadatos:
{"category": "billing", "user_id": 42, "lang": "es", "date": "2026-01"}. Constantemente necesitas buscar dentro de un subconjunto — solo los docs de este usuario, solo la categoría de FAQ, solo documentos más nuevos que una fecha. Una BD vectorial te deja pre-filtrar por metadatos y luego correr la similitud solo sobre lo que sobrevive. -
Escala. Un bucle de coseno por fuerza bruta es O(N). Con unos miles de vectores está bien; con millones es demasiado lento. Las BD vectoriales usan índices de vecinos más cercanos aproximados (próxima lección) para responder en tiempo sub-lineal.
-
Una API limpia. Dos verbos cubren casi todo: upsert (insertar-o-actualizar registros por id) y query (dar un vector de consulta + filtro opcional, obtener el top-k). Toda BD vectorial — Chroma, Pinecone, pgvector, Weaviate, Qdrant — expone esta misma forma.
El salto conceptual que esta lección fija es el #2, el filtrado por metadatos, porque cambia los resultados, no solo la velocidad. Construyamos un store vectorial diminuto que guarde {vector, text, metadata} y soporte un pre-filtro.
El store de abajo es una clase con los dos verbos canónicos. upsert guarda registros en un dict indexado por id (así re-insertar el mismo id actualiza en vez de duplicar — esa es la up de upsert). query toma un vector de consulta y un filtro where opcional; primero acota el conjunto de candidatos por metadatos y luego ordena solo a los sobrevivientes por similitud coseno. Observa cómo la misma consulta devuelve documentos distintos al filtrar por categoría — el filtro cambia la respuesta, no solo el tiempo de ejecución.
Python (in browser)
El mismo vector de consulta devuelve documentos distintos al aplicar un pre-filtro por metadatos — filtrar cambia el conjunto de resultados, no solo la velocidad.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Fíjate en tres propiedades que tu lista en memoria anterior no tenía, todas visibles en esa única clase:
- Upsert por id — llamar a
upsert("d3", ...)de nuevo reemplaza d3 en vez de agregar una segunda copia. Así actualizas un documento cuando su texto fuente cambia, sin reconstruir el índice. - Los registros llevan metadatos — cada vector viaja con un dict por el que puedes filtrar.
- Un contrato de consulta —
query(vector, top_k, where)es la misma firma ya sea que los datos vivan en este dict, en un archivo de disco de Chroma o en un clúster de Pinecone con millones de vectores.
Aquí está el mismo flujo contra un cliente real de Chroma. Lee cómo add/upsert y query(where=...) reflejan nuestra clase uno a uno — la única diferencia es que Chroma persiste a disco y usa un índice ANN en vez de un bucle de Python.
Las BD vectoriales reales exponen los mismos dos verbos — upsert(ids, embeddings, metadatas) y query(vector, top_k, where). La persistencia y el ANN son las partes que obtienes gratis.
Tu bot de soporte debe responder usando solo los documentos que pertenecen al cliente con sesión iniciada. ¿Qué característica de la BD vectorial lo hace correcto y eficiente?
- Una BD vectorial es la capa de almacenamiento de RAG: persiste registros {vector, text, metadata} para que embebas una vez en lugar de en cada arranque.
- El pre-filtrado por metadatos cambia el conjunto de resultados (busca solo este usuario / categoría / idioma), no solo la velocidad — pre-filtra y luego ordena por similitud.
- Toda BD vectorial expone los mismos dos verbos: upsert (insertar-o-actualizar por id) y query (vector + top_k + filtro where).
Todo sistema RAG de producción guarda embeddings en Chroma, Pinecone, pgvector, Weaviate o Qdrant y usa filtros de metadatos para aislamiento multi-tenant, frescura y enrutamiento por categoría.
Si lo quitas: Mantén la recuperación en una lista en memoria y re-embeberás en cada reinicio, filtrarás datos entre tenants y te atascarás cuando el corpus crezca más allá de unos miles de vectores.