LangChain 核心模块学习:Chains

对于简单的大模型应用,单独使用语言模型(LLMs)是可以的。

但更复杂的大模型应用需要将 LLMsChat Models 链接在一起。 要么彼此链接,要么与其他组件链接。

LangChain 为这种“链式”应用程序提供了 Chain 接口。

LangChain 以通用方式定义了 Chain,它是对组件进行调用序列的集合,其中可以包含其他链。

Chain Class 基类

类继承关系:

  1. Chain --> <name>Chain # Examples: LLMChain, MapReduceChain, RouterChain
  1. # 定义一个名为Chain的基础类
  2. class Chain(Serializable, Runnable[Dict[str, Any], Dict[str, Any]], ABC):
  3. """为创建结构化的组件调用序列的抽象基类。
  4. 链应该用来编码对组件的一系列调用,如模型、文档检索器、其他链等,并为此序列提供一个简单的接口。
  5. Chain接口使创建应用程序变得容易,这些应用程序是:
  6. - 有状态的:给任何Chain添加Memory可以使它具有状态,
  7. - 可观察的:向Chain传递Callbacks来执行额外的功能,如记录,这在主要的组件调用序列之外,
  8. - 可组合的:Chain API足够灵活,可以轻松地将Chains与其他组件结合起来,包括其他Chains。
  9. 链公开的主要方法是:
  10. - `__call__`:链是可以调用的。`__call__`方法是执行Chain的主要方式。它将输入作为一个字典接收,并返回一个字典输出。
  11. - `run`:一个方便的方法,它以args/kwargs的形式接收输入,并将输出作为字符串或对象返回。这种方法只能用于一部分链,不能像`__call__`那样返回丰富的输出。
  12. """
  13. # 调用链
  14. def invoke(
  15. self, input: Dict[str, Any], config: Optional[runnableConfig] = None
  16. ) -> Dict[str, Any]:
  17. """传统调用方法。"""
  18. return self(input, **(config or {}))
  19. # 链的记忆,保存状态和变量
  20. memory: Optional[BaseMemory] = None
  21. """可选的内存对象,默认为None。
  22. 内存是一个在每个链的开始和结束时被调用的类。在开始时,内存加载变量并在链中传递它们。在结束时,它保存任何返回的变量。
  23. 有许多不同类型的内存,请查看内存文档以获取完整的目录。"""
  24. # 回调,可能用于链的某些操作或事件。
  25. callbacks: Callbacks = Field(default=None, exclude=True)
  26. """可选的回调处理程序列表(或回调管理器)。默认为None。
  27. 在对链的调用的生命周期中,从on_chain_start开始,到on_chain_end或on_chain_error结束,都会调用回调处理程序。
  28. 每个自定义链可以选择调用额外的回调方法,详细信息请参见Callback文档。"""
  29. # 是否详细输出模式
  30. verbose: bool = Field(default_factory=_get_verbosity)
  31. """是否以详细模式运行。在详细模式下,一些中间日志将打印到控制台。默认值为`langchain.verbose`。"""
  32. # 与链关联的标签
  33. tags: Optional[List[str]] = None
  34. """与链关联的可选标签列表,默认为None。
  35. 这些标签将与对这个链的每次调用关联起来,并作为参数传递给在`callbacks`中定义的处理程序。
  36. 你可以使用这些来例如识别链的特定实例与其用例。"""
  37. # 与链关联的元数据
  38. metadata: Optional[Dict[str, Any]] = None
  39. """与链关联的可选元数据,默认为None。
  40. 这些元数据将与对这个链的每次调用关联起来,并作为参数传递给在`callbacks`中定义的处理程序。
  41. 你可以使用这些来例如识别链的特定实例与其用例。"""

LLMChain

LLMChain 是 LangChain 中最简单的链,作为其他复杂 Chains 和 Agents 的内部调用,被广泛应用。

一个LLMChain由PromptTemplate和语言模型(LLM or Chat Model)组成。它使用直接传入(或 memory 提供)的 key-value 来规范化生成 Prompt Template(提示模板),并将生成的 prompt (格式化后的字符串)传递给大模型,并返回大模型输出。

(图片)

  1. from langchain_openai import OpenAI
  2. from langchain.prompts import PromptTemplate
  3. llm = OpenAI(model_name="gpt-3.5-turbo-instruct", temperature=0.9, max_tokens=500)
  4. prompt = PromptTemplate(
  5. input_variables=["product"],
  6. template="给制造{product}的有限公司取10个好名字,并给出完整的公司名称",
  7. )
  8. from langchain.chains import LLMChain
  9. chain = LLMChain(llm=llm, prompt=prompt)
  10. print(chain.invoke({
  11. 'product': "性能卓越的GPU"
  12. }))

输出:

  1. {'product': '性能卓越的GPU', 'text': '\n\n1. 星火卓越科技有限公司\n2. 利益达太空科技有限公司\n3. 未来视界智能科技有限公司\n4. 强力算力科技有限公司\n5. 极速计算科技有限公司\n6. 无限创想智能科技有限公司\n7. 高能创新科技有限公司\n8. 天马行空科技有限公司\n9. 极致计算科技有限公司\n10. 全能加速科技有限公司'}

Sequential Chain

串联式调用语言模型(将一个调用的输出作为另一个调用的输入)。

顺序链(Sequential Chain )允许用户连接多个链并将它们组合成执行特定场景的流水线(Pipeline)。有两种类型的顺序链:

  • SimpleSequentialChain:最简单形式的顺序链,每个步骤都具有单一输入/输出,并且一个步骤的输出是下一个步骤的输入。
  • SequentialChain:更通用形式的顺序链,允许多个输入/输出。

使用 SimpleSequentialChain 实现戏剧摘要和评论(单输入/单输出)

(图片)

  1. # 这是一个 LLMChain,用于根据剧目的标题撰写简介。
  2. llm = OpenAI(temperature=0.7, max_tokens=1000)
  3. template = """你是一位剧作家。根据戏剧的标题,你的任务是为该标题写一个简介。
  4. 标题:{title}
  5. 剧作家:以下是对上述戏剧的简介:"""
  6. prompt_template = PromptTemplate(input_variables=["title"], template=template)
  7. synopsis_chain = LLMChain(llm=llm, prompt=prompt_template)
  1. # 这是一个LLMChain,用于根据剧情简介撰写一篇戏剧评论。
  2. # llm = OpenAI(temperature=0.7, max_tokens=1000)
  3. template = """你是《纽约时报》的戏剧评论家。根据剧情简介,你的工作是为该剧撰写一篇评论。
  4. 剧情简介:
  5. {synopsis}
  6. 以下是来自《纽约时报》戏剧评论家对上述剧目的评论:"""
  7. prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
  8. review_chain = LLMChain(llm=llm, prompt=prompt_template)

(图片)

  1. # 这是一个SimpleSequentialChain,按顺序运行这两个链
  2. from langchain.chains import SimpleSequentialChain
  3. overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)
  1. review = overall_chain.invoke("星球大战第九季")

输出:

  1. > Entering new SimpleSequentialChain chain...
  2. 《星球大战第九季》是一部充满惊险刺激的科幻戏剧,它延续了《星球大战》系列的传奇故事。在这一季中,我们将见证新的冒险和挑战,因为银河系再次陷入战争的深渊。反抗军和第一秩序之间的战争仍在继续,但这一次,他们将面临着更加强大的敌人。新的角色和老朋友将再次出现,与我们的英雄们一起并肩作战。随着战争的进展,我们也将见证力量和黑暗的永恒对抗,以及对自由和正义的不懈追求。在这场史诗般的战役中,我们将看到勇气、牺牲和爱的力量,同时也会思考我们对未来的选择和命运。《星球大战第九季》将带给观众无与伦比的视觉盛宴和感人心弦的故事,让我们一起来见证这一壮丽的星际冒险!
  3. 《星球大战第九季》是一部不容错过的史诗般的科幻戏剧。该剧延续了《星球大战》系列的传奇故事,将观众带入一个充满惊险和刺激的银河系。在这一季中,我们将再次与我们熟悉的英雄们一起并肩作战,同时也会见证新的挑战和冒险。
  4. 该剧不仅仅是一场视觉盛宴,更是一部让人思考的故事。通过力量和黑暗的永恒对抗,以及对自由和正义的追求,观众将被带入一个关于命运和选择的深刻思考。在这场史诗般的战役中,我们将见证勇气、牺牲和爱的力量,这些都是《星球大战》系列一直探讨的重要主题。
  5. 该剧不仅有着令人惊叹的特效和精彩的动作场面,更重要的是它成功地将这些元素与深刻的人物关系和情感表达相结合。观众将会被带入一个充满希望和感动的星际冒险,与角色们一起经历他们的成长和转变。
  6. 总的来说,《星球大战第九季》是一部充满情感和故事的出色作品,它将让观众沉浸在一个充满惊喜和挑战的星际世界。无论您是《星球大战》系列的忠实粉丝,还是对科幻题材感兴趣的观众,这部戏剧都会给您带来一场令人难忘的体验。不容错过!
  7. > Finished chain.

Router Chain: 实现条件判断的大模型调用

这段代码构建了一个可定制的链路系统,用户可以提供不同的输入提示,并根据这些提示获取适当的响应。

主要逻辑:从prompt_infos创建多个LLMChain对象,并将它们保存在一个字典中,然后创建一个默认的ConversationChain,最后创建一个带有路由功能的MultiPromptChain

(图片)

  1. from langchain.chains.router import MultiPromptChain
  2. from langchain_openai import OpenAI
  3. from langchain.chains import ConversationChain
  4. from langchain.chains.llm import LLMChain
  5. from langchain.prompts import PromptTemplate
  1. physics_template = """你是一位非常聪明的物理教授。
  2. 你擅长以简洁易懂的方式回答关于物理的问题。
  3. 当你不知道某个问题的答案时,你会坦诚承认。
  4. 这是一个问题:
  5. {input}"""
  6. math_template = """你是一位很棒的数学家。你擅长回答数学问题。
  7. 之所以如此出色,是因为你能够将难题分解成各个组成部分,
  8. 先回答这些组成部分,然后再将它们整合起来回答更广泛的问题。
  9. 这是一个问题:
  10. {input}"""
  1. prompt_infos = [
  2. {
  3. "name": "物理",
  4. "description": "适用于回答物理问题",
  5. "prompt_template": physics_template,
  6. },
  7. {
  8. "name": "数学",
  9. "description": "适用于回答数学问题",
  10. "prompt_template": math_template,
  11. },
  12. ]

llm = OpenAI(model_name="gpt-3.5-turbo-instruct")

  1. # 创建一个空的目标链字典,用于存放根据prompt_infos生成的LLMChain。
  2. destination_chains = {}
  3. # 遍历prompt_infos列表,为每个信息创建一个LLMChain。
  4. for p_info in prompt_infos:
  5. name = p_info["name"] # 提取名称
  6. prompt_template = p_info["prompt_template"] # 提取模板
  7. # 创建PromptTemplate对象
  8. prompt = PromptTemplate(template=prompt_template, input_variables=["input"])
  9. # 使用上述模板和llm对象创建LLMChain对象
  10. chain = LLMChain(llm=llm, prompt=prompt)
  11. # 将新创建的chain对象添加到destination_chains字典中
  12. destination_chains[name] = chain
  13. # 创建一个默认的ConversationChain
  14. default_chain = ConversationChain(llm=llm, output_key="text")

使用 LLMRouterChain 实现条件判断调用

这段代码定义了一个chain对象(LLMRouterChain),该对象首先使用router_chain来决定哪个destination_chain应该被执行,如果没有合适的目标链,则默认使用default_chain。

  1. from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
  2. from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
  1. # 从prompt_infos中提取目标信息并将其转化为字符串列表
  2. destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
  3. # 使用join方法将列表转化为字符串,每个元素之间用换行符分隔
  4. destinations_str = "\n".join(destinations)
  5. # 根据MULTI_PROMPT_ROUTER_TEMPLATE格式化字符串和destinations_str创建路由模板
  6. router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)
  7. # 创建路由的PromptTemplate
  8. router_prompt = PromptTemplate(
  9. template=router_template,
  10. input_variables=["input"],
  11. output_parser=RouterOutputParser(),
  12. )
  13. # 使用上述路由模板和llm对象创建LLMRouterChain对象
  14. router_chain = LLMRouterChain.from_llm(llm, router_prompt)

print(destinations)

输出:['物理: 适用于回答物理问题', '数学: 适用于回答数学问题']

  1. # 创建MultiPromptChain对象,其中包含了路由链,目标链和默认链。
  2. chain = MultiPromptChain(
  3. router_chain=router_chain,
  4. destination_chains=destination_chains,
  5. default_chain=default_chain,
  6. verbose=True,
  7. )

print(chain.invoke("黑体辐射是什么??"))

输出:

  1. > Entering new MultiPromptChain chain...
  2. 物理: {'input': 'What is blackbody radiation?'}
  3. > Finished chain.
  4. {'input': 'What is blackbody radiation?', 'text': '\n\n黑体辐射是一种物理现象,指的是由于物体内部的热运动导致的电磁辐射。简单来说,物体在受热时会发出电磁波,这些波的频率和强度取决于物体的温度。黑体辐射的特点是它的辐射频率与物体的温度无关,而只与物体的结构和性质有关。这个概念在热力学和量子力学中都有重要的应用。'}

LangChain基础篇 (02)的更多相关文章

  1. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式

    概要 本章,我们学习“常用的实现多线程的2种方式”:Thread 和 Runnable.之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池来实现多线程.关于线程 ...

  2. WebBug靶场基础篇 — 02

    本篇以第一人称记录这个关卡的第 1-5 关. 由于我记录的过程有点偏向于思考,所以截图截的多 = =!所以文章有点长... 下午一觉醒来,已经 4 点多了,然后开电脑,在虚拟机里,铺了铺靶场,但是毕竟 ...

  3. 【matlab 基础篇 02】基础知识一键扫盲,看完即可无障碍编程(超详细+图文并茂)

    博主快速入门matlab,系统地整理一遍,如何你和我一样是一个新手,那么此文很适合你: 本人能力有限,文中难免有错误和纰漏之处,请大佬们不吝赐教 创作不易,如果本文帮到了您: 请帮忙点个赞

  4. iOS系列 基础篇 02 StoryBoard 故事板文件

    iOS基础 02 StoryBoard 故事板文件 目录: 1. 故事板的导航特点 2. 故事板中的Scene和Segue 3. 本文最后 在上篇HelloWorld工程中有一个Main.storyb ...

  5. MySQL基础篇(02):从五个维度出发,审视表结构设计

    本文源码:GitHub·点这里 || GitEE·点这里 一.数据场景 1.表结构简介 任何工具类的东西都是为了解决某个场景下的问题,比如Redis缓存系统热点数据,ClickHouse解决海量数据的 ...

  6. Java基础篇(02):特殊的String类,和相关扩展API

    本文源码:GitHub·点这里 || GitEE·点这里 一.String类简介 1.基础简介 字符串是一个特殊的数据类型,属于引用类型.String类在Java中使用关键字final修饰,所以这个类 ...

  7. C#基础篇02

    首先:一个完整的方法是包括两部分的,代码和注释!!!! 程序的调试: 3:设置断点:  断点之前的程序已经确保正确,可是在断点后的部分可能出现错误,所以设置完断点后,直接点击启动,然后按F11逐步棸的 ...

  8. Java岗 面试考点精讲(基础篇02期)

    1. 两个对象的hashCode相同,则equals也一定为true,对吗? 不对,答案见下面的代码: @Override public int hashCode() { return 1; } 两个 ...

  9. React基础篇 - 02.JSX 简介

    JSX 简介 请观察下面的变量声明: const element = <h1>Hello, world!</h1>; 这种看起来可能有些奇怪的标签语法既不是字符串也不是HTML ...

  10. Java多线程系列 基础篇02 线程的创建和运行

    1.线程创建的方式常用有两种 1. 继承 Thread 类创建线程 2. 实现 Runnable 接口创建线程 2.Thread 和 Runnable的区别 Thread和Runnable的相同点:都 ...

随机推荐

  1. mysql 触发器阻止不合理数据插入

    今天看到有人问如何判断处理有不符合的数据阻止插入.比如这个数据只能在90天内存在一条,如果有了就拒绝插入. 当然大家都说用代码判断,判断一下90天内是否有数据,有就拒绝. 我这里说一个使用触发器的思路 ...

  2. Python中函数或者类对象带()与不带()的区别——闭包和函数返回时的常见现象

    Python中函数或者类对象带()与不带()的区别-----闭包和函数返回时的常见现象 - 函数不带括号时,调用的是这个函数本身 ,是整个函数体,是一个函数对象,不需等该函数执行完成,返回一个已定义函 ...

  3. Apache Tomcat AJP 实现负载均衡

    大部分一开始接触WEB服务器的人可能和我一样对为什么有Apache又有Tomcat服务器感到奇怪(它们还都是Apache开发的呵呵),其实他们不是冗余的服务器,虽然他们都能对外提供WEB服务器,但总的 ...

  4. Docker之修改默认存储路径

    背景:Docker 默认安装的情况下,会使用 /var/lib/docker/ 目录作为存储目录,用以存放拉取的镜像和创建的容器等.不过由于此目录一般都位于系统盘,遇到系统盘比较小,而镜像和容器多了后 ...

  5. Golang常见问题汇总

    在开始使用golang的时候,经常会遇到各种问题,总结在此 1.unrecognized import path "golang.org/x/.. golang 在 github 上建立了一 ...

  6. 3-XSS渗透与防御

    1.HTTP协议回顾 XSS又名跨站脚本攻击 web页面登陆页面,往往有一个"记住密码"功能 ---> Cookie 1.1 HTTP流程 1.2 HTTP特点: 请求应答模 ...

  7. idea springboot 微服务批量启动

    概要 在使用IDEA开发微服务的时候,微服务比较多,启动起来比较麻烦,下面介绍一下使用批量启动微服务的方法. 方法 编辑当前项目根目录下的 .idea\workspace.xml 文件. 找到 < ...

  8. GPUStack v0.4:文生图模型、语音模型、推理引擎版本管理、离线支持和部署本地模型

    GPUStack 是一个专为运行 AI 模型设计的开源 GPU 集群管理器,致力于支持基于任何品牌的异构 GPU 构建统一管理的算力集群.无论这些 GPU 运行在 Apple Mac.Windows ...

  9. nvm安装node报错Get "https://nodejs.org/dist/latest/SHASUMS256.txt": dial tcp 104.20.23.46:443: i/o timeout

    windows上通过nvm管理node版本,在本地安装了nvm后,通过nvm安装node,报错了,信息: Could not retrieve https://nodejs.org/dist/late ...

  10. 常回家看看之Tcache Stashing Unlink Attack

    前言: 在开始了解这个攻击手法的前提,需要先了解一个函数也就是calloc函数,众所周知,当libc版本大于等于2.27的时候会引入tcachebin,而Tcache Stashing Unlink ...