在第18和19天,我們向大家介紹了LangChain中的幾個核心執行鏈,例如SequentialChain和TransformChain。今天,我們將重點放在一種之前未提及,但在實作中非常關鍵的執行鏈:路由鏈(RouterChain)。
在LangChain框架裡,路由鏈是一個抽象的概念,其主要目的是根據特定的輸入,動態地選擇接下來應該執行的鏈路。這個概念大致上分為兩部分:第一部分是RouterChain本體,負責決定接下來呼叫哪個鏈;而第二部分是目標執行鏈(destination_chains),這些鏈路是RouterChain可以選擇指向的目的地。當RouterChain無法決定哪一個目標執行鏈最適合處理某個輸入時,它將預設轉交給一個特定的執行鏈進行處理。
此外,LangChain框架內部已經內建了一個路由鏈的實作類別,稱作MultiPromptChain。當我們利用MultiPromptChain來實現路由選擇功能時,其整體的訊息處理流程大致如下:
要理解 MultiPromptChain,首先我們需要認識到什麼是 Router Chain。簡單來說,Router Chain 的角色就像是一個交通指揮,它會根據設定決定哪個執行鏈(chain)是最適合回覆使用者目前訊息的。例如,如果有個用戶提出詞彙學習的問題,Router Chain 就會判斷並將這問題導向「詞彙教學」的執行鏈來回答;同理,如果是關於例句的使用和學習問題,則會導向「例句教學」的執行鏈。
當 Router Chain 分析並決定好該用哪個執行鏈時,MultiPromptChain 會將使用者的訊息交由該執行鏈處理。經過該執行鏈的處理後,我們會得到一個適切的回覆,最後這個回覆就會直接呈現給使用者。
下面,我們會詳細介紹 Router Chain 的運作原理。
要使用 MultiPromptChain,首先需要準備 RouterChain。這裏,LangChain 提供了一個內建的 Router Chain。只要定義好 destinations(描述各個執行鏈的名稱及功能),就可以透過 MULTI_PROMPT_ROUTER_TEMPLATE
建立 Router Chain。
首先,我們定義了兩種教學模式的模板:
lex_teaching_template = """您是詞彙教學的專家。\
您精通各種語言的詞彙,並能夠提供詳細的解釋和背景知識。\
當學生想知道某個單詞的意思或用法時,您總是能夠給予清晰且有趣的答案。
這裡有一個問題:
{input}"""
sentence_teaching_template = """您擅長例句教學。\
您知道如何使用例句來解釋語言中的規則和結構。\
當學生對於如何在實際情境中使用某個語句或表達有疑問時,您能夠提供生動且實用的例子。
這裡有一個問題:
{input}"""
此結構存放了所有的任務執行鏈詳細資訊:
prompt_infos = [
{
"name": "lex_teaching", # 執行鏈名稱
"description": "適合回答與詞彙學習相關的問題", # 執行鏈簡單描述
"prompt_template": lex_teaching_template, # 實際任務的提示樣板
},
{
"name": "sentence_teaching", # 執行鏈名稱
"description": "適合回答與例句使用和學習相關的問題", # 執行鏈簡單描述
"prompt_template": sentence_teaching_template , # 實際任務的提示樣板
},
]
擁有上述資訊後,接著使用 MULTI_PROMPT_ROUTER_TEMPLATE
來建立 LLMRouterChain:
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
# 使用任務基本資訊以及 MULTI_PROMPT_ROUTER_TEMPLATE 建立 LLMRouterChain 實例
destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
destinations_str = "\n".join(destinations)
# 這裏使用 MULTI_PROMPT_ROUTER_TEMPLATE 格式化我們的提示訊息
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)
# 建立實例
router_prompt = PromptTemplate(
template=router_template,
input_variables=["input"],
output_parser=RouterOutputParser(),
)
router_chain = LLMRouterChain.from_llm(llm_chat, router_prompt)
接著,我們可以建立實際的執行鏈以及找不到對應時所使用的預設執行鏈:
# 建立任務的執行鏈實例,以及「名稱-實例對照表」
destination_chains = {}
for p_info in prompt_infos:
name = p_info["name"]
prompt_template = p_info["prompt_template"]
prompt = PromptTemplate(template=prompt_template, input_variables=["input"])
chain = LLMChain(llm=llm_chat, prompt=prompt)
destination_chains[name] = chain
# 建立預設執行鏈
default_chain = ConversationChain(llm=llm_chat, output_key="text")
有了所有的準備工作,最後就可以建立 MultiPromptChain
:
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=default_chain,
verbose=True,
)
在我們完整設定建立好 MultiPromptChain 後,我們來進行一些實際的測試案例,以驗證我們的 MultiPromptChain 是否能夠正確地進行路由判斷並給出相對應的回應。
在這個測試中,我們試著詢問一個特定的詞彙的意思,看看我們的 router chain 是否會正確地路由到 lex_teaching:
chain.run("coding 這字什麼意思?")
--- 實際的輸出結果 ---
> Entering new MultiPromptChain chain...
lex_teaching: {'input': 'coding 這字什麼意思?'}
> Finished chain.
coding 是一個英文單詞,它有多種不同的意思和用法。以下是其中一些常見的解釋:...(省略)
結果顯示,當輸入涉及詞彙的查詢時,MultiPromptChain
成功地將其路由到lex_teaching
,並給出相應的解釋。這證明我們的設定在這方面是正確的。
在這次的測試中,我們詢問一個英文句子的意思和用法,目的是確保我們的 chain 能夠正確地判斷並路由到 sentence_teaching:
chain.run("幫我解釋「I like to code」這句話")
--- 實際的輸出結果 ---
> Entering new MultiPromptChain chain...
/usr/local/lib/python3.10/dist-packages/langchain/chains/llm.py:280: UserWarning: The predict_and_parse method is deprecated, instead pass an output parser directly to LLMChain.
warnings.warn(
sentence_teaching: {'input': '幫我解釋「I like to code」這句話'}
> Finished chain.
「I like to code」這句話是一個簡單的英文句子,用來表達「我喜歡編程」的意思。讓我們來分解這個句子的結構和規則:...(省略)
從這次的測試結果中,我們可以看出**MultiPromptChain
當處理到與句子相關的查詢時,正確地將其路由到sentence_teaching
**。這表示對於句子的解釋和用法,我們的chain能夠給出恰當的回應。
最後,我們嘗試一個不符合任何特定任務的訊息,確認 router chain 如何處理此類訊息:
chain.run("你好")
--- 實際的輸出結果 ---
> Entering new MultiPromptChain chain...
None: {'input': '你好'}
> Finished chain.
你好!很高兴和你聊天。有什么我可以帮助你的吗?
在這個測試中,雖然輸入的訊息不符合任何特定的任務,但**MultiPromptChain
**仍然給出了友好的回應,這顯示了其對於一般對話的適應性和靈活性。
今天,我們聚焦於LangChain中的「路由鏈」功能,並一同深入了解了它的工作原理和實際應用。至此,在這系列中關於「LangChain」的基礎元件部分,我們已經完成了一個階段性的學習。你可以將LangChain想像為構建聊天機器人的一套樂高積木,每一塊都有其特定的功能,而今天我們學到的「路由鏈」就是其中一塊關鍵的積木。有了這些積木,我們現在已經有足夠的工具可以開始組裝成一個具有完整功能的機器人了。
在後續的文章中,我們不只是告訴大家理論,更會實際操作,手把手地指導大家如何將所學知識結合起來,一步步建立屬於自己的聊天機器人,真正體驗LangChain的強大之處。
對於本文介紹的「路由鏈」功能,如果大家希望進一步查看相關的程式碼,您可以參考這裏: D27. LangChain 專案實做 - 路由鏈介紹.ipynb。期待在下一篇再次與大家見面!