文章目录
一、什么是提示词工程
其实,在上一章中就提到了提示词工程,输出解析器的原理实际上就是通过提示词明确地告诉 llm 应该做什么。
之前有说过吴恩达老师说过的提示词两大原则:清晰具体的指令、给模型思考的时间。实际上,在 Open AI 的官方文档 GPT 最佳实践中,也给出了和上面这两大原则一脉相承的 6大策略,分别是:
- 写清晰的指示
- 给模型提供参考,也就是示例
- 将复杂任务拆分成子任务
- 给 GPT 时间思考
- 使用外部工具
- 反复迭代问题
二、提示的结构
当然了,从大原则到实践,还是有一些具体工作需要说明,上面是一个实用的提示框架。在这个提示框架中:
- 指令 instruction:
告诉模型这个任务大概要做什么、怎么做。这通常是一个提示模板中比较固定的部分。一个常见用例是告诉模型“你是一个有用的 XX 助手”。 - 上下文 context:
模型的额外知识来源。一个常见的用例时是把从向量数据库查询到的知识作为上下文传递给模型。 - 提示输入 prompt input:
具体的问题或者需要大模型做的具体事情。这个部分和“指令”部分其实也可以合二为一。但是拆分出来成为一个独立的组件,就更加结构化,便于复用模板。
这通常是作为变量,在调用模型之前传递给提示模板,以形成具体的提示。 - 输出指示器 output indicator:
标记要生成的文本的开始。这就像我们小时候的数学考卷,先写一个“解”,就代表你要开始答题了。如果生成 Python 代码,可以使用 “import” 向模型表明它必须开始编写 Python 代码 (因为大多数 Python 脚本以 import 开头) 。
这部分在我们和 ChatGPT 对话时往往是可有可无的,当然 LangChain 中的代理在构建提示模板时,经常性的会用一个“Thought:”作为引导词,指示模型开始输出自己的推理 Reasoning。
三、LangChain 提示模板的类型
LangChain 中提供 String (StringPromptTemplate) 和 Chat (BaseChatPromptTemplate) 两种基本类型的模板,并基于它们构建了不同类型的提示模板:
- PromptTemplate —— 这是最常用的 string 提示模板,我们已经使用过 PromptTemplate 语句导入这个模板。
- ChatPromptTemplate —— 常用的 Chat 提示模板,用于组合各种角色的消息模板,传入 聊天模型 (Chat Model),具体消息模板包括:ChatMessagePromptTemplate、HumanMessagePromptTemplate、AIMessagePromptTemplate、SystemMessagePromptTemplate。
- FewShotPromptTemplate —— 少样本提示模板,通过示例的展示来 “教” 模型如何回答。
- PiplinePrompt —— 用于把几个提示组合在一起使用。
- 自定义模板 —— LangChain 还允许你基于其他模板类来定制自己的提示模板。
这些模板的导入方式如下:
from langchain.prompts.prompt import PromptTemplate
from langchain.prompts import ChatPromptTemplate
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.pipeline import PipelinePromptTemplate
from langchain.prompts import ChatMessagePromptTemplate, SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate
下面我们通过示例来介绍上面这些模版,前两个我们简单了解就好,其中最典型的 FewShotPromptTemplate 会重点讲。至于 PipelinePrompt 和自定义模板,使用起来比较简单,请你 参考 LangChain 文档自己学习。
四、使用 PromptTemplate
下面通过示例简单说明一下 PromptTemplate 的使用。
from langchain import PromptTemplate
template = """\
你是业务咨询顾问。
你给一个销售{product}的电商公司,起一个好的名字?
"""
prompt = PromptTemplate.from_template(template)
print(prompt.format(product="鲜花"))
print(prompt)
"""
你是业务咨询顾问。
你给一个销售鲜花的电商公司,起一个好的名字?
input_variables=['product'] template='你是业务咨询顾问。\n你给一个销售{product}的电商公司,起一个好的名字?\n'
"""
在这里,“你是业务咨询顾问。你给一个销售{product}的电商公司,起一个好的名字?” 就是原始提示模板,其中 {product} 是占位符。
然后通过 PromptTemplate 的 from_template 方法,我们创建了一个提示模板对象,并通过 prompt.format 方法将模板中的 {product} 替换为 “鲜花”。
在上面这个过程中,LangChain 中的模板的一个方便之处是 from_template 方法可以从传入的字符串中 自动提取变量名称,如 product,而无需刻意指定。上面程序中的 product 自动成为了 format 方法中的一个参数。
当然,也可以通过 提示模板类的构造函数,在 创建模板时 手工指定 input_variables,示例如下:
prompt = PromptTemplate(
input_variables=["product", "market"],
template="你是业务咨询顾问。对于一个面向{market}市场的,专注于销售{product}的公司,你会推荐哪个名字?"
)
print(prompt.format(product="鲜花", market="高端"))
"""
你是业务咨询顾问。对于一个面向高端市场的,专注于销售鲜花的公司,你会推荐哪个名字?
"""
五、使用 ChatPromptTemplate
对于 聊天模型 Chat Model,LangChain 也提供了一系列的模板,这些模板的不同之处是它们有对应的 角色。下面代码展示了 OpenAI 的 Chat Model 中的各种消息角色。
import openai
openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who won the world series in 2020?"},
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
{"role": "user", "content": "Where was it played?"}
]
)
OpenAI 对传输到 gpt-3.5-turbo 和 GPT-4 的 messsage 格式说明如下:
消息必须是 消息对象的数组,其中 每个对象 都有 一个角色 (系统、用户或助理) 和 内容。对话可以短至一条消息,也可以来回多次。
通常,对话首先由 系统消息 格式化,然后是交替的 用户消息 和 助理消息。
系统消息 有助于设置助手的行为。例如,你可以 修改助手的个性 或提供 有关其在整个对话过程中应如何表现的具体说明。但请注意,系统消息是可选的,并且没有系统消息的模型的行为可能类似于使用通用消息,例如“你是一个有用的助手”。
用户消息 提供助理响应的请求或评论。
助理消息 存储以前的助理响应,但也可以由你编写以给出所需行为的示例。
LangChain 的 ChatPromptTemplate 这一系列的模板,就是跟着这一系列角色而设计的。下面给出一个示例:
# 导入聊天消息类模板
from langchain.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
# 模板的构建
template="你是一位专业顾问,负责为专注于{product}的公司起名。"
system_message_prompt = SystemMessagePromptTemplate.from_template(template)
print(system_message_prompt,"\n")
human_template="公司主打产品是{product_detail}。"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
print(human_message_prompt,"\n")
prompt_template = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])
print(prompt_template,"\n")
# 格式化提示消息生成提示
prompt = prompt_template.format_prompt(product="鲜花装饰", product_detail="创新的鲜花设计").to_messages()
print(prompt,"\n")
# 下面调用模型,把提示传入模型,生成结果
import os
os.environ["OPENAI_API_KEY"] = '你的OpenAI Key'
from langchain.chat_models import ChatOpenAI
chat = ChatOpenAI()
result = chat(prompt)
print(result)
"""
prompt=PromptTemplate(input_variables=['product'], template='你是一位专业顾问,负责为专注于{product}的公司起名。')
prompt=PromptTemplate(input_variables=['product_detail'], template='公司主打产品是{product_detail}。')
input_variables=['product', 'product_detail'] messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=['product'], template='你是一位专业顾问,负责为专注于{product}的公
司起名。')), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['product_detail'], template='公司主打产品是{product_detail}。'))]
[SystemMessage(content='你是一位专业顾问,负责为专注于鲜花装饰的公司起名。'), HumanMessage(content='公司主打产品是创新的鲜花设计。')]
content='1. 花语创意\n2. 花韵设计\n3. 花艺创新\n4. 花漾装饰\n5. 花语装点\n6. 花翩翩\n7. 花语之美\n8. 花馥馥\n9. 花语时尚\n10. 花之魅力'
additional_kwargs={}
example=False
"""
从上面的输出可以看出,各个语句的具体作用。
六、FewShot 的起源
如果某个人第一次听说某个概念,却能马上知道是什么意思,这就叫 Zero-Shot,表明这个人的天赋不是一般的高,从知识积累和当前语境中就能够推知新词的涵义。有时候我们把 Zero-Shot 翻译为“顿悟”,聪明的大模型,某些情况下也是能够做到的。
Few-Shot(少样本)、One-Shot(单样本)和与之对应的 Zero-Shot(零样本)的概念都起源于机器学习。如何让机器学习模型在极少量甚至没有示例的情况下学习到新的概念或类别,对于许多现实世界的问题是非常有价值的,因为我们往往无法获取到大量的标签化数据。
七、使用 FewShotPromptTemplate
1、创建示例样本
首先,创建一些示例,作为 提示的样本。其中 每个示例 都是 一个字典,其中 键 是 输入变量,值 是这些 输入变量的值。
# 1. 创建一些示例
samples = [
{
"flower_type": "玫瑰",
"occasion": "爱情",
"ad_copy": "玫瑰,浪漫的象征,是你向心爱的人表达爱意的最佳选择。"
},
{
"flower_type": "康乃馨",
"occasion": "母亲节",
"ad_copy": "康乃馨代表着母爱的纯洁与伟大,是母亲节赠送给母亲的完美礼物。"
},
{
"flower_type": "百合",
"occasion": "庆祝",
"ad_copy": "百合象征着纯洁与高雅,是你庆祝特殊时刻的理想选择。"
},
{
"flower_type": "向日葵",
"occasion": "鼓励",
"ad_copy": "向日葵象征着坚韧和乐观,是你鼓励亲朋好友的最好方式。"
}
]
samples 这个 列表,它包含了 四个字典,每个字典 代表了 一种花的类型、适合的场合,以及对应的广告文案。 这些示例样本,就是构建 FewShotPrompt 时,作为例子传递给模型的参考信息。
2、创建提示模板
配置一个提示模板,将一个 samples 格式化为字符串。这个格式化程序应该是一个 PromptTemplate 对象。
# 2. 创建一个提示模板
from langchain.prompts.prompt import PromptTemplate
template="鲜花类型: {flower_type}\n场合: {occasion}\n文案: {ad_copy}"
prompt_sample = PromptTemplate(input_variables=["flower_type", "occasion", "ad_copy"],
template=template)
print(prompt_sample.format(**samples[0]))
"""
鲜花类型: 玫瑰
场合: 爱情
文案: 玫瑰,浪漫的象征,是你向心爱的人表达爱意的最佳选择。
"""
在这个步骤中,我们创建了一个 PromptTemplate 对象。这个对象 根据 指定的输入变量 和 模板字符串 来生成提示。在这里,输入变量包括 “flower_type”、“occasion”、“ad_copy”,模板是一个字符串,其中包含了用大括号包围的变量名,它们会被对应的变量值替换。
到这里,我们就把 字典中的示例格式 转换成了 提示模板,可以形成一个个具体可用的 LangChain 提示。比如我用 samples[0] 中的数据替换了模板中的变量,生成了一个完整的提示。
3、创建 FewShotPromptTemplate 对象
然后,通过使用上一步骤中创建的 prompt_sample,以及 samples 列表中的所有示例, 创建一个 FewShotPromptTemplate 对象,生成更复杂的提示。
# 3. 创建一个FewShotPromptTemplate对象
from langchain.prompts.few_shot import FewShotPromptTemplate
prompt = FewShotPromptTemplate(
examples=samples,
example_prompt=prompt_sample,
suffix="鲜花类型: {flower_type}\n场合: {occasion}",
input_variables=["flower_type", "occasion"]
)
print(prompt.format(flower_type="野玫瑰", occasion="爱情"))
"""
鲜花类型: 玫瑰
场合: 爱情
文案: 玫瑰,浪漫的象征,是你向心爱的人表达爱意的最佳选择。
鲜花类型: 康乃馨
场合: 母亲节
文案: 康乃馨代表着母爱的纯洁与伟大,是母亲节赠送给母亲的完美礼物。
鲜花类型: 百合
场合: 庆祝
文案: 百合象征着纯洁与高雅,是你庆祝特殊时刻的理想选择。
鲜花类型: 向日葵
场合: 鼓励
文案: 向日葵象征着坚韧和乐观,是你鼓励亲朋好友的最好方式。
鲜花类型: 野玫瑰
场合: 爱情
"""
可以看到,FewShotPromptTemplate 是一个更复杂的提示模板,它包含了多个示例和一个提示。
4、调用大模型创建新文案
最后,把这个对象输出给大模型,就可以根据提示,得到我们所需要的文案了!
# 4. 把提示传递给大模型
import os
os.environ["OPENAI_API_KEY"] = '你的Open AI Key'
from langchain.llms import OpenAI
model = OpenAI(model_name='gpt-3.5-turbo-instruct')
result = model(prompt.format(flower_type="野玫瑰", occasion="爱情"))
print(result)
"""
文案: 野玫瑰代表着爱情的坚贞,是你向心爱的人表达爱意的最佳礼物。
"""
八、使用示例选择器
如果我们的示例很多,那么一次性把所有示例发送给模型是不现实而且低效的。另外,这也会导致使用到更多的 token。
LangChain 给我们提供了 示例选择器,来选择最合适的样本 (注意,因为示例选择器使用 向量相似度比较 的功能,此处需要安装 向量数据库,如 Chroma)。
# 5. 使用示例选择器
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
# 初始化示例选择器
example_selector = SemanticSimilarityExampleSelector.from_examples(
samples,
OpenAIEmbeddings(),
Chroma,
k=1
)
# 创建一个使用示例选择器的FewShotPromptTemplate对象
prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=prompt_sample,
suffix="鲜花类型: {flower_type}\n场合: {occasion}",
input_variables=["flower_type", "occasion"]
)
print(prompt.format(flower_type="红玫瑰", occasion="爱情"))
"""
鲜花类型: 玫瑰
场合: 爱情
文案: 玫瑰,浪漫的象征,是你向心爱的人表达爱意的最佳选择。
鲜花类型: 红玫瑰
场合: 爱情
"""
在这个步骤中,它首先创建了一个 SemanticSimilarityExampleSelector 对象,这个对象可以 根据语义相似性 选择最相关的示例。
然后,它创建了一个新的 FewShotPromptTemplate 对象,这个对象使用了上一步创建的选择器来选择最相关的示例生成提示。然后,我们又用这个模板生成了一个新的提示,因为我们的提示中需要创建的是红玫瑰的文案,所以,示例选择器 example_selector 会根据 语义的相似度 (余弦相似度) 找到最相似的示例,也就是“玫瑰”,并用这个示例构建了 FewShot 模板。这样,我们就避免了把过多的无关模板传递给大模型,以节省 Token 的用量。