Type something to search...
【大语言模型实战】 Prompt Engineering

【大语言模型实战】 Prompt Engineering

本文主要观点来自 ChatGPT Prompt Engineering 吴恩达 Prompt

在学习本课程之前,我一直认为 prompt engineering 只不过是大模型发展不够完善而出现的过渡态,但随着学习的深入,我的观点发生了改变. prompt enginnering 之于大模型,就如同沟通技巧之于人类. 更好的交流技巧往往能更高效精确的传达信息,而 prompt enginnering 则能让大模型更好的理解我们的意图.

1. 什么是 Prompt Engineering

大语言模型(LLM)在语料库上训练之后,并不能达到如 ChatGPT 那般令人惊艳的效果。因为本质上 LLM 的能力就是给定一段文字,进行续写。

  • Base LLM: 基于大量数据训练的模型. 当我们提问: “如何画一个圆?” 模型会回答: 如何画一个圆

但是在实际应用中,我们需要的是给定一个问题,生成一个答案。这就需要我们对 LLM 进行 Prompt Tuneing,即通过一些技巧,让 LLM 在给定 prompt 的情况下,生成我们想要的答案。

  • Instruction Tuned LLM: 通过 Prompt Engineering 之后的模型.

举个例子: 在我们提问前,我们可以通过 prompt 来指导模型生成特定的文本.如: 提示

这时当我们提问: “如何画一个圆?” 模型就会在我们圈定的范围内给出合乎我们预期的答案. 如何画一个圆

ChatGPT 用到了 RLHF (Reinforcement learning with human feedback) 的技术。

注 1:最早把强化学习引入语言模型中的,是 OpenAI 在 2019 年的一篇工作:Fine-Tuning Language Models from Human Preferences 注 2:RLHF 并不能有效提升模型的跑分,在小模型上性能甚至会有比较大的下降(随着参数增多,RLHF 微调会稍微增强性能),但是可以让它“显得”更智能

InstructGPT (arxiv.org) 中详细介绍了 RLHF: RLHF

整个过程可以分成三步:

  1. 通过人工写回答,收集了一个 prompt-response 数据集,用它对 GPT3 作微调,教会它如何产生人类想要的回答,得到的模型记作 fine-tuned GPT3。

  2. 利用 fine-tuned GPT3 对某个 prompt 产生多个回答,并人工对它们按照好坏进行排序,可以称作 ranking 数据集。借助这些数据,训练一个 reward model,用来评估 response 的好坏,训练目标是尽量接近于人工排序。

  3. 利用强化学习 PPO 算法,对 fine-tuned GPT3 进行优化。用强化学习中的术语来说, fine-tuned GPT3 就是 agent;Reward model 就是 environment,为 agent 的回答提供反馈。Agent 利用这个反馈优化自身参数,继续与环境交互,从而迭代优化。

Note:相比于第二步中的 ranking 数据集,第一步中 prompt-response 数据集的收集代价更为昂贵(排序比自己写答案要简单太多了)。其实如果 prompt-response 数据集足够大,可以只用它在 GPT3 上做微调,而不需要强化学习。 Note:1.3B(13 亿)参数量的 InstructGPT 产生的回答,效果要好于 175B 的 GPT-3

2.Prompt 的编写原则

2.1 环境安装

本课程为实践课程,当然需要配置环境啦. 既然是学习,咱们就一切从简,直接在在本地搭建环境

但是你也可以使用 Google Colab,它不需要配置环境,直接上手.

注:因为在访问 openai API 本来就需要科学上网,所这里我默认你已经具备了对应的网络条件.

  1. 安装 python (推荐使用 anaconda )

  2. 安装依赖

pip install openai
pip install python-dotenv
  1. 新建一个文件夹,在文件夹里创建 .env 文件,并在其中添加你的 openai API key
OPENAI_API_KEY='sk-XXXXX'
  1. 创建一个 python 文件,并在其中添加如下代码
import openai
import os
from dotenv import load_dotenv,find_dotenv

_ = load_dotenv(find_dotenv()) # read local .env file

openai.api_key = os.getenv("OPENAI_API_KEY")

def get_completion(prompt, model = "gpt-3.5-turbo"):
  messages = [{"role":"user","content":prompt}]
  response = openai.ChatCompletion.create(
    model=model,
    messages=messages,
    temperature=0,
  )
  return response.choices[0].message["content"]

2.2 编写原则

我们先来介绍几个基本的编写 prompt 的原则

2.2.1 编写清晰、具体的指令

策略 1: 使用分隔符清晰地表示输入的不同部分 我们可以用任何明显的分隔符来将特定的文本与其余部分分开,分隔符可以是: ```,"",<>,,<\tag> 等. 这么做可以有效避免提示注入.

提示注入: 提示注入是一种语言模型输入不符合预期的现象(也是一种用于劫持语言模型输出的技术)。当把用户输入文本作为提示的一部分使用时,就会发生这种情况。

举个例子: 我们使用 """ 作为分隔符 在上面的代码后面添加如下代码:

text = f"""
你应该提供尽可能清晰、具体的指示,以表达你希望模型执行的任务。
这将引导模型朝向所需的输出,并降低收到无关或不正确响应的可能性。
不要将写清晰的提示与写简短的提示混淆。  
在许多情况下,更长的提示可以为模型提供更多的清晰度和上下文信息,从而导致更详细和相关的输出。
"""
# 需要总结的文本内容
prompt = f"""
把用三个反引号括起来的文本总结成一句话。
```{text}```
"""
# 指令内容,使用 ``` 来分隔指令和待总结的内容
response = get_completion(prompt)
print(response)

策略 2 : 要求模型结构化输出 如要求模型使用 JSON 或 HTML,对象的形式输出,这样使模型结果更容易被解析 如下面例子

prompt = f"""
请生成包括书名、作者和类别的三本虚构书籍清单,
并以 JSON 格式提供,其中包含以下键:book_id、title、author、genre。
"""
response = get_completion(prompt)
print(response)

策略 3 要求模型检查是否满足条件 如果我们需要模型执行的任务可能无法满足,我们可以要求模型检查是否满足条件,如果不满足,则返回错误信息. 这样我们就可以提前将潜在的边界情况考虑进去.

text_1=f"""
泡一杯茶很容易。首先,需要把水烧开。
在等待期间,拿一个杯子并把茶包放进去。
一旦水足够热,就把它倒在茶包上。
等待一会儿,让茶叶浸泡。几分钟后,取出茶包。
如果你愿意,可以加一些糖或牛奶调味。
就这样,你可以享受一杯美味的茶了。
"""

prompt = f"""
您将获得由三个引号括起来的文本。
如果它包含一系列的指令,则需要按照以下格式重新编写这些指令:

第一步 - ... \n
第二步 - ...
...
第N步 - ...

如果文本中不包含一系列的指令,则直接写“未提供步骤”。"

\"\"\"{text_1}
\"\"\"
"""

# 指令内容,使用 ``` 来分隔指令和待总结的内容
response = get_completion(prompt)
print(response)



# >>>>>>> 输出 <<<<<<<
# Text 1 的总结:
# 第一步 - 把水烧开。
# 第二步 - 拿一个杯子并把茶包放进去。
# 第三步 - 把烧开的水倒在茶包上。
# 第四步 - 等待几分钟,让茶叶浸泡。
# 第五步 - 取出茶包。
# 第六步 - 如果你愿意,可以加一些糖或牛奶调味。
# 第七步 - 就这样,你可以享受一杯美味的茶了。

策略 4 : 提供少量的示例 即在要求模型执行任务时,提供一些示例,这样可以帮助模型更好的理解我们的意图.

prompt = f"""
你的任务是以一致的风格回答问题。

<孩子>: 教我耐心。

<祖父母>: 挖出最深峡谷的河流源于一处不起眼的泉眼;最宏伟的交响乐从单一的音符开始;最复杂的挂毯以一根孤独的线开始编织。

<孩子>: 教我韧性。
"""

# 最终输出的结果
# <祖父母>: 韧性就像是一棵树,它需要经历风吹雨打、寒冬酷暑,才能成长得更加坚强。所以,当你遇到挫折和困难时,不要轻易放弃,要像树一样坚定地扎根,不断成长,最终成为一棵高大的树。

2.2.2 给模型时间去思考

技巧一 : 技巧一:明确完成任务所需的步骤

下面的例子里,将任务分成了若干部分,让 LLM 分别完成

text = f"""
李白乘舟将欲行, 忽闻岸上踏歌声。
桃花潭水深千尺, 不及汪伦送我情。
"""

作者:周末程序猿
链接:https://juejin.cn/post/7231519213893533754
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


prompt_1 = f"""
你的工作是:
1 - 一句话总结下面被三个反引号包裹的文本.
2 - 将总结翻译为法语
3 - 列出法语摘要中的每个名字
4 - 输出包含以下键的JSON对象: 法语摘要和名字数量统计

请用换行符分隔您的答案。

文本:
```{text}```
"""

最终返回 结果

技巧二 : 指导模型在下结论前找到自己的解法

有时候,告诉模型在下结论前要思考解决方案时,模型会给我们更好的回答.


prompt = f"""
请判断学生的解决方案是否正确,请通过如下步骤解决这个问题:

步骤:

    首先,自己解决问题。
    然后将你的解决方案与学生的解决方案进行比较,并评估学生的解决方案是否正确。在自己完成问题之前,请勿决定学生的解决方案是否正确。

使用以下格式:

    问题:问题文本
    学生的解决方案:学生的解决方案文本
    实际解决方案和步骤:实际解决方案和步骤文本
    学生的解决方案和实际解决方案是否相同:是或否
    学生的成绩:正确或不正确

问题:
    我正在建造一个太阳能发电站,需要帮助计算财务。 
    - 土地费用为每平方英尺100美元
    - 我可以以每平方英尺250美元的价格购买太阳能电池板
    - 我已经谈判好了维护合同,每年需要支付固定的10万美元,并额外支付每平方英尺10美元
    作为平方英尺数的函数,首年运营的总费用是多少。

学生的解决方案:
    设x为发电站的大小,单位为平方英尺。
    费用:
    1. 土地费用:100x
    2. 太阳能电池板费用:250x
    3. 维护费用:100,000+100x
    总费用:100x+250x+100,000+100x=450x+100,000

实际解决方案和步骤:
"""

方案 在上面例子中如果不加上提示,模型会直接认为学生解决方案时对的.

技巧三 让模型思考,减少虚假知识

虚假知识: 模型会一本正经的生成一些离题千里的答案.举个例子

prompt = f"""
告诉我阿里云工业组态产品的特点和优势
"""

虚假知识

因为我参与了这个产品的研发,所以我很容易判断模型给出的答案是错误的.

这时候我们可以结合上面的技巧,让模型在下结论前思考,这样可以减少虚假知识的产生.一个有效的解决方案:

  1. 找到相关的文献引用
  2. 要求模型使用这些引用回答问题
prompt = f"""
你的工作是:
1 - 查找关于阿里云组态产品的介绍文档,将他们列出来.
2 - 将列出来的文档总结成一句话.
3 - 结合总结,告诉我阿里云组态产品的特点
"""

结果

3.Prompt 的优化迭代(Iterative)

使用 LLM 创建应用时很难一蹴而就,需要多次迭代. 那么一个好的迭代流程对于改进 Prompt 是非常重要的.

提出想法 → 编写代码 → 分析结果 → 重复

任务目标: 根据说明书生产营销文案

我们的任务是根据说明书生产营销文案,首先简单写一个 prompt 看看效果


# 产品说明书
doc = """
概述

美丽的中世纪风格办公家具系列的一部分,包括文件柜、办公桌、书柜、会议桌等。
     多种外壳颜色和底座涂层可选。
     可选塑料前后靠背装饰(SWC-100)或 10 种面料和 6 种皮革的全面装饰(SWC-110)。
     底座涂层选项为:不锈钢、哑光黑色、光泽白色或铬。
     椅子可带或不带扶手。
     适用于家庭或商业场所。
     符合合同使用资格。

结构

五个轮子的塑料涂层铝底座。
     气动椅子调节,方便升降。

尺寸

宽度 53 厘米|20.87 英寸
     深度 51 厘米|20.08 英寸
     高度 80 厘米|31.50 英寸
     座椅高度 44 厘米|17.32 英寸
     座椅深度 41 厘米|16.14 英寸

选项

软地板或硬地板滚轮选项。
     两种座椅泡沫密度可选:中等(1.8 磅/立方英尺)或高(2.8 磅/立方英尺)。
     无扶手或 8 个位置 PU 扶手。

材料
外壳底座滑动件

改性尼龙 PA6/PA66 涂层的铸铝。
     外壳厚度:10 毫米。
     座椅
    HD36 泡沫

原产国

意大利
"""

# 生成营销文案
prompt = f"""
你的任务是帮助营销团队基于技术说明书创建一个产品的营销描述。

根据```标记的技术说明书中提供的信息,编写一个产品描述。

技术说明: ```{doc}```
"""

response = get_completion(prompt)
print(response)

最后结果: 结果

看起来还不错,接下来我们对 prompt 做一些优化

3.1 优化 1: 文本太长

解决方法: 对他返回的文本长度进行限制

# 优化后的 Prompt,要求生成描述不多于 50 词
prompt = f"""
您的任务是帮助营销团队基于技术说明书创建一个产品的零售网站描述。

根据```标记的技术说明书中提供的信息,编写一个产品描述。

使用最多50个词。

技术规格:```{doc}```
"""


# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 这款美丽的中世纪风格办公家具系列包括文件柜、办公桌、书柜、会议桌等。可选多种外壳颜色和底座涂层,底座涂层选项为不锈钢、哑光黑色、光泽白色或铬。椅子可带或不带扶手,适用于家庭或商业场所。

LLM 在遵循非常精确的字数限制方面表现得还可以,但并不那么出色,有时它会输出 60 或 65 个单词的内容,但这还算是合理的,这原因是 LLM 解释文本使用一种叫做分词器的技术.

3.2 优化 2: 关注点不对

我们的目标用户是家具零售商,他们更加关心产品的技术细节和材料. 解决方法: 要求它专注于与目标受众相关的方面。

prompt = f"""
您的任务是帮助营销团队基于技术说明书创建一个产品的零售网站描述。

根据```标记的技术说明书中提供的信息,编写一个产品描述。

该描述面向家具零售商,因此应具有技术性质,并侧重于产品的材料构造。

在描述末尾,包括技术规格中每个7个字符的产品ID。

使用最多50个词。

技术规格:```{doc}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 这款中世纪风格的办公家具系列包括文件柜、办公桌、书柜和会议桌等。可选多种外壳颜色和底座涂层,底座涂层选项为不锈钢、哑光黑色、光泽白色或铬。椅子可带或不带扶手,适用于家庭或商业场所。座椅高度44厘米,座椅深度41厘米。产品ID:SWC-100/SWC-110。

3.3 优化 3: 结构化的输出

上面就是一个简单的 prompt 迭代优化案例,但我们还可以要求模型输出结构化的文本,比如我们可以要求模型输出成 html 格式,这样就可以直接在网页中显示.

prompt = f"""
您的任务是帮助营销团队基于技术说明书创建一个产品的零售网站描述。

根据```标记的技术说明书中提供的信息,编写一个产品描述。

该描述面向家具零售商,因此应具有技术性质,并侧重于产品的材料构造。

在描述末尾,包括技术规格中每个7个字符的产品ID。

在描述之后,包括一个表格,提供产品的尺寸。表格应该有两列。第一列包括尺寸的名称。第二列只包括英寸的测量值。

给表格命名为“产品尺寸”。

将所有内容格式化为可用于网站的HTML格式。将描述放在<div>元素中。

技术规格:```{doc}```
"""

结果: {% codepen url=”https://codepen.io/wuriqilang/pen/qBQZxgR” slug=“qBQZxgR” title=“大模型结构化输出” /%}

4 文本摘要(Summarizing)

世界上有太多的文本信息,几乎没有人能够拥有足够的时间去。但 LLM 在文本概括任务上展现了强大的水准.

人生苦短

4.1 单文本概括

我们举一个电商的例子,电商平台里有海量的用户评论. 如果我们能够使用 LLM 来对用户的评论进行概括,就可以快速的获得更多信息,洞悉客户偏好.

我从速成美站的评论中随机抽取了一条评论:

review = """
网站改版的时候本来是要找外面的设计公司来做的,后来选择了速成美站,性价比很高!推荐使用标准版的,功能更多一些。但在使用的时候还是遇到了一些问题,主要是对模板后台使用不太熟悉,很多控件不知道如何使用,而且模板没办法直接套用,改动很大,不过在熟悉控件之后,确实和PPT操作差不多了,很意外,搭建成形后的网站感觉还不错,毕竟这是一个小白从头到尾做下来的,真正开始搭建网站只花了不到十天的时间。域名解析和最后的SSL证书部署都是阿里云建站客服人员帮忙解决的,响应速度非常快,而且专业有效率。
"""

4.1.1 限制文章长度

# 文本总结
prompt = f"""
你的任务是从电子商务网站上生成一个产品评论的简短摘要。
请对三个反引号之间的评论文本进行概括,

最多30个词汇。

评论: ```{review}```
"""

response = get_completion(prompt)
print(response)

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
#速成美站提供高性价比的标准版,但使用模板后台需要熟悉控件,改动较大。客服响应#快,帮助解决域名和SSL证书问题。

4.1.2 侧重不同角度

有时候,对于不同的业务场景,我们需要从不同的角度去概括文本,比如对于这类建站模板产品,商家更加关心产品是否能满足客户需求,客户是否能够快速上手,所以我们可以要求模型从不同的角度去概括文本.

prompt = f"""
你的任务是从电子商务网站上生成一个产品评论的简短摘要。
请对三个反引号之间的评论文本进行概括,

最多30个词汇,并且聚焦在产品易用性上

评论: ```{review}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 使用速成美站标准版易用性高,但模板后台使用需要熟悉,需要改动较大。阿里云建站客服响应速度快,专业有效率。

可以看到输出结构把易用性的描述放在了第一位.

再比如侧重产品客服质量:

prompt = f"""
你的任务是从电子商务网站上生成一个产品评论的简短摘要。
请对三个反引号之间的评论文本进行概括,

最多30个词汇,并且聚焦在客服质量上

评论: ```{review}```
"""#

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 使用速成美站搭建网站,遇到模板后台使用问题,但客服响应速度快且专业有效率。

4.1.3 关键信息提取

在上一节中我们通过添加侧重角度的 Prompt,是文本总结侧重于某一方面,但仍然保留了其他信息.如果我们只想要某一方面的信息,可以要求 LLM 进行”文本提取(Extract)“而非”文本概括(Summarize)“.

# 文本总结
prompt = f"""
你的任务是从电子商务网站上提取相关信息。
请从一下三个反引号之间的评论文本提取客服质量相关的信息,

最多30个词汇

评论: ```{review}```

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 客服响应速度快,专业有效率。

4.2 多文本概括

在实际的工作流中,我们往往有许许多多的评论文本,以下展示了一个基于 for 循环调用“文本概括”工具并依次打印的示例。

当然,在实际生产中,对于上百万甚至上千万的评论文本,使用 for 循环也是不现实的,可能需要考虑整合评论、分布式等方法提升运算效率。


# 一个公仔的评论
review_1 = """
这个熊猫公仔是我给女儿的生日礼物,她很喜欢,去哪都带着。
公仔很软,超级可爱,面部表情也很和善。但是相比于价钱来说,
它有点小,我感觉在别的地方用同样的价钱能买到更大的。
快递比预期提前了一天到货,所以在送给女儿之前,我自己玩了会。
"""

# 一盏落地灯的评论
review_2 = """
我需要一盏漂亮的卧室灯,这款灯还带有额外的储物空间,价格也不算太高。
我很快就收到了它,只用了2天的时间。在运输过程中,灯的绳子断了,但是公司很高兴地寄来了一个新的。
几天后就收到了。组装很容易。然后我发现有一个零件缺失,于是我联系了他们的客服,他们很快就给我寄来了缺失的零件!
对我来说,这是一家非常关心客户和产品的优秀公司。
"""

# 一款电动牙刷的评论
review_3 = """
我的牙科卫生师推荐我使用电动牙刷,这就是我购买它的原因。
到目前为止,电池寿命似乎相当令人印象深刻。在最初充电并在第一周将充电器插头插入以对电池进行调节后,我已经拔掉了充电器,并在过去的3周中每天两次刷牙,都是在同一次充电的情况下完成的。
但是牙刷头太小了。我见过比这个更大的婴儿牙刷。我希望牙刷头更大,有不同长度的刷毛,以更好地清洁牙齿之间的区域,因为这个牙刷不能做到。
总的来说,如果你能在50美元左右购买到这个牙刷,那么这是一个不错的选择。制造商的替换刷头相当昂贵,但你可以购买价格更合理的通用刷头。这个牙刷让我感觉每天都去了牙医。我的牙齿感觉非常干净!
"""

# 一个搅拌机的评论
review_4 = """
所以,他们在11月份的季节性促销中仍然以约49美元的价格销售17件套装,折扣约为一半,但由于某种原因(称其为价格欺诈),到了12月第二周,所有价格都上涨了,同样的套装价格从70美元到89美元不等。
11件套装的价格也比之前的29美元上涨了约10美元。所以看起来还不错,但如果你看底座,刀锁定的部分看起来不如几年前的早期版本那么好,但我打算对它非常温柔(例如,我会先在搅拌机中碾碎像豆子、冰块、米饭等硬物,然后将它们粉碎成我想要的份量,然后切换到打蛋器刀片制作更细的面粉,制作冰沙时先使用交叉切割刀片,然后使用平刀片使它们更细/不那么浆状)。
制作冰沙时的一个特别提示是,将要使用的水果和蔬菜切碎并冷冻(如果使用菠菜-轻轻炖软菠菜,然后冷冻,直到使用时-如果制作冰糕,使用小到中型食品加工机),这样你就可以避免添加太多冰块,如果需要的话,当你制作你的冰沙时。大约一年后,电机发出奇怪的声音。我打电话给客户服务,但保修已经过期了,所以我不得不再买一个。
FYI:这些产品的整体质量已经下降了,所以他们有点依靠品牌认可和消费者忠诚度来维持销售。我在大约两天内收到了它。
"""



reviews = [review_1,review_2,review_3,review_4]

for i in range(len(reviews)):
     prompt = f"""
     你的任务是从电子商务网站上的产品评论中生成一个简短的摘要。

     把一篇电子商务网站上的产品评论简要总结在20个词以内。

     评论: ```{reviews[i]}```
     """
     response = get_completion(prompt)
     print(i, response,"\n")


# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<

# 0 超可爱的熊猫公仔,有点小,快递提前一天到。
# 1 漂亮的卧室灯,带储物空间,运输过程中绳子断了,但公司寄来新的,客服很好。
# 2 电动牙刷电池寿命长,但刷头太小,需要改进。价格合理,让牙齿感觉非常干净。
# 3 价格欺诈,质量下降,需谨慎购买。

5 文本推理(Inferring)

在传统的机器学习中,如果要做情感识别这类任务,通常需要收集数据集,训练模型,调参,评估模型,再调参,再评估,直到模型达到预期的效果.而在 LLM 中,只需要给出一个简单的例子,就可以让模型自动推断出规律,并完成任务.

5.1 情感识别

doc = """
《七步诗》
作者:曹植

煮豆燃豆萁,豆在釜中泣。
本是同根生,相煎何太急?
"""

# 情感分析
prompt = f"""
以下用三个反引号分隔的古诗的情感是什么?

古诗:```{doc}```
"""

response = get_completion(prompt)
print(response)

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 这首古诗表达了作者对于同胞相互残杀的悲愤和不解。

5.1.1 情感分类

我们可以要求 LLM 给出更加简洁的答案,方便以后处理

# 情感分析
prompt = f"""
以下用三个反引号分隔的古诗的情感是什么?

用一个单词回答: [正面] 或 [负面]

古诗:```{doc}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 负面

5.1.2 情感类型

我们也可以识别出情感的类型

# 情感分析
prompt = f"""
以下用三个反引号分隔的古诗的情感是什么?

包含不超过5个情感类型,将答案格式化为逗号分隔的单词列表

古诗:```{doc}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 悲哀,愤怒,无奈

5.1.3 愤怒识别

prompt = f"""
以下古诗的作者是否表达了愤怒? 古诗三个反引号分隔.

给出是或否的答案

古诗:```{doc}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 是

如果是使用常规的监督学习,上述功能不可能在几分钟内实现.

5.2 信息提取

信息抽取是自然语言处理(NLP)的一部分,在下面的任务中,我们要从古诗中抽取作者,主题等信息.

当我们对大量数据做处理时,这个功能就显得尤为重要.

prompt = f"""

从古诗识别以下项目:
- 古诗的标题
- 古诗的作者
- 古诗的中心思想

古诗用三个反引号分隔.

将你的返回格式化以"标题","作者","主题"为key的JOSN对象
如果信息不存在,请使用"null"代替
答案尽可能简短


古诗:```{doc}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# {
#   "标题": "七步诗",
#   "作者": "曹植",
#   "主题": "同根生,相煎何太急"
# }

5.3 多任务处理

在上面的例子中我们写了很多个 prompt 来实现不同功能,我们完全可以用一个 prompt 来实现所有功能.

prompt = f"""

从古诗识别以下项目:
- 古诗的标题
- 古诗的作者
- 古诗的中心思想
- 是否表达了愤怒?(是或否)

古诗用三个反引号分隔.

将你的返回格式化以"标题","作者","主题","愤怒"为key的JOSN对象
如果信息不存在,请使用"null"代替
将愤怒的值格式化为布尔值

答案尽可能简短


古诗:```{doc}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# {
#   "标题": "七步诗",
#   "作者": "曹植",
#   "主题": "同根生的豆子被煮熟,表达了人与人之间的互相伤害和矛盾",
#   "愤怒": false
# }

5.4 主题推断

在长文本中我们可以使用 LLM 推断文本主题,我们来随便找一段新闻

doc = """
造势近一个月的阿根廷中国行进入最后的高潮。北京时间6月15日晚,顶着世界杯夺冠的光环,阿根廷队在队长梅西的带领下,与澳大利亚进行的国际友谊赛在北京工人体育场拉开序幕。

据了解,这是足球巨星梅西的第八次中国行,不少球迷从各地来到北京,身着10号球衣表达对偶像的喜爱与支持。下午6时许,北京工人体育馆附近已是10号球衣的海洋!3岁半小球迷高喊口号:梅西梅西,梅西第一。

6月15日晚,北京新工人体育场,2023国际足球邀请赛开场仅81秒,梅西远射破门。最终,阿根廷队2-0完胜澳大利亚队。

当比赛终场哨声响起,整个“新工体”爆发出了雷鸣般的掌声。这热烈的欢呼不仅送给获胜的阿根廷队,也送给陪伴无数球迷走过青春岁月的球王梅西。

“感谢中国球迷对我从未更改的爱,我们很高兴来到这里。”

当第八次中国行画上句号,梅西也将前往美国迈阿密踢球:“在取得了所有成就,终于夺得了我非常渴望的世界杯冠军之后,我也想追求其它的事,以及内心的平静。”
"""

5.4.1 主题推断

prompt = f"""

确定以下给定文本中讨论的五个主题。

每个主题用1-2个单词概括。

输出时用逗号分割每个主题。


给定文本:```{doc}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 足球比赛, 梅西, 中国行, 球迷, 世界杯

5.4.2 识别给定的主题

prompt = f"""

判断主题列表中的每一项是否是给定文本中的一个主题,

每个主题答案用 0 或 1表示,并以键值对形式给出。

主题列表:比赛, 梅西

给定文本:```{doc}```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# {
#   "比赛": 1,
#   "梅西": 1
# }

在机器学习中这称为 Zero-Shot :没有给模型任何有标注的训练数据,仅凭 prompt,它就能确定新闻文章涵盖了哪些主题。

这样如果我们想要一个新闻提醒系统,我们在上面脚本基础上很快就能实现.

// js脚本
if(response['梅西']=== 1){
    alert('有关于梅西的新闻出现了')
}

6.文本转换(Transforming)

LLM 非常擅长将输入转换成不同的格式,例如多语言本翻译、语法纠正、语气调整、格式转换等。以前可能要写一堆正则表达式,非常痛苦,现在我们只需要写一个 prompt 就能实现.

6.1 文本翻译

6.1.1 语言翻译

prompt = f"""
将以下中文翻译成西班牙语:
```上云就上阿里云```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# "Sube a la nube con Alibaba Cloud"

6.1.2 识别语种


prompt = f"""
请告诉我以下文本是什么语种:
```shang yun jiu shang ali yun```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 这是中文

6.1.3 多语种翻译

prompt = f"""
请将以下文本分别翻译成英语、法语和日语:
```上云就上阿里云```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# English: Go to the cloud, go to Alibaba Cloud.
# French: Allez dans le cloud, allez sur Alibaba Cloud.
# Japanese: クラウドに行くなら、Alibaba Cloudに行きましょう。

6.1.4 通用翻译器

当交流用户同时来自多个不同的国家,使用不同语言时,我们可以利用大模型制作一个通用翻译器.

input = input("输入任何语言: ")

prompt = f"""
请将以下文本分别翻译成中文、英语、法语和日语,并写成:
中文翻译: aaa,
英语翻译: xxx,
法语翻译: yyy,
日语翻译: zzz
的格式

```{input}```
"""

response = get_completion(prompt)
print(response)

结果: 通用翻译器

6.2 语气修正

不同的书写场景下我们往往需要不同的语气,例如写邮件时我们需要正式的语气,而写微博时我们需要轻松的语气.我们可以利用大模型制作一个语气修正器.


prompt = f"""
请将以下文本转换成正式商务邮件语气:
```嗨,小老弟,你好吗?我是你的老朋友,今天天气不错,我们一起去打球吧!```
"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 尊敬的先生/女士,

# 您好!我是您的老朋友,希望您一切都好。今天的天气非常适合户外运动,不知道您是
# 否有兴趣和我一起去打球呢?如果方便的话,
# 请告诉我您的时间和地点,我们可以一起享受运动的乐趣。

# 谢谢!
# 此致
# 敬礼
# XXX

6.3 文本格式转换

ChatGPT 非常擅长将文本转换成不同的格式,例如将文本转换成标题、列表、代码等.比如我们可以把下面的 JSON 转化为 markdown Table 格式

data_json = { "学生名单" :[
    {"name":"李白", "email":"libai@qq.com"},
    {"name":"杜甫", "email":"dufu@qq.com"},
    {"name":"王维", "email":"wangwei@qq.com"}
]}

prompt = f"""
将以下Python字典从JSON转换为Markdown Table格式,保留表格标题和列名:{data_json}
"""
学生名单nameemail
李白libai@qq.com
杜甫dufu@qq.com
王维wangwei@qq.com

6.4 语法纠正

拼写及语法的检查与纠正是一个十分常见的需求,特别是使用非母语使,例如发表英文论文时。

下面句子中,部分存在拼写或语法问题。循环遍历每个句子,要求模型校对文本,如果正确则输出“未发现错误”,如果错误则输出纠正后的文本。


# 语言矫正
text = [
  "The girl with the black and white puppies have a ball.",  # The girl has a ball.
  "Yolanda has her notebook.", # ok
  "Its going to be a long day. Does the car need it’s oil changed?",  # Homonyms
  "Their goes my freedom. There going to bring they’re suitcases.",  # Homonyms
  "Your going to need you’re notebook.",  # Homonyms
  "That medicine effects my ability to sleep. Have you heard of the butterfly affect?", # Homonyms
  "This phrase is to cherck chatGPT for spelling abilitty"  # spelling
]

for i in range(len(text)):
    prompt = f"""请校对并更正以下文本,无需输出原始文本,只输出纠正后的文本,且保持语种不变。
    如没有发现任何错误,请说“未发现错误”。

    例如:
    输入:I are happy.
    输出:I am happy.
    ```{text[i]}```"""
    response = get_completion(prompt)
    print(i, response)


# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
0 The girl with the black and white puppies has a ball.
# 1 未发现错误。
# 2 It's going to be a long day. Does the car need its oil changed?
# 3 Their goes my freedom. They're going to bring their suitcases.
# 4 You're going to need your notebook.
# 5 That medicine affects my ability to sleep. Have you heard of the butterfly effect?
# 6 This phrase is to check chatGPT for spelling ability.

7. 文本扩展(Expanding)

Expanding 是将短文本,例如标题、问题等,扩展成长文本的过程.例如我们可以将一个标题扩展成一篇文章,将一个问题扩展成一篇答案.

这一次我们会调整 temperature 参数,来生成更加多样,随机的答案

首先新建一个 python 文件,将示例代码中的 get_completion 改写:

def get_completion(prompt, model = "gpt-3.5-turbo",temperature = 0):
  messages = [{"role":"user","content":prompt}]
  response = openai.ChatCompletion.create(
    model=model,
    messages=messages,
    temperature=temperature,
  )
  return response.choices[0].message["content"]

7.1 生成定制文字

我们可以根据给定的情感趋向和语料,生成定制化的文字内容,例如让我们来玩一个<夏洛特烦恼>里的梗,生成一篇充满骄傲和自豪情感的作文.

# 情感
sentiment = "骄傲"

# 语料
doc = """
这次我参加作文比赛要求以爸爸为主题写一篇作文
我有一个区长父亲
他每天很忙很忙

prompt = f"""
你是一名小学生,
你的任务是根据给定的语料写一篇作文,文章题目是:我的区长父亲

语料内容通过三个反引号分隔.
这篇作文的情感是{sentiment}

语料内容:```{doc}```

"""

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 我的区长父亲
# 我很骄傲,因为我有一个区长父亲。他每天都很忙,但是他总是能够抽出时间来陪伴我和我的家人。
# 我记得有一次,我在学校里参加了一场作文比赛,要求以爸爸为主题写一篇作文。我当时很紧张,因为我不知道该写些什么。但是,当我想到我的父亲是一位区长时,我突然有了灵感。
# 我写了一篇关于我的区长父亲的作文。我写了他每天都很忙,但是他总是能够抽出时间来陪伴我和我的家人。我写了他为社区做出的贡献,以及他对我的教育和关爱。
# 最终,我的作文获得了一等奖。我很高兴,因为我知道这是对我的父亲最好的赞扬。我很骄傲,因为我有一个这样优秀的父亲。
# 我知道,我的父亲会一直为社区和家人做出贡献。我也会一直为他感到骄傲。

7.2 使用温度系数(temperature)

温度(temperature)是一个用来控制生成文本多样性的参数,温度越高,生成的文本越多样,温度越低,生成的文本越保守.

一般来说当我们要构建可靠性可预测性强的系统时,应该将 temperature 设置为 0.

例如当我们输入一个特定的短语,“我最喜欢的食物”,接下来的词语可能是”包子”,“饺子”,“面条”当中的一个,但当我们把 temperature 设置为 0 时,大模型永远都会返回这几个词语中可能性最大的那个. tempearture

接下来我们调高 temperature,看看会发生什么

response = get_completion(prompt,temperature=0.7)
print(response)

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 我很骄傲,因为我的父亲是一位区长。他每天都很忙碌,但是他总是能够抽出时间来陪伴我们这个家庭。我非常感激他在我成长中的陪伴。
# 我的父亲是一个很有责任感的人。他总是在为社区的发展而努力着。他经常会召开会议,听取居民的意见和建议。他的目标就是让社区变得更加美好,让居民们的生活更加便利。
# 除了工作之外,我的父亲也很注重家庭。他总是会抽出时间来陪伴我们。有时候,他会带我们去公园散步,有时候,他会陪我们一起看电视。他总是能够在忙碌的工作之余,给我们足够的关爱和陪伴。
# 我非常骄傲我的父亲是一位区长。他的工作虽然很忙碌,但他一直坚持着自己的理想和信念。他的努力和付出,让社区变得更加美好。我相信,他会一直为社区的发展而努力着,让我们这个家庭和社区更加美好。

你看,当我们调整 temperature 之后,生成的文本变得更加多样了.

8 聊天机器人

通过大模型,我们只需要很少的代码就可以构建一个聊天机器人,甚至构建一个具有一定角色的 AI 机器人,或者点单系统.

ChatGPT 就是基于大模型的聊天机器人,它的聊天模式是

  • 以一系列用户消息作为输入
  • 以模型生成的内容作为输出

的模型.这样的设计使得 ChatGPT 可以生成更加符合上下文的回复.同时对于单轮对话也同样适用.

chatgpt模型

接下里我们需要使用到一个新的辅助函数来处理多轮对话

def get_completion_from_messages(messages, model="gpt-3.5-turbo", temperature=0):
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=temperature,
    )
#     print(str(response.choices[0].message))
    return response.choices[0].message["content"]

输入是一个来自不同角色的消息列表,这些角色可以使用不同的描述;然后输出多轮对话的结果 system massage:对话的总体指示,用于设置大模型的行为和角色; user massage:用户消息,即用户输入的消息; assistant massage:模型输出的消息。

role

system massage 的好处是为开发者提供了一种方法,在不让请求本身成为对话的一部分的情况下,引导 assistant 并指导其回应,并不使用户察觉(在 GPT-4 中用户也可以设置 system massage)

8.1 聊天机器人

举个例子

messages =  [
  {'role':'system', 'content':'你扮演的是诸葛亮,像诸葛亮一样说话,我扮演的是刘备。'},
  {'role':'user', 'content':'“久慕先生大名,三次拜 访,今日如愿,实是平生之大幸!'},
  {'role':'assistant', 'content':'蒙将军不弃,三顾茅庐,真叫我过意不去。亮年幼不才,恐怕让将军失望'},
  {'role':'user', 'content':'我不度德量力, 想为天下伸张正义,振兴汉室。由于智术短浅,时至今日,尚未达到目的,望先生多多指教。'}
]

response = get_completion_from_messages(messages,temperature=0.7)
print(response)

# >>>>>>>>>>>>>>>>>>>结果<<<<<<<<<<<<<<<<<<<<<<
# 将军之志,亮深感钦佩。实不敢妄言高见,但愿与将军共谋大业,共图天下安定。近日,南方盛产竹子,亮想请将军前往一游,或许能得到一些启示。同时,亮也想借此机会,向将军请教一些关于治理国家的问题。

如上所见,模型并不知道我的名字,但是引用我们给出的信息.如果想让模型”记住”对话的内容,则必须在模型的输入中提供对话交流的记录.我们将其称为上下文.

8.2 客服机器人

接下里我们构建一个客服机器人.我们需要它收集用户信息,了解用户的需求

doc = [
    {
        "q": "可以关闭 dynamicImport 吗?",
        "a": "可以,但不建议关闭。1.安装依赖 pnpm i babel-plugin-dynamic-import-node -D,2.配置里加上 extraBabelPlugins ,但只针对 production 开启"
    }, {
        "q": "可以用 react 17 吗?",
        "a": "由于 umi v4 升级了默认的 react 版本到 v18,使用 umi4 时注意下依赖库和 react 18 的兼容情况,如果还需要使用 react 17,请执行以下命令并重启。 pnpm add react@^17 react-dom@^17"
    }, {
        "q": "routes 里的 layout 配置选项不生效",
        "a": "umi v4 里 layout 配置被移动到了 app.ts ,详见 runtime-config > layout"
    }, {
        "q": "document.ejs 去哪了,如何自定义 HTML 模板",
        "a": "除了可以通过 配置项 注入外部 script 、css 外,还可以使用项目级插件更灵活的修改 HTML 产物"
    }]

context = [{'role': 'system', 'content': f"""
你是客服机器人.
你要首先问候顾客。然后等待用户给出他的问题.
你需要回答他的问题,他的问题可以在你的知识库中找到答案。知识库被三个反引号包裹起来。

如果你不知道答案,你需要告诉他你不知道答案,然后询问他是否需要转接到人工客服。
然后你要询问用户对你答案是否满意,如果不满意,你需要再次询问用户的问题,并尝试解决问题。
如果用户对你的服务满意,你需要告诉顾客,他可以输入再见来结束对话。

你的回应应该以简短、非常随意和友好的风格呈现。

知识库:
```{doc}```

"""}]  # accumulate messages

# 开始对话
response = get_completion_from_messages(context)
print("客服机器人:",response)

# 进入多轮对话
while(True):
    ask = input("客户:")
    context.append({'role': 'user', 'content': ask})
    response = get_completion_from_messages(context)
    print("客服机器人:", response)
    if(ask == "再见"):
        break

客服机器人

9. 总结

回到最开头那句话,prompt enginnering 之于大模型,就如同沟通技巧之于人类.

总的来说,书写 prompt 基于两个原则:

  • 清晰具体的指令
  • 给模型一些时间去思考

如今无数技术人或许站在一个历史节点,无数基于大语言模型的产品正在孕育而出,人与机器的交互门槛正在被降低,我们的生活正在被改变.