LangChain大型语言模型(LLM)应用开发(三):Chains

LangChain是一个基于大语言模型(如ChatGPT)用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口,可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互,将多个组件链接在一起,以便在不同的应用程序中使用。

今天我们来学习DeepLearning.AI的在线课程:LangChain for LLM Application Development的第三门课:Chains,该门课程主要讲解LangChain的核心组件:链,即Chain,  chain可以将大型语言模型(LLM)和提示语(prompt)结合在一起, 你还可以创建多种不同功能的Chain,然后将这些chain组合在一起,对文本或其他数据执行一系列操作。

目录

  • LLMChain

  • Sequential Chains

  •       SimpleSequentialChain

  •        SequentialChain

  • Router Chain

下面我们导入本地环境配置文件.env, 在.env文件中我们存放了opai的api key 。

import osfrom dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file

 下面我们导入一个csv文件,该文件存放了用户对产品的评论信息。

import pandas as pd
df = pd.read_csv('Data.csv')
df

LLMChain

LLMChain是最基本的chain,他将LLM和prompt组合在一起,下面我们要实现一个让LLM给生产不同产品的公司取名字的功能:

from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.chains import LLMChain#定义大型语言模型
llm = ChatOpenAI(temperature=0.9)prompt = ChatPromptTemplate.from_template("描述生产{product}的公司的一个最佳名称是什么?"
)#将llm和prompt组合在一起创建一个LLMChain的实例
chain = LLMChain(llm=llm, prompt=prompt)#执行chain
product = "床上用品"
chain.run(product)

 这里我们定义llm时使用的温度参数temperature为0.9,该参数的取值范围为0-1,之所以这里要设置为0.9,因为temperature的值越大,那么llm返回结果的随机性就越大,这里我们要实现的功能是给公司取名字的功能,因此我们需要LLM具有较大的灵活性和多样性,所以llm每次返回的结果可能都会不一样,这也就是我们希望llm实现的功能。

Sequential Chains

Sequential chain 是另一种类型的chain。它可以将多个chain组合在一起,其中一个chain的输出是下一个chain的输入。

SimpleSequentialChain

SimpleSequentialChain是最基本的一种Sequential Chains,因为它只有一个输入和一个输出,其中前一个chain的输出为后一个chain的输入,如下图所示:

下面我们看个例子,在这个例子中我们除了要执行之前LLMChain的给公司取名的功能外,我们还要需要llm生成20个字左右的公司名称的描述信息,所以,在这里需要创建两个chain, 一个chain负责给公司取名字,另一个chain负责就公司的名字生成20个字左右的描述信息,最后我们需要将这两个chain组合在一起,创建一个新的chain,对于这个新的chain它只有一个输入和一个输出,输入就是公司生产的产品,输出则是对公司名称的20字左右的描述信息。

from langchain.chains import SimpleSequentialChainllm = ChatOpenAI(temperature=0.9)# prompt template 1
first_prompt = ChatPromptTemplate.from_template("描述生产{product}的公司的一个最佳名称是什么?"
)# Chain 1
chain_one = LLMChain(llm=llm, prompt=first_prompt)# prompt template 2
second_prompt = ChatPromptTemplate.from_template("为以下公司编写 20 个字的描述:{company_name}”"
)
# chain 2
chain_two = LLMChain(llm=llm, prompt=second_prompt)# 将chain1和chain2组合在一起生成一个新的chain.
overall_simple_chain = SimpleSequentialChain(chains=[chain_one, chain_two],verbose=True)
#执行新的chain
overall_simple_chain.run(product)

 SequentialChain

SequentialChain与SimpleSequentialChain的区别在于它可以有多个输入和输出,而SimpleSequentialChain只有一个输入和输出,如下图所示:

在下面的例子中我对官方课件的代码做了修改,原来官方的代码只有4个chain, 而我在此基础上增加了一个chain,这样的修改主要是让大家更好的理解该案例中涉及的prompt的含义,原先的prompt使用的都是英语,我都将其翻译成了中文,这样大家就比较能看懂这些prompt了,该案例主要功能是要让llm对前面导入的用户评语进行分析,并给出回复,因为用户的评语可能使用的是多种不同的语言,为此我们需要让chain能够识别用户评语使用的是那种语言,并将其翻译成中文,最后给出回复,具体来说包含以下功能和步骤:

  1. 将用户评论翻译成中文
  2. 用一句话概括用户评论
  3. 识别出用户评论使用的语言
  4. 按原始评论语言生成回复
  5. 将回复翻译成中文
from langchain.chains import SequentialChain#定义llm
llm = ChatOpenAI(temperature=0.9)# prompt template 1: 将评论翻译成中文
first_prompt = ChatPromptTemplate.from_template("将下面的评论翻译成中文:""\n\n{Review}"
)
# chain 1: input= Review and output= Chinese_Review
chain_one = LLMChain(llm=llm, prompt=first_prompt, output_key="Chinese_Review")
#概括评论
second_prompt = ChatPromptTemplate.from_template("你能用 1 句话概括以下评论吗:""\n\n{Chinese_Review}"
)
# chain 2: input= Chinese_Review and output= summary
chain_two = LLMChain(llm=llm, prompt=second_prompt, output_key="summary")# prompt template 3: 识别评论使用的语言
third_prompt = ChatPromptTemplate.from_template("下面的评论使用的是什么语言?:\n\n{Review}"
)
# chain 3: input= Review and output= language
chain_three = LLMChain(llm=llm, prompt=third_prompt,output_key="language")# prompt template 4: 生成回复信息
fourth_prompt = ChatPromptTemplate.from_template("使用指定语言编写对以下摘要的后续回复:""\n\n摘要:{summary}\n\n语言:{language}")
# chain 4: input= summary, language and output= followup_message
chain_four = LLMChain(llm=llm, prompt=fourth_prompt,output_key="followup_message")# prompt template 5: 将回复信息翻译成中文
five_prompt = ChatPromptTemplate.from_template("将下面的评论翻译成中文:""\n\n{followup_message}"
)
# chain 5: input= followup_message and output= Chinese_followup_message
chain_five = LLMChain(llm=llm, prompt=five_prompt, output_key="Chinese_followup_message")# overall_chain: input= Review 
# output= language,Chinese_Review,summary, followup_message,#Chinese_followup_message
overall_chain = SequentialChain(chains=[chain_one, chain_two, chain_three, chain_four,chain_five],input_variables=["Review"],output_variables=["language","Chinese_Review", "summary","followup_message","Chinese_followup_message"],verbose=True
)

这里我们看到我们在定义最后的overall_chain 时设置了输入变量和输出变量,因此这些输入和输出变量最后会作为中间结果被输出。

下面我们看一下用户的评语:

review = df.Review[5]
print(review)

 接下来我们执行SequentialChain:

overall_chain(review)

 Router Chain

有一种应用场景就是我们有时候希望根据信息的内容将其传送到不同的chain,而每个chain的职能是只擅长回答自己所属领域的问题,那么在这种场景下我们就需要一种具有"路由器"功能的chain来将信息传输到不同职能的chain.

在下面的例子中,我们有多个不同职能的chain,它们负责回复关于不同学科领域的用户问题,比如数学chain,历史chain,物理chain,计算机chain,每个chain都只擅长回复自己专业领域的问题,这里我们还有一个路由chain, 它的作用是识别用户问题属于哪个领域,然后将问题传输给那个领域的chain,让其来完成回答用户问题的功能。下面我们首先定义4个专业领域的prompt模板,在这些模板中我们将告知llm它的职责与定位:

physics_template="""你是一位非常聪明的物理学教授。\
你擅长以简洁易懂的方式回答有关物理的问题。 \
当你不知道某个问题的答案时,你就承认你不知道。这里有一个问题:
{input}"""math_template="""你是一位非常优秀的数学家。\
你很擅长回答数学问题。 \
你之所以如此出色,是因为你能够将难题分解为各个组成部分,\
回答各个组成部分,然后将它们组合起来回答更广泛的问题。这里有一个问题:
{input}"""history_template = """你是一位非常优秀的历史学家。\
你对各个历史时期的人物、事件和背景有深入的了解和理解。 \
你有能力思考、反思、辩论、讨论和评价过去。 \
你尊重历史证据,并有能力利用它来支持你的解释和判断。这里有一个问题:
{input}"""computerscience_template="""你是一位成功的计算机科学家。\
你有创造力,协作精神,前瞻性思维,自信,有很强的解决问题的能力,\
对理论和算法的理解,以及出色的沟通能力。\
你很擅长回答编程问题。
你是如此优秀,因为你知道如何通过描述一个机器可以很容易理解的命令步骤来解决问题,\
你知道如何选择一个解决方案,在时间复杂度和空间复杂度之间取得良好的平衡。这里有一个问题:
{input}"""

接下来我们还还需要对这些模板功能进行结构化的解释说明这样便于后面的路由chain能找到它们,为此我们要定义一个模板的索引结构:

prompt_infos = [{"name": "physics", "description": "擅长回答有关物理方面的问题", "prompt_template": physics_template},{"name": "math", "description": "擅长回答有关数学方面的问题", "prompt_template": math_template},{"name": "history", "description": "擅长回答有关历史方面的问题", "prompt_template": history_template},{"name": "computer science", "description": "擅长回答有关计算机科学方面的问题", "prompt_template": computerscience_template}
]

接下来我们还需要定义一个目标chain的集合,所谓的目标chain就是指回答单一领域问题的chai成,它们都是基本的LLMChain:

from langchain.chains import LLMChain
from langchain.chains.router import MultiPromptChain
from langchain.chains.router.llm_router import LLMRouterChain,RouterOutputParser
from langchain.prompts import PromptTemplate
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI#定义llm
llm = ChatOpenAI(temperature=0)#创建目标chain
destination_chains = {}
for p_info in prompt_infos:name = p_info["name"]prompt_template = p_info["prompt_template"]prompt = ChatPromptTemplate.from_template(template=prompt_template)chain = LLMChain(llm=llm, prompt=prompt)destination_chains[name] = chain  destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
destinations_str = "\n".join(destinations)
print(destinations_str)

 这里我们还生成了一个目标字符串destinations_str,它会在后面别嵌入到路由chain的prompt模板中来为定位目标chain提供索引。

接下来我们还需要定义default_chain,这个chain的作用是当用户的问题与现有的所有目标chain都无法匹配时,那么就由default_chain来回答用户的问题:

default_prompt = ChatPromptTemplate.from_template("{input}")
default_chain = LLMChain(llm=llm, prompt=default_prompt)

这里我们看到default_prompt 中只包含了一个变量{input},没有包含任何的前缀信息,这是因为default_chain回答的是哪些无法被识别的属于领域的问题,所以这里就让LLM自由发挥了。

下面我们要创建一个路由prompt模板,这是给后续的路由chain使用的:

MULTI_PROMPT_ROUTER_TEMPLATE = """给定一个原始文本输入到\
一个语言模型并且选择最适合输入的模型提示语。\
你将获得可用的提示语的名称以及该提示语最合适的描述。\
如果你认为修改原始输入最终会导致语言模型得到更好的响应,你也可以修改原始输入。<< FORMATTING >>
返回一个 Markdown 代码片段,其中 JSON 对象的格式如下:
```json
{{{{
"destination": string \ 要使用的提示语的名称或"DEFAULT"
"next_inputs": string \ 原始输入的可能修改版本
}}}}
```记住:"destination"必须是下面指定的候选提示语中的一种,\
如果输入语句不适合任何候选提示语,则它就是"DEFAULT"。记住:"next_inputs"可以只是原始输入,如果你认为不需要做任何修改的话。<< CANDIDATE PROMPTS >>
{destinations}<< INPUT >>
{{input}}<< OUTPUT (remember to include the ```json)>>"""

这上面的模板里,我们允许LLM修改用户的原始输入,如果修改后的输入能产生更好的输出效果的话,也就是说LLM会先判断是否需要修改用户的prompt,如果觉得有必要修改用户的prompt,那么就会把修改过的prompt传送给后续处理特定领域问题的chain,如果没必要修改,那么就直接把用户的prompt传送给后续的chain。下面还需要在路由prompt模板中嵌入destinations_str,它是为识别领域chain提供索引: 

router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str
)print(router_template)

接下来我们要定义最核心的MultiPromptChain,它的三个主要参数为路由chain, 领域chain(集合),默认chain。由这3个主要的参数,我们也能猜测出MultiPromptChain的主要处理逻辑:首先让路由chain来识别用户prompt,并将其传送给特定领域chain,如果没找到合适的领域chain,则由默认chain来处理用户prompt,这里我们给MultiPromptChain的verbose设置为True,这样MultiPromptChain在处理用户prompt的时候会输出一些中间结果,这些中间结果有助于我们观察llm的思维过程是否和我们给它的设定的业务逻辑是一致的。如果不想显示中间结果可以把verbose参数设置为False:

router_chain = LLMRouterChain.from_llm(llm, router_prompt)chain = MultiPromptChain(router_chain=router_chain, destination_chains=destination_chains, default_chain=default_chain, verbose=True)

下面我们来设计一些简单的问题,这些问题可能涉及到不同的领域,我们看看llm是怎么来回答用户问题的:

response = chain.run("2+2等于几?")
print(response)

 这里我们可以看到LLM能按照我们的router_template模板内的要求识别出该问题属于"math"领域,然后由该领域的chain来回答这个问题,并最后返回了正确的答案。

response = chain.run("成吉思汗是谁?")
print(response)

 

 对于“成吉思汗是谁?”这个问题,我们发现LLM对问题进行了修改,它把用户的问题改成了:“成吉思汗是哪个历史时期的重要人物?”,可能是LLM觉得修改后的prompt更有助于后续的领域chain来回答这个问题吧。

response = chain.run("什么是黑体辐射?")
print(response)

response = chain.run("为什么学习机器学习都要使用python语言?")
print(response)

response = chain.run("天上一共有多少颗星星?")
print(response)

对于“天上一共有多少颗星星?”这个问题,我们看到LLM并没有识别出来它属于哪个领域,因此它返回的领域值为None, 然后该问题交由默认chain来回答。

总结

今天我们学习了Langchain的核心组件chain。主要分为LLMChain,Sequential Chains,Router Chain,其中LLMChain是最基本的chain它简单的组合了LLM和promt, Sequential Chains主要包含SimpleSequentialChain和SequentialChain,对于SimpleSequentialChain来说它只是简单的将多个LLMChain串联在一起,前一个chain的输出是后一个chain的输入,所以总体上来说SimpleSequentialChain只有一个输入和一个输出,而SequentialChain则具体多个输入或输出。而Router Chain则是具有路由功能的chain ,它可以将用户的问题进行分类,从而将问题传递给特定的chain。

参考资料

https://learn.deeplearning.ai/langchain/lesson/4/chains

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1789.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UE4 塔防游戏系列】10-防御塔升级

目录 效果 步骤 一、根据防御塔等级修改子弹伤害 二、根据防御塔等级修改子弹速度 三、根据防御塔等级修改检测半径 四、根据防御塔等级修改子弹颜色 五、根据防御塔等级修改换弹时间 效果 步骤 一、根据防御塔等级修改子弹伤害 1. 打开“TowerBaseBullet_Child”&…

现代C++语言核心特征解析——专栏介绍

本专栏用来介绍C新特性&#xff0c;其中包括C11、C14、C17、C20的特性。 下面是C各个版本的出现时间&#xff1a; C版本 时间C112011&#xff5e;2013C142014&#xff5e;2016C172017&#xff5e;2019C202020&#xff5e;2022C232023&#xff5e; 接下来特性更新的顺序不会按…

52 类 110 个常用 Java 组件和框架整理

目录 WEB 容器 数据库 缓存 消息队列 负载均衡 分布式存储 数据库集群 分布式协调 大数据相关 日志收集与分析 搜索 系统监控 分库分表 微服务 RPC 框架 网络框架 MVC 框架 数据层框架 数据库连接池 Redis 客户端 Rest 接口设计与开发 数据参数校验 安全…

【ArcGIS Pro二次开发】(48):三调土地利用现状分类面积汇总统计

之前做了一个三调三大类面积统计&#xff0c;有小伙伴反映太粗糙&#xff0c;想要一个完整的地类面积汇总表。 【ArcGIS Pro二次开发】(35)&#xff1a;三调三大类面积统计 本质上并没有多少难度&#xff0c;之前也做过类似的用地用海汇总表&#xff0c;于是拿出来改一改就好了…

python类中的@classmethod和@staticmethod

classmethod classmethod修饰符对应的函数不需要实例化&#xff0c;无需self参数&#xff0c;但需要cls参数以调用类的属性、类的方法 class A:a 10def printb(self, b):print(b)classmethoddef printa(cls):print(cls.a)print(cls().printb(5))A.printa()"""…

scrapy---爬虫中间件和下载中间件

爬虫中间件 # 爬虫中间件 (了解) middlewares.py class MysfirstscrapySpiderMiddleware:classmethoddef from_crawler(cls, crawler):# This method is used by Scrapy to create your spiders.s cls()crawler.signals.connect(s.spider_opened, signalsignals.spider_opene…

JavaScript的static、this、super关键字介绍

JavaScript的static、this、super关键字介绍 static关键字&#xff1a; ☆ static关键字用于定义类的静态方法和静态属性。 ☆ 静态方法是直接与类相关联的方法&#xff0c;不需要实例化类即可调用。 ☆ 静态属性是类本身的属性&#xff0c;而不是实例的属性。 ☆ 在静态方…

Git学习总结

Git学习 目录 Git学习1.基础1.1 Git Commit:提交记录1.2 Git Branch:创建分支1.3 Git Merge:合并分支的方式一1.4 Git Rebase:合并分支的方式二1.5 查看提交记录&#xff1a;提交历史、详细信息、commit_hash1.6 在提交树上移动 2. 高级2.1 HEAD2.2 相对引用2.3 使用相对引用强…

css 弹性布局的详细说明

CSS弹性布局&#xff08;Flexible Box Layout&#xff0c;简称Flexbox&#xff09;是一种用于创建自适应和可伸缩布局的CSS模块。它提供了一种简单而强大的方式来对容器中的子元素进行布局&#xff0c;使它们能够自动调整大小、排列和对齐。 Flexbox通过在容器和子元素上应用一…

window 命令笔记

1.查看端口 输入“netstat -ano”并回车可以获得所有网络连接活动的列表&#xff0c;在表中&#xff0c;本地地址IP地址后方冒号之后的即是端口号&#xff1a; 如果想要查找特定的端口可以输入命令“netstat -aon|findstr “端口号””&#xff0c;例如“netstat -aon|findstr…

My_window类(带有next和quit按钮)

运行代码&#xff1a; //My_window类&#xff08;带有next和quit按钮&#xff09; #include"std_lib_facilities.h" #include"GUI/Simple_window.h" #include"GUI/GUI.h" #include"GUI/Graph.h" #include"GUI/Point.h"//--…

解决错误 “Plugin ‘maven-clean-plugin:3.1.0‘ not found“ 的方法详解

系列文章目录 文章目录 系列文章目录前言一、检查 Maven 插件版本&#xff1a;二、检查 Maven 仓库&#xff1a;三、检查 Maven 配置&#xff1a;总结 前言 在使用 Maven 构建项目时&#xff0c;可能会遇到错误信息 “Plugin ‘maven-clean-plugin:3.1.0’ not found”&#x…

C++基础算法离散化及区间合并篇

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C算法 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 主要讲解了双指针&#xff0c;位运算&#xff0c;离散化以及区间合并。…

有效的括号(C)

bool isValid(char* s) {ST st;StackInit(&st);while (*s) //遍历 -- 与\0终止{//是左括号 压栈if (*s ( || *s [ *s {){StackPush(&st, *s);s;}else{//应对样例&#xff1a; ’]if (StackEmpty(&st)){StackDestroy(&st);return false;}//不是左括号 应该就…

Spark MLlib快速入门(1)逻辑回归、Kmeans、决策树、Pipeline、交叉验证

Spark MLlib快速入门(1)逻辑回归、Kmeans、决策树案例 除了scikit-learn外&#xff0c;在spark中也提供了机器学习库&#xff0c;即Spark MLlib。 在Spark MLlib机器学习库提供两套算法实现的API&#xff1a;基于RDD API和基于DataFrame API。今天&#xff0c;主要介绍下Data…

docker k8s

Docker docker到底与一般的虚拟机有什么不同呢&#xff1f; 我们知道一般的linux系统即GNU/Linux系统包括两个部分&#xff0c;linux系统内核GNU提供的大量自由软件&#xff0c;而centos就是众多GNU/Linux系统中的一个。 虚拟机会在宿主机上虚拟出一个完整的操作系统与宿主机完…

在 3ds Max 中对链模型进行摆放姿势处理

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 建模和“摆姿势”3D链可能看起来是一项繁琐的工作&#xff0c;但实际上可以通过使用阵列工具并将链中的链接视为骨骼来轻松完成。在本教程中&#xff0c;我将向您展示如何对链条进行建模&#xff0c;并通过…

程序员的自我修养(4)

可执行文件的装载与进程 代码经过预编译&#xff0c;编译&#xff0c;汇编&#xff0c;链接过程后生成可执行文件&#xff0c;但可执行文件只有装载到内存程序才可以运行&#xff0c;这节讲可执行文件在linux下的装载过程&#xff0c;以及与进程的关系等等。 1.程序与进程的关…

oled拼接屏在柳州的户外广告中有哪些应用展现?

柳州oled拼接屏是一种高端的显示屏&#xff0c;它采用了OLED技术&#xff0c;具有高亮度、高对比度、高色彩饱和度、高刷新率等优点&#xff0c;能够呈现出更加真实、清晰、细腻的图像效果。 同时&#xff0c;柳州oled拼接屏还具有拼接功能&#xff0c;可以将多个屏幕拼接在一…

vue element select下拉框回显展示数字

vue element select下拉框回显展示数字 问题截图&#xff1a; 下拉框显示数字可以从数据类型来分析错误&#xff0c;接收的数据类型是字符串&#xff0c;但是value是数字类型 <el-form-item prop"classifyLabelId" :label"$t(item.classifyLabelId)"…