Skip to main content
检索器之王(LOTR),也称为 MergerRetriever,以一组检索器作为输入,并将它们各自 get_relevant_documents() 方法的结果合并为一个列表。合并后的结果是一组与查询相关的文档,并由不同检索器进行排名。
MergerRetriever 类可通过多种方式提升文档检索的准确性。首先,它可以合并多个检索器的结果,有助于降低结果中的偏差风险。其次,它可以对不同检索器的结果进行排名,确保最相关的文档优先返回。
import os

import chromadb
from langchain_classic.retrievers import (
    ContextualCompressionRetriever,
    MergerRetriever,
)
from langchain_classic.retrievers.document_compressors import DocumentCompressorPipeline
from langchain_chroma import Chroma
from langchain_community.document_transformers import (
    EmbeddingsClusteringFilter,
    EmbeddingsRedundantFilter,
)
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_openai import OpenAIEmbeddings

# 获取 3 种不同的嵌入模型
all_mini = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
multi_qa_mini = HuggingFaceEmbeddings(model_name="multi-qa-MiniLM-L6-dot-v1")
filter_embeddings = OpenAIEmbeddings()

ABS_PATH = os.path.dirname(os.path.abspath(__file__))
DB_DIR = os.path.join(ABS_PATH, "db")

# 实例化 2 个不同的 chromadb 索引,每个使用不同的嵌入模型
client_settings = chromadb.config.Settings(
    is_persistent=True,
    persist_directory=DB_DIR,
    anonymized_telemetry=False,
)
db_all = Chroma(
    collection_name="project_store_all",
    persist_directory=DB_DIR,
    client_settings=client_settings,
    embedding_function=all_mini,
)
db_multi_qa = Chroma(
    collection_name="project_store_multi",
    persist_directory=DB_DIR,
    client_settings=client_settings,
    embedding_function=multi_qa_mini,
)

# 定义 2 个检索器,使用不同的嵌入模型和不同的搜索类型
retriever_all = db_all.as_retriever(
    search_type="similarity", search_kwargs={"k": 5, "include_metadata": True}
)
retriever_multi_qa = db_multi_qa.as_retriever(
    search_type="mmr", search_kwargs={"k": 5, "include_metadata": True}
)

# 检索器之王将保存两个检索器的输出,可像其他检索器一样用于不同类型的链
lotr = MergerRetriever(retrievers=[retriever_all, retriever_multi_qa])

从合并检索器中移除冗余结果

# 我们可以使用另一个嵌入模型从两个检索器中移除冗余结果。
# 在不同步骤中使用多种嵌入有助于减少偏差。
filter = EmbeddingsRedundantFilter(embeddings=filter_embeddings)
pipeline = DocumentCompressorPipeline(transformers=[filter])
compression_retriever = ContextualCompressionRetriever(
    base_compressor=pipeline, base_retriever=lotr
)

从合并检索器中选取具代表性的文档样本

# 此过滤器将文档向量划分为多个聚类或"语义中心"。
# 然后选取最接近该中心的文档作为最终结果。
# 默认情况下,结果文档将按聚类排序/分组。
filter_ordered_cluster = EmbeddingsClusteringFilter(
    embeddings=filter_embeddings,
    num_clusters=10,
    num_closest=1,
)

# 如果希望最终文档按原始检索器得分排序,
# 需要添加 "sorted" 参数。
filter_ordered_by_retriever = EmbeddingsClusteringFilter(
    embeddings=filter_embeddings,
    num_clusters=10,
    num_closest=1,
    sorted=True,
)

pipeline = DocumentCompressorPipeline(transformers=[filter_ordered_by_retriever])
compression_retriever = ContextualCompressionRetriever(
    base_compressor=pipeline, base_retriever=lotr
)

重排序结果以避免性能下降

无论模型架构如何,当包含 10 篇以上检索文档时,都会出现明显的性能下降。 简而言之:当模型需要从长上下文的中间部分获取相关信息时,往往会忽略所提供的文档。 参见:arxiv.org/abs//2307.03172
# 可以使用额外的文档转换器,在去除冗余后对文档重新排序。
from langchain_community.document_transformers import LongContextReorder

filter = EmbeddingsRedundantFilter(embeddings=filter_embeddings)
reordering = LongContextReorder()
pipeline = DocumentCompressorPipeline(transformers=[filter, reordering])
compression_retriever_reordered = ContextualCompressionRetriever(
    base_compressor=pipeline, base_retriever=lotr
)