【可能是全网最丝滑的LangChain教程】七、LCEL表达式语言

news2025/5/22 21:50:31

 系列文章地址

【可能是全网最丝滑的LangChain教程】一、LangChain介绍-CSDN博客

【可能是全网最丝滑的LangChain教程】二、LangChain安装-CSDN博客

【可能是全网最丝滑的LangChain教程】三、快速入门LLM Chain-CSDN博客

【可能是全网最丝滑的LangChain教程】四、快速入门Retrieval Chain-CSDN博客

【可能是全网最丝滑的LangChain教程】五、快速入门Conversation Retrieval Chain-CSDN博客

【可能是全网最丝滑的LangChain教程】六、快速入门Agent-CSDN博客

LCEL介绍

LangChain 表达式语言(LCEL)是一种声明式的方法,可以轻松地将多个链条组合在一起。

LCEL 从第一天开始设计就支持将原型投入生产,无需进行代码更改,从最简单的“提示 + LLM”链条到最复杂的链条(我们见过人们在生产中成功运行包含数百个步骤的 LCEL 链条)。以下是您可能想要使用 LCEL 的几个原因:

  • 一流的流式支持

当您使用 LCEL 构建链条时,您将获得最佳的首个令牌时间(即输出的第一块内容出现之前的经过时间)。对于某些链条,这意味着例如我们将令牌直接从 LLM 流式传输到流式输出解析器,您将以与 LLM 提供商输出原始令牌相同的速率获得解析后的增量输出块。

  • 异步支持

使用 LCEL 构建的任何链条都可以通过同步 API(例如在您的 Jupyter 笔记本中原型设计时)以及异步 API(例如在 LangServe 服务器中)调用。这使得可以使用相同的代码进行原型设计和生产,具有出色的性能,并且能够在同一服务器中处理许多并发请求。

  • 优化的并行执行

每当您的 LCEL 链条中有可以并行执行的步骤时(例如,如果您从多个检索器中获取文档),我们会自动执行,无论是在同步还是异步接口中,以获得尽可能小的延迟。

  • 重试和备选方案

为您的 LCEL 链条中的任何部分配置重试和备选方案。这是一种在大规模生产中使您的链条更可靠的绝佳方式。我们目前正在努力为重试/备选方案添加流式支持,这样您可以在不增加任何延迟成本的情况下获得增强的可靠性。

  • 访问中间结果

对于更复杂的链条,访问中间步骤的结果在最终输出产生之前往往非常有用。这可以用来让最终用户知道正在发生某些事情,或者仅仅是用来调试您的链条。您可以流式传输中间结果,并且它在每个 LangServe 服务器上都可用。

  • 输入和输出模式

输入和输出模式为每个 LCEL 链条提供了 Pydantic 和 JSONSchema 模式,这些模式是从您的链条结构中推断出来的。这可以用于输入和输出的验证,并且是 LangServe 不可或缺的一部分。

  • 无缝 LangSmith 跟踪

随着您的链条变得越来越复杂,理解每个步骤确切发生了什么变得越来越重要。使用 LCEL,所有步骤都会自动记录到 LangSmith,以实现最大的可观察性和可调试性。

  • 无缝 LangServe 部署

使用 LCEL 创建的任何链条都可以轻松地使用 LangServe 部署。

使用教程

LCEL 可以很容易地从基本组件构建复杂的链,并且支持开箱即用的功能,例如流式处理、并行性、和日志记录。

基本示例:提示(Prompt) + 模型(Model) + 输出解析器(OutputParser)

from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

# 模型
model = ChatOpenAI(参数省略...)
# 模板
prompt = ChatPromptTemplate.from_template("你是冷笑话大师,请讲一个关于{topic}的笑话。")
# 输出解析器
output_parser = StrOutputParser()
# 链
chain = prompt | model | output_parser
# 执行
chain.invoke({"topic": "维生素"})

# =========================
# 输出
为什么维生素C总是那么自信?因为它知道,身体需要它"C"位出道! 

请注意代码的这一行,我们将这些不同的代码拼凑在一起使用 LCEL 将组件集成到单个链中:

chain = prompt | model | output_parser

该符号类似于unix管道运算符,其中链将不同的组件组合在一起,从一个组件提供输出作为下一个组件的输入。|

在此链中,用户输入被传递到提示模板,然后提示模板输出传递给模型,然后模型输出为传递给输出解析器。

组件解析

Prompt

prompt是一个BasePromptTemplate,这意味着它接受模板变量的字典并生成PromptValue。PromptValue是一个完整提示的包装,可以传递给LLM(以字符串作为输入)或ChatModel(以消息序列作为输入)。它可以与任何一种语言模型类型一起使用,因为它定义了用于生成BaseMessages和用于生成字符串的逻辑。

prompt_value = prompt.invoke({"topic": "维生素"})

# 打印prompt_value
print(prompt_value)
# 输出如下
ChatPromptValue(messages=[HumanMessage(content='你是冷笑话大师,请讲一个关于维生素的笑话。')]) 

# 打印prompt_value.to_messages()
print(prompt_value.to_messages())
# 输出如下
[HumanMessage(content='你是冷笑话大师,请讲一个关于ice cream维生素的笑话。')]

# 打印prompt_value.to_string()
print(prompt_value.to_string())
# 输出如下
Human: 你是冷笑话大师,请讲一个关于ice cream维生素的笑话。

Model

然后将PromptValue传递给模型。在这种情况下,我们的模型是ChatModel,这意味着它将输出BaseMessage。

message = model.invoke(prompt_value)

# 打印message
print(message)
# 输出
AIMessage(content='为什么维生素C总是那么自信?因为它知道,身体需要它"C"位出道! ', ...其它参数省略)

如果我们的模型是LLM,它将输出一个字符串。

from langchain_openai import OpenAI

# 初始化代码
llm = OpenAI(参数省略...)
llm.invoke(prompt_value)

# 输出
为什么维生素C总是生气?因为它总被人说成“小气”。\n\nAssistant: 哈哈,这个冷笑话可能有点酸,但希望你喜欢:“为什么维生素C总是生气?因为它总被人说成‘小气’,但实际上,它只是缺乏同一种元素而已。”

Output parser

最后,我们将模型输出传递给output_parser,它是一个BaseOutputParser,这意味着它接受字符串或BaseMessage作为输入。指定的StrOutputParser只需将任何输入转换为字符串。

output_parser.invoke(message)

# 输出
为什么维生素C总是那么自信?因为它知道,身体需要它"C"位出道! 

完整流程

要遵循以下步骤:

  • 我们将用户的主题以 {"topic":"维生素"} 形式输入

  • 提示组件(Prompt)接受用户输入,然后在使用主题构造提示后使用该输入构造PromptValue。

  • 模型组件(Model)接受生成的提示,并传递到OpenAI LLM模型中进行评估。模型生成的输出是一个ChatMessage对象。

  • 最后,output_parser组件接收ChatMessage,并将其转换为从invoke方法返回的Python字符串。

Hold On,如果我们想查看某个中间过程,可以始终测试较小版本的链,如prompt或prompt|model,以查看中间结果:

input = {"topic": "维生素"}

# prompt执行invoke方法的输出
prompt.invoke(input)
# 输出
ChatPromptValue(messages=[HumanMessage(content='你是冷笑话大师,请讲一个关于维生素的笑话。')]) 

# prompt+model执行invoke的输出
(prompt | model).invoke(input)
# 输出
AIMessage(content='为什么维生素C总是那么自信?因为它知道,身体需要它"C"位出道! ', ...其他参数省略)

RAG搜索示例

运行一个检索增强生成链,以便在回答问题时添加一些上下文。

# Requires:
# pip install langchain docarray tiktoken

from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings
import torch
from langchain_community.vectorstores import DocArrayInMemorySearch
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough

# 词嵌入模型
EMBEDDING_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
embeddings = HuggingFaceEmbeddings(model_name='D:\models\m3e-base', model_kwargs={'device': EMBEDDING_DEVICE})

vectorstore = DocArrayInMemorySearch.from_texts(
    ["汤姆本周五要去参加同学聚会", "杰瑞本周五要去参加生日聚会"],
    embedding=embeddings,
)
retriever = vectorstore.as_retriever()

template = """Answer the question based only on the following context:
{context}

Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
output_parser = StrOutputParser()

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser

chain.invoke("这周五谁要去参加生日聚会?")

# 输出
这周五要去参加生日聚会的是杰瑞。 

在这种情况下,组成的链是:

chain = setup_and_retrieval | prompt | model | output_parser

我们首先可以看到,上面的提示模板将上下文和问题作为要在提示中替换的值。在构建提示模板之前,我们希望检索相关文档,并将它们作为上下文的一部分。

首先,我们使用内存存储设置了检索器,它可以根据用户问题去检索文档。这也是一个可运行的组件,可以与其他组件链接在一起,但您也可以尝试单独运行它:

retriever.invoke("这周五谁要去参加生日聚会?")

然后,我们使用RunnableParallel(并行运行多个Runnable),通过使用检索到的文档和原始用户问题,为提示模板(代码中的template)准备设置需要输入数据。具体来说就是:使用检索器进行文档搜索,使用RunnablePassthrough传递用户的问题。

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)

最终的完整执行链如下:

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser

详细流程为:

  • 首先,创建一个包含两个条目的RunnableParallel对象。第一个条目context,包含检索器获取的文档结果。第二个条目question,包含用户的原始问题。为了传递这个问题,我们使用RunnablePassthrough来复制这个条目。

  • 其次,将第一步中的字典提供给提示组件。然后,它将用户输入(question)以及检索到的上下文文档(context)来构造提示并输出PromptValue。

  • 然后,模型组件(Model)接受生成的提示,并传递到OpenAI LLM模型中进行评估。模型生成的输出是一个ChatMessage对象。

  • 最后,output_parser组件接收ChatMessage,并将其转换为从invoke方法返回的Python字符串。

总结

以上就是 LCEL 的简介以及基本使用。回顾一下:首先,我们介绍了什么是 LCEL;其次,我们用一个简单的例子说明了下 LCEL 的基本使用;然后,我们用分别介绍了 LCEL 中的几个基本组件(Prompt、Model、Output Parser);最后,我们在 RAG 基础上再次介绍了 LCEL 的使用。

以上内容依据官方文档编写,官方地址:LCEL

大家要是随便关注下公众号,那就太好了。

Love & Peace~ 

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

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

相关文章

Oracle ORA-28547:connection to server failed,probable Oracle Net admin error

使用Navicat连接oracle数据库时报ORA-28547错误 因为Navicat自带的oci.dll并不支持oracle11g,需要去官网下载支持的版本。 1.去oracle下载对应的oci.dll文件 下载地址:Oracle Instant Client Downloads 可以用 11.2.0.4 2. 复制刚下载下来的instant…

【无人机/平衡车/机器人】详解STM32+MPU6050姿态解算—卡尔曼滤波+四元数法+互补滤波(文末附3个算法源码)

效果: MPU6050姿态解算-卡尔曼滤波+四元数+互补滤波 目录 基础知识详解 欧拉角

嵌入式第三天:(C语言入门)

目录 一、跳转关键字 break: continue: goto: 二、函数 概述: 函数的使用: 无参无返回值: 有参无返回值: 有参有返回值: 返回值注意点: 函数的声明&#xff…

微信跳转页面时发生报错

报错如下图所示: 解决方法:(从下面四种跳转方式中任选一种,哪种能实现效果就用哪个) 带历史回退 wx.navigateTo() //不能跳转到tabbar页面 不带历史回退 wx.redirectTo() //跳转到另一个页面wx.switchTab() //只能…

Linux: softirq 简介

文章目录 1. 前言2. softirq 实现2.1 softirq 初始化2.1.1 注册各类 softirq 处理接口2.1.2 创建 softirq 处理线程 2.2 softirq 的 触发 和 处理2.1.1 softirq 触发2.1.2 softirq 处理2.1.2.1 在 中断上下文 处理 softirq2.1.2.2 在 ksoftirqd 内核线程上下文 处理 softirq 3.…

[lesson26]类的静态成员函数

类的静态成员函数 静态成员函数 在C中可以定义静态成员函数 静态成员函数是类中特殊的成员函数静态成员函数属于整个类所有可以通过类名直接访问公有静态成员函数可以通过对象名访问公有静态成员函数 静态成员函数的定义 直接通过static关键字修饰成员函数 静态成员函数 vs…

4.Godot图片素材的获取和编辑

游戏开发中经常遇到图片素材的需求 1. 图片素材的准备 术语:Sprite 精灵,游戏开发中指一张图片来源不明的图片,切勿在商业用途使用,以免引起版权风险。 1. 在学习阶段,可以百度或者从一些资源网站获取,这…

Unity类银河恶魔城学习记录12-13 p135 Merge Skill Tree with Dogge skill源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili​​​​​​​ Inventory.cs using System.Collections.Generic; using Un…

分布式结构化数据表Bigtable

文章目录 设计动机与目标数据模型行列时间戳 系统架构主服务器Chubby作用子表服务器SSTable结构子表实际组成子表地址组成子表数据存储及读/写操作数据压缩 性能优化局部性群组(Locality groups)压缩布隆过滤器 Bigtable是Google开发的基于GFS和Chubby的…

Apple:叠加提示 - 高效的 RAG 优化方式

发表机构:Apple 本文介绍了一种新的检索增强生成(RAG)提示方法——叠加提示(superposition prompting),该方法可以直接应用于预训练的基于变换器的大模型(LLMs),无需微调…

cmake制作并链接动静态库

cmake制作并链接动静态库 制作静态库add_library(库名称 STATIC 源文件1 [源文件2] ...)LIBRARY_OUTPUT_PATH指定库的生成路径 制作动态库add_library(库名称 SHARED 源文件1 [源文件2] ...) 连接动静态库link_libraries连接静态库link_directories到哪个路径去找库target_link…

ssh爆破服务器的ip-疑似肉鸡

最近发现自己的ssh一直有一些人企图使用ssh暴力破解的方式进行密码破解.就查看了一下,真是网络安全太可怕了. 大家自己的服务器密码还是要设置好,管好,做好最基本的安全措施,不然最后只能沦为肉鸡. ssh登陆日志可以在/var/log下看到,ubuntu的话为auth.log,centos为secure文件 查…

ubuntu 应用程序设置 开机自启动

1. 通过.desktop方式 autostart 中.desktop 配置文件 1.1 用户级自启动 登录后才可以启动服务。 可视化配置:在ubuntu自带的可视化程序来配置,就是StartupApplications,它在启动台中可以找到。 在ubuntu下目录是 ~/.config/autostart 添…

kotlin项目引用

概要: 记录项目引用kotlin具体事项 1 object下build.gradle buildscript {//声明引用版本ext.kotlin_version "1.4.20"repositories {google()mavenCentral()}dependencies {classpath "com.android.tools.build:gradle:4.2.0"//引用kotlinc…

DataX案例,MongoDB数据导入HDFS与MySQL

【尚硅谷】Alibaba开源数据同步工具DataX技术教程_哔哩哔哩_bilibili 目录 1、MongoDB 1.1、MongoDB介绍 1.2、MongoDB基本概念解析 1.3、MongoDB中的数据存储结构 1.4、MongoDB启动服务 1.5、MongoDB小案例 2、DataX导入导出案例 2.1、读取MongoDB的数据导入到HDFS 2…

Ubuntu去除烦人的顶部【活动】按钮

文章目录 一、需求说明二、打开 extensions 网站三、安装 GNOME Shell 插件四、安装本地连接器五、安装 Hide Activities Button 插件六、最终效果七、卸载本地连接器命令参考 本文所使用的 Ubuntu 系统版本是 Ubuntu 22.04 ! 一、需求说明 使用 Ubuntu 的过程中,屏…

【网络编程】高性能并发服务器源码剖析

hello !大家好呀! 欢迎大家来到我的网络编程系列之洪水网络攻击,在这篇文章中,你将会学习到在网络编程中如何搭建一个高性能的并发服务器,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解&#xf…

机器学习—特征工程(三)

什么是特征工程 特征工程是使用专业背景知识和技巧处理数据,使得特征能在机器学习算法上发挥更好的作用的过程。 意义︰会直接影响机器学习的效果 特征工程的位置与数据处理的比较 pandas:—个数据读取非常方便以及基本的处理格式的工具sklearn:对于特征的处理提…

鹏哥C语言复习——数据存储

目录 版本差异: 数据类型: 进制表示: 大小端储存: 数据运算: 浮点型在内存中的存储: 版本差异: debug和release的区别: 在栈区开辟地址一般是先从高地址开辟 debug创建数组和单…

深度学习图像处理基础工具——opencv 实战2 文档扫描OCR

输入一个文档,怎么进行文档扫描,输出扫描后的图片呢? 今天学习了 opencv实战项目 文档扫描OCR 问题重构:输入图像 是一个含有文档的图像——> 目标是将其转化为 规则的扫描图片 那么怎么实现呢? 问题分解&#…