基于LangChain的LLM应用开发2——模型、提示和输出解析
本次会讲解LangChain的三个基本组件:模型、提示和解析器。
名词解析
模型(Models):是指作为基础的大语言模型。LangChain中通过ChatOpenAI
或者AzureChatOpenAI
(部署在微软Azure的openai模型)等类来集成语言模型。
提示(Prompts):是指给模型传递信息,让模型按要求生成我们想要的内容。LangChain中通过ChatPromptTemplate
实现。
解析器(Parsers):用于对大模型的输出进行解析,解析成更结构化的格式,如将大模型返回的json格式结果(用markdown标记)解析成Python的词典对象。LangChain中通过ResponseSchema, StructuredOutputParser
实现。
我们开发LLM应用,往往都是这样的流程:重复提示模型,解析模型输出,LangChain基于此提供了更简洁优雅的封装。
下面通过代码来逐步讲解。
搭建环境
由于众所周知的原因,访问ChatGPT困难重重,特别是调用其API,想付费都无比艰难。所以这里推荐去申请微软的Azure(https://portal.azure.com/)里面的大语言模型,可以用国内的信用卡按量付费。
里面关键是进入“模型部署”部署模型,然后去“密钥和终结点”找到自己的密钥和终结点。
首先要安装python-dotenv和openai模块。使用python-dotenv
库来读取.env
文件初始化环境变量。
!pip install python-dotenv
!pip install openai
在你的项目目录中创建一个.env
文件,并将你的OpenAI API密钥、终结点(在“密钥和终结点”里面找)等添加到该文件中。这里.env文件的概念和Docker Compose用的.env文件是一样的。
.env
文件的内容如下:
OPENAI_API_BASE=https://endpoint.openai.azure.com/ #终结点
OPENAI_API_KEY=youropenapikey #密钥
OPENAI_API_TYPE=azure
OPENAI_API_VERSION=2023-07-01-preview
使用以下代码加载.env
文件中的环境变量:
import openai
from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv()) # read local .env file
deployment = "gpt-35-turbo" # 就是上面的部署名
model = "gpt-3.5-turbo"
通过直接调用Api的方式来进行文本翻译
我们先通过“传统”的调用Api的方式来完成程序的功能:将海盗英语翻译成“优雅尊重的美式英语”。
def get_completion(prompt, model="gpt-3.5-turbo", engine="gpt-35-turbo"):
messages = [{"role": "user", "content": prompt}]
response = openai.ChatCompletion.create(
model=model,
engine=engine, #使用Azure的GPT-3.5模型要用这个
messages=messages,
temperature=0,
)
return response.choices[0].message["content"]
customer_email = """
Arrr, I be fuming that me blender lid \
flew off and splattered me kitchen walls \
with smoothie! And to make matters worse,\
the warranty don't cover the cost of \
cleaning up me kitchen. I need yer help \
right now, matey!
"""
style = """American English in a calm and respectful tone"""
prompt = f"""Translate the text \
that is delimited by triple backticks
into a style that is {style}.
text: ```{customer_email}```
"""
print(prompt)
response = get_completion(prompt)
print(response)
这里使用Python的fstring来实现提示的“参数化”。在Python世界,fstring很常用,就是多了个f感觉不太美观,不如直接用groovy的gstring的形式。
最终传给模型的Prompt是这样的:
Translate the text that is delimited by triple backticks
into a style that is American English in a calm and respectful tone.
text: \```
Arrr, I be fuming that me blender lid flew off and splattered me kitchen walls with smoothie! And to make matters worse,the warranty don't cover the cost of cleaning up me kitchen. I need yer help right now, matey!
\```
这里采用了一个小技巧来明确告诉模型要翻译的文本:把翻译的文本用三重引号(triple backticks)来括住。
模型运行返回了很漂亮的英语:I'm really frustrated that my blender lid flew off and made a mess of my kitchen walls with smoothie! And to add to my frustration, the warranty doesn't cover the cost of cleaning up my kitchen. I could really use your help at this moment, my friend.
通过LangChain的方式来进行文本翻译
下面通过LangChain的方式来完成同样的功能,看看有何不同。
同样,先安装类库:
#!pip install --upgrade langchain
这里用了—upgrade参数,这是因为LangChain还在飞速迭代,版本更新很快,碰到问题可以先考虑升级库。
from langchain.chat_models import AzureChatOpenAI
from langchain.prompts import ChatPromptTemplate
# from langchain.chat_models import ChatOpenAI #直接访问OpenAI的GPT服务
# To control the randomness and creativity of the generated
# text by an LLM, use temperature = 0.0
chat = AzureChatOpenAI(temperature=0.0, deployment_name = deployment, model_name=model)
template_string = """Translate the text \
that is delimited by triple backticks \
into a style that is {style}. \
text: ```{text}```
"""
prompt_template = ChatPromptTemplate.from_template(template_string)
print(prompt_template.messages[0].prompt.input_variables) #['style', 'text']
customer_style = """American English in a calm and respectful tone"""
customer_email = """
Arrr, I be fuming that me blender lid \
flew off and splattered me kitchen walls \
with smoothie! And to make matters worse, \
the warranty don't cover the cost of \
cleaning up me kitchen. I need yer help \
right now, matey!
"""
customer_messages = prompt_template.format_messages(
style=customer_style,
text=customer_email)
# print(type(customer_messages))
# print(type(customer_messages[0]))
# print(customer_messages[0])
# Call the LLM to translate to the style of the customer message
customer_response = chat(customer_messages)
print(customer_response.content)
这里很重要的组件是ChatPromptTemplate,ChatPromptTemplate会从我们的提示字符串中识别出两个输入变量/输入参数['style', 'text'],后面直接把这两个参数传进去就可以构造真正的消息。
运行程序,也能得到一样的结果:
I'm really frustrated that my blender lid flew off and made a mess of my kitchen walls with smoothie! And to make things even worse, the warranty doesn't cover the cost of cleaning up my kitchen. I could really use your help right now, my friend!
我们再进一步,假如客服收到上面客户的投诉,想用海盗英语“怼回去”:
service_reply = """Hey there customer, the warranty does not cover cleaning expenses \
for your kitchen because it's your fault that you misused your blender by forgetting \
to put the lid on before starting the blender. Tough luck! See ya!
"""
service_style_pirate = """a polite tone that speaks in English Pirate"""
service_messages = prompt_template.format_messages(
style=service_style_pirate,
text=service_reply)
print(service_messages[0].content)
service_response = chat(service_messages)
print(service_response.content)
这里就可以直接重用prompt_template,只需要改变调用的参数。
最终得到很”polite”的海盗英语:Ahoy there, matey! I regret to inform ye that the warranty be not coverin' the costs o' cleanin' yer galley, as 'tis yer own fault fer misusin' yer blender by forgettin' to secure the lid afore startin' it. Aye, tough luck, me heartie! Fare thee well!
为什么要使用LangChain的Prompt Template而不直接使用Python的fstring呢?
答案是当你构建复杂的应用时,提示可能会很长很复杂,这时Prompt Template就是很好的抽象,方便你重用提示。LangChain还为一些常见操作提供了提示,如摘要、回答问题、连接到SQL数据库、连接到不同的api等等。LangChain在背后做了优化的动作,不需要再花太多时间在设计提示上面。不然我们自定义一个函数,用fstring也可以重用prompt,只是那就真的要从最底层做起,可能做着做着就做了另一个LangChain?
通过解析器来解析模型输出的结果
上面的例子模型的输出很简单,我们再来看输出复杂的结果(json格式)怎么处理。
我们让模型来帮忙处理客户的评论,针对客户的评论,用json的格式输出是否礼物、送达时间、价格/价值。
from langchain.prompts import ChatPromptTemplate
customer_review = """\
This leaf blower is pretty amazing. It has four settings:\
candle blower, gentle breeze, windy city, and tornado. \
It arrived in two days, just in time for my wife's \
anniversary present. \
I think my wife liked it so much she was speechless. \
So far I've been the only one using it, and I've been \
using it every other morning to clear the leaves on our lawn. \
It's slightly more expensive than the other leaf blowers \
out there, but I think it's worth it for the extra features.
"""
review_template = """\
For the following text, extract the following information:
gift: Was the item purchased as a gift for someone else? \
Answer True if yes, False if not or unknown.
delivery_days: How many days did it take for the product \
to arrive? If this information is not found, output -1.
price_value: Extract any sentences about the value or price,\
and output them as a comma separated Python list.
Format the output as JSON with the following keys:
gift
delivery_days
price_value
text: {text}
"""
prompt_template = ChatPromptTemplate.from_template(review_template)
# print(prompt_template)
messages = prompt_template.format_messages(text=customer_review)
chat = AzureChatOpenAI(temperature=0.0, deployment_name = deployment, model_name=model)
response = chat(messages)
print(response.content)
运行程序,结果看起来很完美,很标准的json格式。
{
"gift": false,
"delivery_days": 2,
"price_value": ["It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features."]
}
注意,这里的gift是false,如果是在ChatGPT网站上用同样的提示,GPT-3.5返回的gift也是false。但是吴恩达教授在视频中展示的结果(参考1)gift是true,是正确的。所以GPT-3.5真的退步了?
如果你打印结果的类型type(response.content)
就知道返回的是str类型。这里可以通过json.loads()
函数将JSON格式的字符串转换为Python的词典对象。
我们来看一下如何使用LangChain的解析器来解析模型的输出结果。
from langchain.output_parsers import ResponseSchema, StructuredOutputParser
gift_schema = ResponseSchema(name="gift",
description="Was the item purchased as a gift for someone else? Answer True if yes,\
False if not or unknown.")
delivery_days_schema = ResponseSchema(name="delivery_days",
type="int", #注意这里根据需要,可能要指定类型
description="How many days did it take for the product to arrive? If this information is not found,\
output -1.")
price_value_schema = ResponseSchema(name="price_value",
description="Extract any\
sentences about the value or \
price, and output them as a \
comma separated Python list.")
response_schemas = [gift_schema,
delivery_days_schema,
price_value_schema]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
format_instructions = output_parser.get_format_instructions()
print(format_instructions)
review_template_2 = """\
For the following text, extract the following information:
gift: Was the item purchased as a gift for someone else? \
Answer True if yes, False if not or unknown.
delivery_days: How many days did it take for the product\
to arrive? If this information is not found, output -1.
price_value: Extract any sentences about the value or price,\
and output them as a comma separated Python list.
text: {text}
{format_instructions}
"""
prompt = ChatPromptTemplate.from_template(template=review_template_2)
messages = prompt.format_messages(text=customer_review,
format_instructions=format_instructions)
# print(messages[0].content)
response = chat(messages)
print(response.content)
output_dict = output_parser.parse(response.content)
print(output_dict)
通过ResponseSchema来定义输出的格式,最终生成的format_instructions如下,就是让模型输出用markdown标记的json。这个可以算是LangChain的价值所在,LangChain可以持续升级优化这些输出,我们只需要跟着升级。
The output should be a markdown code snippet formatted in the following schema,
including the leading and trailing "\```json" and "\```":
\```json
{
"gift": string // Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.
"delivery_days": int // How many days did it take for the product to arrive? If this information is not found, output -1.
"price_value": string // Extract any sentences about the value or price, and output them as a comma separated Python list.
}
\```
response.content得到的是:
\```json
{
"gift": false,
"delivery_days": 2,
"price_value": ["It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features."]
}
\```
再经过output_parser.parse(response.content)
解析,就得到了Python的词典对象,方便我们进一步处理。
参考
- https://learn.deeplearning.ai/langchain/lesson/2/models,-prompts-and-parsers
- https://python.langchain.com/docs/modules/model_io/prompts/
基于LangChain的LLM应用开发2——模型、提示和输出解析的更多相关文章
- 基于gin的golang web开发:模型绑定
在前两篇文章介绍路由的时候,我们了解到gin可用通过类似DefaultQuery或DefaultPostForm等方法获取到前端提交过来的参数.参数不多的情况下也很好用,但是想想看,如果接口有很多个参 ...
- 基于gin的golang web开发:模型验证
Gin除了模型绑定还提供了模型验证功能.你可以给字段指定特定的规则标签,如果一个字段用binding:"required"标签修饰,在绑定时该字段的值为空,那么将返回一个错误.开发 ...
- 基于 Koa平台Node.js开发的KoaHub.js的输出json到页面代码
koahub-body-res koahub body res Format koa's respond json. Installation $ npm install koahub-body-re ...
- 基于gin的golang web开发:永远不要相信用户的输入
作为后端开发者我们要记住一句话:"永远不要相信用户的输入",这里所说的用户可能是人,也可能是另一个应用程序."永远不要相信用户的输入"是安全编码的准则,也就是说 ...
- 基于gin的golang web开发:实现用户登录
前文分别介绍过了Resty和gin-jwt两个包,Resty是一个HTTP和REST客户端,gin-jwt是一个实现了JWT的Gin中间件.本文将使用这两个包来实现一个简单的用户登录功能. 环境准备 ...
- 基于 Koa平台Node.js开发的KoaHub.js的控制器,模型,帮助方法自动加载
koahub-loader koahub-loader是基于 Koa平台Node.js开发的KoaHub.js的koahub-loader控制器,模型,帮助方法自动加载 koahub loader I ...
- 基于ThinkPHP3的微信平台开发_1
微信公众平台是个好东西,具体的就不说了,我直接说技术>_< 下图为目录结构一览: 微信开发 - 文件目录结构 平台功能: 此次开发的平台是面向多微信公众号.微信多公众号主(下面简称号主)的 ...
- 基于AgileEAS.NET企业应用开发平台的分布式解决方案
开篇 分布式应用 AgileEAS.NET基于Microsoft .Net构件技术而构建,Microsoft .Net最吸引人的莫过于分布式应用技术,基已经提供了XML WebService. .Ne ...
- phpcms v9二次开发之模型类的应用(1)
在<phpcms二次开发之模型类model.class.php>中讲到了模型类的建立方法,接下来我讲一下模型类的应用. 前段时间我基于phpcms v9开发了一个足球网.足球网是 ...
- atitit.基于组件的事件为基础的编程模型--服务器端控件(1)---------服务器端控件和标签之间的关系
atitit.基于组件的事件为基础的编程模型--服务器端控件(1)---------服务器端控件和标签之间的关系 1. server控件是要server了解了标签.种类型的server控件: 1 1. ...
随机推荐
- 聊聊Zookeeper技术内幕之客户端与SetData请求处理
从客户端会话创建到网络连接.请求处理,简单的叙述下流程与逻辑 客户端 客户端是开发人员使用ZooKeeper最主要的途径,ZooKeeper的客户端主要由以下几个核心组件组成. ZooKeeper实例 ...
- Pycharm里Python运行窗口显示乱码���的解决方法
当你的Python程序运行后,会在运行窗口中显示乱码 ��� 等字样,如下 原因是 Pycharm中默认设置只显示UTF-8编码的格式,需要修改支持显示中文支持. 解决方法: 菜单中选择 File S ...
- 跟着 GPT-4 从0到1学习 Golang 并发机制(二)
btw: 我的个人博客网站 目录 一.前言 二.开聊 2.1 Golang 中的 sync 包 - Mutex, RWMutex 和 WaitGroup 2.2 条件变量 sync.Cond 2.3 ...
- 基于 NNCF 和 🤗 Optimum 面向 Intel CPU 对 Stable Diffusion 优化
基于隐空间的扩散模型 (Latent Diffusion Model),是解决文本到图片生成问题上的颠覆者.Stable Diffusion 是最著名的一例,广泛应用在商业和工业.Stable Dif ...
- 项目完成小结:使用Blazor和gRPC开发大模型客户端
前言 先介绍下这个项目. 最近我一直在探索大语言模型,根据不同场景训练了好几个模型,为了让用户测试使用,需要开发前端. 这时候,用 Gradio 搭建的前端是不太够的,虽说 GitHub 上也有一堆开 ...
- ISP图像处理之Demosaic算法及相关
CFA及Demosaic介绍 1.Bayer(拜耳滤波器得到彩色) 图像在将实际的景物转换为图像数据时, 通常是将传感器分别接收红. 绿. 蓝三个分量的信息, 然后将红. 绿. 蓝三个分量的信息合成彩 ...
- 2023年郑州轻工业大学校赛邀请赛mjh
首先,很感谢老师的信任,选择我去参加此次比赛:其次,感谢wh,wyh两位队友的全程带飞.此次比赛是我第一次参加线下的比赛活动,内心难免激动与紧张. 比赛开始,队友直接找到了签到题进行解答,而我拿到了看 ...
- js实现图片预览翻页
原文地址 可以直接复制粘贴打开,图片是在线的,原理简单好懂! 效果 源码 <!DOCTYPE html> <html> <!--JQuery在线引用--> < ...
- quarkus实战之三:开发模式(Development mode)
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 前文咱们曾提到过几种启动方式,有一种用mav ...
- 看element源码学到的小技巧
中午无休的时候有点无聊, 看了一下昨天clone 的 element-ui 源码, 发现很多优雅之处, 记录一下让我直接用到项目中的一个点 那就是绝对定位的元素放到body 里面的 同级.这么做的好处 ...