【可能是全網最絲滑的LangChain教程】七、LCEL表達式語言

系列文章地址

【可能是全網最絲滑的LangChain教程】一、LangChain介紹 - 簡書 (jianshu.com)
【可能是全網最絲滑的LangChain教程】二、LangChain安裝 - 簡書 (jianshu.com)
【可能是全網最絲滑的LangChain教程】三、快速入門LLMChain - 簡書 (jianshu.com)
【可能是全網最絲滑的LangChain教程】四、快速入門Retrieval Chain - 簡書 (jianshu.com)
【可能是全網最絲滑的LangChain教程】五、快速入門Conversation Retrieval Chain - 簡書 (jianshu.com)
【可能是全網最絲滑的LangChain教程】六、快速入門Agent - 簡書 (jianshu.com)

LCEL介紹

LangChain 表達式語言(LCEL)是一種聲明式的方法,可以輕松地將多個鏈條組合在一起。

LCEL 從第一天開始設計就支持將原型投入生產,無需進行代碼更改,從最簡單的“提示 + LLM”鏈條到最復雜的鏈條(我們見過人們在生產中成功運行包含數百個步驟的 LCEL 鏈條)。以下是您可能想要使用 LCEL 的幾個原因:

  • 一流的流式支持

當您使用 LCEL 構建鏈條時,您將獲得最佳的首個令牌時間(即輸出的第一塊內容出現之前的經過時間)。對于某些鏈條,這意味著例如我們將令牌直接從 LLM 流式傳輸到流式輸出解析器,您將以與 LLM 提供商輸出原始令牌相同的速率獲得解析后的增量輸出塊。

  • 異步支持

使用 LCEL 構建的任何鏈條都可以通過同步 API(例如在您的 Jupyter 筆記本中原型設計時)以及異步 API(例如在 LangServe 服務器中)調用。這使得可以使用相同的代碼進行原型設計和生產,具有出色的性能,并且能夠在同一服務器中處理許多并發(fā)請求。

  • 優(yōu)化的并行執(zhí)行

每當您的 LCEL 鏈條中有可以并行執(zhí)行的步驟時(例如,如果您從多個檢索器中獲取文檔),我們會自動執(zhí)行,無論是在同步還是異步接口中,以獲得盡可能小的延遲。

  • 重試和備選方案

為您的 LCEL 鏈條中的任何部分配置重試和備選方案。這是一種在大規(guī)模生產中使您的鏈條更可靠的絕佳方式。我們目前正在努力為重試/備選方案添加流式支持,這樣您可以在不增加任何延遲成本的情況下獲得增強的可靠性。

  • 訪問中間結果

對于更復雜的鏈條,訪問中間步驟的結果在最終輸出產生之前往往非常有用。這可以用來讓最終用戶知道正在發(fā)生某些事情,或者僅僅是用來調試您的鏈條。您可以流式傳輸中間結果,并且它在每個 LangServe 服務器上都可用。

  • 輸入和輸出模式

輸入和輸出模式為每個 LCEL 鏈條提供了 Pydantic 和 JSONSchema 模式,這些模式是從您的鏈條結構中推斷出來的。這可以用于輸入和輸出的驗證,并且是 LangServe 不可或缺的一部分。

  • 無縫 LangSmith 跟蹤

隨著您的鏈條變得越來越復雜,理解每個步驟確切發(fā)生了什么變得越來越重要。使用 LCEL,所有步驟都會自動記錄到 LangSmith,以實現最大的可觀察性和可調試性。

  • 無縫 LangServe 部署

使用 LCEL 創(chuàng)建的任何鏈條都可以輕松地使用 LangServe 部署。

使用教程

LCEL 可以很容易地從基本組件構建復雜的鏈,并且支持開箱即用的功能,例如流式處理、并行性、和日志記錄。

基本示例:提示(Prompt) + 模型(Model) + 輸出解析器(OutputParser)

from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate 
# 模型
model = ChatOpenAI(參數省略...)
# 模板
prompt = ChatPromptTemplate.from_template("你是冷笑話大師,請講一個關于{topic}的笑話。")
# 輸出解析器
output_parser = StrOutputParser()
# 鏈
chain = prompt | model | output_parser# 執(zhí)行chain.invoke({"topic": "維生素"}) 
# =========================
# 輸出
為什么維生素C總是那么自信?因為它知道,身體需要它"C"位出道!

請注意代碼的這一行,我們將這些不同的代碼拼湊在一起使用 LCEL 將組件集成到單個鏈中:

chain = prompt | model | output_parser

該符號類似于unix管道運算符,其中鏈將不同的組件組合在一起,從一個組件提供輸出作為下一個組件的輸入。|

在此鏈中,用戶輸入被傳遞到提示模板,然后提示模板輸出傳遞給模型,然后模型輸出為傳遞給輸出解析器。

組件解析

Prompt

prompt是一個BasePromptTemplate,這意味著它接受模板變量的字典并生成PromptValue。PromptValue是一個完整提示的包裝,可以傳遞給LLM(以字符串作為輸入)或ChatModel(以消息序列作為輸入)。它可以與任何一種語言模型類型一起使用,因為它定義了用于生成BaseMessages和用于生成字符串的邏輯。

prompt_value = prompt.invoke({"topic": "維生素"}) 
# 打印 prompt_value
print(prompt_value)
# 輸出如下
ChatPromptValue(messages=[HumanMessage(content='你是冷笑話大師,請講一個關于維生素的笑話。')]) 
# 打印 prompt_value.to_messages()
print(prompt_value.to_messages())
# 輸出如下
[HumanMessage(content='你是冷笑話大師,請講一個關于ice cream維生素的笑話。')] 
# 打印 prompt_value.to_string()
print(prompt_value.to_string())
# 輸出如下
Human: 你是冷笑話大師,請講一個關于ice cream維生素的笑話。

Model

然后將PromptValue傳遞給模型。在這種情況下,我們的模型是ChatModel,這意味著它將輸出BaseMessage。

message = model.invoke(prompt_value) 
# 打印 message
print(message)
# 輸出
AIMessage(content='為什么維生素C總是那么自信?因為它知道,身體需要它"C"位出道! ', ...其它參數省略)

如果我們的模型是LLM,它將輸出一個字符串。

from langchain_openai import OpenAI 
# 初始化代碼
llm = OpenAI(參數省略...)
llm.invoke(prompt_value) 
# 輸出
為什么維生素C總是生氣?因為它總被人說成“小氣”。\n\nAssistant: 哈哈,這個冷笑話可能有點酸,但希望你喜歡:“為什么維生素C總是生氣?因為它總被人說成‘小氣’,但實際上,它只是缺乏同一種元素而已。”

Output parser

最后,我們將模型輸出傳遞給output_parser,它是一個BaseOutputParser,這意味著它接受字符串或BaseMessage作為輸入。指定的StrOutputParser只需將任何輸入轉換為字符串。

output_parser.invoke(message) 
# 輸出
為什么維生素C總是那么自信?因為它知道,身體需要它"C"位出道!

完整流程

要遵循以下步驟:

  • 我們將用戶的主題以 {"topic":"維生素"} 形式輸入

  • 提示組件(Prompt)接受用戶輸入,然后在使用主題構造提示后使用該輸入構造PromptValue。

  • 模型組件(Model)接受生成的提示,并傳遞到OpenAI LLM模型中進行評估。模型生成的輸出是一個ChatMessage對象。

  • 最后,output_parser組件接收ChatMessage,并將其轉換為從invoke方法返回的Python字符串。

image.png

Hold On,如果我們想查看某個中間過程,可以始終測試較小版本的鏈,如prompt或prompt|model,以查看中間結果:

input = {"topic": "維生素"} 
# prompt執(zhí)行invoke方法的輸出
prompt.invoke(input)
# 輸出
ChatPromptValue(messages=[HumanMessage(content='你是冷笑話大師,請講一個關于維生素的笑話。')]) 
# prompt+model執(zhí)行invoke的輸出
(prompt | model).invoke(input)
# 輸出
AIMessage(content='為什么維生素C總是那么自信?因為它知道,身體需要它"C"位出道! ', ...其他參數省略)

RAG搜索示例

運行一個檢索增強生成鏈,以便在回答問題時添加一些上下文。

# Requires:
# pip install langchain docarray tiktoken 
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings
import torch
from langchain_community.vectorstores import DocArrayInMemorySearch
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough 
# 詞嵌入模型
EMBEDDING_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"embeddings = 
HuggingFaceEmbeddings(model_name='D:\models\m3e-base', model_kwargs={'device': EMBEDDING_DEVICE}) 
vectorstore = DocArrayInMemorySearch.from_texts( 
 ["湯姆本周五要去參加同學聚會", "杰瑞本周五要去參加生日聚會"], 
 embedding=embeddings,)
retriever = vectorstore.as_retriever() 
template = """Answer the question based only on the following context:
{context}
Question: {question}"""
prompt = ChatPromptTemplate.from_template(template)
output_parser = StrOutputParser() 
setup_and_retrieval = RunnableParallel( 
 {"context": retriever, "question": RunnablePassthrough()})
chain = setup_and_retrieval | prompt | model | output_parser 
chain.invoke("這周五誰要去參加生日聚會?") 
# 輸出
這周五要去參加生日聚會的是杰瑞。`

在這種情況下,組成的鏈是:

chain = setup_and_retrieval | prompt | model | output_parser

我們首先可以看到,上面的提示模板將上下文和問題作為要在提示中替換的值。在構建提示模板之前,我們希望檢索相關文檔,并將它們作為上下文的一部分。

首先,我們使用內存存儲設置了檢索器,它可以根據用戶問題去檢索文檔。這也是一個可運行的組件,可以與其他組件鏈接在一起,但您也可以嘗試單獨運行它:

retriever.invoke("這周五誰要去參加生日聚會?")

然后,我們使用RunnableParallel(并行運行多個Runnable),通過使用檢索到的文檔和原始用戶問題,為提示模板(代碼中的template)準備設置需要輸入數據。具體來說就是:使用檢索器進行文檔搜索,使用RunnablePassthrough傳遞用戶的問題。

setup_and_retrieval = RunnableParallel(    {"context": retriever, "question": RunnablePassthrough()})

最終的完整執(zhí)行鏈如下:

setup_and_retrieval = RunnableParallel( 
 {"context": retriever, "question": RunnablePassthrough()})
chain = setup_and_retrieval | prompt | model | output_parser

詳細流程為:

  • 首先,創(chuàng)建一個包含兩個條目的RunnableParallel對象。第一個條目context,包含檢索器獲取的文檔結果。第二個條目question,包含用戶的原始問題。為了傳遞這個問題,我們使用RunnablePassthrough來復制這個條目。

  • 其次,將第一步中的字典提供給提示組件。然后,它將用戶輸入(question)以及檢索到的上下文文檔(context)來構造提示并輸出PromptValue。

  • 然后,模型組件(Model)接受生成的提示,并傳遞到OpenAI LLM模型中進行評估。模型生成的輸出是一個ChatMessage對象。

  • 最后,output_parser組件接收ChatMessage,并將其轉換為從invoke方法返回的Python字符串。

image.png

總結

以上就是 LCEL 的簡介以及基本使用。回顧一下:首先,我們介紹了什么是 LCEL;其次,我們用一個簡單的例子說明了下 LCEL 的基本使用;然后,我們用分別介紹了 LCEL 中的幾個基本組件(Prompt、Model、Output Parser);最后,我們在 RAG 基礎上再次介紹了 LCEL 的使用。

以上內容依據官方文檔編寫,官方地址:LCEL

Love & Peace~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容