Realiza una búsqueda de relevancia marginal máxima con LangChain en Bigtable

En esta página, se describe cómo realizar una búsqueda de relevancia marginal máxima (MMR) con la integración de BigtableVectorStore para LangChain en Bigtable y Gemini Enterprise Agent Platform como el servicio de embedding.

MMR es una técnica de búsqueda que se usa en la recuperación de información para mostrar un conjunto de resultados que sean relevantes para la consulta y diversos, lo que evita la redundancia. Si bien la búsqueda de similitud de vectores estándar (por ejemplo, una búsqueda que usa el método de los k vecinos más cercanos) puede mostrar muchos elementos similares, MMR proporciona un conjunto más variado de resultados principales. Esto es útil cuando tienes datos potencialmente superpuestos o duplicados en tu almacén de vectores.

Por ejemplo, en una aplicación de comercio electrónico, si un usuario busca “tomates rojos”, una búsqueda de similitud de vectores podría mostrar varias fichas del mismo tipo de tomate rojo fresco. Una búsqueda de MMR tendría como objetivo mostrar un conjunto más diverso, como “tomates rojos frescos”, “tomates rojos en cubos enlatados”, “tomates cherry orgánicos” y, quizás, incluso “receta de ensalada de tomate”.

Antes de leer esta página, es importante que conozcas los siguientes conceptos:

  • Relevancia: Es una medida de qué tan bien coincide el documento con la consulta.
  • Diversidad: Es una medida de qué tan diferente es un documento de los documentos ya seleccionados en el conjunto de resultados.
  • Multiplicador lambda: Es un factor entre 0 y 1 que equilibra la relevancia y la diversidad. Un valor más cercano a 1 prioriza la relevancia, mientras que un valor más cercano a 0 prioriza la diversidad.

La clase BigtableVectorStore para LangChain implementa MMR como un algoritmo de clasificación. Este algoritmo primero recupera un conjunto más grande de documentos relevantes para la consulta y selecciona los documentos que coinciden con la consulta de una manera que esté equilibrada para la relevancia y la diversidad.

Antes de comenzar

En esta guía, se usa Agent Platform como el servicio de embedding. Asegúrate de tener habilitada la API de Agent Platform en tu proyecto.

Habilita la API de Agent Platform

Roles obligatorios

Para usar Bigtable con LangChain, necesitas los siguientes roles de IAM:

Configura tu entorno

  1. Instala los paquetes de LangChain necesarios:

    pip install --upgrade --quiet langchain-google-bigtable langchain-google-vertexai
    
  2. Autentícate en Google Cloud con tu cuenta de usuario.

    gcloud auth application-default login
    
  3. Establece tu ID del proyecto, el ID de la instancia de Bigtable y el ID de la tabla:

    PROJECT_ID = "your-project-id"
    INSTANCE_ID = "your-instance-id"
    TABLE_ID = "your-table-id"
    

Inicializa el servicio de embedding y crea una tabla

Para usar el almacén de vectores de Bigtable, debes proporcionar embeddings generados por un modelo de IA. En esta guía, se usa el modelo de embedding de texto gemini-embedding-001 en Agent Platform.


from langchain_google_vertexai import VertexAIEmbeddings
from langchain_google_bigtable.vector_store import init_vector_store_table, BigtableVectorStore, ColumnConfig

# Initialize an embedding service
embedding_service = VertexAIEmbeddings(model_name="text-embedding-001", project=PROJECT_ID)

# Define column families
DATA_COLUMN_FAMILY = "product_data"

# Initialize the table (if it doesn't exist)
try:
    init_vector_store_table(
        project_id=PROJECT_ID,
        instance_id=INSTANCE_ID,
        table_id=TABLE_ID,
        content_column_family=DATA_COLUMN_FAMILY,
        embedding_column_family=DATA_COLUMN_FAMILY,
    )
    print(f"Table {TABLE_ID} created successfully.")
except ValueError as e:
    print(e) # Table likely already exists

Crea una instancia de BigtableVectorStore

Para crear la instancia del almacén, pasa el servicio de embedding y los identificadores de la tabla de Bigtable.

# Configure columns
content_column = ColumnConfig(
    column_family=DATA_COLUMN_FAMILY, column_qualifier="product_description"
)
embedding_column = ColumnConfig(
    column_family=DATA_COLUMN_FAMILY, column_qualifier="embedding"
)

# Create the vector store instance
vector_store = BigtableVectorStore.create_sync(
    project_id=PROJECT_ID,
    instance_id=INSTANCE_ID,
    table_id=TABLE_ID,
    embedding_service=embedding_service,
    collection="ecommerce_products",
    content_column=content_column,
    embedding_column=embedding_column,
)
print("BigtableVectorStore instantiated.")

Propaga el almacén de vectores

En esta guía, usamos una situación de ejemplo de un servicio de comercio electrónico ficticio en el que los usuarios quieren buscar elementos relacionados con los tomates rojos. Primero, debemos agregar algunos productos y descripciones relacionados con el tomate al almacén de vectores.

from langchain_core.documents import Document

products = [
    Document(page_content="Fresh organic red tomatoes, great for salads.", metadata={"type": "fresh produce", "color": "red", "name": "Organic Vine Tomatoes"}),
    Document(page_content="Ripe red tomatoes on the vine.", metadata={"type": "fresh", "color": "red", "name": "Tomatoes on Vine"}),
    Document(page_content="Sweet cherry tomatoes, red and juicy.", metadata={"type": "fresh", "color": "red", "name": "Cherry Tomatoes"}),
    Document(page_content="Canned diced red tomatoes in juice.", metadata={"type": "canned", "color": "red", "name": "Diced Tomatoes"}),
    Document(page_content="Sun-dried tomatoes in oil.", metadata={"type": "preserved", "color": "red", "name": "Sun-Dried Tomatoes"}),
    Document(page_content="Green tomatoes, perfect for frying.", metadata={"type": "fresh", "color": "green", "name": "Green Tomatoes"}),
    Document(page_content="Tomato paste, concentrated flavor.", metadata={"type": "canned", "color": "red", "name": "Tomato Paste"}),
    Document(page_content="Mixed salad greens with cherry tomatoes.", metadata={"type": "prepared", "color": "mixed", "name": "Salad Mix with Tomatoes"}),
    Document(page_content="Yellow pear tomatoes, mild flavor.", metadata={"type": "fresh", "color": "yellow", "name": "Yellow Pear Tomatoes"}),
    Document(page_content="Heirloom tomatoes, various colors.", metadata={"type": "fresh", "color": "various", "name": "Heirloom Tomatoes"}),
]

vector_store.add_documents(products)
print(f"Added {len(products)} products to the vector store.")

La tienda contiene muchos productos relacionados con el tomate, pero los usuarios solo quieren buscar ofertas asociadas con “tomates rojos”. Para obtener un conjunto diverso de resultados, usa la técnica de MMR para realizar la búsqueda.

El método clave que se debe usar es el max_marginal_relevance_search que toma los siguientes argumentos:

  • query (str): Es el texto de búsqueda.
  • k (int): Es la cantidad final de resultados de la búsqueda.
  • fetch_k (int): Es la cantidad inicial de productos similares que se recuperarán antes de aplicar el algoritmo de MMR. Te recomendamos que este número sea mayor que el parámetro k.
  • lambda_mult (float): Es el parámetro de ajuste de diversidad. Usa 0.0 para obtener la máxima diversidad y 1.0 para obtener la máxima relevancia.
user_query = "red tomatoes"
k_results = 4
fetch_k_candidates = 10

print(f"Performing MMR search for: '{user_query}'")

# Example 1: Balanced relevance and diversity
mmr_results_balanced = vector_store.max_marginal_relevance_search(
    user_query, k=k_results, fetch_k=fetch_k_candidates, lambda_mult=0.5
)
print(f"MMR Results (lambda=0.5, k={k_results}, fetch_k={fetch_k_candidates}):")
for doc in mmr_results_balanced:
    print(f"  - {doc.metadata['name']}: {doc.page_content}")

print("\n")

# Example 2: Prioritizing Diversity
mmr_results_diverse = vector_store.max_marginal_relevance_search(
    user_query, k=k_results, fetch_k=fetch_k_candidates, lambda_mult=0.1
)
print(f"MMR Results (lambda=0.1, k={k_results}, fetch_k={fetch_k_candidates}):")
for doc in mmr_results_diverse:
    print(f"  - {doc.metadata['name']}: {doc.page_content}")

print("\n")

# Example 3: Prioritizing Relevance
mmr_results_relevant = vector_store.max_marginal_relevance_search(
    user_query, k=k_results, fetch_k=fetch_k_candidates, lambda_mult=0.9
)
print(f"MMR Results (lambda=0.9, k={k_results}, fetch_k={fetch_k_candidates}):")
for doc in mmr_results_relevant:
    print(f"  - {doc.metadata['name']}: {doc.page_content}")

Los diferentes valores de lambda_mult producen diferentes conjuntos de resultados, lo que equilibra la similitud con “tomates rojos” con la singularidad de los productos que se muestran.

Usa MMR con un recuperador

Para usar la búsqueda de MMR, también puedes configurar un recuperador de LangChain. El recuperador proporciona una interfaz uniforme que te permite integrar sin problemas métodos de búsqueda especializados, como MMR, directamente en tus cadenas y aplicaciones.

retriever = vector_store.as_retriever(
    search_type="mmr",
    search_kwargs={
        "k": 3,
        "fetch_k": 10,
        "lambda_mult": 0.3,
    }
)

retrieved_docs = retriever.invoke(user_query)
print(f"\nRetriever MMR Results for '{user_query}':")
for doc in retrieved_docs:
    print(f"  - {doc.metadata['name']}: {doc.page_content}")

¿Qué sigue?