You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LangChain 0.2检索器获取文档相似度分数方法及相关疑问

LangChain检索器相似度分数获取及相关疑问解答

一、FAISS检索器获取相似度分数的方案

你当前用retriever.invoke(query)只能拿到Document列表,无法直接获取分数。可以通过两种方式解决:

  1. 直接调用VectorStore底层方法
    绕开Retriever封装,直接使用FAISS的similarity_search_with_score方法,它会返回(Document, 相似度分数)的元组列表:
    vectorstore = FAISS.from_documents(docs, embeddings_model)
    results_with_score = vectorstore.similarity_search_with_score(query, k=4)
    # 遍历结果
    for doc, score in results_with_score:
        print(f"文档内容:{doc.page_content}\n分数:{score}\n")
    
  2. 自定义Retriever临时添加分数(而非永久元数据)
    官方方案将分数写入文档元数据确实不合理——分数是针对当前查询的临时结果,不该作为文档的永久属性。你可以自定义包装器,仅在本次检索时将分数附加到文档的临时属性(不修改元数据):
    from langchain_core.retrievers import BaseRetriever
    from langchain_core.callbacks import CallbackManagerForRetrieverRun
    from typing import List
    
    class FAISSScoreRetriever(BaseRetriever):
        vectorstore: FAISS
        k: int
    
        def _get_relevant_documents(
            self, query: str, *, run_manager: CallbackManagerForRetrieverRun
        ) -> List[Document]:
            docs_with_score = self.vectorstore.similarity_search_with_score(query, k=self.k)
            # 给每个Document添加临时的score属性,不修改元数据
            for doc, score in docs_with_score:
                doc.score = score
            return [doc for doc, _ in docs_with_score]
    
    # 使用自定义检索器
    semantic_retriever = FAISSScoreRetriever(vectorstore=vectorstore, k=4)
    results = semantic_retriever.invoke(query)
    for doc in results:
        print(f"文档内容:{doc.page_content}\n分数:{doc.score}\n")
    

二、官方方案将分数写入元数据的合理性说明

官方这么设计,主要是为了兼容LangChain的Document标准结构——很多下游组件(比如链、输出格式化)只识别Document对象的metadata字段。但这种方式确实存在弊端:分数是查询相关的临时值,写入元数据会污染文档原始信息,还可能在多次检索时覆盖旧分数。你选择临时存储分数的方案,更符合数据逻辑。

三、LangChain 0.2中invoke与similarity_search_with_score的区别

  • invoke:是BaseRetriever接口的标准统一方法,所有检索器(包括FAISS、BM25、Ensemble)都实现了该方法。返回值是List[Document],专注于提供标准化检索结果,适配LangChain生态(比如和LLM链无缝集成),可能包含检索器的额外逻辑(比如过滤、重排、阈值筛选)。
  • similarity_search_with_score:是VectorStore类的底层方法,仅针对向量存储实现。直接返回List[(Document, float)]元组,给出原始相似度分数,没有额外封装,适合需要直接获取底层检索结果的场景。

简单说:invoke是上层标准化接口,similarity_search_with_score是底层向量检索的原始方法。

四、BM25Retriever获取相似度分数

BM25Retriever默认的invoke方法只返回Document列表,要获取分数可以使用它的get_relevant_documents_with_score方法(LangChain 0.2已支持):

from langchain.retrievers import BM25Retriever

bm25_retriever = BM25Retriever.from_documents(docs, k=4)
results_with_score = bm25_retriever.get_relevant_documents_with_score(query)
for doc, score in results_with_score:
    print(f"文档内容:{doc.page_content}\nBM25分数:{score}\n")

如果需要通过invoke返回带分数的结果,同样可以自定义包装器,给Document添加临时的bm25_score属性。

五、EnsembleRetriever获取相似度分数

EnsembleRetriever默认invoke返回合并后的Document列表,会丢失原始分数。要获取分数,需要自定义处理逻辑:

  1. 分别获取每个子检索器的带分数结果;
  2. 合并结果时保留每个文档的来源检索器和对应分数;
  3. 自定义包装器返回带分数的Document或自定义结构。

示例代码:

from langchain.retrievers import BM25Retriever
from langchain_community.vectorstores import FAISS

# 初始化两个子检索器
bm25_retriever = BM25Retriever.from_documents(docs, k=4)
faiss_retriever = FAISSScoreRetriever(vectorstore=FAISS.from_documents(docs, embeddings_model), k=4)

# 自定义EnsembleRetriever的分数获取逻辑
class EnsembleScoreRetriever(BaseRetriever):
    faiss_retriever: FAISSScoreRetriever
    bm25_retriever: BM25Retriever

    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        # 获取两个检索器的带分数结果
        faiss_results = self.faiss_retriever.invoke(query)
        bm25_results_with_score = self.bm25_retriever.get_relevant_documents_with_score(query)

        # 给BM25结果添加临时属性
        for doc, score in bm25_results_with_score:
            doc.bm25_score = score
            doc.faiss_score = None
        # 给FAISS结果添加标记
        for doc in faiss_results:
            doc.faiss_score = doc.score
            doc.bm25_score = None

        # 合并并去重结果
        combined_results = faiss_results + [doc for doc, _ in bm25_results_with_score]
        seen_content = set()
        unique_results = []
        for doc in combined_results:
            if doc.page_content not in seen_content:
                seen_content.add(doc.page_content)
                unique_results.append(doc)
        return unique_results

# 使用自定义组合检索器
ensemble_retriever = EnsembleScoreRetriever(
    faiss_retriever=faiss_retriever,
    bm25_retriever=bm25_retriever
)
results = ensemble_retriever.invoke(query)
for doc in results:
    print(f"文档内容:{doc.page_content}\nFAISS分数:{doc.faiss_score}\nBM25分数:{doc.bm25_score}\n")

内容的提问来源于stack exchange,提问作者Dev_Man

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 05:42:12