Si alguna vez te has preguntado cómo una aplicación de IA encuentra resultados «con sentido similar» en lugar de coincidencias exactas de palabras, la respuesta vive en este tipo de base de datos.
El problema que resuelven
Las bases de datos relacionales son brillantes para lo que fueron diseñadas: buscar filas exactas en tablas bien definidas. ¿Quieres todos los usuarios con país = 'México'? Perfecto. ¿Quieres todos los artículos que hablen sobre el mismo tema que esta pregunta, aunque usen palabras distintas? Ahí el modelo relacional empieza a fallar.
Una base de datos vectorial nació precisamente para resolver ese problema. En lugar de buscar coincidencias de texto, busca similitud de significado, y lo hace de manera eficiente incluso con millones de registros.
¿Qué hay dentro de cada registro?
A diferencia de una fila tradicional con columnas de texto o números, cada elemento en una base de datos vectorial combina tres partes:
- El vector (embedding): una lista de números de coma flotante que codifica el significado conceptual del elemento. Un fragmento de texto sobre «perros» y otro sobre «cachorros» tendrán vectores matemáticamente cercanos entre sí.
- Los datos originales: el contenido real que se quiere mostrar al usuario — el texto, la URL de una imagen, el fragmento de audio.
- Los metadatos: campos estructurados adicionales (categoría, fecha, autor, idioma) que permiten combinar la búsqueda semántica con filtros tradicionales.
Los tres viajan juntos. Sin los datos originales no puedes mostrar nada; sin los metadatos no puedes filtrar; sin el vector no puedes buscar por significado.
El rol de los embeddings
Los datos no estructurados no pueden entrar directamente a una base de datos vectorial — primero hay que convertirlos en números. Ese proceso lo realizan modelos de Machine Learning (como los de la familia de OpenAI, Cohere o sentence-transformers) que aprenden a representar el significado en un espacio multidimensional.
La intuición clave es esta: en ese espacio de cientos o miles de dimensiones, los elementos con significados parecidos quedan geométricamente cerca. «Rey» y «monarca» estarán cerca. «Rey» y «pizza» estarán lejos. La búsqueda semántica es, en el fondo, una búsqueda de vecinos cercanos en ese espacio.
El algoritmo de búsqueda: HNSW
Aquí está el reto de ingeniería real. Si tienes diez millones de vectores y recibes una consulta, calcular la distancia exacta hacia cada uno de ellos sería computacionalmente inviable en producción.
La solución son los algoritmos de Búsqueda de Vecinos Más Cercanos Aproximados (ANN). El más extendido en las bases de datos vectoriales modernas es HNSW (Hierarchical Navigable Small World), y funciona con una estructura de capas jerárquicas:
Capa superior: saltos largos
La capa más alta contiene muy pocos nodos distribuidos de forma dispersa. Al recibir una consulta, el algoritmo entra por aquí y da saltos grandes hacia el nodo más cercano, cubriendo mucha distancia rápidamente con muy pocas comparaciones.
Capas intermedias: refinamiento progresivo
Desde el nodo alcanzado en la capa superior se baja de nivel. En cada capa hay más densidad de puntos, y el algoritmo navega hacia vecinos cada vez más cercanos al vector de consulta.
Capa inferior: precisión fina
La capa base tiene la densidad total de puntos. Aquí se realizan desplazamientos cortos y precisos hasta localizar el vecino óptimo.
El resultado es una búsqueda que se aproxima al resultado exacto en una fracción del tiempo que requeriría una comparación exhaustiva. Es el equilibrio perfecto entre velocidad y precisión que hace viable el uso en producción.
Implementación práctica con FAISS
Una de las bibliotecas de referencia para trabajar con índices vectoriales es FAISS (Facebook AI Similarity Search). El flujo estándar tiene cuatro pasos:
1. Crear e inicializar el índice
Se define la dimensión de los vectores y la métrica de distancia a utilizar. IndexFlatL2 aplica distancia euclidiana (L2), la más común para embeddings de texto:
import faiss
import numpy as np
dimension = 768 # dimensión del modelo de embeddings
index = faiss.IndexFlatL2(dimension)
2. Añadir los vectores al índice
Una vez que los embeddings están generados, se añaden al índice para su almacenamiento y búsqueda posterior:
# vectors: array de shape (N, dimension) con dtype float32
index.add(vectors.astype("float32"))
print(f"Vectores indexados: {index.ntotal}")
3. Convertir la consulta en un vector
La consulta del usuario pasa por el mismo modelo de embeddings que generó los vectores del índice:
query_text = "¿Cómo funcionan los transformers?"
# Suponiendo un modelo previamente cargado (ej. de sentence-transformers)
query_vector = modelo.encode([query_text]).astype("float32")
4. Ejecutar la búsqueda
Se recuperan los K vecinos más cercanos. FAISS devuelve tanto las distancias como los índices de los elementos más similares:
k = 5 # número de resultados
distances, indices = index.search(query_vector, k)
for i, idx in enumerate(indices[0]):
print(f"Resultado {i+1}: ID={idx}, distancia={distances[0][i]:.4f}")
En resumen
| Concepto | Función |
|---|---|
| Embedding | Transforma datos en vectores numéricos con significado |
| Índice vectorial | Estructura que permite buscar entre millones de vectores |
| HNSW | Algoritmo jerárquico para búsqueda aproximada rápida |
| Metadatos | Permiten combinar búsqueda semántica con filtros exactos |
| FAISS | Biblioteca de referencia para crear y consultar índices |
Las bases de datos vectoriales son la infraestructura silenciosa detrás de los chatbots con memoria, los motores de búsqueda semántica, los sistemas de recomendación y las aplicaciones RAG (Retrieval-Augmented Generation). Entender cómo funcionan es entender uno de los bloques fundamentales del stack de IA moderno.
Tema Relacionado: IA Aplicada