在LLM應用的上下文中,記憶系統允許應用程式儲存使用者互動的參考,無論是短期還是長期。以知名的ChatGPT為例,在與其互動時,你可以提出後續問題而無需明確提及之前的對話內容,系統會自動理解上下文。
此外,所有對話都被儲存在特定的對話串中,如果你想繼續之前的對話,可以重新開啟對話串而無需重新提供所有上下文。這是因為ChatGPT能夠將使用者的互動儲存到記憶變數中,並在處理後續問題時將這些記憶作為上下文使用。
LangChain提供了多種模組來設計應用中的記憶系統,讓它同時具備讀取和寫入能力。
記憶系統的設計與實作
設計記憶系統的第一步是將人機互動實際儲存到某個地方。為此,你可以利用LangChain內建的多種記憶整合,包括Redis、Cassandra和Postgres等第三方提供商。
當涉及到如何查詢記憶系統時,LangChain提供了各種記憶型別:
對話緩衝記憶 (Conversation Buffer Memory)
這是LangChain中最基本的記憶型別,它允許你儲存聊天訊息並將它們提取到變數中。缺點是隨著對話進行,儲存的訊息會不斷增加,可能導致上下文長度超出模型限制。
對話緩衝視窗記憶 (Conversation Buffer Window Memory)
與對話緩衝記憶相同,但增加了一個滑動視窗功能,只保留最近的K次互動,這樣可以更好地管理長時間的聊天歷史。這種方法適合需要較長對話但又不想超出上下文限制的場景。
實體記憶 (Entity Memory)
實體記憶是LangChain的一個特性,它允許語言模型記住關於特定實體的事實。實體可以是人、地點、事物或概念。例如,在"Deven和Sam正在義大利參加駭客松"這句話中,Deven和Sam是實體(人),駭客松是實體(事物),義大利是實體(地點)。
實體記憶透過使用LLM從輸入文字中提取實體訊息來工作。它透過將提取的事實儲存在記憶函式庫中,隨著時間的推移建立對該實體的知識。當語言模型需要回憶或學習關於實體的新訊息時,可以存取和更新這個記憶函式庫。
對話知識圖譜記憶 (Conversation Knowledge Graph Memory)
這種記憶型別使用知識圖譜來重建記憶。知識圖譜是一種以圖形結構表示和組織知識的方式,其中節點是實體,邊是它們之間的關係。知識圖譜可以儲存和整合來自各種來源的資料,並對資料進行語義和上下文編碼。
一個知識圖譜的例子是DBpedia,這是一個社群專案,它從維基百科中提取結構化資料並在網路上提供。DBpedia涵蓋地理、音樂、體育和電影等主題,並提供與GeoNames和WordNet等其他資料集的連結。
你可以使用這種記憶型別將每個對話回合的輸入和輸出儲存為知識三元組(主體、謂詞和客體),然後根據當前上下文生成相關與一致的回應。你還可以查詢知識圖譜以取得當前實體或對話歷史。
對話摘要記憶 (Conversation Summary Memory)
當需要儲存較長對話時,這種記憶型別非常有用,因為它會隨著時間建立對話的摘要(利用LLM)。這種方法可以在保持上下文的同時大幅減少所需的token數量。
對話摘要緩衝記憶 (Conversation Summary Buffer Memory)
這種記憶型別結合了緩衝記憶和對話摘要記憶的理念。它在記憶中保留最近互動的緩衝區,但不是完全重新整理舊的互動(如對話緩衝記憶那樣),而是將它們編譯成摘要並同時使用兩者。
對話標記緩衝記憶 (Conversation Token Buffer Memory)
與前一種類別似,但不同之處在於,為了確定何時開始總結互動,這種記憶型別使用標記長度而不是互動次數(如摘要緩衝記憶中那樣)。
向量儲存支援的記憶 (Vector Store-Backed Memory)
這種記憶型別利用了前面介紹的嵌入和向量儲存的概念。它與所有前面的記憶型別不同,因為它將互動儲存為向量,然後每次查詢時使用檢索器檢索最相似的K個文字。這種方法特別適合處理非線性對話,可以根據當前問題找到最相關的歷史對話片段。
記憶系統的實際應用
LangChain為每種記憶型別提供了特定的模組。讓我們看一個使用對話摘要記憶的例子,我們還需要一個LLM來生成互動摘要:
from langchain.memory import ConversationSummaryMemory, ChatMessageHistory
from langchain.llms import OpenAI
# 初始化對話摘要記憶
memory = ConversationSummaryMemory(llm=OpenAI(temperature=0))
# 儲存對話上下文
memory.save_context(
{"input": "hi, I'm looking for some ideas to write an essay in AI"},
{"output": "hello, what about writing on LLMs?"}
)
# 載入記憶變數
memory.load_memory_variables({})
在上面的程式碼中,我們建立了一個ConversationSummaryMemory
例項,並將OpenAI的LLM作為摘要生成器(設定temperature=0以獲得確定性輸出)。然後,我們使用save_context
方法儲存了一對話(使用者輸入和系統回應)。
當我們呼叫load_memory_variables({})
時,記憶系統會使用LLM將已儲存的對話總結為摘要。輸出結果是:
{'history': '\nThe human asked for ideas to write an essay in AI and the AI suggested writing on LLMs.'}
可以看到,記憶系統利用OpenAI的LLM對話進行了摘要。這種方法在處理長對話時特別有用,因為它可以減少所需的token數量,同時保留關鍵訊息。
沒有固定的公式來定義在應用中使用哪種記憶,但某些場景可能特別適合特定的記憶型別:
- 知識圖譜記憶適用於需要從大型多樣化資料集存取訊息並根據語義關係生成回應的應用
- 對話摘要緩衝記憶適合建立能夠在多個回合中保持一致上下文的對話代理,同時也能壓縮和總結之前的對話歷史
鏈:構建複雜LLM應用的關鍵元件
鏈(Chains)是預先確定的操作和LLM呼叫序列,使構建需要將LLM與其他元件組合的複雜應用變得更加容易。這種元件化設計是LangChain的核心優勢之一。
LangChain提供了四種主要型別的鏈:
LLMChain:最常見的鏈型別
LLMChain是最常見的鏈型別,它由提示範本(PromptTemplate)、LLM和可選的輸出解析器(OutputParser)組成。
輸出解析器是幫助結構化語言模型回應的元件。它是一個實作兩個主要方法的類別:get_format_instructions
和parse
。get_format_instructions
方法回傳一個包含語言模型輸出格式說明的字元串。parse
方法接收一個字元串(假定是語言模型的回應)並將其解析為某種結構,如字典、列表或自定義物件。
這種連結收多個輸入變數,使用PromptTemplate將它們格式化為提示,將其傳遞給模型,然後使用OutputParser(如果提供)將LLM的輸出解析為最終格式。
讓我們以前面建立的提示範本為例:
from langchain import PromptTemplate
# 建立提示範本
template = """Sentence: {sentence} Translation in {language}:"""
prompt = PromptTemplate(template=template, input_variables=["sentence", "language"])
# 將提示範本放入LLMChain
from langchain import OpenAI, LLMChain
llm = OpenAI(temperature=0)
llm_chain = LLMChain(prompt=prompt, llm=llm)
# 使用鏈進行翻譯
llm_chain.predict(sentence="the cat is on the table", language="spanish")
在這個例子中,我們首先建立了一個用於翻譯的提示範本,它有兩個輸入變數:sentence
(要翻譯的句子)和language
(目標語言)。
然後,我們使用OpenAI的LLM和這個提示範本建立了一個LLMChain。當我們呼叫llm_chain.predict()
方法並傳入具體的句子和目標語言時,LLMChain會:
- 使用提示範本將輸入變數格式
LangChain進階技術:從鏈到代理人的應用實踐
LangChain提供了多種強大工具來構建複雜的語言模型應用。在這篇文章中,玄貓將探討如何利用LangChain的進階功能來處理不同型別的使用者請求,以及如何將多個處理步驟組合成一個流暢的工作流程。
多重提示鏈:處理不同型別的使用者請求
多重提示鏈(MultiPromptChain)是一種能夠根據使用者輸入自動選擇最適合的提示範本的技術。這在需要處理多種不同型別請求的應用中特別有用。
例如,如果希望建立一個能夠處理行程規劃和餐廳預訂兩種不同請求的聊天機器人,可以為每種情境定義專門的提示範本:
itinerary_template = """You are a vacation itinerary assistant. \
You help customers finding the best destinations and itinerary. \
You help customers creating an optimized itinerary based on their preferences.
Here is a question: {input}"""
restaurant_template = """You are a restaurant booking assistant. \
You check with customers number of guests and food preferences. \
You pay attention whether there are special conditions to take into account.
Here is a question: {input}"""
這段程式碼定義了兩個不同的提示範本,分別針對旅行程助手和餐廳預訂助手。每個範本都包含角色定義(身份和職責)以及輸入格式。{input}
是一個佔位符,會在執行時被實際使用者問題替換。這種範本設計方式使AI能夠扮演特定角色,並在特定情境下提供專業回應。
透過RouterChain,系統能夠根據使用者查詢的內容自動選擇最合適的提示範本。讓我們看這個系統如何處理兩種不同型別的查詢:
當使用者詢問:“我正計劃從米蘭開車到威尼斯。中途可以參觀什麼景點?“系統會選擇行程範本並產生如下回應:
> 進入新的MultiPromptChain鏈...
itinerary: {'input': "我正計劃從米蘭開車到威尼斯。中途可以參觀什麼景點?"}
> 鏈結束。
回答:
從米蘭開車到威尼斯的途中有許多值得參觀的景點。一些最受歡迎的景點包括科莫湖、維羅納、多洛米蒂山脈,以及風景如畫的貝加莫和佈雷西亞小鎮。你還可以參觀曼圖亞和費拉拉的聯合國教科文組織世界遺產。此外,你還可以探索當地的葡萄酒莊,品嚐該地區的葡萄酒。
而當使用者輸入:“我想預訂今晚的餐桌"時,系統則會選擇餐廳預訂範本:
> 進入新的MultiPromptChain鏈...
restaurant: {'input': '我想預訂今晚的餐桌'}
> 鏈結束。
您好!今晚的預訂您一共有多少位客人?
序列鏈:按順序執行多個處理步驟
序列鏈(SequentialChain)允許按特定順序執行多個鏈,並將前一個鏈的輸出作為下一個鏈的輸入。這在需要多步處理的場景中非常有用。
讓我們實作一個先產生特定主題笑話,然後將其翻譯成另一種語言的AI系統:
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
llm = OpenAI(temperature=.7)
template = """You are a comedian. Generate a joke on the following
{topic}
Joke:"""
prompt_template = PromptTemplate(input_variables=["topic"],
template=template)
joke_chain = LLMChain(llm=llm, prompt=prompt_template)
template = """You are translator. Given a text input, translate it to
{language}
Translation:"""
prompt_template = PromptTemplate(input_variables=["language"],
template=template)
translator_chain = LLMChain(llm=llm, prompt=prompt_template)
# 將兩個鏈結合成序列
from langchain.chains import SimpleSequentialChain
overall_chain = SimpleSequentialChain(chains=[joke_chain, translator_chain], verbose=True)
translated_joke = overall_chain.run("Cats and Dogs")
這個例子展示瞭如何建立兩個獨立的處理鏈,然後將它們組合成一個序列。第一個鏈(joke_chain)接收主題並生成相關笑話,第二個鏈(translator_chain)接收第一個鏈的輸出並將其翻譯。
SimpleSequentialChain
類別負責將這些鏈連線起來,verbose=True
引數讓我們能看到中間過程。這種方法允許將複雜任務分解為更小、更專注的步驟,每個步驟由專門的AI模型處理。
執行結果:
> 進入新的SimpleSequentialChain鏈...
Why did the cat cross the road? To prove to the dog that it could be done!
¿Por qué cruzó el gato la carretera? ¡Para demostrarle al perro que se podía hacer!
> 鏈結束。
轉換鏈:處理和轉換資料
轉換鏈(TransformationChain)允許在處理過程中使用函式或表示式來轉換輸入變數或其他鏈的輸出。這種鏈適用於需要在文書處理前後進行特定修改的場景。
例如,假設我們想要總結一個故事,但首先要將故事中的一隻貓重新命名為"Silvester the Cat”:
from langchain.chains import TransformChain, LLMChain, SimpleSequentialChain
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
transform_chain = TransformChain(
input_variables=["text"], output_variables=["output_text"],
transform=rename_cat )
template = """Summarize this text:
{output_text}
Summary:"""
prompt = PromptTemplate(input_variables=["output_text"],
template=template)
llm_chain = LLMChain(llm=OpenAI(), prompt=prompt)
sequential_chain = SimpleSequentialChain(chains=[transform_chain, llm_chain])
sequential_chain.run(cats_and_dogs)
在這個例子中,我們首先建立了一個轉換鏈,它使用rename_cat
函式(程式碼中未顯示)將文字中所有提到的貓重新命名為"Silvester the Cat”。然後,我們建立一個LLM鏈來總結轉換後的文字。最後,我們使用SimpleSequentialChain
將這兩個鏈串聯起來。
這種方法的優勢是可以在AI處理前後對資料進行自定義處理,比如格式化、清理或修改特定內容,使整個處理流程更加靈活。
輸出結果:
"Silvester the Cat和一隻狗住在一起但相處不好。Silvester the Cat對狗玩了個惡作劇,這讓狗很生氣。
當他們的主人發現他們在打架時,她責罵了他們並讓他們道歉。之後,他們成為了朋友,學會尊重彼此的不同並欣賞彼此的優點。"
總的來說,LangChain的鏈是一種強大的工具,能夠將不同的語言模型和任務組合成單一的工作流程。這些鏈靈活、可擴充套件與易於使用,使用者能夠利用語言模型的強大功能應用於各種目的和領域。
LangChain代理人:人工智慧決策的核心
代理人(Agents)是驅動LLM應用程式中決策過程的實體。它們可以存取一系列工具,並根據使用者輸入和上下文決定呼叫哪個工具。相較於鏈中硬編碼的動作序列,代理人能夠動態適應不同情況,使用LLM作為推理引擎來規劃和執行正確的動作順序。
代理人與工具的核心概念
談到代理人時,工具(Tools)是一個核心概念。代理人可能擅長規劃滿足使用者查詢所需的正確動作,但如果缺乏執行這些動作的訊息或能力,就無法實際完成任務。
例如,假設我想構建一個能夠透過搜尋網路來回答問題的代理人。代理人本身無法存取網路,所以需要提供這個工具。在LangChain中,可以使用SerpAPI(Google搜尋API)整合來實作:
from langchain import SerpAPIWrapper
from langchain.agents import AgentType, initialize_agent
from langchain.llms import OpenAI
from langchain.tools import BaseTool, StructuredTool, Tool, tool
import os
from dotenv import load_dotenv
load_dotenv()
os.environ["SERPAPI_API_KEY"]
search = SerpAPIWrapper()
tools = [Tool.from_function(
func=search.run,
name="Search",
description="useful for when you need to answer questions about current events")]
agent = initialize_agent(tools, llm=OpenAI(), agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run("When was Avatar 2 released?")
這段程式碼演示瞭如何建立一個具有網路搜尋能力的代理人。首先,我們匯入必要的函式庫並載入環境變數。然後,我們建立一個SerpAPIWrapper例項,它提供了Google搜尋功能。接著,我們定義了一個工具列表,只包含一個搜尋工具,並說明瞭它的用途。
最後,我們使用initialize_agent
函式初始化代理人,提供工具列表、語言模型(OpenAI)和代理人型別。AgentType.ZERO_SHOT_REACT_DESCRIPTION
指定了代理人使用"反應"思考方式,即先考慮可用工具,然後決定使用哪個工具,最後執行並觀察結果。
當我們執行這個代理人並詢問"阿凡達2什麼時候上映?“時,代理人會使用搜尋工具查詢相關訊息,然後提供答案。
這種方法的強大之處在於代理人可以根據問題自主決定使用哪些工具以及如何使用它們。與固定流程的鏈不同,代理人能夠根據情境進行調整,處理更複雜、更開放式的任務。
實用應用場景與技術整合
LangChain的鏈和代理人技術為構建人工智慧應用提供了強大的基礎。以下是一些實用的應用場景:
人工智慧客服系統
結合多重提示鏈和代理人技術,可以構建能夠處理多種型別問題的客服系統。系統能夠自動判斷客戶問題的型別(產品查詢、訂單跟蹤、技術支援等),選擇適當的處理路徑,並在需要時使用外部工具(如訂單資料函式庫、知識函式庫等)來提供準確的回答。
內容自動化處理
使用序列鏈和轉換鏈,可以構建自動化內容處理管道,例如:
- 從原始文字提取關鍵訊息
- 重新組織和格式化內容
- 生成摘要或擴充套件特定部分
- 翻譯成多種語言
- 調整語氣和風格以適應不同受眾
人工智慧研究助手
結合代理人技術和多種工具(網路搜尋、PDF解析、資料分析等),可以建立能夠協助研究的人工智慧助手。這類別助手能夠搜尋相關資料、提取重要訊息、生成報告摘要,甚至提出新的研究方向和假設。
個人化學習系統
使用多重提示鏈和序列鏈,可以構建根據學生需求和學習風格自動調整教學內容和方法的系統。系統可以先診斷學生的知識水平和學習偏好,然後生成個人化的學習材料和練習題。
多模態互動系統
透過將LangChain與其他技術(如語音識別、影像處理等)整合,可以建立能夠處理多種輸入形式並產生多種輸出形式的互動系統,實作更自然、更全面的人機交
深入LangChain的AI代理系統
在開發根據大模型語言(LLM)的應用時,AI代理是一個極為強大的概念。代理不僅能夠理解自然語言,還能夠規劃並執行一系列動作來完成複雜任務。讓我們透過一個實際例子來理解AI代理的工作方式。
代理運作例項解析
以下是一個簡單的查詢例子,展示了AI代理如何處理問題並使用工具找到答案:
I need to find out when Avatar 2 was released.
Action: Search Action Input: "Avatar 2 release date"
Observation: December 16, 2022
Thought: I now know the final answer.
Final Answer: Avatar 2 was released on December 16, 2022.
> Finished chain.
這段程式碼展示了一個AI代理處理查詢的完整流程。當我們詢問"Avatar 2的發行日期"時,代理首先識別出需要使用搜尋工具,於是執行了搜尋動作。從搜尋結果中取得到日期資訊後,代理進行了思考,確認已經掌握了所需資訊,最後給出了明確的答案。這正是ReAct(Reasoning and Acting)方法的典型應用 - 代理在「思考」和「行動」之間交替進行,直到解決問題。
ReAct方法:AI代理的思維框架
在初始化代理時,我選擇了ZERO_SHOT_REACT_DESCRIPTION
作為代理型別。這種設定讓代理能夠僅根據工具描述來決定使用哪種工具,並採用ReAct方法進行推理。
ReAct方法是一種讓LLM解決各種語言推理和決策任務的方式,由Shunyu Yao等人在2022年10月的論文《ReAct: Synergizing Reasoning and Acting in Language Models》中提出。這種方法提示LLM生成交織的語言推理軌跡和文字動作,兩者之間產生協同效應。推理軌跡幫助模型計劃、追蹤和更新行動,並處理異常情況;而動作則允許模型與外部資源互動,取得額外訊息。
當我第一次實驗ReAct框架時,我發現它能讓模型在解決問題時更加條理化,尤其是在需要多步驟推理的情境下。模型不僅能給出答案,還能展示其思考過程,這對於開發可解釋的AI系統非常重要。
LangChain中的代理型別
除了基本的ReAct框架,LangChain還提供了多種型別的代理,每種都有其特定的應用場景:
結構化輸入ReAct代理
這種代理型別使用ReAct框架根據結構化輸入資料生成自然語言回應。它能處理不同型別的輸入資料,如表格、列表或鍵值對。代理使用語言模型和提示生成訊息豐富、簡潔與連貫的回應。
在處理大量結構化資料時,這類別代理特別有用。例如,當需要從資料函式庫結果中生成報告或摘要時,結構化輸入ReAct代理能夠高效處理這些訊息。
OpenAI Functions代理
這種代理型別使用OpenAI Functions API存取OpenAI提供的各種語言模型和工具。代理可以使用不同的功能,如GPT-3、Codex、DALL-E、CLIP或ImageGPT。代理使用語言模型和提示生成對OpenAI Functions API的請求並解析回應。
這類別代理在需要利用OpenAI專有模型能力的應用中非常實用,特別是當需要程式碼生成、影像處理等特殊功能時。
對話式代理
對話式代理使用語言模型與使用者進行自然語言對話。它能處理不同型別的對話任務,如閒聊、問答或任務完成。代理使用語言模型和提示生成相關、流暢與具有吸引力的回應。
這類別代理最適合需要與使用者進行持續互動的應用,如客服機器人、虛擬助手或教育工具。在開發這類別系統時,我發現保持對話的自然流動性和上下文一致性是最大的挑戰。
自問自答搜尋代理
這種代理型別使用語言模型生成問題,然後在網路上搜尋答案。代理可以使用這種技術學習新訊息或測試自己的知識。
自問自答搜尋代理特別適合需要不斷取得最新訊息的應用,如研究助手或事實核查工具。這種方法能夠讓模型超越其訓練資料的限制,取得實時更新的訊息。
ReAct檔案儲存代理
這種代理型別使用ReAct框架根據儲存在資料函式庫中的檔案生成自然語言回應。它可以處理不同型別的檔案,如新聞文章、部落格文章或研究論文。
當需要從大量文字資料中提取訊息並生成摘要或答案時,這類別代理非常有效。例如,在法律檔案分析、學術研究或商業情報收集等場景中。
計劃執行代理
這是一種實驗性的代理型別,使用語言模型根據使用者輸入和目標選擇一系列動作。代理可以使用不同的工具或模型執行它選擇的動作。代理使用語言模型和提示生成計劃和動作,然後使用AgentExecutor執行它們。
計劃執行代理在複雜任務管理中表現出色,特別是當任務需要多個步驟與每個步驟可能依賴於前一步驟的結果時。這種代理實作了更高層次的自主性,能夠處理更複雜的工作流程。
LangChain代理的核心價值
LangChain代理的關鍵價值在於它們能讓LLM與外部世界互動。有趣的是,這些代理不僅利用LLM來檢索和生成回應,還將其作為推理引擎來規劃最佳化的動作序列。
結合前面介紹的LangChain元件,代理可以成為LLM驅動應用的核心,正如我們將在後續章節中看到的那樣。
在實際開發過程中,我發現代理的真正強大之處在於它們的可組合性 - 你可以將不同型別的代理組合起來,或者將代理與其他LangChain元件結合,建立更複雜、更強大的系統。例如,你可以使用檔案儲存代理處理公司內部知識函式庫,然後將其與對話式代理結合,建立一個能夠回答有關公司政策和程式問題的虛擬助手。