6 · Un índice vectorial desde cero
Los embeddings más la similitud coseno se vuelven un motor de búsqueda cuando guardas vectores con su texto y devuelves los k más cercanos. Construye un VectorIndex diminuto y mira emerger la búsqueda semántica.
Un índice vectorial es solo una lista de registros (vector, metadatos) más un método de búsqueda: embebe la consulta, puntúa cada vector almacenado por similitud coseno y devuelve los k mejores con su texto original. Esa pequeña estructura de datos — almacenar, luego rankear por cercanía — es el motor de recuperación dentro de cada sistema RAG y base de datos vectorial.
Sin esto:
Sin un índice, re-embeberías y re-escanearías todo desde cero en cada consulta, y no tendrías forma de recuperar el texto original detrás de un vector ganador — así la recuperación no puede darle a un LLM nada legible.
Ahora ensamblamos las piezas de las lecciones 4 y 5 en la abstracción central de todo este track: un índice vectorial. Hace dos cosas:
add(id, vector, text)— almacena un vector junto a sus metadatos (aquí, el texto original, pero en apps reales también cosas como la URL de un documento, el autor o una marca de tiempo).search(query_vector, k)— puntúa cada vector almacenado contra la consulta por similitud coseno y devuelve los k mejores registros, de mayor puntaje primero.
Eso es todo. No hay red neuronal en el índice mismo — la inteligencia vive en el modelo de embeddings que produjo los vectores. El índice es solo contabilidad más un ordenamiento: guarda los vectores y, cuando se pide, los rankea por cercanía.
Un detalle crucial que el índice deja claro: almacenas el vector para buscar pero devuelves el texto para usar. El vector es cómo encuentras el registro correcto; el texto es lo que realmente le das al LLM (en RAG) o le muestras al usuario (en búsqueda). Perder ese mapeo — vector de vuelta a texto — haría inútil la recuperación.
Abajo, VectorIndex está construido sobre una matriz NumPy de vectores almacenados, así un solo @ (producto matriz-vector) puntúa todo el corpus de una vez. Incluimos un embed() diminuto en la misma celda para que toda la tubería de búsqueda — embeber → almacenar → consultar → rankear → devolver texto — corra de principio a fin.
Python (in browser)
Un VectorIndex desde cero: add() almacena vector+texto, search() rankea todo el corpus con un producto matricial y devuelve los k textos originales — búsqueda semántica en ~25 líneas.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Fíjate en lo que acaba de pasar: una consulta como "python bug in the code" sacó a flote tanto d1 como d6 aunque ninguno coincide palabra por palabra con la consulta — comparten las direcciones de vocabulario python/code/bug/error, así que el coseno los rankea arriba. Cambia nuestro embed() de juguete por un modelo de embeddings real y este mismo VectorIndex se vuelve un motor de búsqueda semántica genuino. En producción usarías una base de datos vectorial, pero su modelo mental es exactamente lo que acabas de construir.
FAISS en producción refleja exactamente nuestro índice de juguete: normaliza, add() vectores, search() los k mejores por producto interno (= coseno), luego mapea los ids de vuelta a texto. La misma interfaz, escala industrial.
En el VectorIndex, ¿por qué almacenamos el texto original junto a cada vector en lugar de quedarnos solo con los vectores?
- Un índice vectorial es una lista de registros (vector, metadatos) más un método de búsqueda — almacenar, luego rankear por cercanía coseno.
- search(query, k) embebe la consulta, puntúa todos los vectores almacenados (un producto matricial para vectores unitarios) y devuelve los k mejores con su texto original.
- Almacena el vector para buscar, devuelve el texto para usar — el mapeo vector→texto es lo que hace la recuperación utilizable por un LLM. El escaneo por fuerza bruta sirve hasta ~100k vectores; los índices ANN escalan más con la misma interfaz.
Esta estructura add/search top-k ES el paso de recuperación de RAG y el núcleo de toda base de datos vectorial (Pinecone, Weaviate, pgvector, FAISS, Chroma) — el siguiente capítulo la conecta directo a un prompt de LLM.
Si lo quitas: Sin índice no puedes recuperar contexto relevante de forma eficiente, así que RAG, búsqueda semántica y recomendación no tienen nada que rankear — el LLM queda respondiendo solo de memoria, donde viven las alucinaciones.