5 种经过验证的查询翻译技术可提高您的 RAG 性能

news2024/11/13 19:07:15

如何在用户输入模糊的情况下获得近乎完美的 LLM 性能

多查询、RAG 融合、分解、后退提示和 HyDE 等查询翻译技术极大地提高了基于 RAG 的 LLM 应用程序的性能。

         

       欢迎来到雲闪世界。你认为用户会向 LLM 提出完美的问题,这是大错特错。如果我们不直接执行,而是细化用户的问题,结果会怎样?这就是查询转换。

我们开发了一款应用程序,让用户可以查询我公司曾经制作的所有文档。这些文档包括 PPT、项目提案、进度更新、可交付成果、文档等。这款应用程序非常了不起,因为过去许多此类尝试都以失败告终。感谢 RAG,这次它非常有希望。

我们做了一个演示,每个人都很兴奋地使用它。最初的推出是针对一小部分选定的员工。但我们注意到,结果并不太令人兴奋。

这项技术有望彻底改变我们的工作方式。但大多数用户只试用了几次,之后就再也没有使用过。他们放弃这款应用,就好像它是小学生的玩具项目一样。

日志显示结果令人满意。但是,我们与使用该应用的真实用户进行了交谈,以确定真正的问题。我们学到的经验教训促使我们思考查询翻译技术,以克服用户输入中的歧义。

以下是一个示例情况。

一位用户对我们建议客户“XYZ”收购的时尚相关企业感兴趣。他输入的是:“XYZ 合伙人进行了哪些时尚相关收购?”该应用程序搜索了我们可交付的 PPT,并列出了十几家公司。然而,这份名单与用户的预期相差太大。XYZ 合伙人已经收购了(比如说)7 家时尚商店。但我们得到的名单只有 4 家。这位用户也是一名测试人员,他很清楚有多少次收购。

难怪人们不再使用该工具。但由于逐步淘汰的推广技术,失去的信任是可以逆转的。

我们对应用进行了一系列更改以解决此问题。一项重要更新是查询翻译。

这篇文章旨在介绍我们不同的查询翻译技术,但不会深入介绍。例如,其中一些技术可以与提示技术(如小样本提示和思维链)相结合,以获得更好的结果。不过,这些技术留待以后的另一篇文章再讲。

让我们逐一探索这些技术。但在此之前,这是一个基本的 RAG 示例。

基本 RAG 示例

所有 RAG 应用程序都至少有一个数据库,通常是一个向量存储和一个语言模型。RAG 背后的基本思想很简单。在 LLM 利用其先验知识回答用户的问题之前,它会在数据库中搜索上下文信息并生成更准确的响应。

下图说明了最简单的 RAG 应用程序。

基本 RAG:用户的问题通过向量存储来检索相关文档,然后将其用作语言模型的上下文来回答用户的问题。

基本 RAG 应用程序工作流程 — 图片由作者提供。

在简单的 RAG 应用程序中,仅存在与您的 LLM 模型的一次通信。这可以是OpenAI GPT 模型、Cohere,甚至是您本地托管的模型。

以下代码实现了图中的步骤。我们将以此为基础,构建本文中的其他技术。

# 这是为了安全地加载我们的秘密
from dotenv import load_dotenv 
load_dotenv() 

# 1. 加载内容
# ----------------------------------------- 
import bs4 
from langchain_community.document_loaders import WebBaseLoader 

loader = WebBaseLoader( 
    web_paths=( "https://docs.djangoproject.com/en/5.0/topics/performance/" ,), 
    bs_kwargs= dict ( 
        parse_only=bs4.SoupStrainer( 
            id = "docs-content"
         ) 
    ), 
) 
doc_content = loader.load() 


# 2. 索引
# ----------------------------------------- 
from langchain.text_splitter import RecursiveCharacterTextSplitter 
from langchain_community.vectorstores import Chroma 
from langchain_openai import OpenAIEmbeddings 

text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder( 
    chunk_size= 1000 , chunk_overlap= 200
 ) 
docs = text_splitter.split_documents(doc_content) 

vector_store = Chroma.from_documents(documents=docs, embedding=OpenAIEmbeddings()) 
trieser = vector_store.as_retriever() 


# 3. LLM 
# ----------------------------------------- 
from langchain_openai import ChatOpenAI 
llm = ChatOpenAI(temperature= 0.5 ) 


# 4. RAG Chain 
# ----------------------------------------- 
from langchain_core.prompts import ChatPromptTemplate 
from langchain_core.runnables import RunnablePassthrough 
from langchain_core.output_parsers import StrOutputParser 

prompt = """
在以下上下文中回答问题:
{context}

问题:{question} 
"""

 prompt_template = ChatPromptTemplate.from_template(prompt) 

chain = ( 
    { “上下文”:检索器,“问题”:RunnablePassthrough()} 
    | prompt_template 
    | llm 
    | StrOutputParser()
)


# 5. 调用链
# -----------------------------------------
 response = chain.invoke(
    “我如何提高网站速度?”,
)

打印(响应)

在上面的代码中,我们使用了基于 Web 的加载器从 Django 的文档页面加载页面并将其存储在 Chroma 矢量存储中。除了文档页面,您还可以尝试不同的网页、本地文本文件、PDF 等。

由于我们没有使用复杂的检索技术,因此我们将检索器直接传递到最终的 RAG 链。在后续技术中,我们将传递另一个检索器链而不是检索器本身。本文的其余部分是关于我们如何构建检索链。

后退提示

生成与更广泛的背景不矛盾的一致答案。

后退提示与基本 RAG 非常相似。我们不会向用户询问初始问题,而是使用更广泛的问题从数据库中检索文档。

较宽泛的问题比具体的问题能捕捉到更多的背景信息。因此,最终的 LLM 可以为用户提供更多有用的信息,且不会与更广泛的背景相矛盾。

当初始查询过于具体和详细但缺乏整体视图时,这通常很有用。

后退提示:语言模型扩展用户的输入并从向量存储中检索相关文档以提供上下文并回答用户的初始问题。

后退提示工作流程——图片由作者提供。

以下是后退提示的代码实现。请注意,我们使用了不同的代码,在基本 RAG 示例中,我们传递了一个检索器本身。

# 4. RAG 链
# ----------------------------------------- 
from langchain_core.prompts import ChatPromptTemplate 
from langchain_core.runnables import RunnablePassthrough 
from langchain_core.output_parsers import StrOutputParser 

# 4.1 后退提示
step_back_prompt = """
你是一位专业软件工程师。
你的任务是将给定的问题改写成更通用的形式,以便于回答。

# 示例 1
问题:如何提高 Django 性能?
输出:哪些因素会影响 Web 应用程序性能?

# 示例 2
问题:如何在 Django 中优化浏览器缓存?
输出:有哪些不同的缓存选项?


问题:{question}
输出:
“""

 step_back_prompt_template = ChatPromptTemplate.from_template(step_back_prompt) 

retrieval_chain = step_back_prompt_template | llm | StrOutputParser() |检索器


# 4.2 RAG chain
 prompt = """
在以下上下文中回答问题。
您的回答应全面,且不与以下上下文相矛盾。
如果上下文与问题无关,请说“我不知道”:
{context}

问题:{question} 
"""

 prompt_template = ChatPromptTemplate.from_template(prompt) 

rag_chain = ( 
    { "context" : retrieval_chain, "question" : RunnablePassthrough()} 
    | prompt_template 
    | llm 
    | StrOutputParser() 
)

对于更广泛背景至关重要的申请,后退提示很有帮助。法学硕士将对相关问题提供一致的答案。

HyDE(假设文档嵌入)

利用相关来源生成内容丰富的答案

HyDE 是一种近期流行的文档检索技术。其理念是利用 LLM 的先验知识生成文档。然后,该文档从向量存储中检索相关上下文。

HyDE 的一个很好的用例是,当用户经常使用外行术语来描述问题,但向量存储中的信息非常技术性时。此外,LLM 的描述有更多关键字来检索相关信息。

例如,像“提高 Django 性能的 10 种方法”这样的查询将提供一个全面的答案,其中包括成本影响、缓存、压缩等。

HyDE:LLM 根据用户的输入创建一个假设文档,并使用它从向量存储中检索相关文档,然后将其用作上下文来回答用户的初始问题。

HyDE 文档检索过程——图片由作者提供。

以下是上图的代码实现。这次,我仅提供了使用 HyDE 重新创建检索链的代码片段。

# 4.1 HyDE 提示
hyde_prompt = """
你是一个 AI 语言助手。
你的任务是生成下面问题的更宽泛版本。
通过这样做,你将帮助用户获得更多信息。
不要解释这个问题。只提供更宽泛的版本。
问题:{question}
输出:
""" 

hyde_prompt_template = ChatPromptTemplate.from_template(hyde_prompt) 

retrieval_chain = hyde_prompt_template | llm | StrOutputParser() | trieser

多查询

检索更多文档,克服距离基础,类似地搜索并获得更多相关答案。

多查询是一种有助于解决向量存储中基于距离的搜索问题的技术。大多数向量存储使用余弦相似度来检索向量化文档。只要文档具有一定的相似度,此方法就很有效。但是,当不存在基于距离的相似度时,检索过程将失败。

在多查询方法中,我们要求 LLM 创建同一查询的多个版本。例如,像“如何加速 Django 应用程序”这样的查询将被转换为另一个版本,“如何提高基于 Django 的 Web 应用程序的性能?”这些查询一起将从向量存储中检索更多相关文档。

作为中间步骤,我们必须在将这些文档传递给最终的 RAG-LLM 之前获取一份唯一的文档列表。这是因为很有可能不止一个查询会检索相同的文档。将它们全部与重复项一起传递将毫无意义地达到 LLM 的标记阈值。

多查询 RAG:用户的问题通过 LLM 生成多个版本。每个版本从向量存储中检索文档,然后检索增强生成 (RAG) 语言模型使用一组唯一的文档作为上下文来回答用户的问题。

多查询检索工作流程——图片由作者提供。

代码实现还增加了一个对文档进行重复数据删除的功能。其余部分与其他方法相同。

from langchain.load import loads, dumps 

def get_unique_documents(documents: list[list]) -> list: 
    # 展平列表列表,并将每个 Document 转换为字符串
    flattened_docs = [dumps(doc) for sublist in documents for doc in sublist] 
    # 获取唯一文档
    unique_docs = list(set(flattened_docs)) 
    # 返回
    return [loads(doc) for doc in unique_docs] 

# 4.1 多查询提示
multi_query_prompt = "" "
你是一个 AI 语言模型助手。
你的任务是创建五个版本的用户问题,以从向量数据库中获取文档。
通过提供对用户问题的多种视角,你的目标是帮助用户克服基于距离的相似性搜索的一些限制。
给出这些备选问题,每个问题占一行。
问题:{question}
输出:
" ""

 multi_query_prompt_template = ChatPromptTemplate.from_template(multi_query_prompt) 

retrieval_chain = ( 
    multi_query_prompt_template 
    |llm 
    |StrOutputParser() 
    |(lambda x:x.split( “\n” )) 
    |retriever.map() 
    |get_unique_documents 
)

RAG融合

更多相关文档在答案中发挥更重要的作用

RAG 融合类似于文档检索前端的多查询。我们再次要求 LLM 生成初始查询的不同版本。然后我们针对这些版本分别检索文档并将它们组合起来。

然而,在合并和去重文档的同时,我们也会根据文档的相关性对它们进行排序。以下是 RAG 融合过程的示意图。

RAG-fusion 工作流程——图片由作者提供。

我们不只是进行重复数据删除,还使用排名系统对文档进行排序。互易融合排名 (RRF) 是一种对文档进行排名的巧妙方法。

如果多个查询版本将同一文档检索为最相关文档,则 RRF 会将其排名靠前。如果特定文档仅出现在其中一个查询版本中,并且相似度较低,则 RRF 会将该文档排名靠后。这样,我们就可以获取更相关的信息并对其进行优先排序。

def  reciprocal_rank_fusion ( results: list [ list ], k= 60 ): 
    """ Reciprocal_rank_fusion 采用多个排名文档列表
        和 RRF 公式中使用的可选参数 k """
    
     fused_scores = {} 

    for docs in results: 
        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 

# 4.1 RAG-fusion 提示
multi_query_prompt = """
你是一个 AI 语言助手。
你的任务是生成 5 个不同版本的用户问题。
通过这样做,你正在帮助用户克服基于距离的相似性搜索的局限性。
提供这些以换行符分隔的备选问题。
问题:{question}
输出:
"""

 multi_query_prompt_template = ChatPromptTemplate.from_template(multi_query_prompt) 

retrieval_chain = ( 
    multi_query_prompt_template 
    | llm 
    | StrOutputParser() 
    | ( lambda x: x.split( "\n" )) 
    | trieser. map () 
    | reciprocal_rank_fusion 
)

分解

在回答复杂问题时,让 LLM 将其分解成各个部分并逐步构建答案。

有些情况下,最好不要直接回答。解决更复杂任务的一个好方法是将问题分解成几个部分,然后分别回答。

这不仅仅是一种 LLM 技术,对吧?

是的,我们在查询分解中尝试将初始问题分解为多个子问题。回答这些子问题将为回答初始查询提供一些零碎信息。

查询分解:将用户的查询分解为子查询。从数据存储中检索这些子查询的相关文档并单独回答。然后使用这些问答对作为上下文来回答用户的初始查询。

RAG 中的查询分解——图片由作者提供。

正如您在图中看到的,我们检索每个子问题的相关文档并分别回答它们。然后我们将这些问题和答案对传递给最终的 RAG-LLM。LLM 现在拥有更详细的信息来解决复杂问题。

# 4.1分解提示
decomposition_template = "" "你是一个AI语言助手。
你的任务是将以下问题分解为5个子问题。
通过这样做,你正在帮助用户逐步构建最终答案。
提供用换行符分隔的这些备选问题。
原问题:{question}
输出:
" ""

 decomposition_prompt_template = ChatPromptTemplate.from_template (decomposition_template) def


 query_and_combine ( questions: list[ str ]) ->  str : 
    print (questions) qa_pairs
     = [] 
    for  q  in questions: 
        r = basic_rag_chain.invoke ( q) 
        qa_pairs.append ( (q, r)) 

    qa_pairs_str = "\n" .join ([f "Q: {q}\nA: {a}" for q , a in qa_pairs]). strip()打印(qa_pairs_str)返回qa_pairs_str retrieval_chain =(    { “question”:RunnablePassthrough()}     | decomposition_prompt_template     | llm     | StrOutputParser()    |(lambda x:x.split ( “ \n”))    | query_and_combine )  

    

最后的想法

从演示应用程序到生产有许多步骤。其中一个不可避免的步骤是查询翻译。

我们解决的问题复杂程度不同。需要考虑用户不完善的查询。应该解决检索过程的缺陷。这些都需要考虑。

没有单一的正确方法来选择最佳的查询转换技术。在实际应用中,您可能需要结合多种技术才能获得所需的输出。

感谢关注雲闪世界。(Aws解决方案架构师vs开发人员&GCP解决方案架构师vs开发人员)

 订阅频道(https://t.me/awsgoogvps_Host)
 TG交流群(t.me/awsgoogvpsHost)

#aws CLI cheat sheet #aws cli Debug #aws cli get S3 object #aws cli login with access key #aws cli to download from s3 #aws command line download from s3 #homebrew install aws cli#aws sdk get caller identity #aws s3 cli get object #aws s3 put object#aws s3 headobject#aws s3 put-object #aws s3 sync vs cp

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

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

相关文章

查看DrawCall流程 Unity工具 Frame Debug

切换帧率 基础面板 可以看到 我们可以通过切换面板 看DrwaCall产生 MainTex 就是材质了 如何优化? 合批 就会一次性直接渲染

双端队列Deque

Deque(双端队列)是一种允许在两端都进行插入和删除操作的线性数据结构。它在 Java Collections Framework 中作为一个重要的接口,具有以下结构特点: 1. 双端操作 两端插入和删除:与传统队列(只能在一端入…

迭代次数顺序的双重性

(A,B)---6*30*2---(0,1)(1,0) 收敛误差为7e-4,收敛199次取迭代次数平均值, 让A是4a1,4a2,…,4a16,B全是0得到迭代次数的顺序就是1,2,…,16. 但是如果让训练集A-B矩阵的高…

kafka-go使用:以及kafka一些基本概念说明

关于kafka 作为开发人员kafka中最常关注的几个概念,是topic,partition和group这几个概念。topic是主题的意思,简单的说topic是数据主题,这样解释好像显得很苍白,只是做了个翻译。一图胜前言,我们还是通过图解来说明。…

PDF密码移除技巧: 五大 PDF 密码移除器

如果您想解密或删除 PDF 密码,该怎么办?PDF 是一种经常用于商业的格式,您可以在培训、教育和商业场合使用它。添加这些 PDF 文件的密码可以保护您的安全和隐私。因此,有很多 PDF 都用密码加密,当您想要查看这些 PDF 时…

PTrade常见问题系列22

反馈定义的上午7点执行run_daily函数,但是每周一上午都没法正常执行? 1、run_daily函数加载在initialize函数中,执行后才会创建定时任务; 2、由于周末会有例行重启操作,在重启以后拉起交易时相当于非交易日启动的交易…

【人工智能训练师】2 集群搭建

题目一、基础配置 core-site.xml参数配置详情 官方文档:http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/core-default.xml core-default.xml与core-site.xml的功能是一样的,如果在core-site.xml里没有配置的属性&#xff0c…

【C++二分查找 决策包容性】1300. 转变数组后最接近目标值的数组和

本文涉及的基础知识点 C二分查找 决策包容性 LeetCode1300. 转变数组后最接近目标值的数组和 给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近 target …

【开端】JAVA中的切面使用

一、绪论 在不使用过滤器和 拦截器的前提下,如果统一对JAVA的 方法进行 管理。比如对一类方法或者类进行日志监控,前后逻辑处理。这时就可以使用到切面。它的本质还是一个拦截器。只是通过注解的方式来标识所切的方法。 二、JAVA中切面的使用实例 Aspec…

如何看待“低代码”开发平台的兴起

目录 1.概述 1.1.机遇 1.2.挑战 1.3.对开发者工作方式的影响 2.技术概览 2.1.主要特点 2.2.市场现状 2.3.主流低代码平台 2.4.分析 3.效率与质量的权衡 3.1.提高开发效率 3.2.质量与安全隐患 3.3.企业应用开发的利弊分析 4.挑战与机遇 4.1.机遇 4.2.挑战 4.3.…

为什么需要在线实时预览3D模型?如何实现?

在线实时预览3D模型在现代设计、产品开发、市场营销、以及娱乐等领域中变得越来越重要,原因可以归结为以下几个方面: 1、多平台兼容性: 在线实时预览通常不依赖于特定的操作系统或软件平台,只要设备能够访问互联网和浏览器&…

21-原理图的可读性的优化处理

1.自定义原理图尺寸 先将原理图移动到左下角 2.划分模块 3.放置模块字符串

第三期书生大模型实战营——基础岛

1.书生大模型全链路开源体系 【书生浦语大模型全链路开源开放体系】 https://www.bilibili.com/video/BV18142187g5/?share_sourcecopy_web&vd_source711f676eb7f61df7d2ea626f48ae1769 视频里介绍了书生浦语大模型的开源开放体系,包括了其的技术发展、模型架…

ubuntu系统下安装LNMP集成环境的详细步骤(保姆级教程)

php开发中集成环境的安装是必不可少的技能,而LNMP代表的是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构。今天就给大家分享下LNMP的安装步骤。 1 Nginx安装 在安装Nginx前先执行下更新命令: sudo apt-get update 接下来开始安装Nginx, 提示:Could not get lock /v…

【mysql 第二篇章】请求到真正执行 SQL 到底是一个怎么样的过程?

从用户调用到SQL执行的流程中间发生了什么事情 1、网络请求使用 线程 来处理,当数据库连接池中监听到有连接请求,这个时候会分配一个线程来处理。 2、SQL接口 负责接收 SQL 语句,当线程监听到有请求和读取数据的之后,将 SQL 语句…

Android Fragment:详解,结合真实开发场景Navigation

目录 1)Fragment是什么 2)Fragment的应用场景 3)为什么使用Fragment? 4)Fragment如何使用 5)Fragment的生命周期 6)Android开发,建议是多个activity,还是activity结合fragment&…

SparkSQL——AnalyzedLogicalPlan生成

Rule和RuleExecutor SparkSQL中对LogicalPlan的解析、优化、还有物理执行计划生成都是分成一个个Rule进行的。 RuleExecutor是一个规则引擎,它收集Rule,并对plan按照rule进行执行。 每一个Rule的实现类都要实现apply方法,具体逻辑都放在这个…

mysql中的时间相关函数

MySQL服务器中有3种时区设置: 系统时区(保存在system_time_zone系统变量中)服务器时区(保存在全局系统变量time_zone中)每个客户端连接的时区(保存在会话变量time_zone中) 其中,客…

极米RS10Plus性价比高吗?7款4-6K价位投影仪测评哪款最好

通常家庭想买个投影仪都会选择4-6K这个价位段的投影仪,3K以下的投影配置太低,6K以上的价格略高,4-6K价位段的中高端投影仪正好满足大部分家庭的使用需求。正好极米投影在8月份上新了一款Plus版本的长焦投影:极米RS10Plus&#xff…

剪切走的照片找回:数据恢复实战指南

一、引言:当珍贵瞬间遭遇剪切失误 在数字化时代,照片不仅是记忆的载体,更是情感与故事的传承。然而,一次不经意的剪切操作失误,却可能让这些珍贵的瞬间面临丢失的风险。面对剪切走的照片,许多用户会感到无…