在 Bigtable 上使用 LangChain 执行最大边际相关性搜索

本页面介绍了如何在 Bigtable 中使用 BigtableVectorStore 集成来执行最大边际相关性 (MMR) 搜索,并将 Gemini Enterprise Agent Platform 用作嵌入服务。

MMR 是一种信息检索中使用的搜索技术,用于返回一组与查询相关且多样的结果,避免冗余。虽然标准向量相似性搜索(例如,使用 k- 最近邻方法的搜索)可能会返回许多相似的项,但 MMR 会提供更多样化的热门结果。当向量存储区中可能存在重叠或重复数据时,此功能非常有用。

例如,在电子商务应用中,如果用户搜索“红番茄”,则向量相似性搜索可能会返回多个相同类型的新鲜红番茄商品信息。MMR 搜索的目标是返回更多样化的结果,例如“新鲜红番茄”“罐装红番茄丁”“有机樱桃番茄”,甚至可能是“番茄沙拉食谱”。

在阅读本页面之前,请务必了解以下概念:

  • 相关性:衡量文档与查询的匹配程度。
  • 多样性:衡量文档与结果集中已选择的文档的差异程度。
  • Lambda 乘数:介于 0 和 1 之间的因子,用于 平衡相关性和多样性。值越接近 1,相关性优先级越高;值越接近 0,多样性优先级越高。

LangChain 的 BigtableVectorStore 将 MMR 实现为重排序算法。此算法首先提取与查询相关的一组较大文档,然后选择与查询匹配的文档,并以平衡相关性和多样性的方式进行选择。

准备工作

本指南使用 Agent Platform 作为嵌入服务。确保已在项目中启用 Agent Platform API。

启用 Agent Platform API

所需角色

如需将 Bigtable 与 LangChain 搭配使用,您需要以下 IAM 角色:

设置环境

  1. 安装所需的 LangChain 软件包:

    pip install --upgrade --quiet langchain-google-bigtable langchain-google-vertexai
    
  2. 使用您的用户账号向 Google Cloud 进行身份验证。

    gcloud auth application-default login
    
  3. 设置项目 ID、Bigtable 实例 ID 和表 ID:

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

初始化嵌入服务并创建表

如需使用 Bigtable 向量存储区,您需要提供由 AI 模型生成的嵌入。在本指南中,您将使用 Agent Platform 上的文本嵌入 gemini-embedding-001 模型。


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

实例化 BigtableVectorStore

通过传递嵌入服务和 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.")

填充向量存储区

在本指南中,我们使用虚构的电子商务服务的示例场景,用户希望搜索与红番茄相关的商品。首先,我们需要向向量存储区添加一些与番茄相关的产品和说明。

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

存储区包含许多与番茄相关的产品,但用户只想搜索与“红番茄”相关的商品。如需获得多样化的结果集,请使用 MMR 技术执行搜索。

要使用的关键方法是 max_marginal_relevance_search ,它接受以下实参:

  • query (str):搜索文本。
  • k (int):搜索结果的最终数量。
  • fetch_k (int):在应用 MMR 算法之前要检索的相似产品的初始数量。我们建议此数字大于 k 形参。
  • lambda_mult (浮点数):多样性调整参数。使用 0.0 可实现最大多样性,使用 1.0 可实现最大相关性。
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}")

不同的 lambda_mult 值会产生不同的结果集,从而平衡与“红番茄”的相似性与所显示产品的独特性。

将 MMR 与检索器搭配使用

如需使用 MMR 搜索,您还可以配置 LangChain 检索器。 检索器提供统一的接口,让您可以将 MMR 等专用搜索方法直接无缝集成到链和应用中。

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

后续步骤