为检索结果添加评分:提升检索精度的实用技巧

86 阅读2分钟

引言

在使用向量存储进行文档检索时,返回的 Document 对象通常不包含检索过程中的评分信息(例如与查询的相似度分数)。理解如何将这些评分信息添加到文档的元数据中,可以帮助我们更好地评估和理解检索结果。本文将深入探讨如何从不同类型的 LangChain 检索器中提取和使用评分信息。

主要内容

1. 向量存储检索器

为了从向量存储检索器中获取评分,我们可以在原向量存储的 similarity_search_with_score 方法外层实现一个简短的包装函数,该函数会将评分打包到相关文档的元数据中。

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

# 使用API代理服务提高访问稳定性
result = retriever.invoke("dinosaur")
print(result)

2. 自查询检索器 (SelfQueryRetriever)

SelfQueryRetriever 使用语言模型(LLM)生成可能结构化的查询。可以通过继承 SelfQueryRetriever 并重写 _get_docs_with_query 方法,实现评分信息的传播。

from typing import Any, Dict
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)

3. 多向量检索器 (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)
result = retriever.invoke("cat")
print(result)

常见问题和解决方案

  • 性能问题:在处理大量数据时,评分的计算可能会造成性能瓶颈。可以考虑引入缓存机制或异步处理。

  • 评分不准确:确保使用高质量的向量模型进行嵌入计算,必要时调整模型参数。

总结和进一步学习资源

添加评分信息可以极大地提升检索器的实用性和可解释性。开发者可以参阅 LangChain 文档 了解更多细节。

参考资料

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

---END---