检索增强生成RAG系列4--RAG优化之问题优化

在系列2的章节中罗列了对RAG准确度的几个重要关键点,主要包括2方面,这一章就针对其中问题优化来做详细的讲解以及其解决方案。

在这里插入图片描述

从系列2中,我们知道初始的问题可能对于查询结果不是很好,可能是因为问题表达模糊、语义与文档不一致等等,因此问题优化就是提高RAG准确率的关键节点,那么下面通过问题优化及结果重排等方法,来提供RAG的准确率(注意:其实方法不止以下几种,这里只是写了一些常见的方法技巧供大家参考

目录

  • 1 Multi-Query
    • 1.1 基本思想
    • 1.2 代码演示
  • 2 RAG-Fusion
    • 2.1 基本思想
    • 2.2 代码
  • 3 HyDE
    • 3.1 基本思想
    • 3.2 代码演示
  • 4 Decomposition
    • 4.1 基本思想
    • 4.2 代码演示
  • 5 Step-back prompting
    • 5.1 基本思想
    • 5.2 代码演示
  • 6 总结

说明:以下所有方法实现的前置条件

  • 这里采用智谱AI的API接口,因此可以先去申请一个API KEY(当然你使用其它模型也可以,目前智谱AI的GLM4送token,就拿它来试验吧)
  • 下载m3e-base的embedding模型
  • 给一个文档目录,里面放一些你需要的文档,文档内容如下:

ChatGLM3 是北京智谱华章科技有限公司和清华大学 KEG 实验室联合发布的对话预训练模型。ChatGLM3-6B 是 ChatGLM3 系列中的开源模型,在保留了前两代模型对话流畅、部署门槛低等众多优秀特性的基础上,ChatGLM3-6B 引入了如下特性:
更强大的基础模型: ChatGLM3-6B 的基础模型 ChatGLM3-6B-Base 采用了更多样的训练数据、更充分的训练步数和更合理的训练策略。在语义、数学、推理、代码、知识等不同角度的数据集上测评显示,* ChatGLM3-6B-Base 具有在 10B 以下的基础模型中最强的性能*。
更完整的功能支持: ChatGLM3-6B 采用了全新设计的 Prompt 格式 ,除正常的多轮对话外。同时原生支持工具调用(Function Call)、代码执行(Code Interpreter)和 Agent 任务等复杂场景。
更全面的开源序列: 除了对话模型 ChatGLM3-6B 外,还开源了基础模型 ChatGLM3-6B-Base 、长文本对话模型 ChatGLM3-6B-32K 和进一步强化了对于长文本理解能力的 ChatGLM3-6B-128K。以上所有权重对学术研究完全开放 ,在填写 问卷 进行登记后亦允许免费商业使用。
北京智谱华章科技有限公司是一家来自中国的公司,致力于打造新一代认知智能大模型,专注于做大模型创新。

1 Multi-Query

1.1 基本思想

我们知道,在用户做问题提问的时候,有可能存在提问的问题模糊或者说提问的问题与文档的语义不符合,这时候有一种方法就是使用多个角度提问问题。
Multi Query的基本思想是当用户输入问题时,我们让大模型基于用户的问题再生成多个不同角度句子语句,这些问题句子是从不同的视角来补充用户的问题,然后每条问题都会从向量数据库中检索到一批相关文档,最后所有的相关文档都会扔给大模型,这样大模型就能生成更准确的结果。
在这里插入图片描述

1.2 代码演示

这里使用langchain的MultiQueryRetriever进行多问题检索。

import os
import logging
from langchain_openai import ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.retrievers import MultiQueryRetriever
from langchain_huggingface import HuggingFaceEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_community.document_loaders import DirectoryLoader


# 前置工作1:设置日志,可以让MultiQueryRetriever打印出中间生成的问题
logging.basicConfig()
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)

# 前置工作2:文档存储,如果已经存储了文档,则不需要该步骤
encode_kwargs = {"normalize_embeddings": False}
model_kwargs = {"device": "cuda:0"}
embeddings = HuggingFaceEmbeddings(
    model_name='/root/autodl-tmp/model/AI-ModelScope/m3e-base',  # 换成自己的embedding模型路径
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
if os.path.exists('VectorStore'):
    db = Chroma(persist_directory='VectorStore', embedding_function=embeddings)
loader = DirectoryLoader("/root/autodl-tmp/doc")  # 换成自己的文档路径
documents = loader.load()
text_spliter = CharacterTextSplitter(chunk_size=256, chunk_overlap=0)
documents = text_spliter.split_documents(documents)
database = Chroma.from_documents(documents, embeddings, persist_directory="VectorStore")
database.persist()
# 前置工作3:创建llm
llm = ChatOpenAI(
    temperature=0.95,
    model="glm-4",
    openai_api_key="你的API KEY",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)
# 第一步:优化问题,这里使用langchain的MultiQueryRetriever,已经封装好获取多个问题并查询最终文档结果
question = "ChatGLM是什么?"
question_prompt = PromptTemplate(
    input_variables=["question"],
    template="""你是一个人工智能语言模型助手。你的任务是生成给定用户的3个不同版本从矢量数据库中检索相关文档的问题。
    通过对用户问题产生多种视角,您的目标是帮助用户克服一些限制基于距离的相似度搜索。提供这些选择用换行符分隔的问题。原始问题:{question}""",
)
retriever_from_llm = MultiQueryRetriever.from_llm(
    retriever=database.as_retriever(), prompt=question_prompt, llm=llm, include_original=True
)
unique_docs = retriever_from_llm.invoke(question)
context = ''
for doc in unique_docs:
    context = context+"\n"
    context = context + doc.page_content
# 第二步:获得答案
template = """请根据以下内容回答问题:
{context}
问题: {question}
如果不知道,请回答“不知道”。
"""
prompt = ChatPromptTemplate.from_template(template)
chain = prompt | llm
response = chain.invoke({"question": question, "context": context})
print(response)


2 RAG-Fusion

2.1 基本思想

RAG-Fusion相对于Multi-Query其实就是增加一个RRF(逆向排名融合)技术。也就是同样通过原始问题,让大模型生成更多角度问题,再去查询结果,但是结果通过RRF重排,最终将重排结果给大模型生成答案。
在这里插入图片描述

2.2 代码

前置条件:

  • 这里采用智谱AI的API接口,因此可以先去申请一个API KEY(当然你使用其它模型也可以,目前智谱AI的GLM4送token,就拿它来试验吧)
  • 下载m3e-base的embedding模型
  • 给一个文档目录,里面放一些你需要的文档
import os
from langchain.load import dumps, loads
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_community.document_loaders import DirectoryLoader


# 定义RRF算法函数,代码来自:https://github.com/langchain-ai/langchain/blob/master/cookbook/rag_fusion.ipynb
def reciprocal_rank_fusion(results: list[list], k=60):
    fused_scores = {}
    for docs in results:
        # Assumes the docs are returned in sorted order of relevance
        for rank, doc in enumerate(docs):
            doc_str = dumps(doc)
            if doc_str not in fused_scores:
                fused_scores[doc_str] = 0
            # previous_score = fused_scores[doc_str]
            fused_scores[doc_str] += 1 / (rank + k)

    reranked_results = [
        (loads(doc), score)
        for doc, score in sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)
    ]
    return reranked_results


# 前置工作1:文档存储,如果已经存储了文档,则不需要该步骤
encode_kwargs = {"normalize_embeddings": False}
model_kwargs = {"device": "cuda:0"}
embeddings = HuggingFaceEmbeddings(
    model_name='/root/autodl-tmp/model/AI-ModelScope/m3e-base',  # 换成自己的embedding模型路径
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
if os.path.exists('VectorStore'):
    db = Chroma(persist_directory='VectorStore', embedding_function=embeddings)
loader = DirectoryLoader("/root/autodl-tmp/doc")  # 换成自己的文档路径
documents = loader.load()
text_spliter = CharacterTextSplitter(chunk_size=256, chunk_overlap=0)
documents = text_spliter.split_documents(documents)
database = Chroma.from_documents(documents, embeddings, persist_directory="VectorStore")
database.persist()
# 前置工作2:创建llm
llm = ChatOpenAI(
    temperature=0.95,
    model="glm-4",
    openai_api_key="你的API KEY",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)
# 第一步:优化问题,这里使用langchain的管道,已经封装好获取多个问题并查询最终文档结果,并通过reciprocal_rank_fusion重排
question_prompt = PromptTemplate(
    input_variables=["question"],
    template="""你是一个人工智能语言模型助手。你的任务是生成给定用户的3个不同版本从矢量数据库中检索相关文档的问题。
    通过对用户问题产生多种视角,您的目标是帮助用户克服一些限制基于距离的相似度搜索。提供这些选择用换行符分隔的问题。原始问题:{question}""",
)
question = "ChatGLM是什么"
generate_queries = (
    question_prompt | llm | StrOutputParser() | (lambda x: x.split("\n"))
)
# 可以打印出生成不同角度的问题
# print(generate_queries.invoke({"question": question}))
chain = generate_queries | database.as_retriever().map() | reciprocal_rank_fusion
docs = chain.invoke({"question": question})
context = ''
for doc in docs:
    context = context+"\n"
    context = context + doc[0].page_content
# 第二步:获得答案
template = """请根据以下内容回答问题:
{context}
问题: {question}
如果不知道,请回答“不知道”。
"""
prompt = ChatPromptTemplate.from_template(template)
chain = prompt | llm
response = chain.invoke({"question": question, "context": context})
print(response)

3 HyDE

3.1 基本思想

我们知道有一类问题是用户的问题与文档的语义不一致,这个不一致会导致检索精度不够,因此可以采用HyDE的方法来解决。
HyDE的全称是hypothetical document embedding,使用大模型假设性回答问题,生成一个答案,再使用答案(注意:这里是使用大模型生成的答案,而非使用原始问题)去向量数据库中搜索相关性内容文档。
在这里插入图片描述

3.2 代码演示

前置条件:

  • 这里采用智谱AI的API接口,因此可以先去申请一个API KEY(当然你使用其它模型也可以,目前智谱AI的GLM4送token,就拿它来试验吧)
  • 下载m3e-base的embedding模型
  • 给一个文档目录,里面放一些你需要的文档
import os
from langchain_openai import ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_core.output_parsers import StrOutputParser
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_community.document_loaders import DirectoryLoader


# 前置工作1:文档存储,如果已经存储了文档,则不需要该步骤
encode_kwargs = {"normalize_embeddings": False}
model_kwargs = {"device": "cuda:0"}
embeddings = HuggingFaceEmbeddings(
    model_name='/root/autodl-tmp/model/AI-ModelScope/m3e-base',  # 换成自己的embedding模型路径
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
if os.path.exists('VectorStore'):
    db = Chroma(persist_directory='VectorStore', embedding_function=embeddings)
loader = DirectoryLoader("/root/autodl-tmp/doc")  # 换成自己的文档路径
documents = loader.load()
text_spliter = CharacterTextSplitter(chunk_size=256, chunk_overlap=0)
documents = text_spliter.split_documents(documents)
database = Chroma.from_documents(documents, embeddings, persist_directory="VectorStore")
database.persist()
# 前置工作2:创建llm
llm = ChatOpenAI(
    temperature=0.95,
    model="glm-4",
    openai_api_key="ec5c5980165386142d4d401976234a0a.xGWn3QfonQqfbJPg",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)
# 第一步:优化问题,这里使用langchain的管道,让大模型生成问题的答案
question_prompt = PromptTemplate(
    input_variables=["question"],
    template="""请根据你已有的知识来回答这个问题,字数不超过100字。
    问题:{question}""",
)
question = "ChatGLM是什么"
hyde_answer = (
    question_prompt | llm | StrOutputParser()
)
answer = hyde_answer.invoke({"question": question})
# 打印一下大模型的回答
# print(answer)
# 第二步:通过大模型生成的答案查询向量数据库
retrieval_chain = hyde_answer | database.as_retriever()
docs = retrieval_chain.invoke({"question":question})
context = ''
for doc in docs:
    context = context+"\n"
    context = context + doc.page_content
# 第三步:获得答案
template = """请根据以下内容回答问题:
{context}
问题: {question}
如果不知道,请回答“不知道”。
"""
prompt = ChatPromptTemplate.from_template(template)
chain = prompt | llm
response = chain.invoke({"question": question, "context": context})
print(response)

4 Decomposition

4.1 基本思想

有时候一个问题较为复杂,按照人类的思维方式,会将其分解为子问题,然后一步步的推理,最终得到结果。这里有2种不同的方式:

第一种方式

  • 通过大模型将问题分解成多个子问题
  • 将每个子问题分别检索,获得结果,将子问题与其分别对应的结果组成问题对扔给大模型解答
  • 将子问题与得到的解答再组成问题对,连同原始问题一起扔给大模型,得到最终解答
    在这里插入图片描述

第二种方式

  • 1)通过大模型将问题分解成多个子问题
  • 2)在将第一个子问题进行检索,获得结果
  • 3)将第一个子问题与其分别对应的结果组成问题对,结果原始问题一起扔给大模型解答
  • 4)重复2)和3)步骤,直到结果出来或者达到最大允许查询次数

在这里插入图片描述

注:其中第二种方式的思想来自2篇论文分别是IR-CoT和Least-to-Most prompting

4.2 代码演示

第一种方式代码

import os

from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain_huggingface import HuggingFaceEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.output_parsers import StrOutputParser
from langchain_community.vectorstores.chroma import Chroma
from langchain_community.document_loaders import DirectoryLoader


# 前置工作1:文档存储,如果已经存储了文档,则不需要该步骤
encode_kwargs = {"normalize_embeddings": False}
model_kwargs = {"device": "cuda:0"}
embeddings = HuggingFaceEmbeddings(
    model_name='/root/autodl-tmp/model/AI-ModelScope/m3e-base',  # 换成自己的embedding模型路径
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
if os.path.exists('VectorStore'):
    db = Chroma(persist_directory='VectorStore', embedding_function=embeddings)
loader = DirectoryLoader("/root/autodl-tmp/doc")  # 换成自己的文档路径
documents = loader.load()
text_spliter = CharacterTextSplitter(chunk_size=256, chunk_overlap=0)
documents = text_spliter.split_documents(documents)
database = Chroma.from_documents(documents, embeddings, persist_directory="VectorStore")
database.persist()

# 前置工作2:创建llm
llm = ChatOpenAI(
    temperature=0.01,
    model="glm-4",
    openai_api_key="ec5c5980165386142d4d401976234a0a.xGWn3QfonQqfbJPg",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)

# 第一步:分解问题
template = """您是一个有用的助手,可以生成与输入问题相关的多个子问题。\n
目标是将输入问题分解为一组子问题,这些子问题可以单独得到答案。\n
生成与{question} 相关的多个子问题,输出2个子问题:"""
prompt_decomposition = ChatPromptTemplate.from_template(template)
generate_queries_decomposition = (prompt_decomposition | llm | StrOutputParser() | (lambda x: x.split("\n")))
final_question = "ChatGLM是哪个国家发布的?"
questions = generate_queries_decomposition.invoke({"question": final_question})
print(questions)

# 第二步:循环所有子问题,查询向量数据库获得文档内容,在一起扔给模型回答问题,将子问题和答案存储起来
RAG_prompt = '''你是负责回答问题的助手。使用以下检索到的上下文来回答问题。如果你不知道答案,就说你不知道。最多只用三句话,回答要简明扼要。\n
问题: {question} 
上下文: {context} 
答案:'''
prompt_rag = ChatPromptTemplate.from_template(RAG_prompt)


# 查询向量数据库并扔给大模型函数
def retrieve_and_rag(prompt_tmp):
    # 存储结果
    rag_results = []
    # 循环获取结果
    for sub_question in questions:
        # 查询子问题的文档内容
        retrieved_docs = database.as_retriever().get_relevant_documents(sub_question)
        # 获得答案
        answer = (prompt_tmp | llm | StrOutputParser()).invoke({"context": retrieved_docs, "question": sub_question})
        # 打印子问题和子答案,可以更好看看其获取过程
        # print("子问题: ", sub_question)
        # print(len(retrieved_docs))
        # print("子答案: ", answer)

        rag_results.append(answer)
    return rag_results, questions


answers, questions = retrieve_and_rag(prompt_rag)


# 组成问题对
def format_qa_pairs(sub_questions, sub_answers):
    """Format Q and A pairs"""

    formatted_string = ""
    for i, (question, answer) in enumerate(zip(sub_questions, sub_answers), start=1):
        formatted_string += f"问题 {i}: {question}\n答案 {i}: {answer}\n\n"
    return formatted_string.strip()


context = format_qa_pairs(questions, answers)

# Prompt
template = """下面是一组问题+答案对:

{context}

使用上述问题+答案对来生成问题的答案: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
final_rag_chain = (prompt | llm | StrOutputParser())
response = final_rag_chain.invoke({"context": context, "question": final_question})
print(response)

第二种方式代码

import os
from operator import itemgetter
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain_huggingface import HuggingFaceEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.output_parsers import StrOutputParser
from langchain_community.vectorstores.chroma import Chroma
from langchain_community.document_loaders import DirectoryLoader


# 前置工作1:文档存储,如果已经存储了文档,则不需要该步骤
encode_kwargs = {"normalize_embeddings": False}
model_kwargs = {"device": "cuda:0"}
embeddings = HuggingFaceEmbeddings(
    model_name='/root/autodl-tmp/model/AI-ModelScope/m3e-base',  # 换成自己的embedding模型路径
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
if os.path.exists('VectorStore'):
    db = Chroma(persist_directory='VectorStore', embedding_function=embeddings)
loader = DirectoryLoader("/root/autodl-tmp/doc")  # 换成自己的文档路径
documents = loader.load()
text_spliter = CharacterTextSplitter(chunk_size=256, chunk_overlap=0)
documents = text_spliter.split_documents(documents)
database = Chroma.from_documents(documents, embeddings, persist_directory="VectorStore")
database.persist()

# 前置工作2:创建llm
llm = ChatOpenAI(
    temperature=0.01,
    model="glm-4",
    openai_api_key="你的API KEY",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)

# 第一步:分解问题
template = """您是一个有用的助手,可以生成与输入问题相关的多个子问题。\n
目标是将输入问题分解为一组子问题,这些子问题可以单独得到答案。\n
生成与{question} 相关的多个子问题,输出2个子问题:"""
prompt_decomposition = ChatPromptTemplate.from_template(template)
generate_queries_decomposition = ( prompt_decomposition | llm | StrOutputParser() | (lambda x: x.split("\n")))
question = "ChatGLM是哪个国家发布的?"
questions = generate_queries_decomposition.invoke({"question": question})
print(questions)

# 第二步:将前一个子问题及答案组成问题对,再将问题对与当前子问题组成template扔给大模型解答
template = """下面是你需要回答的问题:

\n——\n {question} \n——\n

以下是任何可用的背景问题+答案对:

\n——\n {q_a_pairs} \n——\n

以下是与该问题相关的其他背景:

\n——\n {context} \n——\n

使用上述上下文和任何背景问题+答案对回答问题:\n {question}
"""
decomposition_prompt = ChatPromptTemplate.from_template(template)


# 组装问题对函数
def format_qa_pair(question, answer):
    formatted_string = ""
    formatted_string += f"问题: {question}\n答案: {answer}\n\n"
    return formatted_string.strip()


# 第三步:循环子问题,重复回到子问题
q_a_pairs = ""
answer = ""
for q in questions:
    rag_chain = (
            {"context": itemgetter("question") | database.as_retriever(),
             "question": itemgetter("question"),
             "q_a_pairs": itemgetter("q_a_pairs")}
            | decomposition_prompt
            | llm
            | StrOutputParser())

    answer = rag_chain.invoke({"question": q, "q_a_pairs": q_a_pairs})
    # print("=====", answer) # 此处可以打印出每个子问题的回答,看到完整的推理过程
    q_a_pair = format_qa_pair(q, answer)
    q_a_pairs = q_a_pairs + "\n---\n" + q_a_pair

print(answer)

5 Step-back prompting

5.1 基本思想

在Decomposition中我们知道处理复杂问题需要分步解决,其实还有另外一种方法,就是面对复杂问题时,人们有时会后退一步,抽象到更高层面的概念和原则上指导过程。听起来也挺抽象的,简单来说就是通过原始问题生成更抽象的Step-back问题,检索向量数据库得到的Step-back问题包含了原理和背景信息,基于Step-back问题得到的答案,推理得到了正确答案。步骤如下:

  • 抽象:首先让大模型提出一个更为抽象或者高级的问题(一般给几个few-shot),通过该问题检索向量数据库,得到答案
  • 推理:基于抽象问题和答案,加上原始问题及查询的答案,一起扔给大模型进行回答,得到最终答案

在这里插入图片描述

5.2 代码演示

import os

from langchain_core.runnables import RunnableLambda
from langchain_openai import ChatOpenAI
from langchain_huggingface import HuggingFaceEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.output_parsers import StrOutputParser
from langchain_community.vectorstores.chroma import Chroma
from langchain_community.document_loaders import DirectoryLoader
from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate

# 前置工作1:文档存储,如果已经存储了文档,则不需要该步骤
encode_kwargs = {"normalize_embeddings": False}
model_kwargs = {"device": "cuda:0"}
embeddings = HuggingFaceEmbeddings(
    model_name='/root/autodl-tmp/model/AI-ModelScope/m3e-base',  # 换成自己的embedding模型路径
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
if os.path.exists('VectorStore'):
    db = Chroma(persist_directory='VectorStore', embedding_function=embeddings)
loader = DirectoryLoader("/root/autodl-tmp/doc")  # 换成自己的文档路径
documents = loader.load()
text_spliter = CharacterTextSplitter(chunk_size=256, chunk_overlap=0)
documents = text_spliter.split_documents(documents)
database = Chroma.from_documents(documents, embeddings, persist_directory="VectorStore")
database.persist()

# 前置工作2:创建llm
llm = ChatOpenAI(
    temperature=0.01,
    model="glm-4",
    openai_api_key="ec5c5980165386142d4d401976234a0a.xGWn3QfonQqfbJPg",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)

# 第一步:设置抽象问题的template和few-shot
examples = [
    {
        "input": "智谱AI是哪个国家?",
        "output": "智谱AI的公司简介是什么?",
    },
    {
        "input": "BYD是哪个国家?",
        "output": "BYD的简介是什么?",
    },
]
example_prompt = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}"),
    ]
)
few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    examples=examples,
)
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """你是世界知识方面的专家。你的任务是通过退一步的方式把一个问题改写成一个更一般的问题,这样更容易回答。这里有几个例子:""",
        ),
        few_shot_prompt,
        ("user", "{question}"),
    ]
)
generate_queries_step_back = prompt | llm | StrOutputParser()
question = "ChatGLM是哪个国家发布的?"

# 可以打印一下中间生成的抽象问题
# answer = generate_queries_step_back.invoke({"question": question})
# print(answer)

# 第二步:基于抽象问题和答案,加上原始问题及查询的答案,一起扔给大模型进行回答,得到最终答案
response_prompt_template = """你是世界知识方面的专家。你是世界知识的专家。
我要问你一个问题。你的回答应该是全面的,如果与下面上下文是相关的,则不要与下面上下文相矛盾。
如果与下面上下文是不相关的,就忽略下面上下文。你的任务是通过退一步的方式把一个问题改写成一个更一般的问题,这样更容易回答。这里有几个例子.

# {normal_context}
# {step_back_context}

# 原始问题: {question}
# 答案:"""
response_prompt = ChatPromptTemplate.from_template(response_prompt_template)

chain = (
    {
        "normal_context": RunnableLambda(lambda x: x["question"]) | database.as_retriever(),
        "step_back_context": generate_queries_step_back | database.as_retriever(),
        "question": lambda x: x["question"],
    }
    | response_prompt
    | llm
    | StrOutputParser()
)

response = chain.invoke({"question": question})
print(response)

6 总结

问题优化其实还有很多其它方法,这里先介绍常见的5种,以下表格做一个总结:

方法描述场景
Multi-Query通过生成更多角度的问题针对问题模糊时
RAG-fusion在Multi-Query基础上,在检索文档结果后增加RRF重排针对问题模糊时
HyDE使用大模型生成假设性答案,再使用答案去查询相似度,而不是原始问题用户问题和语义不在一个空间
Decomposition将问题分解为子问题,再通过并行或串行方式获得答案针对复杂问题或者需要逻辑推理问题
Step-back使用few-shot方式,让大模型基于原始问题提出更为抽象问题针对复杂问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/760389.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringDataJPA系列(2)Commons核心Repository

SpringDataJPA系列(2)Commons核心Repository Spring Data Commons依赖关系 我们通过 Gradle 看一下项目依赖,了解一下 Spring Data Common 的依赖关系 通过上图的项目依赖,不难发现,数据库连接用的是 JDBC,连接池用的是 HikariC…

【MLP-BEV(7)】深度的计算。针孔相机和鱼眼相机对于深度depth的采样一个是均匀采样,一个是最大深度均匀采样

文章目录 1.1 问题提出1.1 看看DD3D 的深度是怎么处理的给出代码示例 1.2 我们看看BEVDepth的代码 1.1 问题提出 针孔相机和鱼眼相机的投影模型和畸变模型不一样,如果对鱼眼的模型不太了解可以到我的这篇博客【鱼眼镜头11】Kannala-Brandt模型和Scaramuzza多项式模…

【深度强化学习】关于混合动作空间转化为连续域空间的一点思考与实现

文章目录 前言问题解决方法以此类推假设动作之间有联系假设动作之间没有联系 前言 根据导师的文章,得到的想法,论文如下: 论文链接:《Deep Reinforcement Learning for Smart Home Energy Management》 问题 现在我有一个环境&…

Excel显示/隐藏批注按钮为什么是灰色?

在excel中,经常使用批注来加强数据信息的提示,有时候会把很多的批注显示出来,但是再想将它们隐藏起来,全选工作表后,“显示/隐藏批注”按钮是灰色的,不可用。 二、可操作方法 批注在excel、WPS表格中都是按…

解决本机电脑只能通过localhost访问,不能通过127.0.0.1访问

背景问题 有天我启动项目,发现项目连接Mysq总是连接不上,查了url、ip、port、用户名和密码都没有错,就是连接不上mysql数据库,后来通过查找资料发现有多个进程占用3306端口。 pid 6016 是mysqld服务 而pid 9672 是一个叫 svchos…

算力时代,算能(SOPHGO)的算力芯片/智算板卡/服务器选型

数字经济时代,算力成为支撑经济社会发展新的关键生产力,全球主要经济体都在加快推进算力战略布局。随着大模型持续选代,模型能力不断增强,带来算力需求持续增长。算力对数字经济和GDP的提高有显著的带动作用,根据IDC、…

安装ubuntu过程中,出现“执行‘grub-install/dev/sda’失败,这是一个致命错误”问题,解决办法!软碟通制作U盘启动盘!

背景 U盘安装ubuntu系统过程中,出现类似如下问题,/dev/sda7内容可能不一样,但问题类似。 可能原因 1.U盘启动盘制作失败 2.U盘启动盘UEFI格式与Ubuntu引导分区有冲突 解决办法 1.用UltraISO(软碟通)重新制作U盘启…

多表执行嵌套查询,减少笛卡尔积,防止内存溢出

问题:当涉及四个表的查询时,会产生大量的笛卡尔积导致内存溢出。 解决办法 :可以使用嵌套查询将多表的联合查询拆分为单个表的查询,使用resultmap中的association(适合一对一) 或 collection(一…

模拟城市5: 未来之城 全DLC for Mac 下载安装包

模拟城市5:未来之城(SimCity BuildIt)是一款由Maxis开发并由 Electronic Arts(EA)发行的城市建设和管理模拟游戏。这款游戏最初在2014年发布,适用于iOS、Android以及Windows Phone平台,随后在20…

聚焦Python分布式爬虫必学框架Scrapy打造搜索引擎(一)

Scrapy综述 Scrapy总体架构 Scrapy架构图(绿线是数据流向) 适用于海量静态页面的数据下载 Scrapy Engine(引擎): 负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。 Scheduler(调度器): 它负责接受引擎发送过来的Request请求&…

生命在于学习——Python人工智能原理(2.4.1)

在这里插一句话,我有两个好兄弟的github项目,感兴趣的可以去看一下,star一下,谢谢。 https://github.com/fliggyaa/fscanpoc https://github.com/R0A1NG/Botgate_bypass 四、Python的程序结构与函数 4.1 Python的分支结构 &…

matlab可以把图像数据转换为小波分析吗

🏆本文收录于《CSDN问答解答》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&…

P1107 [BJWC2008] 雷涛的小猫

[BJWC2008] 雷涛的小猫 题目背景 原最大整数参见 P1012 题目描述 雷涛同学非常的有爱心,在他的宿舍里,养着一只因为受伤被救助的小猫(当然,这样的行为是违反学生宿舍管理条例的)。在他的照顾下,小猫很快…

vb6多线程异步,VB.NET 全用API实现:CreateThread创建多线程,等待线程完成任务

在VB.NET中,你可以使用API函数来创建多线程并等待线程完成任务。以下是一个示例代码,展示如何使用API函数来实现这个功能: Imports System.Runtime.InteropServices Imports System.ThreadingPublic Class Form1Private Delegate Sub ThreadC…

嵌入式系统中静态库与动态库详解

大家好,今天我们来分享一下,动态库与静态库之间的差异有哪些? 计算机的运行当然离不开内存。 程序运行在内存当中,那么程序在内存中的布局是什么样子的呢? 程序的内存分为代码区、数据区、堆区和栈区,它们的布局是这样的,这里重点看代码区。 代码区中是什么呢? 这里主…

【sqlite3】联系人管理系统

SQLite3实现简单的联系人管理系统 有关sqlite3的基础知识请点击:SQLite3的使用 效果展示: 创建一个名为contacts.db的数据库 首先,我们需要创建一个名为contacts.db的数据库,并建立一个名为"contact"的表&#xff0…

某易六月实习笔试

第一题 下面代码需要更改的地方已指出。 解题思路 模拟题,用双指针记录双方当前式神,再记录一下当前谁先手,直到有一方指针越界。 把下面代码now1变为now(now1)%2就行。 第二题 解题思路 01背包变种,只是背包的容量变为多个维度…

打开防火墙设置提示需要使用新应用以打开此windowsdefender

拿到一台新电脑,装好虚拟机。主机ping虚拟机正常,虚拟机上网也正常,但是虚拟机ping主机ping不通。根据我多年虚拟机使用经验,这显然是因为主机防火墙没关。但是当我准备关闭主机防火墙的时候,发现防火墙设置打不开。界…

nvm安装以及idea下vue启动项目过程和注意事项

注意1:nvm版本不要太低,1.1.7会出现下面这个问题,建议1.1.10及其以上版本 然后安装这个教程安装nvm和node.js 链接: nvm安装教程(一篇文章所有问题全搞定,非常详细) 注意2:上面的教程有一步骤…

【WPF】Windows系统桌面应用程序编程开发新手入门-打造自己的小工具

电脑Windows系统上的桌面程序通常是用Visual Studio 开发工具编写出来的,有两种开发方式供选择,一种是WindowForm,简称WinForm,另一种是Windows Presentation Foundation,简称WPF,这里将学习WPF项目。 文章…