| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- from concurrent.futures import ThreadPoolExecutor
- from langchain_core.output_parsers import StrOutputParser
- from langchain_core.prompts import PromptTemplate
- import json
- class RAGWithQueryRewriting:
- """集成查询重写的 RAG 系统"""
- def __init__(self, retriever, llm, rewrite_chain):
- self.retriever = retriever
- self.llm = llm
- self.rewrite_chain = rewrite_chain
- def invoke(self, question):
- # 第一步:重写查询
- rewritten_query = self.rewrite_chain.invoke({"query": question})
- print(f"原始问题: {question}")
- print(f"重写后: {rewritten_query}")
- # 第二步:用重写后的查询进行检索
- docs = self.retriever.invoke(rewritten_query)
- # 第三步:用原始问题 + 检索结果生成答案
- context = "\n\n".join([doc.page_content for doc in docs])
- answer_prompt = f"""基于以下上下文回答用户问题。如果上下文中没有相关信息,请说明。
- 上下文:{context}
- 用户问题:{question}
- 答案:"""
- answer = self.llm.invoke(answer_prompt).content
- return {
- "original_query": question,
- "rewritten_query": rewritten_query,
- "retrieved_docs": docs,
- "answer": answer
- }
- class RAGWithDecomposition:
- """集成查询分解的 RAG 系统"""
- def __init__(self, retriever, llm):
- self.retriever = retriever
- self.llm = llm
- def invoke(self, question):
- decompose_prompt = PromptTemplate(
- input_variables=["question"],
- template="""你是一个问题分解助手。请将用户的复杂问题分解为 2-4 个独立的子问题,
- 每个子问题应该能独立检索和回答。
- 要求:
- 1. 子问题之间互不依赖,可以并行检索
- 2. 子问题覆盖原始问题的所有方面
- 3. 每个子问题简洁明确
- 4. 以 JSON 数组格式输出
- 用户问题: {question}
- 输出格式示例: ["子问题1", "子问题2", "子问题3"]
- 子问题列表:"""
- )
- decompose_chain = decompose_prompt | self.llm | StrOutputParser()
- # 第一步:分解问题
- sub_queries = self.decompose_query(question, decompose_chain)
- print(f"分解为 {len(sub_queries)} 个子问题")
- # 第二步:并行检索并合并
- all_docs = self.parallel_retrieve_and_merge(sub_queries, self.retriever)
- print(f"合并后共 {len(all_docs)} 个文档片段")
- # 第三步:用所有上下文生成综合答案
- context = "\n\n".join([doc.page_content for doc in all_docs])
- answer_prompt = f"""基于以下上下文,全面回答用户的问题。请综合所有相关信息,给出完整、有条理的答案。上下文:{context}
- 用户问题:{question}
- 答案:"""
- answer = self.llm.invoke(answer_prompt).content
- return {
- "question": question,
- "sub_queries": sub_queries,
- "doc_count": len(all_docs),
- "answer": answer
- }
- # 使用 ThreadPoolExecutor 创建线程池,并行执行检索
- # 每个子问题独立检索,互不干扰
- # 按内容哈希值去重(避免重复文档)
- def parallel_retrieve_and_merge(self, sub_queries, retriever, k_per_query=5):
- """
- 对每个子问题并行检索,然后合并去重
- """
- all_docs = []
- seen_contents = set()
- def retrieve_one(query):
- return retriever.invoke(query)
- # 创建线程池,线程数等于子问题数量
- with ThreadPoolExecutor(max_workers=len(sub_queries)) as executor:
- # 提交所有检索任务
- futures = [executor.submit(retrieve_one, q) for q in sub_queries]
- ## 收集结果
- for future in futures:
- docs = future.result()
- for doc in docs:
- # 按内容去重
- content_hash = hash(doc.page_content)
- if content_hash not in seen_contents:
- seen_contents.add(content_hash)
- all_docs.append(doc)
- return all_docs
- def decompose_query(self, question: str, decompose_chain) -> list[str]:
- """将复杂问题分解为子问题"""
- result = decompose_chain.invoke({"question": question})
- # 解析 JSON 数组
- try:
- sub_queries = json.loads(result.strip())
- return sub_queries
- except json.JSONDecodeError:
- # 兜底:按行分割
- return [line.strip() for line in result.strip().split("\n") if line.strip()]
|