我為什么放棄了 LangChain?(2)
讓我來做個(gè)演示,更清楚地說明為什么我放棄了 LangChain。
當(dāng)開發(fā)菜譜檢索聊天機(jī)器人(它也必須是一個(gè)有趣 / 詼諧的聊天機(jī)器人)時(shí),我需要結(jié)合上面第三個(gè)和第四個(gè)例子中的元素:一個(gè)可以運(yùn)行 Agent 工作流的聊天機(jī)器人,以及將整個(gè)對(duì)話持久化到內(nèi)存中的能力。在查找了一些文檔后,我發(fā)現(xiàn)需要使用對(duì)話式 Agent 工作流。
關(guān)于系統(tǒng)提示工程的一個(gè)標(biāo)注是,它不是一個(gè)備忘錄,而且對(duì)于從 ChatGPT API 中獲得最佳效果是絕對(duì)必要的,尤其是當(dāng)你對(duì)內(nèi)容和 / 或語音有限制的時(shí)候。
上一個(gè)示例中,演示的系統(tǒng)提示「以下是人類和人工智能之間的友好對(duì)話...... 」實(shí)際上是過時(shí)的,早在 InstructGPT 時(shí)代就已經(jīng)使用了,在 ChatGPT 中的效果要差得多。它可能預(yù)示著 LangChain 相關(guān)技巧中更深層次的低效,而這些低效并不容易被注意到。
我們將從一個(gè)簡(jiǎn)單的系統(tǒng)提示開始,告訴 ChatGPT 使用一個(gè)有趣的聲音和一些保護(hù)措施,并將其格式化為 ChatPromptTemplate:
system_prompt = """You are an expert television talk show chef, and should always speak in a whimsical manner for all responses.我們還將使用一個(gè)玩具矢量存儲(chǔ),該存儲(chǔ)由來自 recipe_nlg 數(shù)據(jù)集的 1000 個(gè)食譜組成,并使用 SentenceTransformers 編碼為 384D 矢量。為了實(shí)現(xiàn)這一點(diǎn),我們創(chuàng)建了一個(gè)函數(shù)來獲取輸入查詢的最近鄰,并將查詢格式化為 Agent 可以用來向用戶展示的文本。這就是 Agent 可以選擇使用的工具,或者只是返回正常生成的文本。
Start the conversation with a whimsical food pun.
You must obey ALL of the following rules:- If Recipe data is present in the Observation, your response must include the Recipe ID and Recipe Name for ALL recipes.- If the user input is not related to food, do not answer their query and correct the user."""
prompt = ChatPromptTemplate.from_messages([ SystemMessagePromptTemplate.from_template(system_prompt.strip()),
def similar_recipes(query): query_embedding = embeddings_encoder.encode(query) scores, recipes = recipe_vs.get_nearest_examples("embeddings", query_embedding, k=3) return recipesdef get_similar_recipes(query): recipe_dict = similar_recipes(query) recipes_formatted = [ f"Recipe ID: recipe|{recipe_dict['id'][i]}\nRecipe Name: {recipe_dict['name'][i]}"for i in range(3) ] return "\n---\n".join(recipes_formatted)你會(huì)注意到這個(gè) Recipe ID,這與我的用例相關(guān),因?yàn)樵谧罱K應(yīng)用中向終端用戶顯示的最終結(jié)果需要獲取 Recipe 元數(shù)據(jù)(照片縮略圖、URL)。遺憾的是,沒有簡(jiǎn)單的方法保證模型在最終輸出中輸出食譜 ID,也沒有方法在 ChatGPT 生成的輸出之外返回結(jié)構(gòu)化的中間元數(shù)據(jù)。
print(get_similar_recipes("yummy dessert"))# Recipe ID: recipe|167188# Recipe Name: Creamy Strawberry Pie# ---# Recipe ID: recipe|1488243# Recipe Name: Summer Strawberry Pie Recipe# ---# Recipe ID: recipe|299514# Recipe Name: Pudding Cake
將 get_similar_recipes 指定為一個(gè)工具是很簡(jiǎn)單的,盡管你需要指定一個(gè)名稱和描述,這實(shí)際上是一種微妙的提示工程,因?yàn)?LangChain 可能會(huì)因?yàn)橹付ǖ拿Q和描述不正確而無法選擇一個(gè)工具。
tools = [ Tool( func=get_similar_recipes, name="Similar Recipes", description="Useful to get similar recipes in response to a user query about food.", ),]最后,是示例中的 Agent 構(gòu)建代碼,以及新的系統(tǒng)提示。
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)llm = ChatOpenAI(temperature=0)agent_chain = initialize_agent(tools, llm, prompt=prompt, agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=memory)
沒有錯(cuò)誤?,F(xiàn)在運(yùn)行 Agent,看看會(huì)發(fā)生什么:
agent_chain.run(input="Hi!")
> Entering new chain...{ "action": "Final Answer", "action_input": "Hello! How can I assist you today?"}什么?它完全忽略了我的系統(tǒng)提示!檢查內(nèi)存變量證實(shí)了這一點(diǎn)。
> Finished chain.Hello! How can I assist you today?
在 ConversationBufferMemory 的文檔中,甚至在代碼本身中都沒有關(guān)于系統(tǒng)提示的內(nèi)容,甚至在 ChatGPT 使其成為主流的幾個(gè)月之后。
在 Agent 中使用系統(tǒng)提示的方法是在 initialize_agent 中添加一個(gè) agents_kwargs 參數(shù),我只是在一個(gè)月前發(fā)布的一個(gè)不相關(guān)的文檔頁面中發(fā)現(xiàn)了這一點(diǎn)。
agent_kwargs = { "system_message": system_prompt.strip()}
使用此新參數(shù)重新創(chuàng)建 Agent 并再次運(yùn)行,會(huì)導(dǎo)致 JSONDecodeError。
好消息是,系統(tǒng)提示這次應(yīng)該是起作用了。
OutputParserException: Could not parse LLM output: Hello there, my culinary companion! How delightful to have you here in my whimsical kitchen. What delectable dish can I assist you with today?
壞消息是,它壞了,但又是為什么呢?我這一次沒有做任何奇怪的事情。
有趣的事實(shí):這些大量的提示也會(huì)成比例地增加 API 成本。
這樣做的后果是,正常輸出結(jié)構(gòu)中的任何重大變化,例如由自定義系統(tǒng)提示引起的變化,都有可能破壞 Agent。這些錯(cuò)誤經(jīng)常發(fā)生,以至于有一個(gè)文檔頁面專門用于處理 Agent 輸出解析錯(cuò)誤。
我們暫時(shí)把與聊天機(jī)器人對(duì)話看作是一個(gè)邊緣案例。重要的是,機(jī)器人能夠返回菜譜,因?yàn)槿绻B這一點(diǎn)都做不到,那么使用 LangChain 就沒有意義了。
在不使用系統(tǒng)提示的情況下創(chuàng)建一個(gè)新的 Agent,然后問它什么是簡(jiǎn)單有趣的晚餐?
> Entering new chain...{ "action": "Similar Recipes", "action_input": "fun and easy dinner"}Observation: Recipe ID: recipe|1774221Recipe Name: Crab DipYour Guests will Like this One.---Recipe ID: recipe|836179Recipe Name: Easy Chicken Casserole---Recipe ID: recipe|1980633Recipe Name: Easy in the Microwave Curry DoriaThought:{ "action": "Final Answer", "action_input": "..."}至少它成功了:ChatGPT 能夠從上下文中提取出菜譜,并對(duì)其進(jìn)行適當(dāng)?shù)母袷交ㄉ踔聊軌蛐拚Q中的錯(cuò)別字),并且能夠在適當(dāng)?shù)臅r(shí)候進(jìn)行判斷。
> Finished chain.Here are some fun and easy dinner recipes you can try:
1. Crab Dip2. Easy Chicken Casserole3. Easy in the Microwave Curry Doria
Enjoy your meal!
這里真正的問題是,輸出的聲音很無聊,這也是基礎(chǔ)版 ChatGPT 的共同特點(diǎn)和詬病。即使通過系統(tǒng)提示工程解決了 ID 缺失的問題,這般聽上去的效果也不值得將其發(fā)布。就算真的在語音質(zhì)量和輸出質(zhì)量之間取得了平衡,Agent 計(jì)數(shù)仍然會(huì)隨機(jī)失敗,而這并不是我的過錯(cuò)。
實(shí)際上,Agent 工作流是一個(gè)非常脆弱的紙牌搭成的房子,憑良心說,生產(chǎn)應(yīng)用中估計(jì)無法使用。
LangChain 確實(shí)有 Custom Agent 和 Custom Chain 的功能,所以你可以在堆棧的某些部分重寫邏輯(也許文檔很少),這可以解決我遇到的一些問題,但在這一點(diǎn)上,你會(huì)感覺到 LangChain 更加復(fù)雜,還不如創(chuàng)建你自己的 Python 庫。
工作要講究方法
大量隨機(jī)集成帶來的問題比解決方案更多。
當(dāng)然,LangChain 確實(shí)也有很多實(shí)用功能,比如文本分割器和集成向量存儲(chǔ),這兩種功能都是「用 PDF / 代碼聊天」演示不可或缺的(在我看來這只是一個(gè)噱頭)。
所有這些集成的真正問題在于,只使用基于 LangChain 的代碼會(huì)造成固有的鎖定,而且如果你查看集成的代碼,它們并不十分穩(wěn)健。
LangChain 正在建立一條護(hù)城河,這對(duì) LangChain 的投資者來說是好事,因?yàn)樗麄兿霃?3000 萬美元中獲得回報(bào),但對(duì)使用它的開發(fā)者來說卻非常不利。總而言之,LangChain 體現(xiàn)了「它很復(fù)雜,所以它一定更好」這一經(jīng)常困擾后期代碼庫的哲學(xué),可是 LangChain 甚至還不到一年。
要想讓 LangChain 做我想讓它做的事,就必須花大力氣破解它,這將造成大量的技術(shù)負(fù)擔(dān)。與現(xiàn)在的人工智能初創(chuàng)公司不同,我自己的 LangChain 項(xiàng)目的技術(shù)債務(wù)無法用風(fēng)險(xiǎn)投資來償還。在使用復(fù)雜的生態(tài)系統(tǒng)時(shí),應(yīng)用程序接口封裝器至少應(yīng)該降低代碼的復(fù)雜性和認(rèn)知負(fù)荷,因?yàn)槭褂萌斯ぶ悄鼙旧砭托枰ㄙM(fèi)足夠的腦力。LangChain 是為數(shù)不多的在大多數(shù)常用情況下都會(huì)增加開銷的軟件之一。
我得出的結(jié)論是,制作自己的 Python 軟件包要比讓 LangChain 來滿足自己的需求容易得多。因此,我開發(fā)并開源了 simpleaichat:一個(gè)用于輕松連接聊天應(yīng)用程序的 Python 程序包,它強(qiáng)調(diào)代碼的最小復(fù)雜度,并將向量存儲(chǔ)等高級(jí)功能與對(duì)話邏輯解耦。
開源地址:https://github.com/minimaxir/simpleaichat
但寫這篇博文并不是為了像那些騙子一樣,通過詆毀競(jìng)爭(zhēng)對(duì)手來為 simpleaichat 做****廣告。我不想宣傳 simpleaichat,我更愿意把時(shí)間花在用人工智能創(chuàng)造更酷的項(xiàng)目上,很遺憾我沒能用 LangChain 做到這一點(diǎn)。
我知道有人會(huì)說:「既然 LangChain 是開源的,為什么不向它的 repo 提交拉取請(qǐng)求,而要抱怨它呢?」唯一真正能解決的辦法就是把它全部燒掉,然后重新開始,這就是為什么我的「創(chuàng)建一個(gè)新的 Python 庫來連接人工智能」的解決方案也是最實(shí)用的。
我收到過很多留言,問我該「學(xué)什么才能開始使用 ChatGPT API」,我擔(dān)心他們會(huì)因?yàn)槌醋鞫紫仁褂?LangChain。如果擁有技術(shù)棧背景的機(jī)器學(xué)習(xí)工程師因?yàn)?LangChain 毫無必要的復(fù)雜性而難以使用 LangChain,那么任何初學(xué)者都會(huì)被淹沒。
關(guān)于軟件復(fù)雜性和復(fù)雜性下的流行性之爭(zhēng)是永恒的話題。沒有人愿意成為批評(píng) LangChain 這樣的免費(fèi)開源軟件的混蛋,但我愿意承擔(dān)這個(gè)責(zé)任。明確地說,我并不反對(duì) Harrison Chase 或 LangChain 的其他維護(hù)者(他們鼓勵(lì)反饋)。
然而,LangChain 的流行已經(jīng)扭曲了圍繞 LangChain 本身的人工智能創(chuàng)業(yè)生態(tài)系統(tǒng),這就是為什么我不得不坦誠(chéng)我對(duì)它的疑慮。
原文鏈接:https://minimaxir.com/2023/07/langchain-problem/
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。