一.认识Model I/O
1.基本概念:
Model即模型,I/O即Input/Output。模型,位于LangChain框架的最底层,它是基于语言模型构建的应用的核心元素,因为所谓LangChain应用开发,就是以LangChain作为框架,通过API调用大模型来解决具体问题的过程。本文就以模型为核心,浅谈它在LangChain中的实际作用。
2.Model的使用过程:
输入提示、调用模型和输出解析。如图所示,这三块形成了一个整体,因此在LangChain中这个过程被统称为 Model I/O。
二.提示模版
为什么要搭建一个提示模板,而不就在模型中去直接输入信息呢?答案很简单,有了模板,后面的输入都可以填充模板来获取,而不用每次都得新创一个模型。这也体现了LangChain的优势所在,它能够整合信息而不是零散化,这样大大提高了模型工作效率。
下面是构造模板的代码:
from langchain.prompts import PromptTemplate
# 创建原始模板
template = """您是一位专业的鲜花店文案撰写员。\n
对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?
"""
# 根据原始模板创建LangChain提示模板
prompt = PromptTemplate.from_template(template)
# 打印LangChain提示模板的内容
print(prompt)
先从langchain.prompts导入提示模板类(template意思为模板),然后进行输入并赋值给templete,接着利用PromptTemplate的from_template方法将一个原始的模板字符串转化为一个更丰富、更方便操作的对象,这个对象就是LangChain中的提示模板,最后打印出prompt查看结果。
三.语言模型
1.语言模型分类:
这个其实在第一篇文章中就已经介绍的很清楚了,这里再给出具体的类别:
(1)大语言模型(LLM)
也叫Text Model,这些模型将文本字符串作为输入,并返回文本字符串作为输出。Open AI的text-davinci-003、Facebook的LLaMA、ANTHROPIC的Claude,都是典型的LLM。
(2)聊天模型(Chat Model)
主要代表Open AI的ChatGPT系列模型。这些模型通常由语言模型支持,但它们的 API 更加结构化。具体来说,这些模型将聊天消息列表作为输入,并返回聊天消息。
(3)文本嵌入模型(Embedding Model)
这些模型将文本作为输入并返回浮点数列表,也就是Embedding。而文本嵌入模型如OpenAI的text-embedding-ada-002,我们之前已经见过了。文本嵌入模型负责把文档存入向量数据库,和我们这里探讨的提示工程关系不大。
2.代码解析
(1)通过LangChain调用,Chat模型
在我们构造好模板后,就可以进行构造模型,提供prompt获取结果了,代码如下:
import os
from langchain_openai import ChatOpenAI
model=ChatOpenAI(model=os.environ.get("LLM_MOODLEND"))
# 输入
input=prompt.format(flower_name=["玫瑰"],price="50")
# 输出
output=model.invoke(input)
print(output)
从接入OpenAI接口,然后构造模型,再输入到输出最后打印结果,一气呵成。其中prompt.format方法(format意思为格式化)即将模版填充后赋值给input输入,最后将input给与output进行输出得到结果。
(2)通过LangChain调用,Chat模型(循环多输出)
其实我们还可以对几种花及其价格进行整合,通过循环遍历输出,模板代码同上,构造模型代码如下:
import os
from langchain_openai import ChatOpenAI
model=ChatOpenAI(model=os.environ.get("LLM_MOODLEND"))
# 多种花的列表
flowers=["玫瑰","百合","康乃馨"]
prices=["50","30","20"]
for flower,price in zip(flowers,prices):
input=prompt.format(flower,price) # 输入(即prompt)
output=model.invoke(input) # 输出(即response)
print(output)
代码中Python中的zip()函数,它的功能是将可迭代对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。简单来说就是一个整合包,依次输入并输出。
(3)直接调用,Chat模型(循环多输出)
前面我们也说明了直接调用和通过LangChain调用的区别,为了更清晰地讨论这个问题,下面是直接调用的代码:
import openai
prompt_text = "您是一位专业的鲜花店文案撰写员。对于售价为{}元的{},您能提供一个吸引人的简短描述吗?"
flowers = ["玫瑰", "百合", "康乃馨"]
prices = ["50", "30", "20"]
for flower, price in zip(flowers, prices):
# 利用format方法将模板转化为提示
prompt = prompt_text.format(price, flower)
client=OpenAI()
response = client.chat.completions.create(
model=os.environ.get[LLM_MOODLEND],
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompt},
],
max_tokens=100
)
print(response.choices[0].messages.content) # 输出文案
观察这两部分代码,我们可以发现LangChain的优势所在。我们只需要定义一次模板,就可以用它来生成各种不同的提示。更重要的是,使用LangChain提示模板,我们还可以很方便地把程序切换到不同的模型,而不需要修改任何提示相关的代码。
(4)通过LangChain调用,智谱清言开源模型(ZhipuAI)
首先我们需要在AI练中学的终端输入pip install zhipuai,安装好智谱清言后就可以调用了。
调用方法如图,和第一篇文章中调用火山引擎的ark是一样的,在智谱清言的AI开放平台中选择模型,添加密钥即可。这里我选择的是glm-4-plus。
下面我们选择ZhipuAI的开源模型接口进行创建,代码如下:
import os
from zhipuai import ZhipuAI # 导入ZhipuAI类
# 初始化ZhipuAI类,传入API密钥
client = ZhipuAI(api_key="你的模型api")
prompt_text = """You are a flower shop assitiant。\n
For {price} of {flower_name},can you write something for me?
"""
# 填充模板
prompt=prompt_text.format(flower_name=["玫瑰"], price="50")
response=client.chat.completions.create(
model="glm-4-plus",
max_tokens=100,
messages=[
{"role":"system","content":"You are a flower shop assitiant"},
{"role":"user","content":prompt}
],
)
print(response.choices[0].message.content)
我们可以看到代码上和调用ChatOpenAI模型没有什么区别,除了改变模型及其API,所以我们可以重用模板,重用程序结构,通过LangChain框架调用任何模型。
四.输出解析
1.输出解析的作用
LangChain提供的解析模型输出的功能,能够更容易地从模型输出中获取结构化的信息。但是,在开发具体应用的过程中,很明显我们不仅仅需要文字,更多情况下我们需要的是程序能够直接处理的、结构化的数据。
在这个文案中,如果你希望模型返回两个字段:description:鲜花的说明文本,reason:解释一下为何要这样写上面的文案。这样结构清晰的数据结构,如何自动实现?这就需要LangChain中的输出解析器上场了。
2.代码实战:
(1)构造模板
这里我们只要将构造模板的templete改变一下即可:
import os
from langchain.prompts import PromptTemplate
prompt_template = """您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?
{format_instructions}"""
(2)创建输出解析器(output_parser):
# 导入OpenAI模型
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model=os.environ.get("LLM_MODELEND"))
# 导入结构化输出解析器和ResponseSchema
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
# 定义我们想要接收的响应模式
response_schemas = [
ResponseSchema(name="description", description="鲜花的描述文案"),
ResponseSchema(name="reason", description="问什么要这样写这个文案"),
]
# 创建输出解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
# 获取格式指示
format_instructions = output_parser.get_format_instructions()
# 根据模板创建提示,同时在提示中加入输出解析器的说明
prompt = PromptTemplate.from_template(
prompt_template, partial_variables={"format_instructions": format_instructions}
)
我们来分析一下这段代码,引入模型->通过ResponseSchema类定义一个响应模式,即输出样式->利用响应模板创建输出解析器(StructuredOutputParser.from_response_schemas(response_schemas)这个函数的功能是创建一个结构化输出解析器,用于从模型的输出中提取特定的信息,并将其转换为结构化的数据格式。简单来说就是将AI输出结果转化为定义的响应模式)->通过解析器的get_format_instructions()方法获取一个格式指示,如下图(这部分理解我看了这位大佬的笔记juejin.cn/post/743387…)
这里就显示为字符串,定义了模型的输出,我们再来看看prompt的结果:
其实就是在我们的prompt最后加上了
{format_instructions}。
(3)获取模式化输出结果:
首先利用model的invoke方法获取非标准输出output
# 根据提示准备模型的输入
input = prompt.format(flower_name=flower, price=price)
# 获取模型的输出
output = model.invoke(input)
再利用输出解析器output_parser将输出模式化:
# 解析模型的输出(这是一个字典结构)
parsed_output = output_parser.parse(output.content)