"""BM25、Milvus 向量和混合检索。""" from langchain_classic.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever from langchain_core.documents import Document from langchain_milvus import Milvus class MultiRetrieverSystem: """多路召回检索系统,默认使用混合检索。""" def __init__(self, documents, embedding_model, llm): self.documents = documents self.embedding_model = embedding_model self.llm = llm self.setup_retrievers() def setup_retrievers(self): """初始化 BM25、向量和混合检索器。""" self.bm25 = BM25Retriever.from_documents(self.documents) self.bm25.k = 10 self.vectorstore = Milvus( embedding_function=self.embedding_model, connection_args={"uri": "http://localhost:19530"}, collection_name="car_info_collection", primary_field="id", text_field="content", vector_field="embedding", auto_id=True, search_params={"metric_type": "COSINE", "params": {}}, ) self._index_documents_if_empty() self.vector = self.vectorstore.as_retriever(search_kwargs={"k": 10}) # LangChain 当前版本使用 RRF 融合排序,不需要 normalize_scores 参数。 self.ensemble = EnsembleRetriever( retrievers=[self.bm25, self.vector], weights=[0.4, 0.6], ) def _index_documents_if_empty(self): """Milvus 集合为空时才写入分块,避免重复入库。""" rows = self.vectorstore.client.query( collection_name="car_info_collection", filter="id >= 0", output_fields=["id"], limit=1, consistency_level="Strong", ) if not rows: self.vectorstore.add_documents(self.documents) def search(self, query, mode="ensemble") -> list[Document]: """执行检索,mode 可选 bm25、vector、ensemble。""" retriever_map = { "bm25": self.bm25, "vector": self.vector, "ensemble": self.ensemble, } if mode not in retriever_map: choices = ", ".join(retriever_map) raise ValueError(f"不支持的检索模式: {mode},可选: {choices}") return retriever_map[mode].invoke(query)[:10]