检索器(Retriever)是RAG(Retrieval-Augmented Generation)系统中连接知识库与生成模型的关键组件。如果把RAG系统比作一个图书馆,那么检索器就是那个经验丰富的图书管理员——它需要准确理解读者的需求(用户查询),快速从海量藏书(向量数据库)中找出最相关的资料(文本块),并将这些资料传递给专家(LLM)进行答案合成。
在实际工程实践中,我发现很多团队会过度关注生成模型的效果,却忽视了检索环节的优化。这就像给米其林大厨提供劣质食材——无论厨艺多高超,最终菜品质量都会受限。检索器的质量直接决定了LLM能够获取的信息质量上限。
最基础的检索器实现依赖于向量相似度计算。当用户输入查询时:
python复制# 伪代码展示基础检索流程
query_vector = embed(query_text)
scores = []
for doc_vector in document_vectors:
similarity = cosine_similarity(query_vector, doc_vector)
scores.append(similarity)
top_k_indices = argsort(scores)[-k:]
return [documents[i] for i in top_k_indices]
这种方法的优势是实现简单,计算效率高。但在实际业务场景中,我发现单纯的相似度检索经常会遇到几个典型问题:
MMR算法通过平衡相关性与多样性来解决上述问题。其核心公式为:
MMR = argmax[λ·sim(Q, D_i) - (1-λ)·max sim(D_i, D_j)]
其中:
在电商客服系统中,我们曾用MMR有效解决了产品推荐单一化的问题。当用户询问"适合老人的智能手机"时,传统方法会返回多款同一品牌的手机,而MMR能给出不同品牌、不同特点的选项。
对于新闻、社交媒体等时效性强的场景,我们可以在相似度计算中加入时间衰减因子:
score = α·similarity + (1-α)·recency_weight
其中recency_weight可以根据业务需求设计为线性衰减或指数衰减。在金融资讯系统中,我们使用α=0.7的指数衰减,确保新信息获得适当优先。
LangChain提供了开箱即用的VectorStoreRetriever,支持三种检索策略:
python复制from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
# 初始化向量库
vectorstore = Chroma.from_documents(
documents=docs,
embedding=OpenAIEmbeddings()
)
# 创建三种检索器
similarity_retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 4})
mmr_retriever = vectorstore.as_retriever(search_type="mmr", search_kwargs={"k": 4})
threshold_retriever = vectorstore.as_retriever(search_type="similarity_threshold", search_kwargs={"score_threshold": 0.7})
我们在技术文档问答场景下对比了三种检索器的表现:
查询:"如何处理数据库连接池溢出问题?"
| 检索类型 | 返回结果特点 | 适用场景 |
|---|---|---|
| 相似度检索 | 4篇都详细讲解连接池配置参数 | 需要深度聚焦的场景 |
| MMR检索 | 包含配置参数、监控方案、扩容建议各1篇 | 需要全面视角的场景 |
| 阈值检索 | 只返回2篇最匹配的文档 | 严格质量控制的场景 |
实际工程建议:对于知识库完备的场景推荐MMR,对于质量参差不齐的知识库建议使用阈值过滤
在企业级应用中,我们经常需要结合多种检索策略。以下是一个关键词+向量混合检索器的示例:
python复制from langchain.schema import BaseRetriever
from typing import List
class HybridRetriever(BaseRetriever):
def __init__(self, vector_retriever, keyword_retriever):
self.vector_retriever = vector_retriever
self.keyword_retriever = keyword_retriever
def get_relevant_documents(self, query: str) -> List[Document]:
# 获取向量检索结果
vector_docs = self.vector_retriever.get_relevant_documents(query)
# 获取关键词检索结果
keyword_docs = self.keyword_retriever.get_relevant_documents(query)
# 合并并去重
all_docs = vector_docs + keyword_docs
seen = set()
unique_docs = []
for doc in all_docs:
if doc.page_content not in seen:
seen.add(doc.page_content)
unique_docs.append(doc)
return unique_docs
对于大型知识库,添加元数据过滤能显著提升检索精度。例如在API文档系统中,我们可以按产品模块进行过滤:
python复制retriever = vectorstore.as_retriever(
search_kwargs={
"k": 5,
"filter": {"product": "payment-api"}
}
)
在多个项目实施中,我们发现文本分块策略会显著影响检索效果:
除了常规的召回率、准确率外,建议添加:
挑战:
解决方案:
挑战:
解决方案:
挑战:
解决方案:
在实际部署中,我们发现检索器的参数需要定期重新校准。特别是在业务快速发展期,建议每季度进行一次全面的检索质量评估和参数调优。