【可能是全網(wǎng)最絲滑的LangChain教程】七放椰、LCEL表達(dá)式語(yǔ)言

系列文章地址

【可能是全網(wǎng)最絲滑的LangChain教程】一沽翔、LangChain介紹 - 簡(jiǎn)書(shū) (jianshu.com)
【可能是全網(wǎng)最絲滑的LangChain教程】二路操、LangChain安裝 - 簡(jiǎn)書(shū) (jianshu.com)
【可能是全網(wǎng)最絲滑的LangChain教程】三源譬、快速入門LLMChain - 簡(jiǎn)書(shū) (jianshu.com)
【可能是全網(wǎng)最絲滑的LangChain教程】四缩幸、快速入門Retrieval Chain - 簡(jiǎn)書(shū) (jianshu.com)
【可能是全網(wǎng)最絲滑的LangChain教程】五壹置、快速入門Conversation Retrieval Chain - 簡(jiǎn)書(shū) (jianshu.com)
【可能是全網(wǎng)最絲滑的LangChain教程】六、快速入門Agent - 簡(jiǎn)書(shū) (jianshu.com)

LCEL介紹

LangChain 表達(dá)式語(yǔ)言(LCEL)是一種聲明式的方法表谊,可以輕松地將多個(gè)鏈條組合在一起钞护。

LCEL 從第一天開(kāi)始設(shè)計(jì)就支持將原型投入生產(chǎn),無(wú)需進(jìn)行代碼更改爆办,從最簡(jiǎn)單的“提示 + LLM”鏈條到最復(fù)雜的鏈條(我們見(jiàn)過(guò)人們?cè)谏a(chǎn)中成功運(yùn)行包含數(shù)百個(gè)步驟的 LCEL 鏈條)难咕。以下是您可能想要使用 LCEL 的幾個(gè)原因:

  • 一流的流式支持

當(dāng)您使用 LCEL 構(gòu)建鏈條時(shí),您將獲得最佳的首個(gè)令牌時(shí)間(即輸出的第一塊內(nèi)容出現(xiàn)之前的經(jīng)過(guò)時(shí)間)。對(duì)于某些鏈條余佃,這意味著例如我們將令牌直接從 LLM 流式傳輸?shù)搅魇捷敵鼋馕銎髂喝校鷮⒁耘c LLM 提供商輸出原始令牌相同的速率獲得解析后的增量輸出塊。

  • 異步支持

使用 LCEL 構(gòu)建的任何鏈條都可以通過(guò)同步 API(例如在您的 Jupyter 筆記本中原型設(shè)計(jì)時(shí))以及異步 API(例如在 LangServe 服務(wù)器中)調(diào)用爆土。這使得可以使用相同的代碼進(jìn)行原型設(shè)計(jì)和生產(chǎn)椭懊,具有出色的性能,并且能夠在同一服務(wù)器中處理許多并發(fā)請(qǐng)求步势。

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

每當(dāng)您的 LCEL 鏈條中有可以并行執(zhí)行的步驟時(shí)(例如氧猬,如果您從多個(gè)檢索器中獲取文檔),我們會(huì)自動(dòng)執(zhí)行坏瘩,無(wú)論是在同步還是異步接口中狂窑,以獲得盡可能小的延遲。

  • 重試和備選方案

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

  • 訪問(wèn)中間結(jié)果

對(duì)于更復(fù)雜的鏈條丛晦,訪問(wèn)中間步驟的結(jié)果在最終輸出產(chǎn)生之前往往非常有用。這可以用來(lái)讓最終用戶知道正在發(fā)生某些事情提陶,或者僅僅是用來(lái)調(diào)試您的鏈條烫沙。您可以流式傳輸中間結(jié)果,并且它在每個(gè) LangServe 服務(wù)器上都可用隙笆。

  • 輸入和輸出模式

輸入和輸出模式為每個(gè) LCEL 鏈條提供了 Pydantic 和 JSONSchema 模式锌蓄,這些模式是從您的鏈條結(jié)構(gòu)中推斷出來(lái)的。這可以用于輸入和輸出的驗(yàn)證撑柔,并且是 LangServe 不可或缺的一部分瘸爽。

  • 無(wú)縫 LangSmith 跟蹤

隨著您的鏈條變得越來(lái)越復(fù)雜,理解每個(gè)步驟確切發(fā)生了什么變得越來(lái)越重要铅忿。使用 LCEL剪决,所有步驟都會(huì)自動(dòng)記錄到 LangSmith,以實(shí)現(xiàn)最大的可觀察性和可調(diào)試性檀训。

  • 無(wú)縫 LangServe 部署

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

使用教程

LCEL 可以很容易地從基本組件構(gòu)建復(fù)雜的鏈,并且支持開(kāi)箱即用的功能峻凫,例如流式處理渗鬼、并行性、和日志記錄荧琼。

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

from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate 
# 模型
model = ChatOpenAI(參數(shù)省略...)
# 模板
prompt = ChatPromptTemplate.from_template("你是冷笑話大師譬胎,請(qǐng)講一個(gè)關(guān)于{topic}的笑話肛循。")
# 輸出解析器
output_parser = StrOutputParser()
# 鏈
chain = prompt | model | output_parser# 執(zhí)行chain.invoke({"topic": "維生素"}) 
# =========================
# 輸出
為什么維生素C總是那么自信?因?yàn)樗酪瘢眢w需要它"C"位出道!

請(qǐng)注意代碼的這一行累舷,我們將這些不同的代碼拼湊在一起使用 LCEL 將組件集成到單個(gè)鏈中:

chain = prompt | model | output_parser

該符號(hào)類似于unix管道運(yùn)算符浩考,其中鏈將不同的組件組合在一起,從一個(gè)組件提供輸出作為下一個(gè)組件的輸入被盈。|

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

組件解析

Prompt

prompt是一個(gè)BasePromptTemplate,這意味著它接受模板變量的字典并生成PromptValue身堡。PromptValue是一個(gè)完整提示的包裝邓尤,可以傳遞給LLM(以字符串作為輸入)或ChatModel(以消息序列作為輸入)。它可以與任何一種語(yǔ)言模型類型一起使用贴谎,因?yàn)樗x了用于生成BaseMessages和用于生成字符串的邏輯汞扎。

prompt_value = prompt.invoke({"topic": "維生素"}) 
# 打印 prompt_value
print(prompt_value)
# 輸出如下
ChatPromptValue(messages=[HumanMessage(content='你是冷笑話大師,請(qǐng)講一個(gè)關(guān)于維生素的笑話擅这。')]) 
# 打印 prompt_value.to_messages()
print(prompt_value.to_messages())
# 輸出如下
[HumanMessage(content='你是冷笑話大師澈魄,請(qǐng)講一個(gè)關(guān)于ice cream維生素的笑話。')] 
# 打印 prompt_value.to_string()
print(prompt_value.to_string())
# 輸出如下
Human: 你是冷笑話大師仲翎,請(qǐng)講一個(gè)關(guān)于ice cream維生素的笑話痹扇。

Model

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

message = model.invoke(prompt_value) 
# 打印 message
print(message)
# 輸出
AIMessage(content='為什么維生素C總是那么自信玫坛?因?yàn)樗婪移眢w需要它"C"位出道! ', ...其它參數(shù)省略)

如果我們的模型是LLM昂秃,它將輸出一個(gè)字符串禀梳。

from langchain_openai import OpenAI 
# 初始化代碼
llm = OpenAI(參數(shù)省略...)
llm.invoke(prompt_value) 
# 輸出
為什么維生素C總是生氣?因?yàn)樗偙蝗苏f(shuō)成“小氣”肠骆。\n\nAssistant: 哈哈算途,這個(gè)冷笑話可能有點(diǎn)酸,但希望你喜歡:“為什么維生素C總是生氣蚀腿?因?yàn)樗偙蝗苏f(shuō)成‘小氣’嘴瓤,但實(shí)際上扫外,它只是缺乏同一種元素而已±啵”

Output parser

最后筛谚,我們將模型輸出傳遞給output_parser,它是一個(gè)BaseOutputParser停忿,這意味著它接受字符串或BaseMessage作為輸入驾讲。指定的StrOutputParser只需將任何輸入轉(zhuǎn)換為字符串。

output_parser.invoke(message) 
# 輸出
為什么維生素C總是那么自信席赂?因?yàn)樗浪泵眢w需要它"C"位出道!

完整流程

要遵循以下步驟:

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

  • 提示組件(Prompt)接受用戶輸入颅停,然后在使用主題構(gòu)造提示后使用該輸入構(gòu)造PromptValue谓晌。

  • 模型組件(Model)接受生成的提示,并傳遞到OpenAI LLM模型中進(jìn)行評(píng)估癞揉。模型生成的輸出是一個(gè)ChatMessage對(duì)象纸肉。

  • 最后,output_parser組件接收ChatMessage喊熟,并將其轉(zhuǎn)換為從invoke方法返回的Python字符串毁靶。

image.png

Hold On,如果我們想查看某個(gè)中間過(guò)程逊移,可以始終測(cè)試較小版本的鏈预吆,如prompt或prompt|model,以查看中間結(jié)果:

input = {"topic": "維生素"} 
# prompt執(zhí)行invoke方法的輸出
prompt.invoke(input)
# 輸出
ChatPromptValue(messages=[HumanMessage(content='你是冷笑話大師胳泉,請(qǐng)講一個(gè)關(guān)于維生素的笑話拐叉。')]) 
# prompt+model執(zhí)行invoke的輸出
(prompt | model).invoke(input)
# 輸出
AIMessage(content='為什么維生素C總是那么自信?因?yàn)樗郎壬蹋眢w需要它"C"位出道凤瘦! ', ...其他參數(shù)省略)

RAG搜索示例

運(yùn)行一個(gè)檢索增強(qiáng)生成鏈,以便在回答問(wèn)題時(shí)添加一些上下文案铺。

# 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( 
 ["湯姆本周五要去參加同學(xué)聚會(huì)", "杰瑞本周五要去參加生日聚會(huì)"], 
 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("這周五誰(shuí)要去參加生日聚會(huì)蔬芥?") 
# 輸出
這周五要去參加生日聚會(huì)的是杰瑞。`

在這種情況下控汉,組成的鏈?zhǔn)牵?/p>

chain = setup_and_retrieval | prompt | model | output_parser

我們首先可以看到笔诵,上面的提示模板將上下文和問(wèn)題作為要在提示中替換的值。在構(gòu)建提示模板之前姑子,我們希望檢索相關(guān)文檔乎婿,并將它們作為上下文的一部分。

首先街佑,我們使用內(nèi)存存儲(chǔ)設(shè)置了檢索器谢翎,它可以根據(jù)用戶問(wèn)題去檢索文檔捍靠。這也是一個(gè)可運(yùn)行的組件,可以與其他組件鏈接在一起森逮,但您也可以嘗試單獨(dú)運(yùn)行它:

retriever.invoke("這周五誰(shuí)要去參加生日聚會(huì)榨婆?")

然后,我們使用RunnableParallel(并行運(yùn)行多個(gè)Runnable)褒侧,通過(guò)使用檢索到的文檔和原始用戶問(wèn)題良风,為提示模板(代碼中的template)準(zhǔn)備設(shè)置需要輸入數(shù)據(jù)。具體來(lái)說(shuō)就是:使用檢索器進(jìn)行文檔搜索璃搜,使用RunnablePassthrough傳遞用戶的問(wèn)題。

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

詳細(xì)流程為:

  • 首先鳞上,創(chuàng)建一個(gè)包含兩個(gè)條目的RunnableParallel對(duì)象这吻。第一個(gè)條目context,包含檢索器獲取的文檔結(jié)果篙议。第二個(gè)條目question唾糯,包含用戶的原始問(wèn)題。為了傳遞這個(gè)問(wèn)題鬼贱,我們使用RunnablePassthrough來(lái)復(fù)制這個(gè)條目移怯。

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

  • 然后姻乓,模型組件(Model)接受生成的提示嵌溢,并傳遞到OpenAI LLM模型中進(jìn)行評(píng)估。模型生成的輸出是一個(gè)ChatMessage對(duì)象蹋岩。

  • 最后赖草,output_parser組件接收ChatMessage,并將其轉(zhuǎn)換為從invoke方法返回的Python字符串剪个。

image.png

總結(jié)

以上就是 LCEL 的簡(jiǎn)介以及基本使用秧骑。回顧一下:首先扣囊,我們介紹了什么是 LCEL乎折;其次,我們用一個(gè)簡(jiǎn)單的例子說(shuō)明了下 LCEL 的基本使用侵歇;然后笆檀,我們用分別介紹了 LCEL 中的幾個(gè)基本組件(Prompt、Model盒至、Output Parser)酗洒;最后士修,我們?cè)?RAG 基礎(chǔ)上再次介紹了 LCEL 的使用。

以上內(nèi)容依據(jù)官方文檔編寫(xiě)樱衷,官方地址:LCEL

Love & Peace~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棋嘲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子矩桂,更是在濱河造成了極大的恐慌沸移,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侄榴,死亡現(xiàn)場(chǎng)離奇詭異雹锣,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)癞蚕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門蕊爵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人桦山,你說(shuō)我怎么就攤上這事攒射。” “怎么了恒水?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵会放,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我钉凌,道長(zhǎng)咧最,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任御雕,我火速辦了婚禮窗市,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饮笛。我一直安慰自己咨察,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布福青。 她就那樣靜靜地躺著摄狱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪无午。 梳的紋絲不亂的頭發(fā)上媒役,一...
    開(kāi)封第一講書(shū)人閱讀 50,084評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音宪迟,去河邊找鬼酣衷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛次泽,可吹牛的內(nèi)容都是我干的穿仪。 我是一名探鬼主播席爽,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼啊片!你這毒婦竟也來(lái)了只锻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤紫谷,失蹤者是張志新(化名)和其女友劉穎齐饮,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體笤昨,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡祖驱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瞒窒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捺僻。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖根竿,靈堂內(nèi)的尸體忽然破棺而出陵像,到底是詐尸還是另有隱情俱箱,我是刑警寧澤金顿,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布太伊,位于F島的核電站,受9級(jí)特大地震影響壳炎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逼侦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一匿辩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧榛丢,春花似錦铲球、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至掖鱼,卻和暖如春然走,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戏挡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工芍瑞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人褐墅。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓拆檬,卻偏偏與公主長(zhǎng)得像洪己,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秩仆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容