5分钟理透LangChain的Chain

langchain,chain · 浏览次数 : 0

小编点评

LangChain中的Chain概念指的是对LangChain中多个组件的一系列调用。Chain的主要作用是将多个单一任务串联起来,形成一个大的链条,从而完成复杂的任务。使用Chain可以简化代码逻辑,使整个流程更加直观和易于管理。 Chain的必要性在于,期望的LLM能够处理许多复杂任务,而不仅仅是简单的一问一答或单一任务。通过Chain,可以将多个单一任务串起来,形成一个大的链条,多个步骤共同完成某个复杂任务。Chain可以看作是流水线,通过使用Chain,可以将各个步骤定义为独立的模块,然后按顺序串联起来。 在没有Chain的情况下,实现相同功能的代码会比较繁琐,变量和代码也多,不够直观,很容易出错。而有了Chain之后,可以实现相同的功能,代码更简洁,也很直观。 LCEL(LangChain Expression Language)是LangChain中用于构建链的表达式语言。使用LCEL可以让构建链的过程更简单,让链的表达力更清晰更简单。

正文

LangChain几乎是LLM应用开发的第一选择,它的野心也比较大,它致力于将自己打造成LLM应用开发的最大社区。而LangChain最核心的部分非 Chain 莫属。

Chain到底是个啥,概念比较模糊,像雾像雨又像风,这篇文章将带你快速理透 LangChain 中的 Chain 概念。

1. Chain是核心

LangChain的Chain到底是什么?一句话总结:Chain是指对 LangChain 多个组件的一系列调用。

再看看官网的解释:Chain是指调用的序列 - 无论是调用 LLM、工具还是数据预处理步骤,主要支持的方法是使用 LCEL。

官网里还提到了LCEL,LCEL是LangChain 表达式语言,是一种更加高效简介的链接 LangChain 组件的方式,也是官网推荐的方式。

从下图官网的描述,也可以看到,Chain可以是从最简单的“prompt + LLM”链 到 最复杂的链(运行了包含 100 多个步骤的链)。

2. 为什么需要Chain

我们所期待的LLM是能处理许多复杂任务,而非简单的一问一答,也不是简单的处理单一任务。

所以,最终我期待的LLM处理任务的流程应该是这样,它中间的复杂过程对用户来说是一个黑盒:

既然定位是完成复杂任务,那自然就需要通过某个机制将多个单一任务串起来,形成一个大的链条,多个步骤共同完成某个复杂任务。

***Chain可以将多个步骤连接到一起,最终完成各种复杂繁琐的任务。***这就是Chain存在的必要性了。我很喜欢LangChain的Logo,很形象地表达了这一思想。

Chain需要对多个组件一系列的调用或者一系列的串联,这样才能完成复杂任务。当然,我们也可以把 Chain 看作是流水线。通过使用 Chain,你可以将各个步骤定义为独立的模块,然后按顺序串联起来。这样不仅大大简化了代码逻辑,也使得整个流程更加直观和易于管理。

而LCEL的存在,也只是为了让构建链的过程更简单,让链的表达力更清晰更简单。

接下来,我将通过一个示例展示没有 Chain有Chain的2种实现方式,以便更清晰地理解 Chain 的价值。

3. 如果没有Chain

这里举个例子,比如:我们给LLM输入一段项目描述,让LLM给这个项目起一个名称Slogan

如果不使用Chain的话,我们可以这样实现。

# 本次需求:我们给LLM输入一段项目描述,让LLM给这个项目起一个名称和Slogan
# 以下是实现:

from langchain.prompts import PromptTemplate, ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser


proj_desc = """
    我们本次的项目是去森林里探险救援,我们有一个10人小队,
    我们要到达一个叫做“蝴蝶谷”的目的地,去那里解救一位被困的科学家。
    这期间我们可能会遇到许多危险,我们需要共同合作,互相帮助,历经磨难,才能到达目的地。
    我们的任务是要在5天内到达目的地并且救出探险家,才算完成这次探险,否则任务失败,我们将受到惩罚。
    出发前我们要各自准备好自己的装备和干粮,加油!
"""


def name_slogan_by_desc(project_desc):
    """
    根据项目描述,生成项目名称和slogan
    """
    str_parser = StrOutputParser()

    promt_template_project_name = "请你根据<desc>标签里的关于某个项目的描述,生成一个项目名称,只需要返回项目名称。<desc>{project_desc}</desc>"
    promt_project_name = PromptTemplate.from_template(promt_template_project_name)
    final_promt_project_name = promt_project_name.invoke({"project_desc": project_desc})
    res_project_name = model.invoke(final_promt_project_name)
    parsed_res_project_name = str_parser.invoke(res_project_name)


    promt_template_slogan = "请你根据<desc>标签里的关于某个项目的描述,和这个项目的名称{project_name},给这个项目起一个slogan,slogan要求干脆简洁积极向上,只返回slogan。<desc>{project_desc}</desc>"
    promt_slogan = PromptTemplate.from_template(promt_template_slogan)
    final_promt_slogan = promt_slogan.invoke(
        {"project_desc": project_desc, "project_name": parsed_res_project_name}
    )
    response_slogan = model.invoke(final_promt_slogan)
    parsed_response_slogan = str_parser.invoke(response_slogan)


    final_result = {
        "project_name": parsed_res_project_name,
        "slogan": parsed_response_slogan,
    }
    return final_result

# 输入项目描述,输出项目名称和slogan
result = name_slogan_by_desc(proj_desc)
print(result)

执行结果如下:

{'project_name': '蝴蝶谷救援行动', 'slogan': '拯救科学家,共同合作,蝴蝶谷等你来!'}

可以看到,实现过程比较繁琐,变量和代码也多,不够直观,很容易出错。这还只是简单场景,如果碰到复杂场景就更麻烦了。

4. 因为有了Chain

接下来,我们使用 LangChain 的 Chain 功能,来实现相同的功能。代码如下:

# 本次需求:我们给LLM输入一段项目描述,让LLM给这个项目起一个名称和Slogan
# 以下是实现:

from operator import itemgetter
from langchain.prompts import PromptTemplate, ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain.chains import LLMChain, SequentialChain

proj_desc = """
    我们本次的项目是去森林里探险救援,我们有一个10人小队,
    我们要到达一个叫做“蝴蝶谷”的目的地,去那里解救一位被困的科学家。
    这期间我们可能会遇到许多危险,我们需要共同合作,互相帮助,历经磨难,才能到达目的地。
    我们的任务是要在5天内到达目的地并且救出探险家,才算完成这次探险,否则任务失败,我们将受到惩罚。
    出发前我们要各自准备好自己的装备和干粮,加油!
"""

def name_slogan_by_desc(project_desc):
    """
    根据项目描述,生成项目名称和slogan
    """

    # 第1条链
    promt_template_project_name = "请你根据<desc>标签里的关于某个项目的描述,生成一个项目名称,只需要返回项目名称。<desc>{project_desc}</desc>"
    chain_one = LLMChain(
        llm=model,
        prompt=PromptTemplate.from_template(promt_template_project_name),
        output_parser=StrOutputParser(),
        output_key="project_name",
    )

    # 第2条链
    promt_template_slogan = "请你根据<desc>标签里的关于某个项目的描述,和这个项目的名称{project_name},给这个项目起一个slogan,slogan要求干脆简洁积极向上,只返回slogan。<desc>{project_desc}</desc>"
    chain_two = LLMChain(
        llm=model,
        prompt=PromptTemplate.from_template(promt_template_slogan),
        output_parser=StrOutputParser(),
        output_key="slogan",
    )

    # 串联两条链
    sequential_chain = SequentialChain(
        chains=[chain_one, chain_two],
        input_variables=["project_desc"],
        output_variables=["project_name", "slogan"],
    )
    final_res = sequential_chain(project_desc)

    final_result = {
        "project_name": final_res["project_name"],
        "slogan": final_res["slogan"],
    }
    return final_result

# 输入项目描述,输出项目名称和slogan
result = name_slogan_by_desc(proj_desc)
print(result)

执行结果如下:

{'project_name': '蝴蝶谷救援行动', 'slogan': '团结合作,共赴蝴蝶谷'}

可以看到代码更简洁,也很直观,当然,也可以使用LCEL让整个链条更加简洁清晰。

5. LCEL表达式

LCEL方式的代码如下:

# 本次需求:我们给LLM输入一段项目描述,让LLM给这个项目起一个名称和Slogan
# 以下是实现:

from langchain.prompts import PromptTemplate, ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

proj_desc = """
    我们本次的项目是去森林里探险救援,我们有一个10人小队,
    我们要到达一个叫做“蝴蝶谷”的目的地,去那里解救一位被困的科学家。
    这期间我们可能会遇到许多危险,我们需要共同合作,互相帮助,历经磨难,才能到达目的地。
    我们的任务是要在5天内到达目的地并且救出探险家,才算完成这次探险,否则任务失败,我们将受到惩罚。
    出发前我们要各自准备好自己的装备和干粮,加油!
"""

def name_slogan_by_desc(project_desc):
    """
    根据项目描述,生成项目名称和slogan
    """

    # 第1条链
    promt_template_project_name = "请你根据<desc>标签里的关于某个项目的描述,生成一个项目名称,只需要返回项目名称。<desc>{project_desc}</desc>"
    chain_one = (
        PromptTemplate.from_template(promt_template_project_name)
        | model
        | {"project_name": StrOutputParser(), "project_desc": lambda x: project_desc}
    )

    # 第2条链
    promt_template_slogan = "请你根据<desc>标签里的关于某个项目的描述,和这个项目的名称{project_name},给这个项目起一个slogan,slogan要求干脆简洁积极向上,只返回slogan。<desc>{project_desc}</desc>"
    chain_two = (
        PromptTemplate.from_template(promt_template_slogan)
        | model
        | {"slogan": StrOutputParser(), "project_info": lambda x: chain_one}
    )

    # 串联两条链
    final_chain = chain_one | chain_two
    final_res = final_chain.invoke({"project_desc": project_desc})

    final_result = {
        "project_name": final_res["project_info"]["project_name"],
        "slogan": final_res["slogan"],
    }

    return final_result

# 输入项目描述,输出项目名称和slogan
result = name_slogan_by_desc(proj_desc)
print(result)

普通方式和LCEL方式的核心代码对比:

  • 普通方式

  • LCEL方式

6. 总结

本文主要聊了 LangChain 中的 Chain 概念。Chain 是 LangChain 中的核心组件,我们对多个组件的一系列调用就是Chain。

使用Chain可以让构建复杂的任务,更加清晰简洁。

=====>>>>>> 关于我 <<<<<<=====

本篇完结!欢迎点赞 关注 收藏!!!

原文链接:https://mp.weixin.qq.com/s/IdaO8CeS1TKoQDCcjMqWsg

与5分钟理透LangChain的Chain相似的内容:

5分钟理透LangChain的Chain

LangChain几乎是LLM应用开发的第一选择,它的野心也比较大,它致力于将自己打造成LLM应用开发的最大社区。而LangChain最核心的部分非 Chain 莫属。

5分钟了解LangChain的路由链

路由链(RouterChain)是由LLM根据输入的Prompt去选择具体的某个链。路由链中一般会存在多个Prompt,Prompt结合LLM决定下一步选择哪个链。

5分钟带你了解RabbitMQ的(普通/镜像)集群

通过本文我们深入了解了RabbitMQ的集群模式及其优缺点。无论是普通集群还是镜像集群,都有其适用的场景和局限性。普通集群利用Erlang语言的集群能力,但消息可靠性和高可用性方面存在一定挑战;而镜像集群通过主动消息同步提高了消息的可靠性和高可用性,但可能会占用大量网络带宽。因此,在选择集群方案时,...

5分钟明白LangChain 的输出解析器和链

本文介绍 LangChain 的输出解析器OutputParser的使用,和基于LangChain的LCEL构建链。 1. 输出解析器OutputParser 1.1、为什么需要OutputParser 常规的使用LangChain构建LLM应用的流程是:Prompt 输入、调用LLM 、LLM输出

5分钟教你搭建邮件服务器的实用指南

今天我写了一篇实用的文章,重点是教你如何免费搭建一个邮件服务器,这个服务器不仅可以用于发送邮件,还可以供我的待办机器人使用。一开始我试图找一些免费的 API 接口来实现这个功能,但遗憾的是,并没有找到合适的。对于程序员来说,能自己动手实现绝对是最好的选择,幸运的是,我有一台空闲的服务器可以利用。如果...

5 分钟理解 Next.js Static Export

5 分钟理解 Next.js Static Export 在本篇文章中,我们将介绍 Next.js 中的 Static Export 功能,以及它是如何工作的。我们将介绍一些相关的基本概念,以及在 Next.js 中如何使用 Server Components 和 Client Components

5分钟搭建图片压缩应用

摘要:用华为云函数工作流FunctionGraph搭建图片压缩应用。 本文分享自华为云社区《真正的按需计费丨函数工作流 FunctionGraph实战,5分钟搭建图片压缩应用》,作者:华为云PaaS服务小智。 1.背景介绍 互联网时代,各类app,小程序为人们的生活,办公,学习,休闲,娱乐提供着便利

5分钟体验代码仓托管、CloudIDE云端代码编辑、调试、运行

摘要:您将学会如何通过代码托管(CodeHub)创建代码仓,解决软件开发者在跨地域协同、多分支并发、代码版本管理、安全性等方面的问题。 本文分享自华为云社区《5分钟体验代码仓托管、CloudIDE云端代码编辑、调试、运行》,作者:华为云PaaS服务小智 。 您将会学到什么 您将学会如何通过代码托管(

5分钟迁移关系型数据库到图数据库

本文借助Apache Hop及GES插件,提供了多数据源通用、可视化、开箱即用的数据转换工程,可将多种关系型数据库迁移至GES图数据库中。

5分钟入门 next13

上半年vercel 推出了nextjs13 这个大版本,刚好最近有个c端的项目,所以就用了这个框架来写,技术体系基本也是文档提到的 tailwindcss + ts + swr + ssr ,总的来开发体验还可以,不管是打包速度、文档、错误信息提示目前都还满意,只不过目前nextjs13 中文资料有