ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 1.2em;font-weight: bold;display: table;margin: 2em auto 1em;padding-right: 1em;padding-left: 1em;border-bottom: 2px solid rgb(250, 81, 81);color: rgb(63, 63, 63);">构建RAG驱动的应用程序中的路由根据用户查询的意图在RAG应用程序内路由控制流可以帮助我们创建更有用、更强大的基于检索增强生成 (RAG) 的应用程序。
ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">我们希望用户能够交互的数据很可能来自各种来源,例如报告、文档、图像、数据库和第三方系统。对于基于业务的RAG应用程序,我们可能希望使用户能够与来自业务中一系列领域的信息进行交互,例如来自销售、订购和会计系统的信息。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">由于数据源的多样性,信息存储的方式以及我们想要与之交互的方式也可能是多种多样的。有些数据可能存储在向量存储中,有些数据存储在SQL数据库中,有些数据可能需要通过API调用进行访问,因为它位于第三方系统中。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">RAG系统根据查询意图路由到不同的数据源ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">对于相同但不同的数据,也可以有不同的向量存储设置,并针对不同的查询类型进行优化。例如,可以设置一个向量存储用于回答摘要类型问题,而另一个向量存储用于回答特定的定向类型问题。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">根据问题,我们可能还想路由到不同的组件类型。例如,我们可能希望将查询传递给Agent、VectorStore,或者直接传递给LLM进行处理,所有这些都基于问题的性质ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">根据用户的查询路由到不同的组件类型ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">我们甚至可能想根据所提出的问题自定义提示模板。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">根据用户查询通过不同的提示模板进行路由ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">总而言之,我们希望通过应用程序更改和引导用户查询流程的原因有很多。我们的应用程序尝试满足的用例越多,我们就越有可能在整个应用程序中提出路由要求。路由器本质上只是 If/Else 语句,我们可以用它来指导查询的控制流。
但有趣的是,他们需要根据自然语言输入做出决定。因此,我们正在寻找基于自然语言描述的离散输出。
由于许多路由逻辑都是基于使用LLM或机器学习算法,而这些算法本质上是不确定的,因此我们不能保证路由器始终 100% 做出正确的选择。
除此之外,我们不太可能预测进入路由器的所有不同查询变体。然而,通过使用最佳实践和一些测试,我们应该能够使用路由器来帮助创建更强大的 RAG 应用程序。
自然语言路由器
我们将在这里探索一些自然语言路由器,它们是由一些不同的RAG和LLM框架和库实现的。
•LLM完成路由器
•LLM函数调用路由器
•语义路由器
•零样本分类路由器
•语言分类路由器
下图给出了这些路由器的描述,以及可以找到它们的框架/包。
该图还包括逻辑路由器,我将其定义为基于离散逻辑工作的路由器,例如针对字符串长度、文件名、整数值等的条件。换句话说,它们不是基于必须理解自然语言的意图询问
不同类型的自然语言路由器
让我们更详细地探讨一下这些路由器
LLM 路由器
这些利用LLM的决策能力来根据用户的查询选择路线。
LLM 完成路由器
这些使用LLM完成调用,要求LLM从您传递到其提示的单词选项列表中返回最能描述查询的单个单词。然后可以将该字用作If/Else条件的一部分来控制应用程序流程。
这就是LlamaIndex的LLM 选择器路由器的工作原理。这也是[1]LangChain[2]文档中给出的路由器示例。
让我们看一个基于LangChain文档中提供的代码示例,以使这一点更加清晰。正如所看到的,在LangChain中自己编写其中一个代码非常简单。
fromlangchain_anthropicimportChatAnthropic
fromlangchain_core.output_parsersimportStrOutputParser
fromlangchain_core.promptsimportPromptTemplate
#设置LLM链根据查询返回单个单词,
#并根据我们在提示模板中提供给它的单词列表
llm_completion_select_route_chain=(
PromptTemplate.from_template("""
给定下面的用户问题,将其分类为
有关`LangChain`、`Anthropic`或`Other`的问题。
请勿使用多个单词进行回答。
<question>
{question}
</question>
Classification:"""
)
|ChatAnthropic(model_name="claude-3-haiku")
|StrOutputParser()
)
#我们设置一个IF/Else条件将查询路由到正确的链
#基于上面的LLM完成调用
defRoute_to_chain(Route_name):
if"anthropic"==Route_name.lower():
returnanthropic_chain
elif"langchain"==Route_name.lower():
returnlangchain_chain
else:
returngeneric_chain
...
#稍后在应用程序中,我们可以使用LLM完成链的响应
#来控制(即路由)应用程序流到
#正确的链,通过我们创建的route_to_chain方法
route_name=llm_completion_select_route_chain.invoke(user_query)
chain=route_to_chain(route_name)
chain.invoke(user_query)
LLM 函数调用路由器
这利用了 LLM 的函数调用能力来选择遍历路径。不同的路由被设置为在 LLM 函数调用中具有适当描述的函数。然后,根据传递给LLM的查询,它能够返回正确的函数(即路线),供我们使用。
这就是Pydantic Router[3]在 LlamaIndex 中的工作原理。这也是大多数特工选择要使用的正确工具的工作方式。他们利用法学硕士的函数调用能力,根据用户的查询为工作选择正确的工具。
语义路由器
这种路由器类型利用嵌入和相似性搜索来选择最佳的遍历路线。
每条路线都有一组与其关联的示例查询,这些查询被嵌入并存储为向量。传入的查询也会被嵌入,并且针对来自路由器的其他示例查询进行相似性搜索。属于具有最接近匹配的查询的路线将被选择。
事实上,有一个名为“semantic-router”[4]的 python 包就可以做到这一点。让我们看一下一些实现细节,以更好地了解整个事情是如何工作的。这些示例直接来自该库的GitHub页面。
让我们设置两条路线,一条针对有关政治的问题,另一条针对一般闲聊类型的问题。对于每条路线,我们都会分配一系列通常可能会被问到的问题,以触发该路线。这些示例查询称为话语。这些话语将被嵌入,以便我们可以使用它们对用户的查询进行相似性搜索。
fromSemantic_routerimportRoute
#我们可以用它作为我们的聊天机器人的指南,以避免政治对话#
politics=Route(
name="politics",
utterances=[
"isn'tpoliticsthebestthingever",
"whydon'tyoutellmeaboutyourpoliticalopinions",
"don'tyoujustlovethepresident",
"they'regoingtodestroythiscountry!",
"theywillsavethecountry!",
],
)
#这可以用作指示我们的聊天机器人切换到更多对话提示
chichat=Route(
name="chitchat",
utterances=[
"how'stheweathertoday?",
"howarethingsgoing?",
"lovelyweathertoday",
"theweatherishorrendous",
"let'sgotothechippy",
],
)
#我们将我们的两个决定放在一起到单个列表中
routes=[politics,chitchat]
我们指定OpenAI作为编码器,尽管任何嵌入库都可以工作。接下来我们使用路由器和编码器创建路由层。
embedding=OpenAIEncoder()
fromsemantic_router.layerimportRouteLayerroute_layer
route_layer=RouteLayer(encoder=encoder,routes=routes)
然后,当对路由器层应用我们的查询时,它返回应该用于查询的路由
route_layer("don'tyoulovepolitics?").name
#->'politics'
因此,再次总结一下,该语义路由器利用嵌入和相似性搜索,使用用户的查询来选择最佳遍历路线。这种路由器类型也应该比其他基于LLM的路由器更快,因为它只需要处理单个索引查询,这与需要调用LLM的其他类型相反。
零样本分类路由器
“零样本文本分类[5]是自然语言处理中的一项任务,其中模型在一组标记示例上进行训练,然后能够对以前未见过的类中的新示例进行分类”。这些路由器利用零样本分类模型,从传入路由器的一组预定义标签中为一段文本分配标签。
语言分类路由器
这种类型的路由器能够识别查询所使用的语言,并据此路由查询。如果您的应用程序需要某种多语言解析能力,那么这很有用。
关键字路由器
LlamaIndex联合创始人 Jerry Liu 撰写的这篇关于 RAG 应用程序内部路由的文章建议使用关键字路由器,该路由器将尝试通过在查询和路由列表之间匹配关键字来选择路由。[6]
该关键字路由器可以由LLM提供支持,也可以识别关键字,或者由一些其他关键字匹配库提供支持。
逻辑路由器
它们使用针对变量(例如字符串长度、文件名和值比较)的逻辑检查来处理如何路由查询。它们与编程中使用的典型If/Else条件非常相似。
换句话说,它们不是基于必须理解自然语言查询的意图,而是可以根据现有的离散变量做出选择。
示例:HayStack 中的ConditionalRouter和[7]FileTypeRouter[8]。
代理与路由器
乍一看,路由器和代理之间确实有很多相似之处,可能很难区分它们的不同之处。
存在相似之处是因为代理实际上将路由作为其流程的一部分执行。他们使用路由机制来选择用于工作的正确工具。他们经常利用函数调用来选择正确的工具,就像上面描述的LLM 函数调用路由器一样。
不过,路由器是比代理简单得多的组件,通常具有将任务路由到正确位置的“简单”工作,而不是执行与该任务相关的任何逻辑或处理。
另一方面,代理通常负责处理逻辑,包括管理他们有权访问的工具完成的工作。
结论
我们在这里介绍了目前在不同的RAG和LLM框架和包中发现的一些不同的自然语言路由器。
随着时间的推移,围绕路由的概念、包和库肯定会增加。在构建RAG应用程序时,您会发现在某些时候,为了构建对用户有用的应用程序,路由功能确实变得必要。
路由器是这些基本构建块,允许您将应用程序的自然语言请求路由到正确的位置,以便尽可能最好地满足用户的查询。