上個(gè)月LangChain剛剛發(fā)布了正式的0.1穩(wěn)定版本(沒錯(cuò),是0.1而不是1.0),在版本公告里面首當(dāng)其沖宣布的最重要更新,是在這個(gè)版本里面引入了一個(gè)最新庫 - LangGraph。這是一個(gè)面向當(dāng)前LLM開發(fā)領(lǐng)域最火熱的AI Agent開發(fā)與控制的開發(fā)庫,也是LangChain試圖用來彌補(bǔ)其在Agent開發(fā)、特別是復(fù)雜的多Agent系統(tǒng)定制方面的不足的重大嘗試,相信也會(huì)成為LangChain在2024升級更新的重點(diǎn)領(lǐng)域! 我們會(huì)用一系列文章深入LangGraph,結(jié)合官方例子介紹與剖析其在幾個(gè)重點(diǎn)Agent方向的應(yīng)用。
由于官方文檔較為晦澀,加上LangChain一貫的“重量級”風(fēng)格。為了更好地幫助深入淺出的理解LangGraph,并照顧到?jīng)]有LangChain基礎(chǔ)的朋友,我們首先來了解一些“預(yù)備知識”。 PART 01 預(yù)備知識 【LangChain中的鏈與LCEL】 Chain(鏈)是LangChain中最核心的概念之一(看名字就知道)。簡單的說,就是把自然語言輸入、關(guān)聯(lián)知識檢索、Prompt組裝、可用Tools信息、大模型調(diào)用、輸出格式化等這些LLM 應(yīng)用中的常見動(dòng)作,組裝成一個(gè)可以運(yùn)行的“鏈”式過程。鏈可以直接調(diào)用,也可以用來進(jìn)一步構(gòu)建更強(qiáng)大的Agent。 LCEL即LangChain Express Language,即LangChain表達(dá)語言。這是LangChain提供的一種簡潔的、用于組裝上述“鏈”的聲明性方式。 我們看一個(gè)官方使用LCEL“組裝”Chain的例子就明白: prompt = ChatPromptTemplate.from_template('講一個(gè)關(guān)于 {topic} 的笑話') #調(diào)用chain 這個(gè)官方的例子中,把提示(prompt)、大模型(model)、輸出解析(output_parser)幾個(gè)組件使用管道符號“|”鏈接在一起,上個(gè)組件的輸出作為下一個(gè)組件的輸入,一起形成了一個(gè)鏈。 對于最常見的RAG應(yīng)用來說,使用LCEL也無非是在此之上增加一個(gè)檢索相關(guān)文檔的動(dòng)作,類似: chain = setup_and_retrieval | prompt | model | output_parser 這里很清晰地看到一個(gè)簡單的RAG應(yīng)用處理過程:檢索關(guān)聯(lián)文檔 => 組裝Prompt => 調(diào)用大模型 => 輸出處理。 最后總結(jié)一下:LCEL就是LangChain提供用來組裝Chain的一種簡單表示方式。用這種方式組裝鏈,可以自動(dòng)獲得諸如批量、流輸出、并行、異步等一系列能力;而且鏈可以進(jìn)一步通過LCEL組裝成更復(fù)雜的鏈與Agent。 【LCEL構(gòu)建與調(diào)度Agent】 那么如何用LCEL來創(chuàng)建一個(gè)AI Agent并調(diào)度運(yùn)行呢?以最常見的React(推理&行動(dòng))范式的Agent來說,相對于Chain需要擴(kuò)展的能力有:
以LCEL來組裝并創(chuàng)建運(yùn)行一個(gè)Agent的簡單過程如下: ''' 注意到,相對于Chain.invoke()直接運(yùn)行,這里的Agent_executor的作用就是為了能夠?qū)崿F(xiàn)多次循環(huán)ReAct的動(dòng)作,以最終完成任務(wù)。 【什么是圖(Graph)】 圖是計(jì)算機(jī)科學(xué)中的一種數(shù)據(jù)結(jié)構(gòu)。大部分人可能都接觸過一些基本的數(shù)據(jù)結(jié)構(gòu),比如隊(duì)列(Queue)、堆棧(Stack)、鏈表(List)或者樹(Tree)等,圖(Graph)也是其中的一種相對復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。我們無意在此普及圖的數(shù)據(jù)結(jié)構(gòu)知識,你只需要了解的圖的幾個(gè)基本知識:
PART 02 LangGraph的驅(qū)動(dòng)力 即然上文介紹的LCEL已經(jīng)很強(qiáng)大,但是為什么還需要LangGraph呢?基于LCEL構(gòu)建的Chain與Agent又存在哪些不足呢? * 鏈(Chain):無法滿足在循環(huán)中調(diào)用LLM以完成任務(wù)。 上文中,我們可以輕易地使用LCEL來快速創(chuàng)建一個(gè)鏈,但是很顯然的一個(gè)問題是:如果我們把鏈中的組件想象成Graph中的節(jié)點(diǎn),組件之間的聯(lián)系想象成Graph中的邊,那么這個(gè)鏈就是一個(gè)有向無環(huán)圖(DAG)。即在一次Chain運(yùn)行中,一個(gè)調(diào)用節(jié)點(diǎn)無法重復(fù)/循環(huán)進(jìn)入。 那么為什么需要將循環(huán)引入運(yùn)行時(shí)呢?考慮一個(gè)增強(qiáng)的RAG應(yīng)用: 在這個(gè)RAG應(yīng)用設(shè)計(jì)中,我們可以對語義檢索出來的關(guān)聯(lián)文檔(上下文)進(jìn)行評估:如果評估的文檔質(zhì)量很差,可以對檢索的問題進(jìn)行重寫(Rewrite,比如把輸入的問題結(jié)合對話歷史用更精確的方式來表達(dá)),并把重寫結(jié)果重新交給檢索器,檢索出新的關(guān)聯(lián)文檔,這樣有助于獲得更精確的結(jié)果。 這里把Rewrite的問題重新交給檢索器,就是一個(gè)典型的“循環(huán)”動(dòng)作。而在目前LangChain的簡單鏈中是無法支持的。 其他一些典型的依賴“循環(huán)”的場景包括:
* AgentExecutor:盡管支持“循環(huán)”,但缺乏精確控制能力。 那么,如果我們需要在循環(huán)中調(diào)用LLM能力,就需要借助于AgentExecutor。其調(diào)用的過程主要就是兩個(gè)步驟:
這里的AgentExecute存在的問題是:過于黑盒,所有的決策過程隱藏在AgentExecutor背后,缺乏更精細(xì)的控制能力,在構(gòu)建復(fù)雜Agent的時(shí)候受限。這些精細(xì)化的控制要求比如:
所以,讓我們簡單總結(jié)LangGraph誕生的動(dòng)力:LangChain簡單的鏈(Chain)不具備“循環(huán)”能力;而AgentExecutor調(diào)度的Agent運(yùn)行又過于“黑盒”。因此需要一個(gè)具備更精細(xì)控制能力的框架來支持更復(fù)雜場景的LLM應(yīng)用。 PART 03 LangGraph的設(shè)計(jì)思想 LangGraph并非一個(gè)獨(dú)立于Langchain的新框架,它是基于Langchain之上構(gòu)建的一個(gè)擴(kuò)展庫,可以與Langchain現(xiàn)有的鏈、LCEL等無縫協(xié)作。LangGraph能夠協(xié)調(diào)多個(gè)Chain、Agent、Tool等共同協(xié)作來完成輸入任務(wù),支持LLM調(diào)用“循環(huán)”以及Agent過程的更精細(xì)化的控制。 LangGraph的實(shí)現(xiàn)方式是把之前基于AgentExecutor的黑盒調(diào)用過程用一種新的形式來構(gòu)建:狀態(tài)圖(StateGraph)。把基于LLM的任務(wù)(比如RAG、代碼生成等)細(xì)節(jié)用Graph進(jìn)行精確的定義(定義圖的節(jié)點(diǎn)與邊),最后基于這個(gè)圖來編譯生成應(yīng)用;在任務(wù)運(yùn)行過程中,維持一個(gè)中央狀態(tài)對象(state),會(huì)根據(jù)節(jié)點(diǎn)的跳轉(zhuǎn)不斷更新,狀態(tài)包含的屬性可自行定義。 我們用官方的一個(gè)增強(qiáng)的RAG應(yīng)用的Graph來幫助理解: 這個(gè)Graph中體現(xiàn)了LangGraph的幾個(gè)基本概念:
在上圖中,推理函數(shù)調(diào)用、調(diào)用檢索器、生成響應(yīng)內(nèi)容、問題重寫等都是其中的任務(wù)節(jié)點(diǎn)。
在上圖中,Check Relevance就是一個(gè)條件邊,它的上游節(jié)點(diǎn)是檢索相關(guān)文檔,條件函數(shù)是判斷文檔是否相關(guān),如果相關(guān),則進(jìn)入下游節(jié)點(diǎn)【產(chǎn)生回答】;如果不相關(guān),則進(jìn)入下游節(jié)點(diǎn)【重寫輸入問題】。 在構(gòu)建好StateGraph,并增加Node和Edge后,可以通過compile編譯成可運(yùn)行的應(yīng)用: app = graph.compile() 接下來你就可以調(diào)用這個(gè)app來完成你的任務(wù)。 PART 04 LangGraph構(gòu)建基礎(chǔ)Agent 我們可以粗暴的認(rèn)為LangGraph就是把現(xiàn)在黑盒的AgentExecutor揉碎掰開,允許你定義內(nèi)部的細(xì)節(jié)結(jié)構(gòu)(用圖的方式),從而實(shí)現(xiàn)更強(qiáng)大的功能。那么我們當(dāng)然可以用LangGraph來重新實(shí)現(xiàn)原來的AgentExecutor,即實(shí)現(xiàn)一個(gè)最基礎(chǔ)的ReAct范式的Agent應(yīng)用。 對應(yīng)的Graph如下: 簡單的實(shí)現(xiàn)代碼如下(省略了部分細(xì)節(jié)):
代碼中的注釋對graph構(gòu)建的細(xì)節(jié)做了解釋。顯然,這要比簡單的使用agentExecutor要復(fù)雜的多,但同時(shí)也展示了LangGraph在構(gòu)建LLM應(yīng)用時(shí)強(qiáng)大的控制能力:通過Graph的定義,可以對一個(gè)LLM應(yīng)用的處理過程進(jìn)行非常細(xì)節(jié)的編排設(shè)計(jì),從而滿足大量復(fù)雜場景的AI Agent應(yīng)用。 由于LangGraph剛推出不久,一些細(xì)節(jié)與易用性在后期也會(huì)不斷完善。比如未來是否會(huì)提供更直觀的定義界面等,也值得期待。在后續(xù)的文章中,我們將逐漸實(shí)踐幾個(gè)代表性場景下的LangGraph的應(yīng)用,比如代碼助手,自省式RAG,多Agent應(yīng)用等,敬請期待。 END |
|