Bases de datos vectoriales
Ahora que ya tienes claro qué son los embeddings, viene el siguiente paso: ¿dónde los guardas y cómo los organizas para poder buscar eficientemente? Aquí es donde entran las bases de datos vectoriales.
Una base de datos vectorial es como un almacén súper organizado donde cada embedding (que recordarás que es una lista de números) se guarda junto con el texto original del que procede. Pero no es un almacén cualquiera - está diseñado específicamente para encontrar vectores similares de forma rapidísima.
Imagínate que tienes miles de documentos. Cada uno se convierte en embeddings y se almacena en esta base de datos. Cuando llega tu pregunta, el sistema:
- Convierte tu pregunta en embeddings
- Busca en la base de datos vectorial qué embeddings son más parecidos
- Recupera los textos originales correspondientes a esos embeddings similares
El truco está en que estas bases de datos usan algoritmos especiales para comparar vectores. En lugar de mirar documento por documento (que sería lentísimo), crean una especie de "mapa" donde vectores similares están cerca unos de otros. Es como si organizases tu biblioteca no por orden alfabético, sino agrupando los libros por temática.
Las más conocidas son Pinecone, Weaviate, Chroma o FAISS. Cada una tiene sus ventajas, pero todas hacen lo mismo: permiten hacer búsquedas por similitud semántica en milisegundos, aunque tengas millones de documentos.
La clave es que cuando buscas "recetas de pasta", no necesitas que aparezca exactamente esa frase - la base de datos vectorial encuentra automáticamente textos sobre "cocinar espaguetis" o "preparar fideos" porque sus embeddings están próximos en el espacio vectorial.
Repasemos los pasos que hemos seguido para llegar hasta aquí: cargar documentos, splittearlos, crear embeddings y almacearlos.
FAISS para prototipado rápido
FAISS (Facebook AI Similarity Search) representa una excelente opción para comenzar a trabajar con vector stores en proyectos de RAG.
Esta biblioteca, desarrollada por Meta, está optimizada para realizar búsquedas de similaridad de manera eficiente y no requiere configuración de servidores externos, lo que la convierte en la herramienta ideal para prototipado rápido y experimentación.
Ventajas de FAISS para desarrollo inicial
FAISS destaca por su simplicidad de implementación y su capacidad para manejar grandes volúmenes de vectores sin necesidad de infraestructura compleja. A diferencia de soluciones que requieren bases de datos dedicadas, FAISS funciona completamente en memoria o puede persistir en archivos locales, permitiendo iteraciones rápidas durante el desarrollo.
La biblioteca utiliza algoritmos de indexación avanzados que proporcionan búsquedas extremadamente rápidas, incluso con millones de vectores. Esto la hace perfecta para validar conceptos y probar diferentes estrategias de embedding antes de migrar a soluciones más robustas en producción.
Configuración básica con LangChain
Para comenzar a utilizar FAISS en LangChain, necesitamos importar las dependencias necesarias y configurar nuestro modelo de embeddings:
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
# Configuración del modelo de embeddings
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small"
)
# Preparación de documentos de ejemplo
documentos = [
"Python es un lenguaje de programación versátil y fácil de aprender.",
"LangChain facilita la construcción de aplicaciones con modelos de lenguaje.",
"Los vector stores permiten búsquedas semánticas eficientes.",
"FAISS es una biblioteca optimizada para búsquedas de similaridad."
]
Creación del vector store desde documentos
El método más directo para crear un vector store FAISS es utilizando from_texts()
, que procesa una lista de textos y genera automáticamente los embeddings correspondientes:
# Crear vector store directamente desde textos
vector_store = FAISS.from_texts(
texts=documentos,
embedding=embeddings
)
print(f"Vector store creado con {vector_store.index.ntotal} documentos")
Para casos más complejos donde trabajamos con objetos Document, podemos utilizar from_documents()
:
from langchain_core.documents import Document
# Crear documentos con metadatos
docs = [
Document(
page_content="Python es un lenguaje de programación versátil.",
metadata={"categoria": "programacion", "nivel": "basico"}
),
Document(
page_content="LangChain facilita el desarrollo con LLMs.",
metadata={"categoria": "ia", "nivel": "intermedio"}
)
]
# Crear vector store desde documentos
vector_store = FAISS.from_documents(
documents=docs,
embedding=embeddings
)
Persistencia y carga del índice
Una característica fundamental de FAISS es su capacidad para persistir el índice en disco, evitando recalcular embeddings en cada ejecución:
# Guardar el vector store en disco
vector_store.save_local("mi_indice_faiss")
# Cargar vector store existente
vector_store_cargado = FAISS.load_local(
"mi_indice_faiss",
embeddings,
allow_dangerous_deserialization=True
)
El parámetro allow_dangerous_deserialization=True
es necesario para cargar índices desde archivos locales, aunque debe usarse con precaución en entornos de producción.
Configuración avanzada del índice
FAISS ofrece diferentes tipos de índices optimizados para distintos casos de uso. Por defecto, LangChain utiliza un índice plano (Flat), pero podemos especificar otros tipos para mejorar el rendimiento:
# Crear vector store con configuración específica
vector_store = FAISS.from_texts(
texts=documentos,
embedding=embeddings,
# Configuración adicional del índice
normalize_L2=True # Normalización L2 para mejor precisión
)
# Verificar información del índice
print(f"Dimensiones del vector: {vector_store.index.d}")
print(f"Número total de vectores: {vector_store.index.ntotal}")
Integración con procesamiento de documentos
En aplicaciones reales, FAISS se integra perfectamente con el pipeline de procesamiento de documentos de LangChain:
# Ejemplo completo con división de texto
texto_largo = """
LangChain es un framework que simplifica la construcción de aplicaciones
con modelos de lenguaje. Proporciona abstracciones para trabajar con
diferentes proveedores de LLM y herramientas para crear cadenas complejas
de procesamiento. Los vector stores son componentes clave para implementar
sistemas de recuperación de información basados en similaridad semántica.
"""
# Dividir el texto en chunks
splitter = RecursiveCharacterTextSplitter(
chunk_size=100,
chunk_overlap=20
)
chunks = splitter.split_text(texto_largo)
# Crear vector store desde los chunks
vector_store = FAISS.from_texts(
texts=chunks,
embedding=embeddings,
metadatas=[{"chunk_id": i} for i in range(len(chunks))]
)
Esta aproximación permite trabajar con documentos extensos dividiéndolos en fragmentos manejables, manteniendo la trazabilidad mediante metadatos y optimizando tanto el almacenamiento como la precisión de las búsquedas.
Operaciones básicas: indexar y buscar
Guarda tu progreso
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
Una vez configurado nuestro vector store FAISS, las operaciones fundamentales que realizaremos son indexar nuevos documentos y ejecutar búsquedas por similaridad. Estas operaciones forman el núcleo de cualquier sistema RAG, permitiendo tanto la expansión dinámica de la base de conocimiento como la recuperación eficiente de información relevante.
Indexación de documentos individuales
La indexación dinámica permite agregar nuevos documentos al vector store sin necesidad de recrear todo el índice. FAISS en LangChain proporciona métodos específicos para esta tarea:
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
# Vector store inicial
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = FAISS.from_texts(
texts=["Documento inicial sobre Python"],
embedding=embeddings
)
# Agregar un nuevo documento
nuevo_texto = "FastAPI es un framework web moderno para Python"
vector_store.add_texts([nuevo_texto])
print(f"Total de documentos: {vector_store.index.ntotal}")
Para documentos con metadatos específicos, utilizamos el parámetro metadatas
:
# Agregar múltiples documentos con metadatos
nuevos_textos = [
"Django es un framework web completo para Python",
"Flask es un microframework minimalista para web"
]
metadatos = [
{"framework": "django", "tipo": "completo"},
{"framework": "flask", "tipo": "micro"}
]
vector_store.add_texts(
texts=nuevos_textos,
metadatas=metadatos
)
Búsquedas por similaridad básicas
El método similarity_search()
constituye la operación de búsqueda principal en FAISS. Permite encontrar documentos semánticamente similares a una consulta dada:
# Búsqueda básica
query = "¿Qué frameworks web existen para Python?"
resultados = vector_store.similarity_search(
query=query,
k=3 # Número de resultados a devolver
)
# Mostrar resultados
for i, doc in enumerate(resultados):
print(f"Resultado {i+1}: {doc.page_content}")
if doc.metadata:
print(f"Metadatos: {doc.metadata}")
Búsquedas con puntuaciones de similaridad
Para obtener información más detallada sobre la relevancia de los resultados, utilizamos similarity_search_with_score()
:
# Búsqueda con puntuaciones
resultados_con_score = vector_store.similarity_search_with_score(
query="frameworks web Python",
k=3
)
for doc, score in resultados_con_score:
print(f"Puntuación: {score:.4f}")
print(f"Contenido: {doc.page_content}")
print(f"Metadatos: {doc.metadata}")
print("-" * 50)
Las puntuaciones más bajas indican mayor similaridad, ya que FAISS utiliza distancia euclidiana por defecto. Una puntuación cercana a 0 representa alta similaridad semántica.
Filtrado por metadatos
FAISS permite filtrar resultados basándose en metadatos específicos, útil para restringir búsquedas a categorías o tipos de documentos:
# Búsqueda con filtro de metadatos
resultados_filtrados = vector_store.similarity_search(
query="framework web",
k=5,
filter={"tipo": "micro"} # Solo frameworks tipo micro
)
print(f"Resultados filtrados: {len(resultados_filtrados)}")
for doc in resultados_filtrados:
print(f"- {doc.page_content}")
Búsquedas por vector directo
Para casos avanzados donde ya disponemos del vector de embedding, podemos realizar búsquedas directas sin procesar texto:
# Generar embedding de la consulta
query_embedding = embeddings.embed_query("desarrollo web con Python")
# Búsqueda directa por vector
resultados_vector = vector_store.similarity_search_by_vector(
embedding=query_embedding,
k=2
)
for doc in resultados_vector:
print(f"Encontrado: {doc.page_content}")
Operaciones de mantenimiento del índice
El mantenimiento del índice incluye operaciones para optimizar el rendimiento y gestionar el contenido:
# Obtener información del índice
print(f"Dimensiones: {vector_store.index.d}")
print(f"Total vectores: {vector_store.index.ntotal}")
# Verificar si el índice está entrenado (para algunos tipos de índice)
print(f"Índice entrenado: {vector_store.index.is_trained}")
Búsquedas con umbral de similaridad
Para controlar la calidad de los resultados, podemos implementar filtros basados en umbral de similaridad:
def buscar_con_umbral(vector_store, query, umbral=0.8, k=10):
"""Buscar documentos con umbral de similaridad"""
resultados = vector_store.similarity_search_with_score(
query=query,
k=k
)
# Filtrar por umbral (menor score = mayor similaridad)
resultados_filtrados = [
(doc, score) for doc, score in resultados
if score <= umbral
]
return resultados_filtrados
# Uso del filtro
resultados_relevantes = buscar_con_umbral(
vector_store=vector_store,
query="Python web development",
umbral=0.5
)
print(f"Documentos relevantes encontrados: {len(resultados_relevantes)}")
Integración con retriever
Para facilitar la integración con cadenas LCEL, podemos convertir nuestro vector store en un retriever:
# Crear retriever desde el vector store
retriever = vector_store.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
# Usar el retriever en una búsqueda
documentos_recuperados = retriever.invoke("frameworks Python web")
for doc in documentos_recuperados:
print(f"Recuperado: {doc.page_content[:100]}...")
Esta aproximación permite utilizar el vector store directamente en cadenas de procesamiento más complejas, manteniendo la compatibilidad con el ecosistema LCEL de LangChain y facilitando la construcción de sistemas RAG completos.
Alternativa más directa
Una posibilidad es usar InMemoryVectorStore del propio LangChain Core:
El cuál utiliza numpy a nivel interno para calcular el score:
Esto permite realizar pruebas rápidas.
No osbtante, para entornos de producción reales es recomendable utilizar una base de datos vectorial como por ejemplo ChromaDB o PGVector o Pinecone.
En las próximas lecciones veremos ChromaDB y PGVector.
Aprendizajes de esta lección
- Comprender las ventajas y características principales de FAISS para vector stores.
- Aprender a crear y configurar un vector store FAISS desde textos y documentos con LangChain.
- Conocer cómo persistir y cargar índices FAISS para optimizar el desarrollo.
- Realizar operaciones básicas de indexación dinámica y búsqueda por similaridad, incluyendo filtrado por metadatos.
- Integrar FAISS con pipelines de procesamiento y retrievers para sistemas RAG completos.
Completa LangChain y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs