LLM之RAG实战(三十五)| 使用LangChain的3种query扩展来优化RAG

news2024/11/25 0:53:20

         RAG有时无法从矢量数据库中检索到正确的文档。比如我们问如下问题:

从1980年到1990年,国际象棋的规则是什么?

       RAG在矢量数据库中进行相似性搜索,来查询与国际象棋规则问题相关的相关文档。然而,在某些情况下,我们的向量数据库没有存储完整的信息,例如,我们的矢量数据库没有存储不同年份的规则。这样,数据库可以返回与国际象棋规则相关但与特定问题不直接相关的文档。

       针对上述情况,我们可以采用查询扩展技术,该技术可以对用户的原始查询生成更全面、信息更丰富的搜索。这个新生成的查询将从矢量数据库中获取更多相关文档。

本文,我们将介绍三种查询扩展方法:

一、后退提示(Step Back Prompting)

       Step back prompting来自论文《Take A Step Back: Evoking Reasoning Via Abstraction In Large Language Models》[1]

       Step back prompting是谷歌deepmind开发的一种方法,它首先使用LLM创建用户查询的抽象(从用户具体查询到通用查询的转换,因此称为后退提示),然后根据生成的通用查询来生成答案。

以下是“原始查询”和“后退查询”的示例:

{    "Original_Query": "Could the members of The Police perform lawful arrests?",    "Step_Back_Query": "what can the members of The Police do?",},{    "Original_Query": "Jan Sindel’s was born in what country?",    "Step_Back_Query": "what is Jan Sindel’s personal history?",}
#---------------------Prepare VectorDB-----------------------------------# Build a sample vectorDBfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.document_loaders import WebBaseLoaderfrom langchain_community.vectorstores import Chromafrom langchain.embeddings import OpenAIEmbeddingsimport osos.environ["OPENAI_API_KEY"] = "Your OpenAI KEY"# Load blog postloader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")data = loader.load()# Splittext_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)splits = text_splitter.split_documents(data)# VectorDBembedding = OpenAIEmbeddings()vectordb = Chroma.from_documents(documents=splits, embedding=embedding)#-------------------Prepare Step Back Prompt Pipeline------------------------from langchain_core.output_parsers import StrOutputParserfrom langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplatefrom langchain_core.runnables import RunnableLambdafrom langchain.chat_models import ChatOpenAIretriever = vectordb.as_retriever()llm = ChatOpenAI()# Few Shot Examplesexamples = [    {        "input": "Could the members of The Police perform lawful arrests?",        "output": "what can the members of The Police do?",    },    {        "input": "Jan Sindel’s was born in what country?",        "output": "what is Jan Sindel’s personal history?",    },]# We now transform these to example messagesexample_prompt = ChatPromptTemplate.from_messages(    [        ("human", "{input}"),        ("ai", "{output}"),    ])few_shot_prompt = FewShotChatMessagePromptTemplate(    example_prompt=example_prompt,    examples=examples,)prompt = ChatPromptTemplate.from_messages(    [        (            "system",            """You are an expert at world knowledge. Your task is to step back and paraphrase a question to a more generic step-back question, which is easier to answer. Here are a few examples:""",        ),        # Few shot examples        few_shot_prompt,        # New question        ("user", "{question}"),    ])question_gen = prompt | llm | StrOutputParser()#--------------------------QnA using Back Prompt Technique-----------------from langchain import hubdef format_docs(docs):    doc_strings = [doc.page_content for doc in docs]    return "\n\n".join(doc_strings)response_prompt = hub.pull("langchain-ai/stepback-answer")chain = (    {        # Retrieve context using the normal question        "normal_context": RunnableLambda(lambda x: x["question"]) | retriever | format_docs,        # Retrieve context using the step-back question        "step_back_context": question_gen | retriever | format_docs,        # Pass on the question        "question": lambda x: x["question"],    }    | response_prompt    | llm    | StrOutputParser())result = chain.invoke({"question": "What Task Decomposition that work in 2022?"})

       上面的代码是使用后退提示技术运行QnA的langchain脚本。需要注意的是:使用OPENAI_API_KEY并准备自己的向量数据库。

# Load blog post # You can use different loader to load different type fileloader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")data = loader.load()# Splittext_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)splits = text_splitter.split_documents(data)

我们尝试如下问题:

Original Query: What Task Decomposition that work in 2022?

后退查询结果如下:

Step Back Query: What are some examples of task decomposition in the current year?

       这两个查询将用于提取相关文档,我们将这些文档组合在一起作为上下文,并在下面的这一部分中提供给您的LLM。

{    # Retrieve context using the normal question    "normal_context": RunnableLambda(lambda x: x["question"]) | retriever | format_docs,    # Retrieve context using the step-back question    "step_back_context": question_gen | retriever | format_docs,    # Pass on the question    "question": lambda x: x["question"],}

二、Multi Query(多查询)[2]

       多查询是一种使用LLM从第一个查询生成更多查询的技术。这种技术试图回答一些用户提示没有那么具体的情况,这些生成的查询将用于在矢量数据库中查找文档。目标是细化查询,使其与主题更加相关,从而从数据库中检索更多相关的文档。

       细节可以参考:LLM之RAG实战(三十四)| 使用LangChain的三个函数来优化RAG

三、Cross Encoding Re-Ranking(交叉编码器重排序)

       交叉编码是多查询和交叉编码器重新排序的结合,因为用户能够使用LLM生成更多的问题,每个生成的查询都能够从矢量数据库中提取出几个文档。这些提取的文档必须通过交叉编码器,以获得与初始查询的相似性分数。现在我们可以对相关文档进行重新排序,并选择前5名作为LLM摘要的上下文。

       为什么我们需要选择前5个文档?在这种情况下,我们试图避免从矢量数据库中检索到不相关的文档,这种选择确保了交叉编码器专注于最相似和最有意义的文档,从而生成更准确、更简洁的摘要。

#------------------------Prepare Vector Database--------------------------# Build a sample vectorDBfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.document_loaders import WebBaseLoaderfrom langchain_community.vectorstores import Chromafrom langchain.embeddings import OpenAIEmbeddingsfrom langchain.chat_models import ChatOpenAIimport osos.environ["OPENAI_API_KEY"] = "Your API KEY"# Load blog postloader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")data = loader.load()llm = ChatOpenAI()# Splittext_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)splits = text_splitter.split_documents(data)# VectorDBembedding = OpenAIEmbeddings()vectordb = Chroma.from_documents(documents=splits, embedding=embedding)#--------------------Generate More Question----------------------------------#This function use to generate queries using LLMdef create_original_query(original_query):    query = original_query["question"]    qa_system_prompt = """            You are an AI language model assistant. Your task is to generate five         different versions of the given user question to retrieve relevant documents from a vector         database. By generating multiple perspectives on the user question, your goal is to help        the user overcome some of the limitations of the distance-based similarity search.         Provide these alternative questions separated by newlines."""        qa_prompt = ChatPromptTemplate.from_messages(        [            ("system", qa_system_prompt),            ("human", "{question}"),        ]    )        rag_chain = (        qa_prompt        | llm        | StrOutputParser()    )        question_string = rag_chain.invoke(        {"question": query}    )        lines_list = question_string.splitlines()    queries = []    queries = [query] + lines_list        return queries#-------------------Retrieve Document and Cross Encoding--------------------from sentence_transformers import CrossEncoderfrom langchain_core.prompts import ChatPromptTemplatefrom langchain_core.runnables import RunnableLambda, RunnablePassthroughfrom langchain_core.output_parsers import StrOutputParserimport numpy as npcross_encoder = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')#Cross Encoding happens in heredef create_documents(queries):    retrieved_documents = []    for i in queries:        results = vectordb.as_retriever().get_relevant_documents(i)        docString = [doc.page_content for doc in results]        retrieved_documents.extend(docString)            unique_a = []    #If there is duplication documents for each query, make it unique    for item in retrieved_documents:        if item not in unique_a:            unique_a.append(item)        unique_documents = list(unique_a)        pairs = []    for doc in unique_documents:        pairs.append([queries[0], doc])        #Cross Encoder Scoring    scores = cross_encoder.predict(pairs)        final_queries = []    for x in range(len(scores)):        final_queries.append({"score":scores[x],"document":unique_documents[x]})        #Rerank the documents, return top 5    sorted_list = sorted(final_queries, key=lambda x: x["score"], reverse=True)    first_five_elements = sorted_list[:6]    return first_five_elements#-----------------QnA Document-----------------------------------------------qa_system_prompt = """        Assistant is a large language model trained by OpenAI. \        Use the following pieces of retrieved context to answer the question. \        If you don't know the answer, just say that you don't know. \        {context}"""qa_prompt = ChatPromptTemplate.from_messages(    [        ("system", qa_system_prompt),        ("human", "{question}"),    ])def format(docs):    doc_strings = [doc["document"] for doc in docs]    return "\n\n".join(doc_strings)chain = (    # Prepare the context using below pipeline    # Generate Queries -> Cross Encoding -> Rerank ->return context    {"context": RunnableLambda(create_original_query)| RunnableLambda(create_documents) | RunnableLambda(format), "question": RunnablePassthrough()}    | qa_prompt    | llm    | StrOutputParser())result = chain.invoke({"question":"What Task Decomposition that work in 2022?"})

       参考从上面的langchain脚本,创建2个自定义函数用于生成查询和交叉编码。

create_original_query是生成查询,基本上会返回5个生成的问题加上原始查询。

create_documents将用于检索基于6个问题的24个相关文档。这24份相关文件将被复制,因此我们需要保留唯一的文件并删除重复的文件。

scores = cross_encoder.predict(pairs)

       上述代码可以计算出文档和原始查询之间的交叉编码分数。最后,代码将尝试根据交叉编码得分对文档进行重新排序,并只保留前5个文档。

四、结论

       这3种方法真的很有帮助,特别是如果你得到的反馈是RAG没有返回正确和详细的答案,你可以使用上面的查询扩展方法来解决这些问题。

参考文献:

[1] https://arxiv.org/pdf/2310.06117.pdf

[2] https://python.langchain.com/docs/modules/data_connection/retrievers/MultiQueryRetriever

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式 —— 设计原则

在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。 开闭原则 对扩展开放,对修改关闭。在程序需…

0 决策树基础

目录 1 绪论 2 模型 3 决策树面试总结 1 绪论 决策树算法包括ID3、C4.5以及C5.0等,这些算法容易理解,适用各种数据,在解决各种问题时都有良好表现,尤其是以树模型为核心的各种集成算法,在各个行业和领域都有广泛的…

蓝桥杯基础练习详细解析(四)——Fibonacci费伯纳西数列(题目分析、代码实现、Python)

试题 基础练习 Fibonacci数列 提交此题 评测记录 资源限制 内存限制:256.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 问题描述 Fibonacci数列的递推公式为:FnFn-1Fn-2,其…

RANSAC算法:从原理到图像拼接的实践

RANSAC算法:从原理到图像拼接的实践 1. RANSAC算法原理和步骤 1.1 RANSAC算法简介 RANSAC (RANdom SAmple Consensus, 随机采样一致) 算法是从一组含有“外点”(outliers)的数据中正确估计数学模型参数的迭代算法。“外点”一般指的的数据中的噪声,比…

阿里云CentOS7安装MySQL8

创建目录 [rootnode1 ~]# mkdir /usr/local/mysql [rootnode1 ~]# cd /usr/local/mysql/ 下载安装包 到MySQL官网查看需要下载的版本,并获取到下载地址 https://downloads.mysql.com/archives/community/下载 [rootnode1 mysql]# wget https://downloads.mysql…

uniapp 微信小程序 canvas 手写板获取书写内容区域并输出

uni.canvasGetImageData 返回一个数组,用来描述 canvas 区域隐含的像素数据,在自定义组件下,第二个参数传入自定义组件实例 this,以操作组件内 组件。 // 获取目标 canvas 的像素信息 pixelData let canvas uni.createSelector…

平台介绍-搭建赛事运营平台(3)

上文介绍了品牌隔离的基本原理,就是通过不同的前端和微服务来实现。但是确实很多功能是类似的,所以从编程角度还是有些管理手段的。 前端部分:前端部分没有什么特别手段,就是两个独立的项目工程,分别维护。相同的部分复…

MoonBit MeetUp回顾——张正、宗喆:编程语言在云原生与区块链领域的技术探索

宗喆和张正分别给我们带了 KCL 相关的最新进展,由蚂蚁集团开发的 Rust 编写的开源 DSL,目标是优化云原生策略配置和用户体验。它通过引入动态配置管理、配置校验和基础设施抽象等核心概念,解决开发者认知负担、配置膨胀和标准化工具缺乏的问题…

Flink系列之:Flink SQL Gateway

Flink系列之:Flink SQL Gateway 一、Flink SQL Gateway二、部署三、启动SQL Gateway四、运行 SQL 查询五、SQL 网关启动选项六、SQL网关配置七、支持的端点 一、Flink SQL Gateway SQL 网关是一项允许多个客户端从远程并发执行 SQL 的服务。它提供了一种简单的方法…

机器学习(三)

神经网络: 神经网络是由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。 f为激活(响应)函数: 理想激活函数是阶跃函数,0表示抑制神经元而1表示激活神经元。 多层前馈网络结构: BP(误差逆…

比较AI编程工具Copilot、Tabnine、Codeium和CodeWhisperer

主流的几个AI智能编程代码助手包括Github Copilot、Codeium、Tabnine、Replit Ghostwriter和Amazon CodeWhisperer。 你可能已经尝试过其中的一些,也可能还在不断寻找最适合自己或公司使用的编程助手。但是,这些产品都会使用精选代码示例来实现自我宣传…

【携程笔试题汇总】2024-03-28-携程春招笔试题-三语言题解(CPP/Python/Java)

🍭 大家好这里是KK爱Coding ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新携程近期的春秋招笔试题汇总~ 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢&#x1f…

PYTHON初级笔记1

0、python? 简单的编程语言 python环境搭建: ①、开发环境:vscode、sublime、pycharm...... ②、运行环境:cpython解释器 python如何写代码? ①、在终端上的命令行上写,可以是我们cmd的中终端,…

【C语言】预处理常见知识详解(宏详解)

文章目录 1、预定义符号2、define2.1 define 定义常量2.2 define 定义宏 3、#和##3.1 **#**3.2 **##** 4、条件编译(开关) 1、预定义符号 在C语言中内置了一些预定义符号,可以直接使用,这些符号实在预处理期间处理的,…

【IntelliJ IDEA】运行测试报错解决方案(附图)

IntelliJ IDEA 版本 2023.3.4 (Ultimate Edition) 测试报错信息 命令行过长。 通过 JAR 清单或通过类路径文件缩短命令行,然后重新运行 解决方案 修改运行配置,里面如果没有缩短命令行,需要再修改选项里面勾选缩短命令行让其显示&#x…

深入浅出的揭秘游标尺模式与迭代器模式的神秘面纱 ✨

​🌈 个人主页:danci_ 🔥 系列专栏:《设计模式》 💪🏻 制定明确可量化的目标,坚持默默的做事。 🚀 转载自:设计模式深度解析:深入浅出的揭秘游标尺模式与迭代…

【机器学习】深入探讨基于实例的学习及K-最近邻算法

深入探讨基于实例的学习及K-最近邻算法 在机器学习的众多策略中,基于实例的学习方法因其简单性和高效性而备受关注。这种方法的核心理念在于利用已知的数据实例来预测新数据的标签或属性。本文将深入探讨其中的两个重要概念:最近邻算法和K-最近邻算法&a…

ArcGIS Pro横向水平图例

终于知道ArcGIS Pro怎么调横向图例了! 简单的像0一样 旋转,左转右转随便转 然后调整图例项间距就可以了,参数太多就随便试,总有一款适合你! 要调整长度,就调整图例块的大小。完美! 好不容易…

CorelDRAW25.0.0.230最新2024版本Crack下载安装方法

CorelDRAW (CDR)是一款专业的平面设计软件。本软件是由加拿大Corel公司开发的一款功能强大的专业平面设计软件、矢量设计软件、矢量绘图软件。本矢量图形制作工具软件广泛应用于商标设计、标志制作、封面设计、CIS设计、产品包装设计、模型绘制、插画绘制、时装/服装设计、印刷…

STM32G473之flash存储结构汇总

STM32G4系列单片机,为32位的微控制器,理论上其内部寄存器地址最多支持4GB的命名及查找(2的32次方,地址命名为0x00000000至0xFFFFFFFF)。STM32官方对4GB的地址存储进行编号时,又分割成了8个block区域&#x…