Objetivos de aprendizaje
Después de leer este artículo podrás:
- Entender qué son los embeddings y la distancia entre vectores
- Ver cómo la búsqueda aproximada (HNSW e IVFFlat) gana velocidad
- Decidir entre una base vectorial dedicada y una extensión vectorial en una base común
Una base de datos tradicional responde bien a preguntas exactas: "el usuario con id 42", "pedidos con valor superior a 100". Le va mal con "muéstrame los textos que hablan de algo parecido a esto", porque la similitud de significado no es igualdad de palabras. "Subir mi código al servidor" y "hacer deploy" no comparten ningún término, pero dicen lo mismo.
La base de datos vectorial resuelve ese problema. Está detrás de la búsqueda semántica, de las recomendaciones y de buena parte de los sistemas de RAG, que dan contexto a un LLM antes de que responda.
¿Qué es un embedding?#
Un embedding es una lista de números (un vector) que un modelo genera para representar el significado de un contenido. El mismo modelo convierte "cómo alojar un bot" y "poner mi bot en línea" en vectores cercanos, y "receta de pastel" en un vector lejano.
Cada posición del vector es una dimensión. Los modelos reales usan cientos o miles. No interpretas los números uno por uno: lo que importa es la distancia entre dos vectores.
¿Cómo mide la base lo "parecido"?#
Por la distancia entre vectores. Las tres medidas más comunes:
- Distancia coseno: compara la dirección de los vectores e ignora el tamaño. Es la más usada para texto.
- Producto interno: mide alineación y magnitud juntas. Equivale al coseno cuando los vectores están normalizados.
- Distancia euclidiana (L2): la distancia en línea recta entre los dos puntos.
El ejemplo siguiente hace toda la búsqueda en memoria, con tres "documentos" inventados de 3 dimensiones (el vector de consulta representa "subir mi código al servidor"):
import numpy as np
docs = {
"Cómo alojar un bot": [0.9, 0.1, 0.0],
"Receta de pastel": [0.0, 0.1, 0.9],
"Deploy de una API en Python": [0.8, 0.3, 0.1],
}
consulta = np.array([0.85, 0.2, 0.05])
def coseno(a, b):
a, b = np.array(a), np.array(b)
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
ranking = sorted(docs, key=lambda t: coseno(docs[t], consulta), reverse=True)
for t in ranking:
print(f"{coseno(docs[t], consulta):.3f} {t}")La salida es 0.991 Cómo alojar un bot, 0.990 Deploy de una API en Python y 0.082 Receta de pastel. En la práctica, los vectores vienen de un modelo de embeddings, no de números escritos a mano. Llamar a ese modelo por API también cuesta dinero, y la cuenta sigue la lógica del costo de las APIs de LLM.
¿Por qué no comparar con todos los vectores?#
Comparar la consulta con todos los registros (búsqueda exacta, o fuerza bruta) da el resultado perfecto, pero el costo crece con el tamaño de los datos. Con pocos miles de vectores es aceptable. Con millones, se vuelve lento.
Por eso las bases vectoriales usan índices de vecino más cercano aproximado (ANN). Cambian un poco de precisión (el recall) por mucha velocidad. Dos índices aparecen en casi todas las herramientas:
- HNSW: arma un grafo de varias capas. Ofrece el mejor equilibrio entre velocidad y recall, pero tarda más en construirse y usa más memoria. Se puede crear sobre una tabla vacía.
- IVFFlat: divide los vectores en listas (grupos) y busca solo en algunas. Se construye más rápido y usa menos memoria, pero las consultas rinden peor. Hay que crearlo cuando la tabla ya tiene datos.
En ambos casos el resultado es aproximado: el índice puede, raramente, dejar fuera un vecino que la búsqueda exacta traería.
¿Base vectorial dedicada o extensión en una base común?#
Hay dos caminos:
- Una extensión en una base que administras tú mismo. Algunas bases relacionales, como PostgreSQL, aceptan extensiones que agregan un tipo de vector y operadores de distancia. Tus vectores quedan en la misma tabla y la misma transacción que el resto de los datos, y filtras con SQL normal.
- Una base vectorial dedicada. Servicios y servidores hechos solo para vectores, con recursos propios de escala y filtrado. Tienen sentido cuando el volumen o la latencia superan lo que entrega una extensión.
Para la mayoría de los proyectos pequeños y medianos, empezar con la extensión evita operar un sistema más. Migrar después es posible, porque el dato es solo una lista de números más un id.
¿Cómo es una búsqueda vectorial en la práctica?#
El flujo es parecido en casi todas las herramientas:
- Genera el embedding de cada documento y guarda el vector junto con un id y los metadatos.
- Crea el índice ANN con la misma medida de distancia que usarán las consultas. Un índice hecho para coseno no acelera una búsqueda por L2.
- En la consulta, genera el embedding de la pregunta con el mismo modelo y pide los k vecinos más cercanos, por ejemplo los 5 primeros.
- Ajusta el equilibrio entre recall y velocidad con el parámetro de búsqueda del índice: en HNSW, cuántos candidatos examina el grafo; en IVFFlat, cuántas listas se visitan.
Usa siempre el mismo modelo para documentos y consultas. Los vectores de modelos distintos no son comparables, aunque tengan el mismo número de dimensiones.
¿Cuándo no usar una base vectorial?#
- Buscas por palabra exacta, código o id. Un índice común o la búsqueda de texto completo lo resuelve mejor.
- La base es pequeña. Pocos miles de vectores caben en memoria y la fuerza bruta basta.
- Necesitas siempre el resultado exacto. Los índices ANN son aproximados.
La calidad de la búsqueda, por cierto, depende del modelo de embeddings, no de la base. Un modelo malo para tu idioma o dominio entrega malos vecinos, por rápido que sea el índice.
Cómo hacerlo en Square Cloud#
Square Cloud no ofrece base de datos vectorial, y el PostgreSQL alojado es el nativo, sin extensiones: nada de pgvector. Los vectores van en un servicio vectorial externo o, si la base es pequeña, en la memoria de la propia aplicación, con la búsqueda por fuerza bruta del ejemplo de arriba. Lo que alojas es la aplicación que ejecuta el flujo: una API que recibe la pregunta, genera el embedding, consulta los vectores y llama al LLM. Corre en un contenedor aislado y siempre encendido y, como aplicación web, usa como mínimo 512 MB del pool de RAM del plan. Mira cómo publicarla en hosting de APIs.
Cada pieza tiene su lugar:
- Embeddings: un proveedor externo. El runtime de Python trae wheels listas de torch y transformers, pero un modelo local de embeddings no cabe en el Hobby de 1 GB.
- Documentos y metadatos: el hosting de bases de datos empieza en el Standard, desde R$44,99 al mes. El Hobby, desde R$12,49, no incluye bases de datos.
- La respuesta del LLM: desde el Standard, el AI Gateway genera el texto final con una API compatible con la de OpenAI y el modelo
cubic. No genera embeddings ni guarda vectores: solo cubre el último paso, con los fragmentos recuperados en el prompt. Está en beta, con acceso anticipado sin costo extra, y los límites pueden cambiar cuando termine la beta.
IA
Aloja apps con IA en contenedores siempre encendidos
Preguntas frecuentes
¿Cuál es la diferencia entre una base vectorial y una base SQL?
La base SQL compara valores exactos (igual, mayor, contiene). La vectorial ordena los registros por la distancia entre vectores, lo que responde 'qué se parece más a esto'. Algunas bases SQL hacen las dos cosas con una extensión vectorial.
¿Necesito una base vectorial para usar RAG?
No es obligatorio. Con pocos miles de fragmentos, una búsqueda por fuerza bruta en memoria alcanza. La base vectorial dedicada compensa cuando el volumen y la latencia piden un índice.
¿Qué es un embedding?
Es una lista de números que un modelo genera para representar el significado de un texto, imagen o audio. Contenidos parecidos generan vectores cercanos.
Más en IA
Ver los 13 artículos- ¿Cuánto cuesta crear y mantener un chatbot con IA?Mantener un chatbot con IA cuesta tokens del modelo, hosting, base de datos y, con RAG, contexto extra. Mira cómo hacer la cuenta por conversación.
- ¿Cuánto cuesta un sistema RAG? Embeddings, base de datos y LLMUn RAG tiene tres costos: embeddings, base de datos vectorial y LLM. Mira la cuenta para mil preguntas al día, con precios hipotéticos, y dónde crece la factura.
- Cómo crear un bot de Discord con IACrea un bot de Discord con IA en discord.js: comando /ask, clave de API en variable de entorno, memoria por canal, límites de Discord y cómo ponerlo en línea.
- Cómo alojar un chatbot con IAUn chatbot con IA es una API que recibe mensajes, llama a un modelo de lenguaje y devuelve la respuesta en streaming. Mira el código y cómo ponerlo en línea.