Suche nach maximaler Grenzrelevanz mit LangChain in Bigtable durchführen

Auf dieser Seite wird beschrieben, wie Sie mit der BigtableVectorStore-Integration für LangChain in Bigtable und Vertex AI als Einbettungsdienst eine Suche mit maximaler Grenzrelevanz (Maximal Marginal Relevance, MMR) durchführen.

MMR ist eine Suchtechnik, die beim Abrufen von Informationen verwendet wird, um eine Reihe von Ergebnissen zurückzugeben, die sowohl für die Anfrage relevant als auch vielfältig sind und Redundanz vermeiden. Während bei der standardmäßigen Suche nach Vektorähnlichkeit (z. B. bei einer Suche mit der Methode „k-Nearest Neighbor“) möglicherweise viele ähnliche Elemente zurückgegeben werden, liefert MMR eine vielfältigere Auswahl der besten Ergebnisse. Das ist nützlich, wenn Sie potenziell sich überschneidende oder doppelte Daten in Ihrem Vektorspeicher haben.

Wenn ein Nutzer in einer E-Commerce-Anwendung beispielsweise nach „rote Tomaten“ sucht, kann eine Vektorähnlichkeitssuche mehrere Einträge desselben Typs von frischen roten Tomaten zurückgeben. Bei einer MMR-Suche wird eine vielfältigere Gruppe zurückgegeben, z. B. „frische rote Tomaten“, „gewürfelte rote Tomaten aus der Dose“, „Bio-Cherrytomaten“ und vielleicht sogar „Rezept für Tomatensalat“.

Bevor Sie diese Seite lesen, sollten Sie mit den folgenden Konzepten vertraut sein:

  • Relevanz: Ein Maß dafür, wie gut das Dokument mit der Anfrage übereinstimmt.
  • Vielfalt: Ein Maß dafür, wie unterschiedlich ein Dokument von den bereits im Ergebnissatz ausgewählten Dokumenten ist.
  • Lambda-Multiplikator: Ein Faktor zwischen 0 und 1, der Relevanz und Vielfalt ausgleicht. Ein Wert näher an 1 priorisiert die Relevanz, während ein Wert näher an 0 die Vielfalt priorisiert.

Die BigtableVectorStore-Klasse für LangChain implementiert MMR als Re-Ranking-Algorithmus. Dieser Algorithmus ruft zuerst eine größere Menge von Dokumenten ab, die für die Anfrage relevant sind, und wählt dann die Dokumente aus, die in einer Weise mit der Anfrage übereinstimmen, die Relevanz und Vielfalt berücksichtigt.

Hinweise

In diesem Leitfaden wird Vertex AI als Embedding-Dienst verwendet. Achten Sie darauf, dass die Vertex AI API in Ihrem Projekt aktiviert ist.

Vertex AI API aktivieren

Erforderliche Rollen

Wenn Sie Bigtable mit LangChain verwenden möchten, benötigen Sie die folgenden IAM-Rollen:

Umgebung einrichten

  1. Installieren Sie die erforderlichen LangChain-Pakete:

    pip install --upgrade --quiet langchain-google-bigtable langchain-google-vertexai
    
  2. Authentifizieren Sie sich mit Ihrem Nutzerkonto bei Google Cloud .

    gcloud auth application-default login
    
  3. Legen Sie Ihre Projekt-ID, Bigtable-Instanz-ID und Tabellen-ID fest:

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

Embedding-Dienst initialisieren und Tabelle erstellen

Wenn Sie den Bigtable-Vektorspeicher verwenden möchten, müssen Sie Einbettungen bereitstellen, die von einem KI-Modell generiert wurden. In dieser Anleitung verwenden Sie das gemini-embedding-004-Modell für Texteinbettungen in Vertex AI.

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-004", 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

BigtableVectorStore instanziieren

Erstellen Sie die Speicherinstanz, indem Sie den Einbettungsdienst und die Bigtable-Tabellenkennungen übergeben.

# 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.")

Vektorspeicher füllen

In dieser Anleitung verwenden wir ein Beispielszenario eines fiktiven E-Commerce-Dienstes, in dem Nutzer nach Artikeln im Zusammenhang mit roten Tomaten suchen möchten. Zuerst müssen wir dem Vektorspeicher einige tomatenbezogene Produkte und Beschreibungen hinzufügen.

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.")

Das Geschäft bietet viele Produkte rund um Tomaten an, aber Nutzer möchten nur nach Angeboten für „rote Tomaten“ suchen. Wenn Sie eine vielfältige Ergebnismenge erhalten möchten, verwenden Sie die MMR-Technik für die Suche.

Die zu verwendende Schlüsselmethode ist max_marginal_relevance_search mit den folgenden Argumenten:

  • query (str): Der Suchtext.
  • k (int): Die endgültige Anzahl der Suchergebnisse.
  • fetch_k (int): Die ursprüngliche Anzahl ähnlicher Produkte, die vor der Anwendung des MMR-Algorithmus abgerufen werden sollen. Wir empfehlen, dass diese Zahl größer als der Parameter k ist.
  • lambda_mult (Gleitkommazahl): Der Parameter für die Abstimmung der Vielfalt. Verwenden Sie 0.0 für maximale Vielfalt und 1.0 für maximale Relevanz.
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}")

Unterschiedliche lambda_mult-Werte führen zu unterschiedlichen Ergebnissen. Dabei wird die Ähnlichkeit zu „rote Tomaten“ mit der Einzigartigkeit der angezeigten Produkte in Einklang gebracht.

MMR mit einem Retriever verwenden

Wenn Sie die MMR-Suche verwenden möchten, können Sie auch einen LangChain-Retriever konfigurieren. Der Retriever bietet eine einheitliche Schnittstelle, mit der Sie spezielle Suchmethoden wie MMR nahtlos in Ihre Chains und Anwendungen einbinden können.

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}")

Nächste Schritte