文章目录
- 面向开发者的Prompt Engineering
- 简介
- Prompt设计原则
- 1 环境配置
- 2.两个基本原则
- 2.1 原则1:编写清晰、具体的指令
- 2.1.1 策略一:分割
- 2.1.2 策略2:结构化输出
- 2.1.3 策略3:模型检测
- 2.1.4 策略4:提供示例
- 2.2 原则二:给模型足够时间思考
- 2.2.1 策略5:指定步骤
- 2.2.2 策略6:结论之前找出一个自己的办法
- 3. 局限性
面向开发者的Prompt Engineering
简介
随着LLM的发展,其大致可以分为两种类型:基础LLM和指令微调LLM。
- 基础LLM:假设你以
“从前有一只独角兽”
作为提示,基础的LLM可能会继续预测“生活在一个与所有的独角兽朋友的神奇森林中”
。但是如果以"法国的首都是什么?"
,其回答可能为"法国的人口是多少?法国最大的城市是什么?"
。因为其训练的语料可能是有关法国国家的问答题目列表。 - 指令微调LLM:指的是被训练来能够遵循特定指令的LLM。而这样的模型通常是在基础LLMs上,使用输入的指令、输出是其应该返回的结果来对其进行微调,要求它遵循这些指令。而通常使用一种称为RLHF(reinforcement learning from human feedback,人类反馈强化学习)的技术进一步改进,使系统能够有帮助得遵循命令。
Prompt设计原则
1 环境配置
针对基本的开发环境在这里不强调,主要说明一哈这里的chatgpt的api key【可以淘宝购买】。
- 设置api-key
openai.api_key = "REPLASE BY YOUR API KEY"
- 下载三方包
pip install openai
- 加载基本的代码
# 一个封装 OpenAI 接口的函数,参数为 Prompt,返回对应结果
def get_completion(prompt, model="gpt-3.5-turbo"):'''prompt: 对应的提示model: 调用的模型,默认为 gpt-3.5-turbo(ChatGPT),有内测资格的用户可以选择 gpt-4'''messages = [{"role": "user", "content": prompt}]response = openai.ChatCompletion.create(model=model,messages=messages,temperature=0, # 模型输出的温度系数,控制输出的随机程度)# 调用 OpenAI 的 ChatCompletion 接口return response.choices[0].message["content"]
2.两个基本原则
2.1 原则1:编写清晰、具体的指令
提供清晰具体的指示,避免无关或不正确响应,不要混淆写清晰和写简短,更长的提示可以提供更多清晰度和上下文信息,导致更详细和相关的输出。
2.1.1 策略一:分割
使用分割符清晰得表示输入的不同部分,分割符号可以是:```,””,<>,<tag>,<\tag>等
实际操作中,可以使用任何明显的的标点符号将特点的文本部分与其提示词分开。这可以可以使模型明确知道这是一个单独部分的标记。同时这样分割避免了输入里面可能包含其他的指令,会覆盖掉自己的指令。以下是一个具体的例子:
- 不使用分割策略
# 消息本身text = "今天天气不错,我出去玩一会儿我有错吗?"# 嵌入前面消息的Promptexpanding_prompt = "请扩展下面表达{}".format(text)# 获取模型返回response = get_completion(expanding_prompt)print(response)# 今天天气真是太好了,阳光明媚,微风轻拂,我实在忍不住想出去玩一会儿。我有错吗?
- 使用分割策略
# 消息本身text = "今天天气不错,我出去玩一会儿我有错吗?"# 嵌入前面消息的Promptexpanding_prompt = "请扩展下面引号号内的表达`{}`".format(text)# 获取模型返回response = get_completion(expanding_prompt)print(response)# "今天天气不错,我出去玩一会儿我有错吗?生活就是要享受,不是吗?我需要放松一下,让自己的心情变得更加愉快。"
2.1.2 策略2:结构化输出
要求模型结构输出,可以是JSON、HTML、Markdown等格式
使用结构输出的列子更容易满足用户的输出要求,避免模型遗忘某些重要的细节,如下例子(后面有完整的代码):
def prompt_strategy_02():# 消息本身text = "我正在完成一道中国传统美食——鱼香茄子的制作,你能否给出一些建议?"# 要求输出格式format_prompt = "针对括号内制作的细节以json格式输出[{}]".format(text)return format_prompt
# {
# "细节": {
# "1": "茄子切块后要用盐腌制一段时间,使其出水,减少油炸时吸油量",
# "2": "油炸茄子时要掌握好油温,避免茄子吸油过多",
# "3": "炒茄子时要先炒至金黄,再加入调料翻炒均匀",
# "4": "调味料要根据个人口味适量调整,以保持鱼香味道的平衡",
# "5": "可以适量加入葱姜蒜等调料,提升菜品的香气和口感"
# }
#}
2.1.3 策略3:模型检测
要求模型检查是否满足条件
在某种情况下,比如场景可能包含多种情形,则可以告知模型针对不同情形(条件检测)如何来对场景进行描述。比如如下的一个例子:
def prompt_strategy_03():"""策略3:模型检查条件:return:"""# 场景描述text = f"""泡一杯茶很容易。首先,需要把水烧开。\在等待期间,拿一个杯子并把茶包放进去。\一旦水足够热,就把它倒在茶包上。\等待一会儿,让茶叶浸泡。几分钟后,取出茶包。\如果你愿意,可以加一些糖或牛奶调味。\就这样,你可以享受一杯美味的茶了。"""# 设计promptprompt = f"""您将获得由括号括起来的文本。\如果它包含一系列的指令,则需要按照以下格式重新编写这些指令:第一步 - ...第二步 - ……第N步 - …如果文本中不包含一系列的指令,则直接写“未提供步骤“[{text}] """return prompt
输出如下
第一步 - 把水烧开。
第二步 - 拿一个杯子并把茶包放进去。
第三步 - 把烧开的水倒在茶包上。
第四步 - 等待一会儿,让茶叶浸泡。几分钟后,取出茶包。
第五步 - 如果你愿意,可以加一些糖或牛奶调味。
第六步 - 就这样,你可以享受一杯美味的茶了。
2.1.4 策略4:提供示例
提供一些帮助模型理解的例子可以使得输出结果更好
比如一个经典的回答中是要求回复的风格,看下面的例子:
def prompt_strategy_04():"""策略04:提供示例:return:"""# 消息本身text = "天气真没!"# 嵌入到提示词中prompt = f"""你的任务是以一致的风格回答问题。<Q>: 你人真好!<A>: 人好不一定其心好,心怀不代表人不好!<Q>: {text}"""return prompt
输出如下:
<A>: 天气不好不代表心情不好,心情不好也不代表天气不好!愿你心情常好,无论天气如何!
2.2 原则二:给模型足够时间思考
在很多情况下,为了保证速度,模型并不能拥有充足的时间去思考,这跟人的思考是相同的,因此提供给模型足够的思考时间,也许能获得更好的结果,但是也面临着消耗更多的技术资源。
2.2.1 策略5:指定步骤
指定完成任务所需的步骤
def prompt_strategy_05():"""策略5:指定任务所需的步骤:return: """text = f"""在一个迷人的村庄里,兄妹杰克和吉尔出发去一个山顶井里打水。\他们一边唱着欢乐的歌,一边往上爬,\然而不幸降临——杰克绊了一块石头,从山上滚了下来,吉尔紧随其后。\虽然略有些摔伤,但他们还是回到了温馨的家中。\尽管出了这样的意外,他们的冒险精神依然没有减弱,继续充满愉悦地探索。"""# example 1prompt = f"""执行以下操作:1-用一句话概括下面用三个双引号括起来的文本。2-将摘要翻译成法语。3-在法语摘要中列出每个人名。4-输出一个 JSON 对象,其中包含以下键:French_summary,num_names。请用换行符分隔您的答案。Text:\"\"\"{text}\"\"\""""return prompt
其输出如下:
1- 兄妹在山顶井里打水时发生意外,但他们的冒险精神依然不减。
2- Dans un charmant village, les frère et sœur Jack et Jill partent chercher de l'eau dans un puits au sommet d'une montagne.
3- Jack, Jill
4-
{"French_summary": "Dans un charmant village, les frère et sœur Jack et Jill partent chercher de l'eau dans un puits au sommet d'une montagne.","num_names": 2
}
事实上,这里的prompt可以更好,如下:
prompt_better = f"""1-用一句话概括下面用<>括起来的文本。2-将摘要翻译成英语。3-在英语摘要中列出每个名称。4-输出一个 JSON 对象,其中包含以下键:English_summary,num_names。请使用以下格式:文本:<要总结的文本>摘要:<摘要>翻译:<摘要的翻译>名称:<英语摘要中的名称列表>输出 JSON:<带有 English_summary 和 num_names 的 JSON>Text: <{text}>"""
新的提示词输出如下:
摘要:在一个迷人的村庄里,兄妹杰克和吉尔在去山顶井打水的途中遭遇意外,但他们的冒险精神依然坚定。翻译:In a charming village, siblings Jack and Jill encounter an accident on their way to fetch water from a well on the mountaintop, but their adventurous spirit remains strong.名称:Jack, Jill输出 JSON:{"English_summary": "In a charming village, siblings Jack and Jill encounter an accident on their way to fetch water from a well on the mountaintop, but their adventurous spirit remains strong.", "num_names": 2}
2.2.2 策略6:结论之前找出一个自己的办法
指导模型下结论之前找到一个自己的方法
有时候,在明确指导模型在做决策之前要思考自己的解决方案,我们可能会得到一个更好的结果。如下的例子:
def prompt_strategy_06():prompt = f"""判断学生的解决方案是否正确。问题:我正在建造一个太阳能发电站,需要帮助计算财务。土地费用为 100美元/平方英尺我可以以 250美元/平方英尺的价格购买太阳能电池板我已经谈判好了维护合同,每年需要支付固定的10万美元,并额外支付每平方英尺10美元作为平方英尺数的函数,首年运营的总费用是多少。学生的解决方案:设x为发电站的大小,单位为平方英尺。费用:土地费用:100x太阳能电池板费用:250x维护费用:100,000美元+100x总费用:100x+250x+100,000美元+100x=450x+100,000美元"""return prompt
其输出如下:
学生的解决方案是正确的。总费用的计算是正确的,即总费用为450x + 100,000美元。
但是实际上,学生的方案是有问题的。在接下来这个 Prompt 中,我们要求模型先自行解决这个问题,再根据自己的解法与学生的解法进行对比,从而判断学生的解法是否正确。同时,我们给定了输出的格式要求。通过明确步骤,让模型有更多时间思考,有时可以获得更准确的结果。在这个例子中,学生的答案是错误的,但如果我们没有先让模型自己计算,那么可能会被误导以为学生是正确的。
def prompt_strategy_06():# prompt = f"""# 判断学生的解决方案是否正确。## 问题:# 我正在建造一个太阳能发电站,需要帮助计算财务。## 土地费用为 100美元/平方英尺# 我可以以 250美元/平方英尺的价格购买太阳能电池板# 我已经谈判好了维护合同,每年需要支付固定的10万美元,并额外支付每平方英尺10美元# 作为平方英尺数的函数,首年运营的总费用是多少。## 学生的解决方案:# 设x为发电站的大小,单位为平方英尺。# 费用:## 土地费用:100x# 太阳能电池板费用:250x# 维护费用:100,000美元+100x# 总费用:100x+250x+100,000美元+100x=450x+100,000美元# """prompt = f"""请判断学生的解决方案是否正确,请通过如下步骤解决这个问题:步骤:首先,自己解决问题。然后将你的解决方案与学生的解决方案进行比较,并评估学生的解决方案是否正确。在自己完成问题之前,请勿决定学生的解决方案是否正确。使用以下格式:问题:问题文本学生的解决方案:学生的解决方案文本实际解决方案和步骤:实际解决方案和步骤文本学生的解决方案和实际解决方案是否相同:是或否学生的成绩:正确或不正确问题:我正在建造一个太阳能发电站,需要帮助计算财务。 - 土地费用为每平方英尺100美元- 我可以以每平方英尺250美元的价格购买太阳能电池板- 我已经谈判好了维护合同,每年需要支付固定的10万美元,并额外支付每平方英尺10美元作为平方英尺数的函数,首年运营的总费用是多少。学生的解决方案:设x为发电站的大小,单位为平方英尺。费用:1. 土地费用:100x2. 太阳能电池板费用:250x3. 维护费用:100,000+100x总费用:100x+250x+100,000+100x=450x+100,000实际解决方案和步骤:"""return prompt
其输出为:
首先,计算土地费用:100美元/平方英尺 * x平方英尺 = 100x美元然后,计算太阳能电池板费用:250美元/平方英尺 * x平方英尺 = 250x美元接着,计算维护费用:10万美元 + 10美元/平方英尺 * x平方英尺 = 10万 + 10x美元最后,计算总费用:100x + 250x + 10万 + 10x = 360x + 10万美元学生的解决方案和实际解决方案是否相同:否学生的成绩:不正确
3. 局限性
因为LLM并不能感知信息的边界,同时其接触了大量知识,因为在很多某些时候其会编造一些信息。