Langchain实战:基于Chain实现Prompt的高级应用

embedded/2024/9/23 14:30:27/

Langchain实战

    • 一. Langchain介绍
    • 二. 项目背景
    • 三. 代码实现
      • 3.1 导入必要的库并调用GPT大模型
      • 3.2 输出解析器(指定输出格式)
      • 3.3 定义Prompt模板
      • 3.4 构造LLMChain并推理
      • 3.5 解析推理结果
      • 3.6 异步调用
    • 四. 参考文献

一. Langchain介绍

LangChain 是一个用于开发由语言模型驱动的应用程序的框架。它使得应用程序能够:

  • 具有上下文感知能力:将语言模型连接到上下文来源(提示指令,少量的示例,需要回应的内容等)。
  • 具有推理能力:依赖语言模型进行推理(根据提供的上下文如何回答,采取什么行动等)。

LangChain 包的主要价值主张是:

  • 组件:用于处理语言模型的可组合工具和集成。无论你是否使用 LangChain 框架的其余部分,组件都是模块化的,易于使用
  • 现成的链:用于完成高级任务的组件的内置组合

现成的链使得开始变得容易。组件使得定制现有链和构建新链变得容易。

LangChain的安装与入门请参考:快速入门指南

二. 项目背景

假设有一串长文本,我们希望利用大模型提取出文本中与指定类型的商品品牌、型号等相关的信息,并通过JSON格式将商品信息输出,如下所示:

文本内容:"我今天买了一台Huawei Mate 60, 请你帮我送到华中科技大学南大门, 手机是蓝色的, 512G"
输出:
```json
{"品牌": "Huawei","品类": "手机","属性": {"型号": "Mate 60","颜色": "蓝色","存储容量": "512G"},"商品名称": "Huawei Mate 60 蓝色 512G"
}

三. 代码实现

3.1 导入必要的库并调用GPT大模型

# 导入Langchain相关的库
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.prompts import (ChatPromptTemplate,PromptTemplate,SystemMessagePromptTemplate,AIMessagePromptTemplate,HumanMessagePromptTemplate
)
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
from langchain.chat_models import ChatOpenAI
from langchain.schema import (AIMessage,HumanMessage,SystemMessage
)

OPENAI_API_KEYOPENAI_API_BASE 是两个与 OpenAI API 交互时常用的环境变量,它们各自有不同的用途:

  • OPENAI_API_KEY
    用途:这是一个用于身份验证的密钥,允许你访问 OpenAI 的服务。当你通过 API 发送请求时,需要在请求头中包含这个 API 密钥,以便 OpenAI 能够验证请求者的身份。
    格式:通常是一个由数字和字母组成的字符串,长度固定。

  • OPENAI_API_BASE
    用途:这个环境变量用于指定 OpenAI API 的基础 URL。它决定了你的请求将被发送到哪个服务器。
    默认值:通常情况下,你不需要更改它,因为默认值已经指向了 OpenAI 的生产环境服务器。
    如何使用:如果你需要将请求发送到不同的服务器(如沙盒环境、自定义端点或其他地区特定的服务器),你可以设置这个环境变量。

import os
os.environ["OPENAI_API_KEY"] = "..."
os.environ["OPENAI_API_BASE"] = "..."
# 导入ChatModel
chat = ChatOpenAI(temperature=0)

3.2 输出解析器(指定输出格式)

在Langchain中封装了结构化输出的功能,通过Promt的输出解析器,可以直接将LLM的输出结果转化为指定格式:结构化输出解析器 structured

比如前面提到,我们希望输出为JSON格式,那么:

#定义输出格式
response_schemas = [ResponseSchema(name="品牌", description="商品的品牌"),ResponseSchema(name="品类", description="商品的品类"),ResponseSchema(name="属性", description="商品除品牌、品类外能够提炼的其他属性"),ResponseSchema(name="商品名称", description="根据提取的信息输出商品名称"),
]# 初始化解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)format_instructions = output_parser.get_format_instructions()

format_instructions将作为Prompt的一部分输入大模型

3.3 定义Prompt模板

LangChain 提供了不同类型的 MessagePromptTemplate。最常用的是 AIMessagePromptTemplateSystemMessagePromptTemplateHumanMessagePromptTemplate,分别用于创建 AI 消息、系统消息和人工消息:与聊天相关的提示模板

# 创建SystemMessagePromptTemplate
SystemPrompt = PromptTemplate(template="你是一个 {industry} 行业的专家,你对行业内各个品牌的名称和型号了如指掌。",input_variables=["industry"]
)
SystemMessagePrompt = SystemMessagePromptTemplate(prompt=SystemPrompt)
# 创建HumanMessagePromptTemplate
HumanPrompt = PromptTemplate(template="""用户问题:给你一段输入文本,请从里面提炼与{goods}相关的以下信息:品牌:商品的品牌品类:商品的品类属性:商品除品牌、品类外能够提炼的其他属性,以json形式给出商品名称:根据提取的信息输出商品描述<context>{ocr_result}</context>根据<context>里的信息回答用户问题输出格式: {format_instructions}让我们一步一步分析,给出你分析的过程,并注意以下要点:1.只提取和{goods}相关的信息,如果无法提炼返回空json.只输出一个可能性最大的商品信息,输出的json只包含一种商品;2:参考{industry}行业内的常见品牌,并将文本中识别错误的品牌信息,根据字体之间的相似性与已有品牌进行对应;3.一般来说商品的品牌会在商品描述的前面,并且他们距离不会太远,如果提取到多个品牌信息,则考虑品牌和商品描述之间的距离;4.你需要判断提取到的品牌是否属于{industry}行业,若提取到的品牌明显不属于{industry}行业,则忽略该品牌信息;5.保证输出json的合法性,输出你分析的过程.""",input_variables=["goods","ocr_result","format_instructions","industry"]
)
HumanMessagePrompt = HumanMessagePromptTemplate(prompt=HumanPrompt)# 组合多个Prompt
chat_template = ChatPromptTemplate.from_messages([SystemMessagePrompt,HumanMessagePrompt])

在上面的Prompt中,我们需要外部导入四个参数,分别是:

  • goods:商品类别,比如手机,电脑等。
  • ocr_result:希望大模型提取信息的文本。
  • format_instructions:3.2中定义的输出解析器。
  • industry:该商品所处的行业,比如3C,家用电器等。

关于Prompt如何设计,请参考:Prompt之美:如何设计提示词让大模型变“聪明”

3.4 构造LLMChain并推理

链允许我们将多个组件组合在一起,创建一个单一的、一致的应用程序。例如,我们可以创建一个链,该链接接受用户输入,使用 PromptTemplate 对其进行格式化,然后将格式化后的响应传递给 LLM。我们可以通过将多个链组合在一起,或者通过将链与其他组件组合在一起,来构建更复杂的链:快速开始: 使用LLMChain

chain=LLMChain(llm=chat, prompt=chat_template)

接下来就可以推理了:

ocr = '我今天买了一台Huawei Mate 60, 请你帮我送到华中科技大学南大门, 手机是蓝色的, 512G'
res = chain.run(industry="电子产品",ocr_result=ocr,goods="手机",format_instructions=format_instructions)
print(res)
#输出:
1. 首先从文本中提取可能与手机相关的信息:- 品牌:Huawei- 商品名称:Mate 60- 属性:蓝色、512G2. 根据电子产品行业内的常见品牌,确认Huawei属于电子产品行业的品牌,无需修正。3. 根据文本中的描述,品牌信息在商品描述前面,且距离不远,因此可以确定Huawei是商品的品牌。4. 综合以上信息,得出提取的手机相关信息如下:```json
{"品牌": "Huawei","品类": "手机","属性": "蓝色, 512G","商品名称": "Huawei Mate 60"
}

3.5 解析推理结果

在得到输出结果后,我们希望解析输出的字符串中的JSON信息,从而提取我们想要的品牌、品类相关信息:

output = output_parser.parse(res)
print(output)
#输出
{'品牌': 'Huawei', '品类': '手机', '属性': '蓝色, 512G', '商品名称': 'Huawei Mate 60'}

解析出来的信息为字典格式,然后就可以从中提取我们想要的信息。

3.6 异步调用

上文展示了如何推理一条文本信息,当我们有大量的文本信息时,采用串行执行的方式将会非常耗时,因此考虑能不能采用并行执行的方式提高推理速度。Langchain支持通过利用asyncio库为代理提供异步支持:
如何使用异步API进行代理
Langchain(五)进阶之异步调用

import time,sys
import asyncio #异步调用#测试
async def async_function():print("Hello, async!")sys.stdout.flush()  # 刷新标准输出流await async_function()

假设我们有如下的五条文本信息:

ocr_results = [
'我今天买了一台Huawei Mate 60, 请你帮我送到华中科技大学南大门, 手机是蓝色的, 512G',
'这款红米Note 13 Pro手机现在在京东有优惠活动,原价1399元的商品,通过领取满1000元减100元、满99元减30元的优惠券后,实付价格低至1262.01元。如果购买京东PLUS会员,还可以享受立减6.99元的优惠。这款手机采用了6.67英寸超细四窄边直屏,搭载了Pro+同款金刚骨骼架构和第二代1.5K高光护眼屏。',
'2023 年 10 月 31 日,苹果发布了全新 M3 系列芯片(M3、M3 Pro、M3 Max),首次采用 3nm 工艺,同时发布了搭载 M3 系列芯片的全新 MacBook Pro 14/16 英寸,以及 24 英寸的 iMac。',
'据外媒 FujiFrmors 报道,富士 X-T50 相机有望于 5 月 16 日发布,这款相机将引入机身五轴防抖(IBIS)功能,同时还将搭载 X-T5 同款 X-Trans V CMOS 传感器,内置 1 个 SD 卡插槽。',
'联想旗下新款ThinkBook 16+笔记本电脑现已上架,其中集成显卡版本售价为7699元,配备RTX 4060独立显卡的版本售价为9999元。'
]

首先不采用异步调用串联推理:

result = []
s = time.perf_counter()
for ocr in ocr_results:res = chain1.run(industry="电子产品",ocr_result=ocr,goods="电子产品",format_instructions=format_instructions)output = output_parser.parse(res)result.append(output['商品名称'])
print(result)
elapsed = time.perf_counter() - s
print("\033[1m" + f"Serial executed in {elapsed:0.2f} seconds." + "\033[0m")

输出:

['Huawei Mate 60', 
'红米Note 13 Pro手机', 
'苹果 MacBook Pro 16 英寸(搭载 M3 系列芯片)', 
'富士 X-T50 相机', 
'联想ThinkBook 16+ 笔记本电脑']
Serial executed in 18.15 seconds.

采用异步调用并联推理:

async def async_generate(ocr_result):res = await chain.arun(industry='电子产品',ocr_result=ocr_result,goods='电子产品',format_instructions=format_instructions)output = output_parser.parse(res)return output['商品名称']async def generate_concurrently(data):tasks = [async_generate(ocr) for ocr in data]return await asyncio.gather(*tasks) ####异步调用s = time.perf_counter()
result=await generate_concurrently(ocr_results)
print(result)
elapsed = time.perf_counter() - s
print("\033[1m" + f"Concurrent executed in {elapsed:0.2f} seconds." + "\033[0m")

输出:

['Huawei Mate 60', 
'红米Note 13 Pro手机', 
'Apple MacBook Pro with M3 Series Chip, 14/16-inch', 
'富士 X-T50 相机', 
'ThinkBook 16+笔记本电脑']
Concurrent executed in 7.21 seconds.

可以看到二者输出的商品描述几乎一致,但是推理时间从18.15s减少到了7.21s,速度快了一倍还要多。

四. 参考文献

LangChain 中文网,500页码中文文档教程
LangChain 中文文档 v0.1.7


http://www.ppmy.cn/embedded/18799.html

相关文章

【JVM】从i++到JVM栈帧

【JVM】从i到JVM栈帧 本篇博客将用两个代码例子&#xff0c;简单认识一下JVM与栈帧结构以及其作用 从i与i说起 先不急着看i和i&#xff0c;我们来看看JVM虚拟机&#xff08;请看VCR.JPG&#xff09; 我们初学JAVA的时候一定都听到过JAVA“跨平台”的特性&#xff0c;也就是…

12 c++版本的坦克大战

前言 呵呵 这大概是 大学里面的 c 贪吃蛇了吧 有一些 面向对象的理解, 但是不多 这里 具体的实现 就不赘述, 仅仅是 发一下代码 以及 具体的使用 坦克大战 #include<iostream> #include<windows.h> #include<conio.h> #include<ctime> #include…

【Jenkins】持续集成与交付 (一):深入理解什么是持续集成?

【Jenkins】持续集成与交付 (一):深入理解什么是持续集成? 1、软件开发生命周期与持续集成2、 持续集成的流程3、持续集成的好处4、Jenkins的应用实践5、结语💖The Begin💖点点关注,收藏不迷路💖 1、软件开发生命周期与持续集成 软件开发生命周期(SDLC)是指软件从…

光纤网络电力控制系统设计方案:623-6U CPCI的光纤网络电力控制系统

6U CPCI的光纤网络电力控制系统 一、设备概述 柔性直流输电系统中用于控制与测量的FS系统&#xff0c;适用于风电和太阳能发电的并网快速数值计算和闭环控制&#xff0c;以及与直流输电系统的换流器有关的特殊控制功能&#xff0c;包括门控单元的信号处理。该控制板的最大…

数据分析(3)

数据分析&#xff08;3&#xff09; 索引和数据筛选 在我们导入数据将其存储在data变量中后&#xff0c;接下来就要开始对其进行操作了&#xff0c;要对指定的数据进行操作&#xff0c;首先要在一堆数据中确定它&#xff0c;因此需要索引 import pandas as pd data pd.read…

设计模式之责任链模式(二): 实现方式

C设计模式专栏&#xff1a;http://t.csdnimg.cn/8Ulj3 相关文章系列 设计模式之责任链模式(一)-CSDN博客 目录 1.引言 2.实现方式1 3.实现方式2 4.总结 1.引言 责任链设计模式(Chain of Responsibiliy DesignPattern)简称职责链模式。在GOF的《设计模式:可复用面向…

从递归角度串联二叉树-图论-动态规划

一、深度理解二叉树的前中后序遍历 二叉树遍历框架如下&#xff1a; void traverse(TreeNode* root) {if (root nullptr) {return;}// 前序位置traverse(root->left);// 中序位置traverse(root->right);// 后序位置 }先不管所谓前中后序&#xff0c;单看 traverse 函数…

【神经网络基础辨析】什么是神经网络的主干(backbone)、颈部(neck)和头部(head)网络

在神经网络中&#xff0c;通常将网络分为三个部分&#xff1a;骨干网络&#xff08;Backbone&#xff09;、颈部网络&#xff08;Neck&#xff09;、和头部网络&#xff08;Head&#xff09;。 骨干网络&#xff08;Backbone&#xff09; 骨干网络通常是神经网络的主要部分&a…