LangChain 核心模块学习:Chains
对于简单的大模型应用,单独使用语言模型(LLMs)是可以的。
但更复杂的大模型应用需要将 LLMs
和 Chat Models
链接在一起。 要么彼此链接,要么与其他组件链接。
LangChain 为这种"链式"应用程序提供了 Chain
接口。
LangChain 以通用方式定义了 Chain
,它是对组件进行调用序列的集合,其中可以包含其他链。
Chain Class 基类
类继承关系:
Chain --> <name>Chain # Examples: LLMChain, MapReduceChain, RouterChain
python
# 定义一个名为Chain的基础类
class Chain(Serializable, Runnable[Dict[str, Any], Dict[str, Any]], ABC):
"""为创建结构化的组件调用序列的抽象基类。
链应该用来编码对组件的一系列调用,如模型、文档检索器、其他链等,并为此序列提供一个简单的接口。
Chain接口使创建应用程序变得容易,这些应用程序是:
- 有状态的:给任何Chain添加Memory可以使它具有状态,
- 可观察的:向Chain传递Callbacks来执行额外的功能,如记录,这在主要的组件调用序列之外,
- 可组合的:Chain API足够灵活,可以轻松地将Chains与其他组件结合起来,包括其他Chains。
链公开的主要方法是:
- `__call__`:链是可以调用的。`__call__`方法是执行Chain的主要方式。它将输入作为一个字典接收,并返回一个字典输出。
- `run`:一个方便的方法,它以args/kwargs的形式接收输入,并将输出作为字符串或对象返回。这种方法只能用于一部分链,不能像`__call__`那样返回丰富的输出。
"""
# 调用链
def invoke(
self, input: Dict[str, Any], config: Optional[runnableConfig] = None
) -> Dict[str, Any]:
"""传统调用方法。"""
return self(input, **(config or {}))
# 链的记忆,保存状态和变量
memory: Optional[BaseMemory] = None
"""可选的内存对象,默认为None。
内存是一个在每个链的开始和结束时被调用的类。在开始时,内存加载变量并在链中传递它们。在结束时,它保存任何返回的变量。
有许多不同类型的内存,请查看内存文档以获取完整的目录。"""
# 回调,可能用于链的某些操作或事件。
callbacks: Callbacks = Field(default=None, exclude=True)
"""可选的回调处理程序列表(或回调管理器)。默认为None。
在对链的调用的生命周期中,从on_chain_start开始,到on_chain_end或on_chain_error结束,都会调用回调处理程序。
每个自定义链可以选择调用额外的回调方法,详细信息请参见Callback文档。"""
# 是否详细输出模式
verbose: bool = Field(default_factory=_get_verbosity)
"""是否以详细模式运行。在详细模式下,一些中间日志将打印到控制台。默认值为`langchain.verbose`。"""
# 与链关联的标签
tags: Optional[List[str]] = None
"""与链关联的可选标签列表,默认为None。
这些标签将与对这个链的每次调用关联起来,并作为参数传递给在`callbacks`中定义的处理程序。
你可以使用这些来例如识别链的特定实例与其用例。"""
# 与链关联的元数据
metadata: Optional[Dict[str, Any]] = None
"""与链关联的可选元数据,默认为None。
这些元数据将与对这个链的每次调用关联起来,并作为参数传递给在`callbacks`中定义的处理程序。
你可以使用这些来例如识别链的特定实例与其用例。"""
LLMChain
LLMChain 是 LangChain 中最简单的链,作为其他复杂 Chains 和 Agents 的内部调用,被广泛应用。
一个LLMChain由PromptTemplate和语言模型(LLM or Chat Model)组成。它使用直接传入(或 memory 提供)的 key-value 来规范化生成 Prompt Template(提示模板),并将生成的 prompt (格式化后的字符串)传递给大模型,并返回大模型输出。
from langchain_openai import OpenAI
from langchain.prompts import PromptTemplate
llm = OpenAI(model_name="gpt-3.5-turbo-instruct", temperature=0.9, max_tokens=500)
prompt = PromptTemplate(
input_variables=["product"],
template="给制造{product}的有限公司取10个好名字,并给出完整的公司名称",
)
from langchain.chains import LLMChain
chain = LLMChain(llm=llm, prompt=prompt)
print(chain.invoke({
'product': "性能卓越的GPU"
}))
输出:
{'product': '性能卓越的GPU', 'text': '\n\n1. 星火卓越科技有限公司\n2. 利益达太空科技有限公司\n3. 未来视界智能科技有限公司\n4. 强力算力科技有限公司\n5. 极速计算科技有限公司\n6. 无限创想智能科技有限公司\n7. 高能创新科技有限公司\n8. 天马行空科技有限公司\n9. 极致计算科技有限公司\n10. 全能加速科技有限公司'}
Sequential Chain
串联式调用语言模型(将一个调用的输出作为另一个调用的输入)。
顺序链(Sequential Chain )允许用户连接多个链并将它们组合成执行特定场景的流水线(Pipeline)。有两种类型的顺序链:
- SimpleSequentialChain:最简单形式的顺序链,每个步骤都具有单一输入/输出,并且一个步骤的输出是下一个步骤的输入。
- SequentialChain:更通用形式的顺序链,允许多个输入/输出。
使用 SimpleSequentialChain 实现戏剧摘要和评论(单输入/单输出)
# 这是一个 LLMChain,用于根据剧目的标题撰写简介。
llm = OpenAI(temperature=0.7, max_tokens=1000)
template = """你是一位剧作家。根据戏剧的标题,你的任务是为该标题写一个简介。
标题:{title}
剧作家:以下是对上述戏剧的简介:"""
prompt_template = PromptTemplate(input_variables=["title"], template=template)
synopsis_chain = LLMChain(llm=llm, prompt=prompt_template)
# 这是一个LLMChain,用于根据剧情简介撰写一篇戏剧评论。
# llm = OpenAI(temperature=0.7, max_tokens=1000)
template = """你是《纽约时报》的戏剧评论家。根据剧情简介,你的工作是为该剧撰写一篇评论。
剧情简介:
{synopsis}
以下是来自《纽约时报》戏剧评论家对上述剧目的评论:"""
prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template)
# 这是一个SimpleSequentialChain,按顺序运行这两个链
from langchain.chains import SimpleSequentialChain
overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)
review = overall_chain.invoke("星球大战第九季")
输出:
> Entering new SimpleSequentialChain chain...
《星球大战第九季》是一部充满惊险刺激的科幻戏剧,它延续了《星球大战》系列的传奇故事。在这一季中,我们将见证新的冒险和挑战,因为银河系再次陷入战争的深渊。反抗军和第一秩序之间的战争仍在继续,但这一次,他们将面临着更加强大的敌人。新的角色和老朋友将再次出现,与我们的英雄们一起并肩作战。随着战争的进展,我们也将见证力量和黑暗的永恒对抗,以及对自由和正义的不懈追求。在这场史诗般的战役中,我们将看到勇气、牺牲和爱的力量,同时也会思考我们对未来的选择和命运。《星球大战第九季》将带给观众无与伦比的视觉盛宴和感人心弦的故事,让我们一起来见证这一壮丽的星际冒险!
《星球大战第九季》是一部不容错过的史诗般的科幻戏剧。该剧延续了《星球大战》系列的传奇故事,将观众带入一个充满惊险和刺激的银河系。在这一季中,我们将再次与我们熟悉的英雄们一起并肩作战,同时也会见证新的挑战和冒险。
该剧不仅仅是一场视觉盛宴,更是一部让人思考的故事。通过力量和黑暗的永恒对抗,以及对自由和正义的追求,观众将被带入一个关于命运和选择的深刻思考。在这场史诗般的战役中,我们将见证勇气、牺牲和爱的力量,这些都是《星球大战》系列一直探讨的重要主题。
该剧不仅有着令人惊叹的特效和精彩的动作场面,更重要的是它成功地将这些元素与深刻的人物关系和情感表达相结合。观众将会被带入一个充满希望和感动的星际冒险,与角色们一起经历他们的成长和转变。
总的来说,《星球大战第九季》是一部充满情感和故事的出色作品,它将让观众沉浸在一个充满惊喜和挑战的星际世界。无论您是《星球大战》系列的忠实粉丝,还是对科幻题材感兴趣的观众,这部戏剧都会给您带来一场令人难忘的体验。不容错过!
> Finished chain.
Router Chain: 实现条件判断的大模型调用
这段代码构建了一个可定制的链路系统,用户可以提供不同的输入提示,并根据这些提示获取适当的响应。
主要逻辑:从prompt_infos
创建多个LLMChain
对象,并将它们保存在一个字典中,然后创建一个默认的ConversationChain
,最后创建一个带有路由功能的MultiPromptChain
。
from langchain.chains.router import MultiPromptChain
from langchain_openai import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
physics_template = """你是一位非常聪明的物理教授。
你擅长以简洁易懂的方式回答关于物理的问题。
当你不知道某个问题的答案时,你会坦诚承认。
这是一个问题:
{input}"""
math_template = """你是一位很棒的数学家。你擅长回答数学问题。
之所以如此出色,是因为你能够将难题分解成各个组成部分,
先回答这些组成部分,然后再将它们整合起来回答更广泛的问题。
这是一个问题:
{input}"""
prompt_infos = [
{
"name": "物理",
"description": "适用于回答物理问题",
"prompt_template": physics_template,
},
{
"name": "数学",
"description": "适用于回答数学问题",
"prompt_template": math_template,
},
]
llm = OpenAI(model_name="gpt-3.5-turbo-instruct")
# 创建一个空的目标链字典,用于存放根据prompt_infos生成的LLMChain。
destination_chains = {}
# 遍历prompt_infos列表,为每个信息创建一个LLMChain。
for p_info in prompt_infos:
name = p_info["name"] # 提取名称
prompt_template = p_info["prompt_template"] # 提取模板
# 创建PromptTemplate对象
prompt = PromptTemplate(template=prompt_template, input_variables=["input"])
# 使用上述模板和llm对象创建LLMChain对象
chain = LLMChain(llm=llm, prompt=prompt)
# 将新创建的chain对象添加到destination_chains字典中
destination_chains[name] = chain
# 创建一个默认的ConversationChain
default_chain = ConversationChain(llm=llm, output_key="text")
使用 LLMRouterChain 实现条件判断调用
这段代码定义了一个chain对象(LLMRouterChain),该对象首先使用router_chain来决定哪个destination_chain应该被执行,如果没有合适的目标链,则默认使用default_chain。
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
# 从prompt_infos中提取目标信息并将其转化为字符串列表
destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
# 使用join方法将列表转化为字符串,每个元素之间用换行符分隔
destinations_str = "\n".join(destinations)
# 根据MULTI_PROMPT_ROUTER_TEMPLATE格式化字符串和destinations_str创建路由模板
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)
# 创建路由的PromptTemplate
router_prompt = PromptTemplate(
template=router_template,
input_variables=["input"],
output_parser=RouterOutputParser(),
)
# 使用上述路由模板和llm对象创建LLMRouterChain对象
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
print(destinations)
输出:['物理: 适用于回答物理问题', '数学: 适用于回答数学问题']
# 创建MultiPromptChain对象,其中包含了路由链,目标链和默认链。
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=default_chain,
verbose=True,
)
print(chain.invoke("黑体辐射是什么??"))
输出:
> Entering new MultiPromptChain chain...
物理: {'input': 'What is blackbody radiation?'}
> Finished chain.
{'input': 'What is blackbody radiation?', 'text': '\n\n黑体辐射是一种物理现象,指的是由于物体内部的热运动导致的电磁辐射。简单来说,物体在受热时会发出电磁波,这些波的频率和强度取决于物体的温度。黑体辐射的特点是它的辐射频率与物体的温度无关,而只与物体的结构和性质有关。这个概念在热力学和量子力学中都有重要的应用。'}