RAG實(shí)戰(zhàn)篇:精準(zhǔn)判斷用戶查詢意圖,自動選擇最佳處理方案

0 評論 823 瀏覽 1 收藏 9 分鐘

在人工智能領(lǐng)域,理解和準(zhǔn)確響應(yīng)用戶的查詢是構(gòu)建高效交互系統(tǒng)的關(guān)鍵。這篇文章將帶你深入了解如何通過高級查詢轉(zhuǎn)換技術(shù),優(yōu)化大型語言模型的理解能力,使其更貼近用戶的真正意圖。

《RAG實(shí)戰(zhàn)篇:構(gòu)建一個最小可行性的Rag系統(tǒng)》中,風(fēng)叔詳細(xì)介紹了Rag系統(tǒng)的整體實(shí)現(xiàn)框架,以及如何搭建一個最基本的Naive Rag。

在前面兩篇文章中,風(fēng)叔分別介紹了索引(Indexing)和查詢轉(zhuǎn)換(Query Translation)環(huán)節(jié)的優(yōu)化方案。

在這篇文章中,圍繞Routing(路由)環(huán)節(jié),如下圖橙色框內(nèi)所示,風(fēng)叔詳細(xì)介紹一下面對不同的用戶輸入,如何讓大模型更智能地路由到最佳方案。

路由的作用,是為每個Query選擇最合適的處理管道,以及依據(jù)來自模型的輸入或補(bǔ)充的元數(shù)據(jù),來確定將啟用哪些模塊。比如當(dāng)用戶的輸入問題涉及到跨文檔檢索、或者對于復(fù)雜文檔構(gòu)建了多級索引時,就需要使用路由機(jī)制。

下面,我們結(jié)合源代碼,介紹一下Logical routing(基于邏輯的路由)和Sematic Routing(基于語義的路由)兩種方案。

一、Logical routing(基于邏輯的路由)

基于邏輯的路由,其原理非常簡單。大模型接收到問題之后,根據(jù)決策步驟,去選擇正確的索引數(shù)據(jù)庫,比如圖數(shù)據(jù)庫、向量數(shù)據(jù)庫等等,如下圖所示。

其使用函數(shù)調(diào)用(function calling)來產(chǎn)生結(jié)構(gòu)化輸出。

下面我們結(jié)合源代碼來分析一下Logical Routing的流程:

  • 首先我們定義了三種文檔,pytion、js、golang
  • 然后通過prompt告訴大模型,需要根據(jù)所涉及的編程語言,將用戶問題路由到適當(dāng)?shù)臄?shù)據(jù)源
  • 定義Router
# Data model
class RouteQuery(BaseModel):
    """Route a user query to the most relevant datasource."""
    datasource: Literal["python_docs", "js_docs", "golang_docs"] = Field(
        ...,
        description="Given a user question choose which datasource would be most relevant for answering their question",
    )
# LLM with function call 
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm = llm.with_structured_output(RouteQuery)
# Prompt 
system = """You are an expert at routing a user question to the appropriate data source.
Based on the programming language the question is referring to, route it to the relevant data source."""
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "{question}"),
    ])
# Define router 
router = prompt | structured_llm

接著給出了一個使用示例,用戶提問后,路由器根據(jù)問題的內(nèi)容判斷出數(shù)據(jù)源為 python_docs,并返回了相應(yīng)的結(jié)果。

question = """Why doesn't the following code work:
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages(["human", "speak in {language}"])
prompt.invoke("french")
"""
result = router.invoke({"question": question})
result.datasource
def choose_route(result):
    if "python_docs" in result.datasource.lower():
        ### Logic here 
        return "chain for python_docs"
    elif "js_docs" in result.datasource.lower():
        ### Logic here 
        return "chain for js_docs"
    else:
        ### Logic here 
        return "golang_docs"
from langchain_core.runnables import RunnableLambda
full_chain = router | RunnableLambda(choose_route)
full_chain.invoke({"question": question})

二、Sematicrouting(基于語義的路由)

基于語義的路由,其原理也非常簡單,大模型根據(jù)query的語義相似度,去自動配置不同的prompt。

我們先定義兩種不同的Prompt,一個讓大模型扮演物理專家,一個讓大模型扮演數(shù)學(xué)專家,并將其轉(zhuǎn)為嵌入向量。

# Two prompts
physics_template = """You are a very smart physics professor. 
You are great at answering questions about physics in a concise and easy to understand manner. 
When you don't know the answer to a question you admit that you don't know.
Here is a question:
{query}"""

 

math_template = """You are a very good mathematician. You are great at answering math questions. 
You are so good because you are able to break down hard problems into their component parts, 
answer the component parts, and then put them together to answer the broader question.
Here is a question:
{query}"""

 

embeddings = OpenAIEmbeddings()
prompt_templates = [physics_template, math_template]
prompt_embeddings = embeddings.embed_documents(prompt_templates)

然后計算query embedding和prompt embedding的向量相似度

# Route question to prompt 
def prompt_router(input):
    # Embed question
    query_embedding = embeddings.embed_query(input["query"])
    # Compute similarity
    similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]
    most_similar = prompt_templates[similarity.argmax()]
    # Chosen prompt 
    print("Using MATH" if most_similar == math_template else "Using PHYSICS")
    return PromptTemplate.from_template(most_similar)

 

chain = (
    {"query": RunnablePassthrough()}
    | RunnableLambda(prompt_router)
    | ChatOpenAI()
    | StrOutputParser()
)
print(chain.invoke("What's a black hole"))

在上述案例中,最終的輸出會使用物理專家的Prompt。

到這里,兩種常用的路由策略就介紹完了。當(dāng)然,我們也可以自主構(gòu)建更復(fù)雜的路由策略,比如構(gòu)建專門的分類器、打分器等等,這里就不詳細(xì)展開了。

三、總結(jié)

在這篇文章中,風(fēng)叔介紹了實(shí)現(xiàn)查詢路由的具體方法,包括Logical routing和Semantic routing兩種實(shí)現(xiàn)方式。

很多時候,在一些特殊的場景下,我們需要將用戶的輸入轉(zhuǎn)化為特定的語句,比如數(shù)據(jù)庫查詢動作。在下一篇文章中,風(fēng)叔將重點(diǎn)圍繞Query Construction(查詢構(gòu)建)環(huán)節(jié),介紹如何將用戶輸入轉(zhuǎn)變?yōu)樘囟ǖ南到y(tǒng)執(zhí)行語句。

本文由人人都是產(chǎn)品經(jīng)理作者【風(fēng)叔】,微信公眾號:【風(fēng)叔云】,原創(chuàng)/授權(quán) 發(fā)布于人人都是產(chǎn)品經(jīng)理,未經(jīng)許可,禁止轉(zhuǎn)載。

題圖來自 Pixabay,基于 CC0 協(xié)議。

更多精彩內(nèi)容,請關(guān)注人人都是產(chǎn)品經(jīng)理微信公眾號或下載App
評論
評論請登錄
  1. 目前還沒評論,等你發(fā)揮!