链(Chains)结合提示(Prompt)使用大语言模型(LLM),处理文本或数据。它们可接受多输入,格式化后传递给LLM,可组合构建复杂操作。
一、大语言模型链
LLMChain是基础且强大的链,为其他链提供支持。
1.1 初始化语言模型
import warnings
warnings.filterwarnings('ignore')
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.chains import LLMChain
# 这里我们将参数temperature设置为0.0,从而减少生成答案的随机性。
# 如果你想要每次得到不一样的有新意的答案,可以尝试调整该参数。
llm = ChatOpenAI(temperature=0.0)
1.2 初始化提示模版
初始化提示,这个提示将接受一个名为product的变量。该prompt将要求LLM生成一个描述制造该产品的公司的最佳名称
prompt = ChatPromptTemplate.from_template(“描述制造{product}的一个公司的最佳名称是什么?”)
1.3 构建大语言模型链
将大语言模型(LLM)和提示(Prompt)组合成链。这个大语言模型链非常简单,可以让我们以一种顺序的方式去通过运行提示并且结合到大语言模型中。
chain = LLMChain(llm=llm, prompt=prompt)
1.4 运行大语言模型链
我们有一个名为"Queen Size Sheet Set"的产品,我们可以通过使用chain.run将其通过这个链运行
product = "大号床单套装"
chain.run(product)
‘“豪华床纺”’
您可以输入任何产品描述,然后查看链将输出什么结果。
二、简单顺序链
顺序链(SequentialChains)按预定顺序执行,简单顺序链是其基础形式,每个步骤的输出作为下一个步骤的输入。
from langchain.chains import SimpleSequentialChain
llm = ChatOpenAI(temperature=0.9)
2.1 创建两个子链
# 提示模板 1 :这个提示将接受产品并返回最佳名称来描述该公司
first_prompt = ChatPromptTemplate.from_template(
"描述制造{product}的一个公司的最好的名称是什么"
)
chain_one = LLMChain(llm=llm, prompt=first_prompt)
# 提示模板 2 :接受公司名称,然后输出该公司的长为20个单词的描述
second_prompt = ChatPromptTemplate.from_template(
"写一个20字的描述对于下面这个\
公司:{company_name}的"
)
chain_two = LLMChain(llm=llm, prompt=second_prompt)
2.2 构建简单顺序链
我们可以组合两个LLMChain,在单一步骤中生成公司名称和描述。
overall_simple_chain = SimpleSequentialChain(chains=[chain_one, chain_two],
verbose=True)
给一个输入,然后运行上面的链
2.3 运行简单顺序链
product = "大号床单套装"
overall_simple_chain.run(product)
> Entering new SimpleSequentialChain chain...
优床制造公司
优床制造公司是一家专注于生产高品质床具的公司。
> Finished chain.
'优床制造公司是一家专注于生产高品质床具的公司。'
三、 顺序链
简单顺序链适用于单一输入输出,多输入输出需用顺序链。
import pandas as pd
from langchain.chains import SequentialChain
from langchain.chat_models import ChatOpenAI #导入OpenAI模型
from langchain.prompts import ChatPromptTemplate #导入聊天提示模板
from langchain.chains import LLMChain #导入LLM链。
llm = ChatOpenAI(temperature=0.9)
接下来我们将创建一系列的链,然后一个接一个使用他们
3.1 创建四个子链
#子链1
# prompt模板 1: 翻译成英语(把下面的review翻译成英语)
first_prompt = ChatPromptTemplate.from_template(
"把下面的评论review翻译成英文:"
"\n\n{Review}"
)
# chain 1: 输入:Review 输出:英文的 Review
chain_one = LLMChain(llm=llm, prompt=first_prompt, output_key="English_Review")
#子链2
# prompt模板 2: 用一句话总结下面的 review
second_prompt = ChatPromptTemplate.from_template(
"请你用一句话来总结下面的评论review:"
"\n\n{English_Review}"
)
# chain 2: 输入:英文的Review 输出:总结
chain_two = LLMChain(llm=llm, prompt=second_prompt, output_key="summary")
#子链3
# prompt模板 3: 下面review使用的什么语言
third_prompt = ChatPromptTemplate.from_template(
"下面的评论review使用的什么语言:\n\n{Review}"
)
# chain 3: 输入:Review 输出:语言
chain_three = LLMChain(llm=llm, prompt=third_prompt, output_key="language")
#子链4
# prompt模板 4: 使用特定的语言对下面的总结写一个后续回复
fourth_prompt = ChatPromptTemplate.from_template(
"使用特定的语言对下面的总结写一个后续回复:"
"\n\n总结: {summary}\n\n语言: {language}"
)
# chain 4: 输入: 总结, 语言 输出: 后续回复
chain_four = LLMChain(llm=llm, prompt=fourth_prompt, output_key="followup_message")
3.2 对四个子链进行组合
#输入:review
#输出:英文review,总结,后续回复
overall_chain = SequentialChain(
chains=[chain_one, chain_two, chain_three, chain_four],
input_variables=["Review"],
output_variables=["English_Review", "summary","followup_message"],
verbose=True
)
通过链传递评论,从法语原文到英文翻译,再到基于英文的总结,最后是法语续写。
df = pd.read_csv('../data/Data.csv')
review = df.Review[5]
overall_chain(review)
> Entering new SequentialChain chain...
> Finished chain.
{'Review': "Je trouve le goût médiocre. La mousse ne tient pas, c'est bizarre. J'achète les mêmes dans le commerce et le goût est bien meilleur...\nVieux lot ou contrefaçon !?",
'English_Review': "I find the taste mediocre. The foam doesn't hold, it's weird. I buy the same ones in stores and the taste is much better...\nOld batch or counterfeit!?",
'summary': "The reviewer finds the taste mediocre, the foam doesn't hold well, and suspects the product may be either an old batch or a counterfeit.",
'followup_message': "后续回复(法语):Merci beaucoup pour votre avis. Nous sommes désolés d'apprendre que vous avez trouvé le goût médiocre et que la mousse ne tient pas bien. Nous prenons ces problèmes très au sérieux et nous enquêterons sur la possibilité que le produit soit soit un ancien lot, soit une contrefaçon. Nous vous prions de nous excuser pour cette expérience décevante et nous ferons tout notre possible pour résoudre ce problème. Votre satisfaction est notre priorité et nous apprécions vos commentaires précieux."}
四、 路由链
若需更复杂操作,可使用路由链根据输入类型将任务路由至相应子链。
路由器由两个组件组成:
- 路由链(Router Chain):路由器链本身,负责选择要调用的下一个链
- destination_chains:路由器链可以路由到的链
例如,通过不同prompt在各种链间进行路由。
from langchain.chains.router import MultiPromptChain #导入多提示链
from langchain.chains.router.llm_router import LLMRouterChain,RouterOutputParser
from langchain.prompts import PromptTemplate
llm = ChatOpenAI(temperature=0)
4.1 定义提示模板
定义适用于不同场景的提示模板。
# 中文
#第一个提示适合回答物理问题
physics_template = """你是一个非常聪明的物理专家。 \
你擅长用一种简洁并且易于理解的方式去回答问题。\
当你不知道问题的答案时,你承认\
你不知道.
这是一个问题:
{input}"""
#第二个提示适合回答数学问题
math_template = """你是一个非常优秀的数学家。 \
你擅长回答数学问题。 \
你之所以如此优秀, \
是因为你能够将棘手的问题分解为组成部分,\
回答组成部分,然后将它们组合在一起,回答更广泛的问题。
这是一个问题:
{input}"""
#第三个适合回答历史问题
history_template = """你是以为非常优秀的历史学家。 \
你对一系列历史时期的人物、事件和背景有着极好的学识和理解\
你有能力思考、反思、辩证、讨论和评估过去。\
你尊重历史证据,并有能力利用它来支持你的解释和判断。
这是一个问题:
{input}"""
#第四个适合回答计算机问题
computerscience_template = """ 你是一个成功的计算机科学专家。\
你有创造力、协作精神、\
前瞻性思维、自信、解决问题的能力、\
对理论和算法的理解以及出色的沟通技巧。\
你非常擅长回答编程问题。\
你之所以如此优秀,是因为你知道 \
如何通过以机器可以轻松解释的命令式步骤描述解决方案来解决问题,\
并且你知道如何选择在时间复杂性和空间复杂性之间取得良好平衡的解决方案。
这还是一个输入:
{input}"""
4.2 对提示模版进行命名和描述
定义提示模板后,为每个命名并描述。如物理学模板用于回答相关问题,信息传递给路由链,由其决定何时使用子链。
# 中文
prompt_infos = [
{
"名字": "物理学",
"描述": "擅长回答关于物理学的问题",
"提示模板": physics_template
},
{
"名字": "数学",
"描述": "擅长回答数学问题",
"提示模板": math_template
},
{
"名字": "历史",
"描述": "擅长回答历史问题",
"提示模板": history_template
},
{
"名字": "计算机科学",
"描述": "擅长回答计算机科学问题",
"提示模板": computerscience_template
}
]
LLMRouterChain(此链使用 LLM 来确定如何路由事物)
在这里需要多提示链进行不同提示模板间路由,但也可路由任何类型链。
实现大模型路由器链类,用语言模型在子链间路由,使用上述描述和名称。
4.3 基于提示模版信息创建相应目标链
目标链是由路由链调用的链,每个目标链都是一个语言模型链
destination_chains = {}
for p_info in prompt_infos:
name = p_info["名字"]
prompt_template = p_info["提示模板"]
prompt = ChatPromptTemplate.from_template(template=prompt_template)
chain = LLMChain(llm=llm, prompt=prompt)
destination_chains[name] = chain
destinations = [f"{p['名字']}: {p['描述']}" for p in prompt_infos]
destinations_str = "\n".join(destinations)
4.4 创建默认目标链
除了目标链,还需一个默认链,用于路由器无法确定子链时。如输入与示例学科无关时调用。
default_prompt = ChatPromptTemplate.from_template("{input}")
default_chain = LLMChain(llm=llm, prompt=default_prompt)
4.5 定义不同链之间的路由模板
这包括要完成的任务的说明以及输出应该采用的特定格式。
注意:为帮助"gpt-3.5-turbo"模型理解模板,原教程增补了示例。"text-davinci-003"或"gpt-4-0613"模型能更好适应。
例如:
<< INPUT >>
“What is black body radiation?”
<< OUTPUT >>
{{{{
"destination": string \ name of the prompt to use or "DEFAULT"
"next_inputs": string \ a potentially modified version of the original input
}}}}
# 多提示路由模板
MULTI_PROMPT_ROUTER_TEMPLATE = """给语言模型一个原始文本输入,\
让其选择最适合输入的模型提示。\
系统将为您提供可用提示的名称以及最适合改提示的描述。\
如果你认为修改原始输入最终会导致语言模型做出更好的响应,\
你也可以修改原始输入。
<< 格式 >>
返回一个带有JSON对象的markdown代码片段,该JSON对象的格式如下:
```json
{{{{
"destination": 字符串 \ 使用的提示名字或者使用 "DEFAULT"
"next_inputs": 字符串 \ 原始输入的改进版本
}}}}
记住:“destination”必须是下面指定的候选提示名称之一,\
或者如果输入不太适合任何候选提示,\
则可以是 “DEFAULT” 。
记住:如果您认为不需要任何修改,\
则 “next_inputs” 可以只是原始输入。
<< 候选提示 >>
{destinations}
<< 输入 >>
{{input}}
<< 输出 (记得要包含 ```json)>>
样例:
<< 输入 >>
"什么是黑体辐射?"
<< 输出 >>
```json
{{{{
"destination": 字符串 \ 使用的提示名字或者使用 "DEFAULT"
"next_inputs": 字符串 \ 原始输入的改进版本
}}}}
"""
4.6 构建路由链
我们创建了一个通用的路由器模板,适用于多种目标,可以添加不同学科如英语或拉丁语。接着,基于此模板生成提示模板。最终,通过传入语言模型和路由提示创建路由链,包含路由输出解析,这关键地帮助链决定子链路间的路由。
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(
destinations=destinations_str
)
router_prompt = PromptTemplate(
template=router_template,
input_variables=["input"],
output_parser=RouterOutputParser(),
)
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
4.7 创建整体链路
#多提示链
chain = MultiPromptChain(router_chain=router_chain, #l路由链路
destination_chains=destination_chains, #目标链路
default_chain=default_chain, #默认链路
verbose=True
)
4.8 进行提问
如果我们问一个物理问题,我们希望看到他被路由到物理链路。
chain.run(“什么是黑体辐射?”)
> Entering new MultiPromptChain chain...
物理学: {'input': '什么是黑体辐射?'}
> Finished chain.
'黑体辐射是指一个理想化的物体,它能够完全吸收并且以最高效率地辐射出所有入射到它上面的电磁辐射。这种辐射的特点是它的辐射强度与波长有关,且在不同波长下的辐射强度符合普朗克辐射定律。黑体辐射在物理学中有广泛的应用,例如研究热力学、量子力学和宇宙学等领域。'
如果我们问一个数学问题,我们希望看到他被路由到数学链路。
chain.run(“2+2等于多少?”)
> Entering new MultiPromptChain chain...
数学: {'input': '2+2等于多少?'}
> Finished chain.
'2+2等于4。'
如果我们传递一个与任何子链路都无关的问题时,会发生什么呢?
如果问题与子链路无关,如生物学问题,将传递至默认链路,它本身只是对语言模型的通用调用。默认链路是语言模型的通用调用。由于语言模型广泛知识,它仍可提供帮助。
> Entering new MultiPromptChain chain...
物理学: {'input': '为什么我们身体里的每个细胞都包含DNA?'}
> Finished chain.
'我们身体里的每个细胞都包含DNA,因为DNA是遗传信息的载体。DNA是由四