大模型應(yīng)用與LUI(自然語言交互)落地的關(guān)鍵模塊——語義路由實(shí)現(xiàn)總結(jié)
在傳統(tǒng)搜索應(yīng)用中,有一個(gè)核心的模塊叫意圖識別,即識別用戶搜索請求時(shí)的意圖,基于意圖不同生成不同的Query或執(zhí)行不同的邏輯。在RAG應(yīng)用中,仍然存在著類似的路由模塊完成請求分發(fā)。這樣的路由分發(fā)需求無處不在,特別是在自然語言交互的應(yīng)用中,并且會隨著系統(tǒng)支持的場景和功能增多而變得越來越重要。所謂路由就是一個(gè)能夠根據(jù)一段自然語言輸入進(jìn)行意圖判斷形成離散輸出的模塊。
典型的場景比如智能客服中的指令任務(wù)分發(fā),硬盤搜索助手根據(jù)用戶需求搜索圖片還是搜索文件。下面是一些常見的情景的總結(jié)。
1)基于問題不同生成不同Prompt的情況,這和意圖識別生成不同Query一樣。
2)根據(jù)數(shù)據(jù)存儲的多樣性分發(fā)到不同數(shù)據(jù)存儲和服務(wù)中,比如數(shù)據(jù)庫、API等
3)即使存儲介質(zhì)相同,也可能存在多個(gè)需要分發(fā)的情況。
4)根據(jù)問題的類型分發(fā)到不同的組件。比如,根據(jù)問題的性質(zhì)將查詢分發(fā)給向量數(shù)據(jù)庫、Agent或服務(wù)等。
對于路由的實(shí)現(xiàn)大致分為兩類,邏輯路由(Logical Routers)和 自然語言路由。相較于自然語言路由,邏輯路由不依賴于對路由輸入的語義理解,而自然語言路由是需要關(guān)注語義的。
以下是這些路由的介紹:
1.LLM 路由
利用 LLM 的決策(decision making )能力根據(jù)用戶的查詢分發(fā)。
a.LLM生成路由
這類路由利用 LLM Completion接口實(shí)現(xiàn),要求 LLM 從提示的單詞選項(xiàng)列表中返回最能描述查詢的單個(gè)單詞。然后,該詞可以作為 If/Else 條件的一部分來控制應(yīng)用程序流程。
在llamaindex及LangChain都有這種思路的實(shí)現(xiàn)。下面是 LangChain 的一個(gè)使用例子。
from langchain_anthropic import ChatAnthropic
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
# Set up the LLM Chain to return a single word based on the query,
# and based on a list of words we provide to it in the prompt template
llm_completion_select_route_chain = (
PromptTemplate.from_template("""
Given the user question below, classify it as either
being about `LangChain`, `Anthropic`, or `Other`.
Do not respond with more than one word.
<question>
{question}
</question>
Classification:"""
)
| ChatAnthropic(model_name="claude-3-haiku")
| StrOutputParser()
)
# We setup an IF/Else condition to route the query to the correct chain
# based on the LLM completion call above
def route_to_chain(route_name):
if "anthropic" == route_name.lower():
return anthropic_chain
elif "langchain" == route_name.lower():
return langchain_chain
else:
return general_chain
...
# Later on in the application, we can use the response from the LLM
# completion chain to control (i.e route) the flow of the application
# to the correct chain via the route_to_chain method we created
route_name = llm_completion_select_route_chain.invoke(user_query)
chain = route_to_chain(route_name)
chain.invoke(user_query)
b.LLM 函數(shù)調(diào)用路由器
利用了 LLM 的function call能力來選擇要執(zhí)行的分支函數(shù)。
LlamaIndex中的Pydantic路由就是這個(gè)原理。大多數(shù)Agent選擇要使用的正確工具也是采用這樣的方式。它們利用 LLM 的函數(shù)調(diào)用能力,根據(jù)用戶的查詢選擇適合的工具。下面是Pydantic路由的使用例子:
from llama_index.core.query_engine import RouterQueryEngine
from llama_index.core.selectors import PydanticSingleSelector
from llama_index.core.selectors.pydantic_selectors import Pydantic
from llama_index.core.tools import QueryEngineTool
from llama_index.core import VectorStoreIndex, SummaryIndex
# define query engines
...
# initialize tools
list_tool = QueryEngineTool.from_defaults(
query_engine=list_query_engine,
descriptinotallow="Useful for summarization questions related to the data source",
)
vector_tool = QueryEngineTool.from_defaults(
query_engine=vector_query_engine,
descriptinotallow="Useful for retrieving specific context related to the data source",
)
# initialize router query engine (single selection, pydantic)
query_engine = RouterQueryEngine(
selector=PydanticSingleSelector.from_defaults(),
query_engine_tools=[
list_tool,
vector_tool,
],
)
query_engine.query("<query>")
2.語義路由
利用語義相關(guān)性檢索來選擇最佳的分支。
每個(gè)路由都有一組與之關(guān)聯(lián)的示例查詢,這些查詢會被embedding并存儲為向量。傳入的查詢也會被embedding,并針對路由器中的其他示例查詢進(jìn)行相似性搜索。匹配度最高的查詢的路由將被選中。
以semantic-router(https://github.com/aurelio-labs/semantic-router)這個(gè)項(xiàng)目為例了解其具體細(xì)節(jié)。例如,設(shè)置兩個(gè)路由,一個(gè)用于政治問題的問答,另一個(gè)用于一般閑聊類型的問答。對于每個(gè)路由都會分配一個(gè)通??赡鼙挥脕碛|發(fā)該路由分支的問題列表。這些示例查詢(utterances)將被embedding,以便可以將它們用于針對用戶查詢的相似性搜索。
from semantic_router import Route
# we could use this as a guide for our chatbot to avoid political
# conversations
politics = Route(
name="politics",
utterances=[
"isn't politics the best thing ever",
"why don't you tell me about your political opinions",
"don't you just love the president",
"they're going to destroy this country!",
"they will save the country!",
],
)
# this could be used as an indicator to our chatbot to switch to a more
# conversational prompt
chitchat = Route(
name="chitchat",
utterances=[
"how's the weather today?",
"how are things going?",
"lovely weather today",
"the weather is horrendous",
"let's go to the chippy",
],
)
# we place both of our decisions together into single list
routes = [politics, chitchat]
#創(chuàng)建路由層
encoder = OpenAIEncoder()
from semantic_router.layer import RouteLayer
route_layer = RouteLayer(encoder=encoder, routes=routes)
使用時(shí),輸入問題,便能獲得路由決策。
route_layer("don't you love politics?").name
# -> 'politics'
由于這種路由本質(zhì)上是向量檢索,無需調(diào)用LLM,因而比其他基于 LLM 的路由器更快。
3.零樣本文本分類路由
零樣本文本分類(Zero-shot text classification)是NLP中的一項(xiàng)任務(wù),其中模型在一個(gè)標(biāo)記樣本集上進(jìn)行訓(xùn)練,進(jìn)而獲得能夠?qū)碜韵惹拔匆娺^的樣本進(jìn)行分類,比如基于bert的分類器。
而這類路由便是利用零樣本分類模型給一段文本打上標(biāo)簽,而這些標(biāo)簽來自于預(yù)定義的標(biāo)簽路由。
比如,Haystack 中的 ZeroShotTextRouter便是這種實(shí)現(xiàn)路徑。具體參考:https://github.com/deepset-ai/haystack/blob/main/haystack/components/routers/zero_shot_text_router.py#L130
4.語言分類路由
這類路由器能夠識別查詢所使用的語言,并根據(jù)該語言路由查詢。如果應(yīng)用程序需要某種多語言解析能力,這將非常有用。
比如,Haystack 中的 TextClassificationRouter,它利用 langdetect 庫來檢測文本的語言,該庫本身使用樸素貝葉斯算法來檢測語言。參考:https://github.com/deepset-ai/haystack/blob/main/haystack/components/routers/text_language_router.py#L90
5.關(guān)鍵字路由
該類路由將嘗試通過匹配查詢和路由列表之間的關(guān)鍵字來選擇分支。
這個(gè)關(guān)鍵字路由器也可以由 LLM 來識別關(guān)鍵字,或者由其他一些關(guān)鍵字匹配庫來實(shí)現(xiàn)。
6.邏輯路由
它們使用邏輯檢查變量,例如字符串長度、文件名和值比較來處理如何路由查詢。它們與編程中使用的典型 If/Else 條件非常相似。它們不是基于必須理解自然語言查詢的意圖,而是可以根據(jù)現(xiàn)有和變量參數(shù)做出選擇。典型實(shí)現(xiàn)如HayStack 中的 ConditionalRouter 和 FileTypeRouter。
小結(jié)
不管是RAG應(yīng)用還是普通的業(yè)務(wù)系統(tǒng),都存在著大量的分支判斷,這種判斷早期由于自然語言技術(shù)的落后(準(zhǔn)確率和性能)導(dǎo)致大多數(shù)實(shí)現(xiàn)為語法判斷,而隨著LLM技術(shù)的發(fā)展,必然會帶動語義判斷和分發(fā)需求的大發(fā)展,另一方面,隨著自然語言交互(LUI)的不斷普及,路由模塊將成為其中核心實(shí)現(xiàn)受到更大重視。
參考:
??https://towardsdatascience.com/routing-in-rag-driven-applications-a685460a7220??
本文轉(zhuǎn)載自?? AI工程化??,作者: ully
