模型I/O,输入提示、模型调用、解析输出!

Model 模型,位于 LangChain 框架的最底层,它是基于语言模型构建的应用的核心元素。

Model I/O

可以把对模型的使用过程拆解成三块:

  • 分别是输入提示(对应图中的Format)、调用模型(对应图中的Predict)和输出解析(对应图中的Parse)。

这三块形成了一个整体,因此在LangChain中这个过程被统称为 Model I/O。

image-20240815115228093

在模型 I/O的每个环节,LangChain都提供了模板和工具,快捷地形成调用各种语言模型的接口。

提示模板:

使用模型的第一个环节是把提示信息输入到模型中,可以创建LangChain模板。

  • 根据实际需求动态选择不同的输入,针对特定的任务和应用调整输入。

语言模型:

LangChain允许你通过通用接口来调用语言模型。

  • 这意味着无论你要使用的是哪种语言模型,都可以通过同一种方式进行调用,这样就提高了灵活性和便利性。

输出解析:

LangChain还提供了从模型输出中提取信息的功能。

通过输出解析器,你可以精确地从模型的输出中获取需要的信息,而不需要处理冗余或不相关的数据。

  • 更重要的是还可以把大模型给回的非结构化文本,转换成程序可以处理的结构化数据。

提示模板

吴恩达老师在他的提示工程课程中所说的:

给予模型清晰明确的指示。

让模型慢慢地思考。

1
2
3
4
5
6
7
8
9
10
11
# 导入LangChain中的提示模板
from langchain import PromptTemplate
# 创建原始模板
template = """
您是一位专业的鲜花店文案撰写员。\n
对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?
"""
# 根据原始模板创建LangChain提示模板
prompt = PromptTemplate.from_template(template)
# 打印LangChain提示模板的内容
print(prompt)

提示模板的具体内容如下:

1
2
3
4
5
6
input_variables=['flower_name', 'price'] 
output_parser=None partial_variables={}
template='/\n您是一位专业的鲜花店文案撰写员。
\n对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?\n'
template_format='f-string'
validate_template=True

在这里,所谓模板就是一段描述某种鲜花的文本格式,它是一个f-string。

其中有两个变量{flower_name}和{price}表示花的名称和价格,这两个值是模板里面的占位符。

  • 在实际使用模板生成提示时会被具体的值替换。

代码中的from_template是一个类方法,它允许我们直接从一个字符串模板中创建一个PromptTemplate对象。

打印出这个PromptTemplate对象,你可以看到这个对象中的信息包括输入的变量:

  • (在这个例子中就是flower_name和price)、输出解析器(这个例子中没有指定)
  • 模板的格式(这个例子中为’f-string’)、是否验证模板(这个例子中设置为True)

因此PromptTemplate的from_template方法就是将一个原始的模板字符串转化为一个更丰富、更方便操作的PromptTemplate对象。

这个对象就是LangChain中的提示模板。

语言模型

LangChain中支持的模型有三大类:

大语言模型(LLM),也叫Text Model,这些模型将文本字符串作为输入,并返回文本字符串作为输出。

  • Open AI的text-davinci-003、Facebook的LLaMA、ANTHROPIC的Claude,都是典型的LLM。

聊天模型(Chat Model),主要代表Open AI的ChatGPT系列模型。

  • 这些模型通常由语言模型支持,但它们的 API 更加结构化。

  • 具体来说,这些模型将聊天消息列表作为输入,并返回聊天消息。

文本嵌入模型(Embedding Model),这些模型将文本作为输入并返回浮点数列表,也就是Embedding。

  • 而文本嵌入模型如OpenAI的text-embedding-ada-002,

然后,我们将调用语言模型,让模型帮我们写文案,并且返回文案的结果。

1
2
3
4
5
6
7
8
9
10
# 导入LangChain中的OpenAI模型接口
from langchain import OpenAI
# 创建模型实例
model = OpenAI(model_name='text-davinci-003')
# 输入提示
input = prompt.format(flower_name=["玫瑰"], price='50')
# 得到模型的输出
output = model(input)
# 打印输出内容
print(output)

input = prompt.format(flower_name=["玫瑰"], price='50')这行代码的作用是将模板实例化。

此时将{flower_name}替换为”玫瑰”,{price}替换为’50’,形成了具体的提示:

  • 您是一位专业的鲜花店文案撰写员。对于售价为50元的玫瑰,您能提供一个吸引人的简短描述吗?

输出解析

在开发具体应用的过程中,很明显不仅仅需要文字,更多情况下需要的是程序能够直接处理的、结构化的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 通过LangChain调用模型
from langchain import PromptTemplate, OpenAI

# 导入OpenAI Key
import os
os.environ["OPENAI_API_KEY"] = '你的OpenAI API Key'

# 创建原始提示模板
prompt_template = """您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?
{format_instructions}"""

# 创建模型实例
model = OpenAI(model_name='text-davinci-003')

# 导入结构化输出解析器和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})

# 数据准备
flowers = ["玫瑰", "百合", "康乃馨"]
prices = ["50", "30", "20"]

# 创建一个空的DataFrame用于存储结果
import pandas as pd
df = pd.DataFrame(columns=["flower", "price", "description", "reason"]) # 先声明列名

for flower, price in zip(flowers, prices):
# 根据提示准备模型的输入
input = prompt.format(flower_name=flower, price=price)

# 获取模型的输出
output = model(input)

# 解析模型的输出(这是一个字典结构)
parsed_output = output_parser.parse(output)

# 在解析后的输出中添加“flower”和“price”
parsed_output['flower'] = flower
parsed_output['price'] = price

# 将解析后的输出添加到DataFrame中
df.loc[len(df)] = parsed_output

# 打印字典
print(df.to_dict(orient='records'))

# 保存DataFrame到CSV文件
df.to_csv("flowers_with_descriptions.csv", index=False)

输出
[{'flower': '玫瑰', 'price': '50', 'description': 'Luxuriate in the beauty of this 50 yuan rose, with its deep red petals and delicate aroma.', 'reason': 'This description emphasizes the elegance and beauty of the rose, which will be sure to draw attention.'},
{'flower': '百合', 'price': '30', 'description': '30元的百合,象征着坚定的爱情,带给你的是温暖而持久的情感!', 'reason': '百合是象征爱情的花,写出这样的描述能让顾客更容易感受到百合所带来的爱意。'},
{'flower': '康乃馨', 'price': '20', 'description': 'This beautiful carnation is the perfect way to show your love and appreciation. Its vibrant pink color is sure to brighten up any room!', 'reason': 'The description is short, clear and appealing, emphasizing the beauty and color of the carnation while also invoking a sense of love and appreciation.'}]

这段代码中,首先定义输出结构,我们希望模型生成的答案包含两部分:

  • 鲜花的描述文案(description)和撰写这个文案的原因(reason)。

所以定义了一个名为response_schemas的列表,其中包含两个ResponseSchema对象,分别对应这两部分的输出。

根据这个列表,通过StructuredOutputParser.from_response_schemas方法创建了一个输出解析器。

然后,通过输出解析器对象的get_format_instructions()方法获取输出的格式说明(format_instructions)。

  • 再根据原始的字符串模板和输出解析器格式说明创建新的提示模板(这个模板就整合了输出解析结构信息)。

再通过新的模板生成模型的输入,得到模型的输出。

此时模型的输出结构将尽最大可能遵循我们的指示,以便于输出解析器进行解析。