Source code for rag.haystack_pipeline

"""
Haystack Pipeline模块 - 实现基于Haystack的RAG管道
"""

import logging
from typing import Any, Dict, List, Optional

from haystack import Pipeline
from haystack.components.retrievers import InMemoryEmbeddingRetriever
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack_integrations.components.embedders.sentence_transformers import SentenceTransformersTextEmbedder

from connectors.base_llm_connector import BaseLLMConnector
from connectors.haystack_llm_connector import ConnectorChatGenerator
from rag.prompt_builder import ChatPromptBuilder

# 配置日志
logger = logging.getLogger(__name__)


[docs] class RAGPipeline: """基于Haystack的RAG管道"""
[docs] def __init__( self, llm_connector: BaseLLMConnector, document_store: Optional[InMemoryDocumentStore] = None, retriever_top_k: int = 5, prompt_builder: Optional[ChatPromptBuilder] = None, embedding_model: str = "sentence-transformers/all-MiniLM-L6-v2", ): """ 初始化RAG管道 Args: llm_connector (BaseLLMConnector): LLM连接器 document_store (InMemoryDocumentStore, optional): 当前Haystack检索器使用内存文档存储,如果为None则创建InMemoryDocumentStore retriever_top_k (int): 检索器返回的最大文档数量 prompt_builder (ChatPromptBuilder, optional): 提示构建器,如果为None则创建默认构建器 embedding_model (str): 嵌入模型名称 """ self.llm_connector = llm_connector self.document_store = document_store or InMemoryDocumentStore() self.retriever_top_k = retriever_top_k self.prompt_builder = prompt_builder or ChatPromptBuilder() self.embedding_model = embedding_model # 创建查询嵌入器 self.query_embedder = SentenceTransformersTextEmbedder(model=embedding_model, progress_bar=False) # 创建检索器 self.retriever = InMemoryEmbeddingRetriever(document_store=self.document_store, top_k=retriever_top_k) # 创建Pipeline self._create_pipeline()
def _create_pipeline(self) -> None: """创建Haystack Pipeline""" self.pipeline = Pipeline() # 向Pipeline中添加组件 self.pipeline.add_component("query_embedder", self.query_embedder) self.pipeline.add_component("retriever", self.retriever) self.pipeline.add_component("prompt_builder", self.prompt_builder) self.pipeline.add_component("llm", ConnectorChatGenerator(self.llm_connector)) # 定义组件之间的连接 self.pipeline.connect("query_embedder.embedding", "retriever.query_embedding") self.pipeline.connect("retriever.documents", "prompt_builder.documents") self.pipeline.connect("prompt_builder.messages", "llm.messages") logger.info("成功创建Haystack RAG Pipeline (含查询嵌入器)")
[docs] def run( self, query: str, chat_history: Optional[List[Dict[str, str]]] = None, filters: Optional[Dict[str, Any]] = None, generation_kwargs: Optional[Dict[str, Any]] = None, top_k: Optional[int] = None, ) -> Dict[str, Any]: """ 运行RAG管道 Args: query (str): 用户查询 chat_history (list, optional): 聊天历史记录,格式为[{"role": "user", "content": "消息内容"}] filters (dict, optional): 检索过滤条件 generation_kwargs (dict, optional): 生成参数 top_k (int, optional): 单次检索数量,省略时保留配置默认值 Returns: dict: 包含回答和检索文档的结果 """ try: if top_k is not None and (type(top_k) is not int or top_k <= 0): raise ValueError("top_k must be a positive integer") # 准备输入 inputs: Dict[str, Dict[str, Any]] = { "query_embedder": {"text": query}, "retriever": {"filters": filters}, "llm": {"generation_kwargs": generation_kwargs or {}}, "prompt_builder": {"query": query, "chat_history": chat_history or []}, } if top_k is not None: inputs["retriever"]["top_k"] = top_k # 运行管道 logger.info(f"运行RAG管道: 查询='{query}'") logger.debug(f"Pipeline inputs: {inputs}") output = self.pipeline.run(inputs, include_outputs_from={"retriever"}) # 调试:打印输出结构 logger.debug(f"Pipeline output keys: {list(output.keys())}") if "retriever" in output: logger.debug(f"Retriever output documents count: {len(output['retriever']['documents'])}") # 提取结果 answer = output["llm"]["replies"][0].text documents = output["retriever"]["documents"] if "retriever" in output else [] # 构建结果 result = { "answer": answer, "documents": [{"id": doc.id, "content": doc.content, "metadata": doc.meta} for doc in documents], "query": query, } logger.info(f"RAG管道运行成功: 检索到{len(documents)}个文档") return result except Exception as e: logger.error(f"RAG管道运行失败: {str(e)}") return {"answer": f"查询处理失败: {str(e)}", "documents": [], "query": query, "error": str(e)}
[docs] def update_retriever( self, document_store: Optional[InMemoryDocumentStore] = None, top_k: Optional[int] = None ) -> None: """ 更新检索器 Args: document_store (DocumentStore, optional): 新的文档存储 top_k (int, optional): 新的最大检索文档数量 """ # 更新文档存储 if document_store: self.document_store = document_store # 更新top_k if top_k: self.retriever_top_k = top_k # 重新创建检索器 self.retriever = InMemoryEmbeddingRetriever(document_store=self.document_store, top_k=self.retriever_top_k) # 重新创建Pipeline self._create_pipeline() logger.info(f"已更新检索器: top_k={self.retriever_top_k}")
[docs] def update_llm_connector(self, llm_connector: BaseLLMConnector) -> None: """ 更新LLM连接器 Args: llm_connector (BaseLLMConnector): 新的LLM连接器 """ self.llm_connector = llm_connector self._create_pipeline() logger.info(f"已更新LLM连接器: model={llm_connector.model}")
[docs] class RAGPipelineFactory: """RAG管道工厂类,创建基于Haystack的RAG管道"""
[docs] @staticmethod def create_pipeline( llm_connector: BaseLLMConnector, document_store: Optional[InMemoryDocumentStore] = None, retriever_top_k: int = 5, prompt_builder: Optional[ChatPromptBuilder] = None, config: Optional[Dict[str, Any]] = None, ) -> RAGPipeline: """ 创建RAG管道 Args: llm_connector (BaseLLMConnector): LLM连接器 document_store (InMemoryDocumentStore, optional): 当前Haystack检索器使用内存文档存储 retriever_top_k (int): 检索器返回的最大文档数量 prompt_builder (ChatPromptBuilder, optional): 提示构建器 config (dict, optional): 配置参数 Returns: RAGPipeline: RAG管道实例 """ # 处理配置参数 if config: retriever_top_k = config.get("retriever_top_k", config.get("top_k", retriever_top_k)) if isinstance(retriever_top_k, bool) or not isinstance(retriever_top_k, int) or retriever_top_k <= 0: raise ValueError("retriever_top_k/top_k must be a positive integer") return RAGPipeline( llm_connector=llm_connector, document_store=document_store, retriever_top_k=retriever_top_k, prompt_builder=prompt_builder, )