浅谈LangChain中的链 | 豆包MarsCode AI刷题

137 阅读10分钟

什么是 Chain

这里引用一下小册中对Chain的介绍:如果你想开发更复杂的应用程序,那么就需要通过 “Chain” 来链接LangChain的各个组件和功能——模型之间彼此链接,或模型与其他组件链接。

这种将多个组件相互链接,组合成一个链的想法简单但很强大。它简化了复杂应用程序的实现,并使之更加模块化,能够创建出单一的、连贯的应用程序,从而使调试、维护和改进应用程序变得容易。

如何实现Chain

首先,根据上面的介绍我们可以知道,Chain实际上是对LangChain的多个组件和功能进行连接,最终使得这些模型之间彼此连接,同时,也可以让这些模型与其他组件连接。个人认为,其实这就相当于我们平时在开发中对类的设计和封装。

LLMChain的一个基本实现

在LangChain中,为我们提供一个了LLMChain的接口,通过LLMChain这个接口,我们可以实现LLMChain从0到1的使用:我们可以使用PromptTemplate模板类对我们的输入进行格式化并作为输入提示传给LLMChain,之后再让LLMChain为我们提供响应。如果学习过前面的内容其实你会发现,在这里,LLMChain实际上就是对前面讲到过的Model I/O的流程进行了封装。

基本实现引发的思考

可能你会想:既然只是对Model I/O的简单封装,为什么还要单独将其归为一个模块呢?实际上,Chain的最大魅力并不只在于对这些基本流程和功能的封装,而是在于Chain在实现了这些基本流程的前提下,我们还可以将这些基本的流程组合在一起,并可以与一些组件相结合,达到我们原本在单一使用这些基础模块无法达到的效果,完成原本无法完成的任务。

LangChain中为我们提供的一些预置链

LangChain中提供了很多种类型的预置链,具体列表如下:

在本文中,我们仅介绍LLMChain以及SequentialChain(顺序链),帮助你了解链的基本使用及功能,如果想要了解及使用其他的链,可以查阅开发文档或资料,这里不做过多赘述。

LLMChain:最基础的链

在上面的简单实现中,我们其实就是基于LLMChain进行的,正如我们在上文中所提到的那样,LLMChain看起来像是对Model I/O的流程进行了封装,事实上也确实如此,LLMChain就是在LLM的基础上添加了一些功能并对其进行了封装,也就相当于整合了PromptTemplate、语言模型和 Output Parser,把Model I/O的三个模块放在一个链中整体操作。

下面,我们先在不使用链的情况下向大模型提出一个问题:

import os

# ----第一步 创建提示
# 导入LangChain中的提示模板
from langchain_core.prompts import PromptTemplate

# 原始字符串模板
template = "{thing}的功能是?"
# 创建LangChain模板
prompt_temp = PromptTemplate.from_template(template)
# 根据模板创建提示
prompt = prompt_temp.format(thing="筷子")
# 打印提示的内容
print(prompt)

# ----第二步 创建并调用模型
# 导入大语言模型接口
from langchain_openai import ChatOpenAI

# 创建模型实例
model = ChatOpenAI(temperature=0, model=os.environ.get("LLM_MODELEND"))
# 传入提示,调用模型,返回结果
result = model.invoke(prompt)
print(result)

执行以上代码,结果如下:

image.png

下面,让我们使用LLMChain,再向大模型提出一个同样的问题:

import os

# 导入大语言模型接口,LLMChain接口,PromptTemplate接口
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

# 提供模板
template = "{thing}的功能是什么?"

# 实例化大语言模型
model = ChatOpenAI(model=os.environ.get("LLM_MODELEND"),temperature=0.8)

# 实例化LLMChain
llm = LLMChain(llm=model, prompt=PromptTemplate.from_template(template))

# 调用LLMChain回答问题并返回结果
print(llm.invoke("筷子"))

返回结果如下:

{'thing': '筷子', 'text': '筷子主要有以下功能:\n\n### 一、进食工具\n1. **夹取食物**\n   - 这是筷子最基本的功能。无论是蔬菜(如细长的豆角、青菜叶)、肉类(如片状的肉片、块状的肉丁)、米饭、面条等主食,还是豆类(如小巧的花生米)等食物,都可以用筷子方便地夹取。\n   - 例如吃火锅时,人们可以用筷子灵活地夹取在滚烫汤底中涮煮的各种食材,像毛肚、鸭肠等需要精准夹取和快速捞出,以保证最佳的口感。\n2. **挑取食物**\n   - 对于一些丝状或细碎的食物,如豆芽菜中的豆皮、肉丝中的配菜等,可以用筷子尖进行挑取。\n   - 在吃凉拌菜时,可能会有一些细碎的葱、蒜或者调料渣混在菜里,筷子可以轻松地将不需要的部分挑出。\n3. **辅助进食**\n   - 在食用一些带骨的肉类时,筷子可以辅助将肉从骨头上分离下来。例如吃鸡翅时,先用筷子固定住鸡翅,再用牙齿或其他餐具将肉慢慢撕咬下来。\n\n### 二、烹饪工具\n1. **搅拌食材**\n   - 在烹饪过程中,筷子可以用来搅拌食材。例如在制作凉拌菜时,将各种蔬菜、调料放入碗中后,用筷子搅拌,使调料均匀地裹在蔬菜上。\n   - 在制作饺子馅时,用筷子搅拌馅料,使肉和蔬菜充分混合,并且可以通过搅拌来控制馅料的湿度和口感。\n2. **拨弄食材**\n   - 在炒菜时,尤其是炒一些颗粒状的食材,如炒花生米、炒豆子等,筷子可以用来拨弄食材,使它们受热均匀,防止局部烧焦。\n   - 在煎鱼时,也可以用筷子轻轻拨弄鱼身,查看鱼的受热情况和煎制的程度。'}

对比以上代码可以看出,两种方式都可以得到模型的回复,但是相比起来,使用LLMChain所写的代码更加简洁,因为LLMChain把模型的封装和调用结合在了一起,同时在回复中也为我们将回复解析成了json格式,这也就印证了我们之前的说法,在LLMChain中,其实是对输出解析也进行了一定的封装

Sequential Chain:顺序链

以我个人的理解:顾名思义,顺序链实际上,就是按照顺序,将一系列的链封装在一起,并给顺序链一个输入,让他们逐级处理(即其中一个链处理之后,会将输出信息传递给下一个基本链,下一个基本链就会将其作为输入提示,并对其按照自己的设定进行进一步解析,循环下去,直到到达最后一个链,输出结果),最终会得到一个多个链综合进行逐级筛选过滤得到的一个较为优秀的结果。下面用一个简单的实例演示一下:

import os

from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
llm = ChatOpenAI(temperature=0, model=os.environ.get("LLM_MODELEND"))

template = """
你是一个植物学家。给定花的名称和类型,你需要为这种花写一个200字左右的介绍。
花名: {name}
颜色: {color}
植物学家: 这是关于上述花的介绍:"""
prompt_template = PromptTemplate.from_template(template)
introduction_chain = LLMChain(
    llm=llm, prompt=prompt_template, output_key="introduction"
)

# 第二个LLMChain:根据鲜花的介绍写出鲜花的评论
template = """
你是一位鲜花评论家。给定一种花的介绍,你需要为这种花写一篇200字左右的评论。
鲜花介绍:
{introduction}
花评人对上述花的评论:"""
prompt_template = PromptTemplate.from_template(template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review")

# 第三个LLMChain:根据鲜花的介绍和评论写出一篇自媒体的文案
template = """
你是一家花店的社交媒体经理。给定一种花的介绍和评论,你需要为这种花写一篇社交媒体的帖子,300字左右。
鲜花介绍:
{introduction}
花评人对上述花的评论:
{review}
社交媒体帖子:
"""
prompt_template = PromptTemplate.from_template(template)
social_post_chain = LLMChain(
    llm=llm, prompt=prompt_template, output_key="social_post_text"
)

from langchain.chains import SequentialChain

# 总的链:按顺序运行三个链
overall_chain = SequentialChain(
    chains=[introduction_chain, review_chain, social_post_chain],
    input_variables=["name", "color"],
    output_variables=["introduction", "review", "social_post_text"],
    verbose=True,
)

# 运行链并打印结果
result = overall_chain({"name": "玫瑰", "color": "黑色"})
print(result)

运行结果如下:

> Finished chain.
{'name': '玫瑰', 'color': '黑色', 'introduction': '黑色玫瑰是玫瑰家族中极为独特的存在。从外观上看,它那深邃的黑色花瓣犹如神秘的夜幕,散发着一种冷艳而高贵的气息。实际上,黑色玫瑰的花瓣并非真正的黑色,而是红到极致所呈现出的近似黑色的深紫红色。它的花朵饱满,花瓣层层叠叠,质感柔软却又坚韧。在生长习性方面,它与普通玫瑰相似,喜爱阳光充足、通风良好的环境。在文化寓意上,黑色玫瑰常常被视为神秘、独特和高贵的象征,也被用于表达极致的爱情,是花卉中的珍品。', 'review': '《评黑色玫瑰》\n\n黑色玫瑰在花卉界宛如一颗神秘的星。其外观独特,那看似黑色的花瓣实则是深紫红色,却成功营造出神秘冷艳之感,花朵饱满且花瓣层叠,柔软又坚韧的质感更添魅力。生长习性与普通玫瑰相近,易于被花匠打理。在文化寓意上,它承载着神秘、高贵的内涵,象征极致爱情,这使它超脱于普通花卉。它是玫瑰家族的珍品,无论是作为观赏花卉,还是表达特殊情感的载体,黑色玫瑰都以其独特的美,在花卉的舞台上散发着无与伦比的魅力。', 'social_post_text': '💐【黑色玫瑰:神秘冷艳的花卉珍品】💐\n\n宝子们,今天要给大家介绍玫瑰家族中超级独特的黑色玫瑰。😎\n\n它的花瓣看起来像深邃的夜幕,是那种冷艳高贵的黑色,其实是红到极致的深紫红色呢。花朵饱满,层层叠叠的花瓣柔软又坚韧,就像一位神秘而坚强的佳人。🤩\n\n在种植方面,和普通玫瑰类似,只要阳光充足、通风良好就好,花匠们打理起来也比较轻松。🧑\u200d🌾\n\n它的文化寓意更是迷人,象征着神秘、独特和高贵,还能表达极致的爱情。无论是放在家中观赏,还是作为特殊情感的表达,黑色玫瑰都无可替代。它就像花卉舞台上一颗独特的星,散发着无与伦比的魅力,不愧是花卉中的珍品。💕#黑色玫瑰 #花卉之美 #独特花卉'}

从以上代码中,如果细心观察其实不难发现,我们在每个基础链的提示模板中其中的参数,都是上一个链的输出(即template的输入参数和上一条链的output_key一样),正是因为如此,才能让顺序链达到了对一个问题让不同模型进行多重优化,最终得到一个比较优秀的回答,这其实类似于前面的CoT和ToT,不过在这里,进行提示的具体实现有所不同,引导方式也从直接思维模式引导转换为了向大模型提问,只给出大体流程框架,具体细节由大模型自行发挥。

总结

通过本文的介绍,你应该能体会到Chain在大模型应用中为我们提供的便利,可能也会感叹顺序链实现的巧妙。实际上,在LangChain中,还为我们提供了很多种链,我们可以根据实际应用的需求选择各种不同的链。另外,对于其他链,也可以自行对其尝试学习使用,学习之后一定会让你对Chain这个模块拥有更深的理解,祝大家学习顺利~