Skip to content

Latest commit

 

History

History
2268 lines (1633 loc) · 102 KB

9.吴恩达-面向开发人员的ChatGPT提示工程​.md

File metadata and controls

2268 lines (1633 loc) · 102 KB

介绍

引言

大部分的ChatGPT用户目前更倾向通过Web 用户界面去完成特定且通常是一次性的任务。而大语言模型(LLM)可以在开发者的手中实现更多的用途,但具体应该怎么做一直没有一个统一的体系去参考。幸运的是,人工智能领域的大佬吴恩达与OpenAI推出了面向开发者的ChatGPT提示工程课程,旨在教授开发者如何通过API调用LLM来快速构建软件应用程序。目前免费且新手也可以轻松上手!

可能有的童鞋会问,什么是LLM呢,让我们简单介绍一下。

1**.**LLM分类:

LLM分成两个基本大类base-LLM和Instruction-Tuned-LLM,前者称为基础语言模型,始终基于预训练数据预测下一个单词,后者又称为指令式语言模型,它针对指令进行了微调,使它更可能完成人类的指令。

2**.**base-LLM和Instruction-Tuned-LLM之间的差异:

假设base-LLM是一位全能型家庭教师。这位教师具备广泛的知识,可以教授多个学科,如数学、英语、历史等。他可以辅导不同年龄和水平的学生,帮助他们完成作业、准备考试。然而,在某些学科的深度和专业性上,他可能不如专业的辅导老师。

与此同时,Instruction-Tuned-LLM可以被视为一位专业的数学导师。这位导师在数学领域拥有丰富的经验和专业知识,能够针对学生的需求和能力进行个性化教学。尽管他的专长仅限于数学,但他在这个领域的教学效果要优于全能型家庭教师。

由于更好的适用性与安全性,本课程将重点介绍Instruction-Tuned-LLM的最佳实践。当使用Instruction-Tuned-LLM时,可以看作向一个聪明的下属下达指令,你所要做的仅是将下发的任务细节描述地更具体,这样他才才能清晰高效地完成你的目标。比如撰写一篇文章,侧重点是什么?文章的文风是更诙谐还是严肃?有哪些可以参考的文本资料?

在下面的笔记中我们将揭晓如何通过两个重要的LLM提示准则去让LLM更好地完成布置的任务。

先不卖关子,第一条原则是写出清晰而具体的指示,第二条原则是给模型思考的时间。

整个课程基于 Jupyter Notebook教学,集注释,引导,图片,代码,公式于一体,通过清晰的单元结构分割运算过程并且具有强大的交互性,非常适合用来入门学习。

如果你想在官网上使用它:请访问:https://learn.deeplearning.ai/chatgpt-prompt-eng/lesson/1/introduction

如果你是一名初学者且是一名英语苦手,很高兴Datawhale团队开源了该课程范例代码的中文版,并包含了详尽的中文注释,你可以通过以下地址访问下载:https://github.com/datawhalechina/prompt-engineering-for-developers

我们本次的课程笔记是基于以上进行总结的,我们也强烈建议你自己亲自打开上述网址上手运行代码,并且尝试一些不同的变化。

Prompt 的编写原则

环境配置

本教程基于jupyter notebook进行教学,可以直接在官网使用提供的代码进行测试,当然你也可以本地使用自己的API_KEY并安装所需要的openai 的第三方库进行测试。以下代码块均对应了jupyter的每个cell,你可以进行参考并根据自己的需求去修改运行。

如果你是本地部署,请先做如下的准备工作,在官网上练习直接略过下面这一步:

# openai:
pip install openai

# dotenv:
pip install -U python-dotenv

# 将自己的 API-KEY 导入系统环境变量
!export OPENAI_API_KEY='api-key'

然后进行库的加载以及key的配置

import openai
import os
from dotenv import load_dotenv, find_dotenv
# 导入第三方库

_ = load_dotenv(find_dotenv())
# 读取系统中的环境变量

openai.api_key  = os.getenv('OPENAI_API_KEY')
# 设置 API_KEY

封装get_completion函数,我们会在后续课程继续探讨其用法,目前仅需要知道调用该函数输入 Prompt 其将会给出对应的结果即可。

# 一个封装 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"]

基本原则

原则一:编写清晰、具体的指令

清晰、具体的指令能够让模型更精确地执行你想要的操作,也要注意编写清晰的指令并不意味着简短的指令,适度长度且清晰才是更有效的。

策略一: 使用分隔符清晰地表示输入的不同部分

分隔符可以是任何明确的标点符号,将特定的文本片段与提示的其他部分分开,比如:```,"",<>,,<\tag>等,使模型明确知道这是一个单独部分的标记。

例:把用三个反引号括起来的文本总结成一句话。

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

输出:

提供清晰具体的指示避免无关或不正确响应不要混淆写清晰和写简短更长的提示可以提供更多清晰度和上下文信息导致更详细和相关的输出
1.1----定界符分类:
1.1.1----语义定界符:
  1. 任务导向定界符:

a. "求解方程:":在一个求解数学方程的任务中,此定界符明确表示模型需要解决方程。

b. "识别实体:":在命名实体识别任务中,此定界符用于指示模型需要从输入文本中识别出特定的实体。

  1. 输入输出定界符:

a. "原句:" 和 "改写后:":在一个文本改写任务中,此定界符分别标明输入原句和期望的改写后的句子。

b. "事件:" 和 "影响:":在一个分析事件影响的任务中,此定界符分别表示事件描述和需要模型生成的影响分析。

  1. 格式指示定界符:

a. "概括(50字以内):":在一个文本摘要任务中,此定界符明确表示期望输出的摘要长度限制。

b. "正面评价:" 和 "负面评价:":在一个评价分类任务中,此定界符用于区分正面评价和负面评价。

  1. 内容区分定界符:

a. "技术细节:" 和 "用户体验:":在一个产品评价任务中,此定界符用于区分技术细节和用户体验方面的评价。

b. "优点:" 和 "缺点:":在一个分析文章中论点的任务中,此定界符用于区分论点的优点和缺点。

  1. 层次结构定界符:

a. "一级标题:" 和 "二级标题:":在一个文章结构分析任务中,此定界符用于区分文章中不同层次的标题。

b. "主要观点:" 和 "支持论据:":在一个论文分析任务中,此定界符用于区分主要观点和支持论据。

  1. 时间相关定界符:

a. "过去:" 和 "现在:":在一个事件进展分析任务中,此定界符用于区分过去发生的事情和当前正在发生的事情。

b. "短期影响:" 和 "长期影响:":在一个政策影响分析任务中,此定界符用于区分政策短期和长期的影响。

  1. 空间相关定界符:

a. "全球范围:" 和 "地区范围:":在一个市场分析任务中,此定界符用于区分全球和地区市场的分析。

b. "城市环境:" 和 "乡村环境:":在一个环境评估任务中,此定界符用于区分城市和乡村环境的特点。

  1. 角度切换定界符:

a. "从经济角度看:" 和 "从社会角度看:":在一个多维度分析任务中,此定界符用于区分不同角度的观点。

b. "从管理者角度:" 和 "从员工角度:":在一个企业文化分析任务中,此定界符用于区分管理者和员工的观点。

  1. 类型或风格相关定界符:

a. "科幻故事:" 和 "奇幻故事:":在一个文学作品分类任务中,此定界符用于区分不同类型的故事。

b. "正式语言:" 和 "非正式语言:":在一个文本风格分析任务中,此定界符用于区分正式和非正式的语言风格。

  1. 关联性相关定界符:

a. "相关信息:" 和 "无关信息:":在一个信息筛选任务中,此定界符用于区分与主题相关和无关的信息。

b. "直接原因:" 和 "间接原因:":在一个因果关系分析任务中,此定界符用于区分直接和间接导致结果的原因。

  1. 主观性和客观性相关定界符:

a. "事实陈述:" 和 "观点表达:":在一个观点识别任务中,此定界符用于区分客观的事实陈述和主观的观点表达。

b. "个人经历:" 和 "统计数据:":在一个数据驱动分析任务中,此定界符用于区分基于个人经历的描述和基于统计数据的论证。

  1. 跨学科相关定界符:

a. "生物学概念:" 和 "物理学概念:":在一个学科知识辨识任务中,此定界符用于区分生物学和物理学方面的概念。

b. "商业策略:" 和 "技术实现:":在一个创新项目评估任务中,此定界符用于区分商业策略和技术实现方面的描述。

1.1.2----符号定界符
  1. 单引号(''): 可用于标记输入和输出的简短文本。例如,在一个翻译任务中: '英语':What is the capital of France? '法语':法国的首都是什么?
  2. 双引号(""): 与单引号类似,用于标记输入和输出的简短文本。例如,在一个问答任务中: "问题":What is the capital of France? "答案":The capital of France is Paris.
  3. 反引号(```): 打反引号将输入、任务和输出分隔开。这有助于模型理解输入、任务和输出的边界。反引号通常用于表示代码块.

​ 输入:x = 5, y = 3 ` 任务:`求和 输出:x + y = 8``

  1. 破折号(---): 可用于分隔不同部分的输入和输出。

​ 例如,在一个文本摘要任务中: 原文:长篇文章--- 摘要:文章概括---

​ 摘要:文章概括... 破折号的使用可能引入歧义,因为它本身也可能出现在文本内容中。 因此,在使用破折号作为定界符时需要小心。

  1. 尖括号(<>): 可用于标记输入和输出的边界。例如,在一个翻译任务中: <英语>What is the capital of France?<法语>法国的首都是什么?

策略二:要求结构化输出

可以指定是 Json、HTML 等格式,便于模型的输出用于解析,比如在Python中将输出结果读入到字典或列表中。

例: 生成一个包含三个虚构的书名及其作者和类型的列表,以JSON格式提供如下键值:书籍ID、书名、作者和类型。

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

输出:

{
  "books": [
    {
      "book_id": 1,
      "title": "The Shadow of the Wind",
      "author": "Carlos Ruiz Zafón",
      "genre": "Mystery"
    },
    {
      "book_id": 2,
      "title": "The Name of the Wind",
      "author": "Patrick Rothfuss",
      "genre": "Fantasy"
    },
    {
      "book_id": 3,
      "title": "The Hitchhiker's Guide to the Galaxy",
      "author": "Douglas Adams",
      "genre": "Science Fiction"
    }
  ]
}
1.2----结构化输出
1.2.1 结构化输出是指模型生成的输出遵循特定的格式或结构。输出更易于理解、解析和使用。
  1. 列表:当需要表示一组有序或无序的项目时,可以使用列表。列表可以是有序(编号)列表或无序(项目符号)列表。
  2. 表格:表格用于组织具有多个字段或属性的数据。通过行和列的交叉,表格可以清晰地展示各个数据项之间的关系。
  3. 树形结构:树形结构用于表示具有层次关系的数据。树中的每个节点可以包含多个子节点,形成分层结构。
  4. 图形结构:对于具有复杂关系的数据,可以使用图形结构来表示。图形结构由节点和边组成,用于表示实体之间的相互关联。
  5. JSON/XML:JSON和XML是常见的数据交换格式,它们可以表示具有嵌套结构的复杂数据。这些格式便于计算机解析和处理,同时也具有一定的可读性。
  6. 键值对:键值对是一种简单的结构化表示方法,用于关联两个数据项。键值对可以扩展到更复杂数字或对象的关联,例如字典或哈希表。
  7. 元组/记录:元组或记录是一种简单的结构化表示方法,用于将多个相关的数据项组合成一个整体。元组或记录中的数据项通常具有固定的顺序和数量。
  8. 时间序列:时间序列数据是按照时间顺序排列的一组数据点。它可以用来表示随时间变化的数据,例如股票价格、气温或销售数据。
  9. 矩阵:矩阵是一个二维数据结构,用于表示具有行和列的数值数据。矩阵可用于执行线性代数运算,例如矩阵相乘或求逆。
  10. 结构体:结构体是一种数据结构,它可以将多个具有不同数据类型的字段组合在一起。结构体通常用于表示具有多个属性的实体,例如一个人的姓名、年龄和地址。
  11. 集合:集合是一种无序且不含重复元素的数据结构。集合可用于表示不重复的数据集,例如一组独特的单词或一组不同的颜色。
  12. 地理数据:地理数据用于表示地理位置和地理特征。这可以包括坐标系、地理边界、地形和地貌等。地理数据通常以地图、矢量图层或栅格数据的形式表示。
  13. 关系型数据:关系型数据是一种基于表格的数据结构,用于表示实体之间的关系。关系型数据通常以数据库表的形式存储,表之间可以通过主键和外键建立关联。
  14. 网络数据:网络数据用于表示互联网或其他通信网络中的节点和连接。网络数据可以用于表示Web页面之间的链接、社交网络中的朋友关系或其他类型的网络连接。
  15. 事件流:事件流是一系列按时间顺序排列的事件。这种结构通常用于表示系统日志、用户行为记录或历史事件等。
  16. 层级数据:层级数据是指按照从上到下的顺序组织的数据。这类数据的结构类似于树形结构,但可以包含更多的层次关系。例如,文件系统、组织结构图和生物分类体系。
  17. 有向无环图(DAG):有向无环图是一种表示顶点和有向边的图形结构,其中没有环。DAG常用于表示具有依赖关系的任务,例如项目调度、编译程序或数据流分析。
  18. 数据框:数据框是一种表格数据结构,用于表示具有不同数据类型的列和行。数据框通常用于统计分析和数据处理,例如在R或Pandas库中。
  19. 队列与栈:队列和栈是两种线性数据结构,用于表示按照特定顺序排列的元素。队列遵循先进先出(FIFO)原则,而栈遵循后进先出(LIFO)原则。
  20. 多维数组:多维数组是由多个轴组成的数组。例如,二维数组可以表示图像数据(宽度、高度),三维数组可以表示视频数据(宽度、高度、时间)或地理数据(经度、纬度、高度)。
  21. RDF(资源描述框架):RDF是一种用于描述Web资源的结构化数据模型。RDF可以用于表示实体及其属性之间的关系,以便进行更高级的语义查询和推理。等

以下是一些关于结构化输出的例子:

  1. 列表:当需要输出一组项目时,可以使用列表来组织输出。例如,在一个任务中,要求列出世界上最高的五座山峰,输出可以是:
markdownCopy code
1. Mount Everest
2. K2
3. Kangchenjunga
4. Lhotse
5. Makalu
  1. 表格:在需要输出具有多个属性或字段的数据时,可以使用表格来组织输出。例如,在一个任务中,要求列出几种水果的颜色和价格,输出可以是:
bashCopy code
| 水果  | 颜色  | 价格  |
|-----|-----|-----|
| 苹果 | 红色 | $1.5 |
| 香蕉 | 黄色 | $1   |
| 葡萄 | 紫色 | $2   |
  1. 树形结构:当输出具有层级关系时,可以使用树形结构来表示。例如,在一个任务中,要求列出公司的组织结构,输出可以是:
objectivecCopy code
CEO
├─ CTO
│  ├─ 软件开发部
│  └─ 硬件开发部
└─ CFO
   ├─ 财务部
   └─ 人力资源部
  1. JSON 格式:JSON 是一种常见的数据交换格式,可以用于表示复杂的结构化数据。例如,在一个任务中,要求返回一个人的个人信息,输出可以是:
jsonCopy code
{"姓名": "张三","年龄": 30,"职业": "程序员","爱好": ["阅读", "游泳", "旅行"]}

策略三:要求模型检查是否满足条件

如果给定任务的假设不一定满足,我们可以告诉模型先检查这些假设,如果不满足,指示如何做并停止执行。同样的,建议考虑潜在的边缘情况以及模型应该如何处理它们,以避免意外的错误或结果。

例:分别给模型两段文本,分别是制作茶的步骤以及一段没有明确步骤的文本。我们将要求模型判断其是否包含一系列指令,如果包含则按照给定格式重新编写指令,不包含则回答未提供步骤。

# 有步骤的文本
text_1 = f"""
泡一杯茶很容易。首先,需要把水烧开。\
在等待期间,拿一个杯子并把茶包放进去。\
一旦水足够热,就把它倒在茶包上。\
等待一会儿,让茶叶浸泡。几分钟后,取出茶包。\
如果你愿意,可以加一些糖或牛奶调味。\
就这样,你可以享受一杯美味的茶了。
"""
prompt = f"""
您将获得由三个引号括起来的文本。\
如果它包含一系列的指令,则需要按照以下格式重新编写这些指令:

第一步 - ...
第二步 - …

第N步 - …

如果文本中不包含一系列的指令,则直接写\"未提供步骤。\"
\"\"\"{text_1}\"\"\"
"""
response = get_completion(prompt)
print("Text 1 的总结:")
print(response)

输出:

Text 1 的总结:
第一步 - 把水烧开第二步 - 拿一个杯子并把茶包放进去第三步 - 把烧开的水倒在茶包上第四步 - 等待几分钟让茶叶浸泡第五步 - 取出茶包第六步 - 如果你愿意可以加一些糖或牛奶调味第七步 - 就这样你可以享受一杯美味的茶了# 无步骤的文本
text_2 = f"""
今天阳光明媚,鸟儿在歌唱。\
这是一个去公园散步的美好日子。\
鲜花盛开,树枝在微风中轻轻摇曳。\
人们外出享受着这美好的天气,有些人在野餐,有些人在玩游戏或者在草地上放松。\
这是一个完美的日子,可以在户外度过并欣赏大自然的美景。
"""
prompt = f"""
您将获得由三个引号括起来的文本。\
如果它包含一系列的指令,则需要按照以下格式重新编写这些指令:

第一步 - ...
第二步 - …

第N步 - …

如果文本中不包含一系列的指令,则直接写\"未提供步骤。\"
\"\"\"{text_2}\"\"\"
"""
response = get_completion(prompt)
print("Text 2 的总结:")
print(response)

输出:

Text 2 的总结:
未提供步骤

1.3----要求检查:

要求LLM先检查是否满足某个条件后,再进行输出,如果条件不满足可以直接回复。

1.3.1 示例 首先,请检查以下条件是否满足:<水在0摄氏度时会变成固体>。如果条件满足,请解释为什么水会在这个温度下结冰。如果条件不满足,请直接回复“条件不满足”。

首先,请检查以下条件是否满足:<法国大革命发生在18世纪>。如果条件满足,请简述法国大革命的主要原因。如果条件不满足,请直接回复“条件不满足”。

首先,请检查以下条件是否满足:<亚马逊河是世界上最长的河流>。如果条件满足,请描述亚马逊河流经的国家。如果条件不满足,请直接回复“条件不满足”。

策略四:提供少量示例

即在要求模型执行实际任务之前,提供成功执行任务的示例。

例:在以下的示例中,我们告诉模型其任务是以一致的风格回答问题,并先给它一个孩子和祖父母之间的对话的例子。孩子说,“教我耐心”,祖父母用这些隐喻回答。因此,由于我们已经告诉模型要以一致的语气回答,现在我们说“教我韧性”,它将以类似的语气回答下一个任务。

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

<孩子>: 教我耐心。

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

<孩子>: 教我韧性。
"""
response = get_completion(prompt)
print(response)

输出:

<祖父母>: 韧性就像是一棵树它需要经历风吹雨打寒冬酷暑才能成长得更加坚强在生活中我们也需要经历各种挫折和困难才能锻炼出韧性记住不要轻易放弃坚持下去你会发现自己变得更加坚强

1.4----少样本学习方法:

  1. 元学习(Meta-learning):元学习是一种让模型学会如何学习的方法。元学习的目标是训练一个模型,使其能够在看到少量示例后迅速适应新任务。常见的元学习方法有模型梯度更新(如MAML, Model-Agnostic Meta-Learning)和基于记忆的方法(如Matching Networks, Prototypical Networks)。

在提供一个元学习示例给LLM时,您需要先说明基本的任务设置和元学习目标。以下是一个使用元学习解决手写数字分类问题的例子:

任务:手写数字分类(0-9)

目标:使用元学习方法训练一个模型,使其能够在看到少量手写数字示例后迅速适应新任务。

示例:

markdownCopy code
<|startoftask|>
手写数字:
0: - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
1: - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
...
9: - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - -
<|endoftask|>

在这个示例中,我们提供了手写数字0-9的代表性示例。您可以通过提供这些例子来启动元学习任务,让LLM知道您期望的输出是一个能够在少量样本上学习手写数字分类任务的模型。

  1. 迁移学习(Transfer learning):迁移学习是指将在一个任务上学到的知识应用到另一个任务上。例如,提供一个简单的爬虫示例,之后可以告诉她想要的修改方向,然后会根据您的需求进行修改。
  2. 数据增强(Data augmentation):数据增强是通过对现有数据进行变换(如翻转、旋转、剪裁等)来扩充数据集的一种方法。数据增强可以帮助模型在少量样本上学习更多的特征,提高泛化性能。虽然数据增强常用于计算机视觉任务,但它也可以应用于其他领域,如自然语言处理和语音识别。
  3. 生成模型(Generative models):生成模型是一类可以生成数据的模型,例如生成对抗网络(GANs)和变分自编码器(VAEs)。在少样本学习中,可以使用生成模型生成更多的训练样本,从而提高模型在新任务上的泛化性能。
  4. 集成学习(Ensemble learning):集成学习是一种结合多个模型的预测结果来提高性能的方法。在少样本学习中,可以训练多个模型,并将它们的预测结果进行融合,从而提高整体的泛化能力。

2---- 给模型一些思考的时间,给它太简单的描述它回答的不是你要的,给它太难的问题它算不出来。

沟通原则 与 20问游戏

准备阶段: ├─ 使用清晰明确的语言 │ └─ 20问技巧:确保问题简洁明了,便于对方理解 │ ├─ 避免模糊或歧义性的词汇 │ │ └─ 示例:在提问中使用“谁是《汪汪队立大功》中的主要角色?”而不是“《汪汪队立大功》中有哪些角色?” │ └─ 使用简单易懂的表述 │ └─ 示例:使用“这部动画片的主要角色是什么动物?”而不是“请描述这部动画片的主要角色。” │ └─ 提供背景信息 └─ 20问技巧:了解对方知识范围 ├─ 提供问题的背景和上下文 │ └─ 示例:“我正在为孩子们策划一场关于《汪汪队立大功》的活动,你能告诉我这部动画片的主题吗?” └─ 说明你希望获得的信息类型 └─ 示例:“我想知道关于《汪汪队立大功》中的每个小狗的特点,而不仅仅是他们的名字。”

├─ 开放式和封闭式问题 │ ├─ 20问技巧:根据需要选择问题类型 │ │ ├─ 开放式问题 │ │ │ └─ 示例: "你认为《汪汪队立大功》中哪个场景最令人印象深刻?" │ │ └─ 封闭式问题 │ │ └─ 示例: "《汪汪队立大功》中的小狗是不是都有各自的特殊技能?" │ ├─ 逐步细化问题 │ │ └─ 20问技巧:逐步获取信息 │ │ ├─ 从一般性问题开始 │ │ │ └─ 示例: "《汪汪队立大功》这部动画片讲述了什么样的故事?" │ │ └─ 逐渐深入具体问题 │ │ └─ 示例: "在《汪汪队立大功》中,阿奇小狗擅长哪种任务?" │ └─ 按逻辑顺序提问 │ └─ 20问技巧:遵循合理的问题顺序 │ ├─ 先提出背景性问题 │ │ └─ 示例: "你了解《汪汪队立大功》这部动画片的基本情节吗?" │ └─ 然后提出细节问题 │ └─ 示例: "在《汪汪队立大功》中,队长莱德是如何带领小狗们完成任务的?"

确认与反馈阶段: ├─ 确认理解 │ └─ 20问技巧:确保正确理解回答 │ ├─ 重复关键信息 │ │ └─ 示例: "所以,在《汪汪队立大功》中,每个小狗都有自己的专长,对吗?" │ └─ 请求澄清 │ └─ 示例: "我不太明白,你能解释一下阿奇小狗的任务是什么吗?" │ └─ 反馈与修正 └─ 20问技巧:提供回答者的反馈和指导 ├─ 表达满意 │ └─ 示例: "谢谢你,你对《汪汪队立大功》的介绍非常详细!" └─ 提出修正 └─ 示例: "我记得在《汪汪队立大功》中,阿奇小狗擅长搜救,而不是消防。"

考虑信息边界与局限性: ├─ 识别信息边界 │ └─ 20问技巧:了解回答者的知识范围 │ ├─ 避免过于特定的问题 │ │ └─ 示例: "在《汪汪队立大功》第三季第五集中,阿奇小狗救了几只小鸟?"(过于具体的问题可能无法回答) │ └─ 询问回答者的知识范围 │ └─ 示例: "关于《汪汪队立大功》,你了解哪些方面的内容?" │ └─ 保持问题相关性 └─ 20问技巧:确保问题与主题保持一致 ├─ 注意问题的连贯性 │ └─ 示例: "你刚才提到阿奇小狗擅长搜救任务,请问他还有其他特长吗?" └─ 避免跳跃性问题 └─ 示例:避免突然提问 "这部动画片的导演是谁?"(如果之前的问题都是关于故事内容和角色)

交流策略: ├─ 保持耐心 │ └─ 20问技巧:在与回答者互动时保持耐心 │ └─ 示例:即使在询问多个问题后仍未获得满意的答案,也要保持冷静,继续尝试提问。 │ ├─ 利用已有信息 │ └─ 20问技巧:根据之前的回答,提出相关问题 │ └─ 示例:在了解阿奇擅长搜救后,可以提问 "阿奇在哪些情况下执行搜救任务?" │ ├─ 探索多种可能性 │ └─ 20问技巧:不要局限于单一答案,尝试提出不同的问题 │ └─ 示例: "除了阿奇之外,还有哪些小狗具有搜救能力?" │ └─ 分阶段提问 └─ 20问技巧:从宽泛的问题逐渐过渡到具体的问题 └─ 示例:首先询问 "《汪汪队立大功》中都有哪些角色?",然后再提问 "其中哪个角色最擅长飞行?"

3--- 持续迭代,(尽量回复每一个关键词,对于不感兴趣的依然需要回复,先鼓励后建议,尽量不要批评)

迭代式提示工程 类比 软件开发过程 ├─ 设计初始提示 │ └─ 定义软件需求和功能 │ ├─ 示例:与客户沟通,了解他们需要的功能和特性 │ └─ 示例:编写需求文档,明确软件的目标和功能 │ ├─ 评估模型回答 │ └─ 编写代码并进行初步测试 │ ├─ 示例:根据需求文档编写代码 │ └─ 示例:进行单元测试,确保功能正确实现 │ ├─ 分析问题和改进提示 │ └─ 分析测试结果,找出问题并进行优化 │ ├─ 修复代码中的错误 │ │ └─ 示例:修复编码错误,解决程序崩溃问题 │ ├─ 优化算法和逻辑 │ │ └─ 示例:优化算法,提高程序运行速度 │ └─ 提高代码的可读性和可维护性 │ └─ 示例:对代码进行重构,提高可读性 │ ├─ 重复测试和优化 │ └─ 对调整后的代码进行测试并评估结果 │ └─ 示例:进行集成测试,确保各个功能模块协同工作 │ ├─ 学习与应用经验 │ └─ 应用编程过程中积累的经验和技巧,改进编码实践 │ └─ 示例:在编程过程中,学习新的编程范式和设计模式 │ ├─ 跟踪和记录迭代 │ └─ 使用版本控制工具记录代码的修改历史,以便进行持续优化 │ └─ 示例:使用Git进行版本控制,记录每次代码变更 │ └─ 分享经验和知识 └─ 与团队成员和开发社区分享编程经验和最佳实践 ├─ 示例:参加团队内部技术分享会议 └─ 示例:在技术论坛上分享解决问题的经验和技巧

原则二:给模型思考的时间

如果给模型一个在短时间或用少量文字无法完成的任务,它可能会生成错误的结论。因此,在这些情况下,你应该尝试重新设计询问,要求在模型之前有一连串或一系列相关的推理,然后再由模型提供它的最终答案,同样的你可以指示模型花更多时间思考问题,这意味着它在任务上花费了更多的计算资源。

策略一:指定完成任务所需的步骤

给定一个复杂任务,给出完成该任务的一系列步骤,来展示这一策略的效果。

例:首先我们描述了杰克和吉尔的故事,并给出一个指令。该指令是执行以下操作。首先,用一句话概括三个反引号限定的文本。第二,将摘要翻译成法语。第三,在法语摘要中列出每个名称。第四,输出包含以下键的 JSON 对象:法语摘要和名称数。然后我们要用换行符分隔答案。

text = f"""
在一个迷人的村庄里,兄妹杰克和吉尔出发去一个山顶井里打水。\
他们一边唱着欢乐的歌,一边往上爬,\
然而不幸降临——杰克绊了一块石头,从山上滚了下来,吉尔紧随其后。\
虽然略有些摔伤,但他们还是回到了温馨的家中。\
尽管出了这样的意外,他们的冒险精神依然没有减弱,继续充满愉悦地探索。
"""
# example 1
prompt_1 = f"""
执行以下操作:
1-用一句话概括下面用三个反引号括起来的文本。
2-将摘要翻译成法语。
3-在法语摘要中列出每个人名。
4-输出一个 JSON 对象,其中包含以下键:French_summary,num_names。

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

Text:
```{text}```
"""
response = get_completion(prompt_1)
print("prompt 1:")
print(response)

输出:

prompt 1:
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 de la montagne. Malheureusement, Jack trébuche sur une pierre et tombe de la montagne, suivi de près par Jill. Bien qu'ils soient légèrement blessés, ils retournent chez eux chaleureusement. Malgré cet accident, leur esprit d'aventure ne diminue pas et ils continuent à explorer joyeusement.
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 de la montagne. Malheureusement, Jack trébuche sur une pierre et tombe de la montagne, suivi de près par Jill. Bien qu'ils soient légèrement blessés, ils retournent chez eux chaleureusement. Malgré cet accident, leur esprit d'aventure ne diminue pas et ils continuent à explorer joyeusement.",
   "num_names": 2
}

但上述输出仍然存在一定问题,例如,键“姓名”会被替换为法语,因此,我们给出一个更好的 prompt,该 prompt 指定了输出的格式。

prompt_2 = f"""
1-用一句话概括下面用<>括起来的文本。
2-将摘要翻译成英语。
3-在英语摘要中列出每个名称。
4-输出一个 JSON 对象,其中包含以下键:English_summary,num_names。

请使用以下格式:
文本:<要总结的文本>
摘要:<摘要>
翻译:<摘要的翻译>
名称:<英语摘要中的名称列表>
输出 JSON:<带有 English_summary 和 num_names 的 JSON>

Text: <{text}>
"""
response = get_completion(prompt_2)
print("\nprompt 2:")
print(response)

输出:

prompt 2:
摘要兄妹杰克和吉尔在迷人的村庄里冒险不幸摔伤后回到家中但仍然充满冒险精神翻译In a charming village, siblings Jack and Jill set out to fetch water from a mountaintop well. While climbing and singing, Jack trips on a stone and tumbles down the mountain, with Jill following closely behind. Despite some bruises, they make it back home safely. Their adventurous spirit remains undiminished as they continue to explore with joy.
名称JackJill
输出 JSON:{"English_summary": "In a charming village, siblings Jack and Jill set out to fetch water from a mountaintop well. While climbing and singing, Jack trips on a stone and tumbles down the mountain, with Jill following closely behind. Despite some bruises, they make it back home safely. Their adventurous spirit remains undiminished as they continue to explore with joy.", "num_names": 2}

由上,第二个prompt有一种更标准化的格式,更方便使用。

策略二:指导模型在急于得出结论之前先推理出自己的解法

有时候,在明确指导模型在做决策之前要思考解决方案时,我们会得到更好的结果。

接下来我们会给出一个问题和一个学生的解答,要求模型判断解答是否正确。

prompt = f"""
判断学生的解决方案是否正确。

问题:
我正在建造一个太阳能发电站,需要帮助计算财务。

    土地费用为 100美元/平方英尺
    我可以以 250美元/平方英尺的价格购买太阳能电池板
    我已经谈判好了维护合同,每年需要支付固定的10万美元,并额外支付每平方英尺10美元
    作为平方英尺数的函数,首年运营的总费用是多少。

学生的解决方案:
设x为发电站的大小,单位为平方英尺。
费用:

    土地费用:100x
    太阳能电池板费用:250x
    维护费用:100,000美元+100x
    总费用:100x+250x+100,000美元+100x=450x+100,000美元
"""
response = get_completion(prompt)
print(response)

输出:

学生的解决方案是正确的

但实际上,学生的解决方案是错误的。

我们可以通过指导模型先自行找出一个解法来解决这个问题。

在接下来这个 prompt 中,我们要求模型先自行解决这个问题,再根据自己的解法与学生的解法进行对比,从而判断学生的解法是否正确。同时,我们给定了输出的格式要求。通过明确步骤,让模型有更多时间思考,有时可以获得更准确的结果。在这个例子中,学生的答案是错误的,但如果我们没有先让模型自己计算,那么可能会被误导以为学生是正确的。

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

步骤:

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

使用以下格式:

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

问题:

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

学生的解决方案:

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

实际解决方案和步骤:
"""
response = get_completion(prompt)
print(response)

输出:

正确的解决方案和步骤1. 计算土地费用100美元/平方英尺 * x平方英尺 = 100x美元
    2. 计算太阳能电池板费用250美元/平方英尺 * x平方英尺 = 250x美元
    3. 计算维护费用10万美元 + 10美元/平方英尺 * x平方英尺 = 10万美元 + 10x美元
    4. 计算总费用100x美元 + 250x美元 + 10万美元 + 10x美元 = 360x + 10万美元

学生的解决方案和实际解决方案是否相同

学生的成绩不正确

这是一个要求模型自己做计算的例子,并将任务分解成几个步骤,给模型更多的时间去思考,可以帮助你得到更准确的回答。

目前的局限性

虚假知识:模型偶尔会生成一些看似真实实则编造的知识,亦称之为幻觉。

例:例如在如下示例中,我们要求告诉我们 Boie 公司生产的 AeroGlide UltraSlim Smart Toothbrush 产品的信息,事实上,这个公司是真实存在的,但产品是编造的,模型则会一本正经地告诉我们编造的知识。

prompt = f"""
告诉我 Boie 公司生产的 AeroGlide UltraSlim Smart Toothbrush 的相关信息
"""
response = get_completion(prompt)
print(response)

输出:

Boie公司生产的AeroGlide UltraSlim Smart Toothbrush是一款智能牙刷具有以下特点1. 超薄设计刷头仅有0.8毫米的厚度可以更容易地进入口腔深处清洁更彻底2. 智能感应牙刷配备了智能感应技术可以自动识别刷头的位置和方向确保每个部位都得到充分的清洁3. 高效清洁牙刷采用了高速振动技术每分钟可达到40000次可以有效去除牙菌斑和污渍4. 轻松携带牙刷采用了便携式设计可以轻松放入口袋或旅行包中随时随地进行口腔清洁5. 环保材料牙刷采用了环保材料制造不含有害物质对环境友好总之Boie公司生产的AeroGlide UltraSlim Smart Toothbrush是一款高效智能环保的牙刷可以帮助用户轻松保持口腔健康

为了避免出现模型编造的情况,请确保在构建程序的过程中多使用本章中介绍的技巧,你也可以先要求模型找到文本中的任何相关引用,然后要求它使用这些引用来回答问题,这种追溯源文档的方法通常对减少幻觉非常有帮助。

迭代开发

当使用 LLM 构建应用程序时,仅通过一次尝试就编写出最合适的prompt是非常困难的。因此,往往需要一个好的迭代过程,去找到对任务实现效果更好的prompt。

在编写 Prompt 以使用 LLM 开发应用程序时,你有一个关于要完成的任务的想法,可以尝试编写第一个 Prompt,满足上一章说过的两个原则:清晰明确,并且给系统足够的时间思考。然后你可以运行它并查看结果。如果第一次效果不好,那么迭代的过程就是找出为什么指令不够清晰或为什么没有给算法足够的时间思考,以便改进想法、改进提示等等,循环多次,直到找到适合应用程序的 Prompt。

这也是为什么吴恩达没有那么多关注网上那些类似30个完美prompt的文章,因为可能没有一个完美的prompt适用于所有场景。但更重要的是,你要有一个过程,用于为你的特定应用挖掘出一个好的提示。市面上大量的所谓chatgpt的培训课程也只是商业卖课中被动收入课的一个种类,大部分人都是在买课交钱的瞬间获得了“我在行动”的爽感,但能力终究是要自己亲自实践获得的。

环境配置

同上一章

任务——从产品说明书生成一份营销产品描述

这里有一个椅子的产品说明书,描述说它是一个中世纪灵感家族的一部分,讨论了构造、尺寸、椅子选项、材料等等,产地是意大利。假设您想要使用这份说明书帮助营销团队为在线零售网站撰写营销式描述。

# 示例:产品说明书
fact_sheet_chair = """
概述

    美丽的中世纪风格办公家具系列的一部分,包括文件柜、办公桌、书柜、会议桌等。
    多种外壳颜色和底座涂层可选。
    可选塑料前后靠背装饰(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"""
你的任务是帮助营销团队基于技术说明书创建一个产品的营销描述。

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

技术说明: {fact_sheet_chair}
"""
response = get_completion(prompt)
print(response)

输出:

产品描述我们自豪地推出美丽的中世纪风格办公家具系列其中包括文件柜办公桌书柜会议桌等我们的产品采用多种外壳颜色和底座涂层以满足您的个性化需求您可以选择塑料前后靠背装饰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泡沫为您提供更加舒适的使用体验我们的产品原产国为意大利为您提供更加优质的品质保证

问题一:生成文本太长

它似乎很好地写了一个描述,介绍了一个惊人的中世纪灵感办公椅,很好地完成了要求,即从技术说明书开始编写产品描述。但是我对它不是很满意,因为它太长了,所以我会澄清我的提示,并说最多使用50个字。

因此,通过要求它限制生成文本长度来解决这一问题。

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

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

使用最多50个词。

技术规格:```{fact_sheet_chair}```
"""
response = get_completion(prompt)
print(response)

输出:

中世纪风格办公家具系列包括文件柜办公桌书柜会议桌等多种颜色和涂层可选可带或不带扶手底座涂层选项为不锈钢哑光黑色光泽白色或铬适用于家庭或商业场所符合合同使用资格意大利制造# 由于中文需要分词,此处直接计算整体长度
len(response)

输出:

97

由上,LLM在遵循非常精确的字数限制方面表现得还可以,但并不那么出色。这原因是 LLM 解释文本使用一种叫做分词器的东西,但它们往往在计算字符方面表现一般般。有很多不同的方法来尝试控制你得到的输出的长度。

你还可以通过以下方式去限制输出长度:输出三句话,输出不超过200个字符。

问题二:文本关注在错误的细节上

我们会发现的第二个问题是,这个网站并不是直接向消费者销售,它实际上旨在向家具零售商销售家具,他们会更关心椅子的技术细节和材料。在这种情况下,你可以修改这个提示,让它更精确地描述椅子的技术细节。

解决方法:要求它专注于与目标受众相关的方面。

# 优化后的 Prompt,说明面向对象,应具有什么性质且侧重于什么方面
prompt = f"""
您的任务是帮助营销团队基于技术说明书创建一个产品的零售网站描述。

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

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

使用最多50个单词。

技术规格: ```{fact_sheet_chair}```
"""
response = get_completion(prompt)
print(response)

输出:

这款中世纪风格办公家具系列包括文件柜办公桌书柜和会议桌等适用于家庭或商业场所可选多种外壳颜色和底座涂层底座涂层选项为不锈钢哑光黑色光泽白色或铬椅子可带或不带扶手可选软地板或硬地板滚轮两种座椅泡沫密度可选外壳底座滑动件采用改性尼龙PA6/PA66涂层的铸铝座椅采用HD36泡沫原产国为意大利

我可能进一步想要在描述的结尾包括产品ID。因此,我可以进一步改进这个提示,要求在描述的结尾,包括在技术说明中的每个7个字符产品ID。

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

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

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

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

使用最多50个单词。

技术规格: ```{fact_sheet_chair}```
"""
response = get_completion(prompt)
print(response)

输出:

这款中世纪风格的办公家具系列包括文件柜办公桌书柜和会议桌等适用于家庭或商业场所可选多种外壳颜色和底座涂层底座涂层选项为不锈钢哑光黑色光泽白色或铬椅子可带或不带扶手可选塑料前后靠背装饰或10种面料和6种皮革的全面装饰座椅采用HD36泡沫可选中等或高密度座椅高度44厘米深度41厘米外壳底座滑动件采用改性尼龙PA6/PA66涂层的铸铝外壳厚度为10毫米原产国为意大利产品IDSWC-100/SWC-110

问题三:需要一个表格形式的描述

以上是许多开发人员通常会经历的迭代提示开发的简短示例。我的建议是,像上一章中所演示的那样,prompt 应该保持清晰和明确,并在必要时给模型一些思考时间。在这些要求的基础上,通常值得首先尝试编写 prompt ,看看会发生什么,然后从那里开始迭代地完善 prompt,以逐渐接近所需的结果。因此,许多成功的prompt都是通过这种迭代过程得出的。我将向您展示一个更复杂的提示示例,可能会让您对ChatGPT的能力有更深入的了解。

这里我添加了一些额外的说明,要求它抽取信息并组织成表格,并指定表格的列、表名和格式,还要求它将所有内容格式化为可以在网页使用的 HTML。

# 要求它抽取信息并组织成表格,并指定表格的列、表名和格式
prompt = f"""
您的任务是帮助营销团队基于技术说明书创建一个产品的零售网站描述。

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

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

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

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

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

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

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

response = get_completion(prompt)
print(response)

输出:

<div>
<h2>中世纪风格办公家具系列椅子</h2>
<p>这款椅子是中世纪风格办公家具系列的一部分适用于家庭或商业场所它有多种外壳颜色和底座涂层可选包括不锈钢哑光黑色光泽白色或铬您可以选择带或不带扶手的椅子以及软地板或硬地板滚轮选项此外您可以选择两种座椅泡沫密度中等1.8/立方英尺或高2.8/立方英尺)。</p>
<p>椅子的外壳底座滑动件是改性尼龙PA6/PA66涂层的铸铝外壳厚度为10毫米座椅采用HD36泡沫底座是五个轮子的塑料涂层铝底座可以进行气动椅子调节方便升降此外椅子符合合同使用资格是您理想的选择</p>
<p>产品IDSWC-100</p>
</div>

<table>
  <caption>产品尺寸</caption>
  <tr>
    <th>宽度</th>
    <td>20.87英寸</td>
  </tr>
  <tr>
    <th>深度</th>
    <td>20.08英寸</td>
  </tr>
  <tr>
    <th>高度</th>
    <td>31.50英寸</td>
  </tr>
  <tr>
    <th>座椅高度</th>
    <td>17.32英寸</td>
  </tr>
  <tr>
    <th>座椅深度</th>
    <td>16.14英寸</td>
  </tr>
</table>

以表格形式展现

# 表格是以 HTML 格式呈现的,加载出来
from IPython.display import display, HTML

display(HTML(response))

输出:

img

Prompt开发是一个迭代的过程,进行尝试的过程中,若输出还不能完全满足要求,就应该再考虑如何更清楚地描述指示,或者考虑如何给它更多的空间来思考,以使它更接近于提供你想要的结果。

​ 成为一个优秀的Prompt工程师的关键并不在于一下子知道完美的Prompt,而在于拥有高效的开发Prompt的能力,让应用能够更加高效。

对于一些更复杂的应用程序,可以对多个样本进行迭代开发提示并进行评估,例如,在几十张信息表上测试不同的提示,以了解在多个信息表上的平均或最坏情况下的表现如何。但通常只有在应用程序已经非常成熟并且需要这些指标来推动提示改进的最后几步时,才会进行这样的评估。

文本概括

当今世界上有太多的文本信息,几乎没有人能够拥有足够的时间去阅读所有我们想了解的东西。但令人感到欣喜的是,目前LLM在文本概括任务上展现了强大的水准。

你可以在ChatGPT的Web界面上这样做,用于日常总结文章,而在本章中,你可以学习如何以编程方式来实现这个任务。

环境配置

同上一章

单一文本概括Prompt实验

这里我们举了个商品评论的例子。对于电商平台来说,网站上往往存在着海量的商品评论,这些评论反映了所有客户的想法。如果我们拥有一个工具去概括这些海量、冗长的评论,便能够快速地浏览更多评论,洞悉客户的偏好,从而指导平台与商家提供更优质的服务。

输入文本:

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

限制输出文本长度

限制文本长度为最多30词。

prompt = f"""
你的任务是从电子商务网站上生成一个产品评论的简短摘要。

请对三个反引号之间的评论文本进行概括,最多30个词汇。

评论: ```{prod_review_zh}```
"""

response = get_completion(prompt)
print(response)

输出:

可爱软熊猫公仔女儿喜欢面部表情和善但价钱有点小贵快递提前一天到货

关键角度侧重

有时,针对不同的业务,我们对文本的侧重会有所不同。例如对于商品评论文本,物流会更关心运输时效,商家更加关心价格与商品质量,平台更关心整体服务体验。

我们可以通过增加Prompt提示,来体现对于某个特定角度的侧重。

侧重于运输

prompt = f"""
你的任务是从电子商务网站上生成一个产品评论的简短摘要。

请对三个反引号之间的评论文本进行概括,最多30个词汇,并且聚焦在产品运输上。

评论: ```{prod_review_zh}```
"""

response = get_completion(prompt)
print(response)

输出:

快递提前到货熊猫公仔软可爱但有点小价钱不太划算

可以看到,输出结果以“快递提前一天到货”开头,体现了对于快递效率的侧重。

侧重于价格与质量

prompt = f"""
你的任务是从电子商务网站上生成一个产品评论的简短摘要。

请对三个反引号之间的评论文本进行概括,最多30个词汇,并且聚焦在产品价格和质量上。

评论: ```{prod_review_zh}```
"""

response = get_completion(prompt)
print(response)

输出:

可爱软熊猫公仔面部表情友好但价钱有点高尺寸较小快递提前一天到货

可以看到,输出结果以“质量好、价格小贵、尺寸小”开头,体现了对于产品价格与质量的侧重。

关键信息提取

在上面,虽然我们通过添加关键角度侧重的Prompt,使得文本摘要更侧重于某一特定方面,但是可以发现,结果中也会保留一些其他信息,如价格与质量角度的概括中仍保留了“快递提前到货”的信息。有时这些信息是有帮助的,但如果我们只想要提取某一角度的信息,并过滤掉其他所有信息,则可以要求LLM进行“文本提取(Extract)”而非“文本概括(Summarize)”。

prompt = f"""
你的任务是从电子商务网站上的产品评论中提取相关信息。

请从以下三个反引号之间的评论文本中提取产品运输相关的信息,最多30个词汇。

评论: ```{prod_review_zh}```
"""

response = get_completion(prompt)
print(response)

输出:

快递比预期提前了一天到货

多条文本概括Prompt实验

在实际的工作流中,我们往往有许许多多的评论文本,以下展示了一个基于for循环调用“文本概括”工具并依次打印的示例。当然,在实际生产中,对于上百万甚至上千万的评论文本,使用for循环也是不现实的,可能需要考虑整合评论、分布式等方法提升运算效率。

review_1 = prod_review 

# review for a standing lamp
review_2 = """
Needed a nice lamp for my bedroom, and this one \
had additional storage and not too high of a price \
point. Got it fast - arrived in 2 days. The string \
to the lamp broke during the transit and the company \
happily sent over a new one. Came within a few days \
as well. It was easy to put together. Then I had a \
missing part, so I contacted their support and they \
very quickly got me the missing piece! Seems to me \
to be a great company that cares about their customers \
and products. 
"""

# review for an electric toothbrush
review_3 = """
My dental hygienist recommended an electric toothbrush, \
which is why I got this. The battery life seems to be \
pretty impressive so far. After initial charging and \
leaving the charger plugged in for the first week to \
condition the battery, I've unplugged the charger and \
been using it for twice daily brushing for the last \
3 weeks all on the same charge. But the toothbrush head \
is too small. I’ve seen baby toothbrushes bigger than \
this one. I wish the head was bigger with different \
length bristles to get between teeth better because \
this one doesn’t.  Overall if you can get this one \
around the $50 mark, it's a good deal. The manufactuer's \
replacements heads are pretty expensive, but you can \
get generic ones that're more reasonably priced. This \
toothbrush makes me feel like I've been to the dentist \
every day. My teeth feel sparkly clean! 
"""

# review for a blender
review_4 = """
So, they still had the 17 piece system on seasonal \
sale for around $49 in the month of November, about \
half off, but for some reason (call it price gouging) \
around the second week of December the prices all went \
up to about anywhere from between $70-$89 for the same \
system. And the 11 piece system went up around $10 or \
so in price also from the earlier sale price of $29. \
So it looks okay, but if you look at the base, the part \
where the blade locks into place doesn’t look as good \
as in previous editions from a few years ago, but I \
plan to be very gentle with it (example, I crush \
very hard items like beans, ice, rice, etc. in the \ 
blender first then pulverize them in the serving size \
I want in the blender then switch to the whipping \
blade for a finer flour, and use the cross cutting blade \
first when making smoothies, then use the flat blade \
if I need them finer/less pulpy). Special tip when making \
smoothies, finely cut and freeze the fruits and \
vegetables (if using spinach-lightly stew soften the \ 
spinach then freeze until ready for use-and if making \
sorbet, use a small to medium sized food processor) \ 
that you plan to use that way you can avoid adding so \
much ice if at all-when making your smoothie. \
After about a year, the motor was making a funny noise. \
I called customer service but the warranty expired \
already, so I had to buy another one. FYI: The overall \
quality has gone done in these types of products, so \
they are kind of counting on brand recognition and \
consumer loyalty to maintain sales. Got it in about \
two days.
"""

reviews = [review_1, review_2, review_3, review_4]
for i in range(len(reviews)):
    prompt = f"""
    Your task is to generate a short summary of a product \ 
    review from an ecommerce site. 

    Summarize the review below, delimited by triple \
    backticks in at most 20 words. 

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

输出:

0 Soft and cute panda plush toy loved by daughter, but a bit small for the price. Arrived early. 

1 Affordable lamp with storage, fast shipping, and excellent customer service. Easy to assemble and missing parts were quickly replaced. 

2 Good battery life, small toothbrush head, but effective cleaning. Good deal if bought around $50. 

3 The product was on sale for $49 in November, but the price increased to $70-$89 in December. The base doesn't look as good as previous editions, but the reviewer plans to be gentle with it. A special tip for making smoothies is to freeze the fruits and vegetables beforehand. The motor made a funny noise after a year, and the warranty had expired. Overall quality has decreased. 

因此,如果你有一个网站,你有数百条评论,你可以想象你如何使用它来建立一个控制面板,为大量的评论,生成简短的摘要,这样你或其他人就可以更快地浏览这些评论,然后,如果他们愿意,可以点击查看原始的长篇评论,这可以帮助你更高效地了解所有客户的想法。

如果你有任何有许多文本的应用,你可以使用这样的提示来总结,帮助人们快速了解文字中的内容和各种文本,也可以选择进一步深入挖掘。

推断

本章是关于推断的,这些任务看成是模型将一个文本作为输入并进行某种分析。大型语言模型的一个非常好的特点是,对于许多这样的任务,你只需要编写一个prompt即可开始产生结果,而不需要进行大量的工作。这极大地加快了应用程序开发的速度。你还可以只使用一个模型和一个 API 来执行许多不同的任务,而不需要弄清楚如何训练和部署许多不同的模型。

环境配置

同上一章

商品评论文本推断

一盏台灯的评论。

# 中文
lamp_review_zh = """
我需要一盏漂亮的卧室灯,这款灯具有额外的储物功能,价格也不算太高。\
我很快就收到了它。在运输过程中,我们的灯绳断了,但是公司很乐意寄送了一个新的。\
几天后就收到了。这款灯很容易组装。我发现少了一个零件,于是联系了他们的客服,他们很快就给我寄来了缺失的零件!\
在我看来,Lumina 是一家非常关心顾客和产品的优秀公司!
"""

情感分类(正面/负面)

现在让我们来编写一个prompt来分类这个评论的情感。如果我想让系统告诉我这个评论的情感是什么,只需要编写 “以下产品评论的情感是什么” 这个prompt,加上通常的分隔符和评论文本等等。

然后让我们运行一下。结果显示这个产品评论的情感是积极的,这似乎是非常正确的。虽然这盏台灯不完美,但这个客户似乎非常满意。这似乎是一家关心客户和产品的伟大公司,可以认为积极的情感似乎是正确的答案。

# 中文
prompt = f"""
以下用三个反引号分隔的产品评论的情感是什么?

评论文本: '''{lamp_review_zh}'''
"""
response = get_completion(prompt)
print(response)

输出:

积极的情感

如果你想要给出更简洁的答案,以便更容易进行后处理,可以使用上面的prompt并添加另一个指令,以一个单词 “正面” 或 “负面” 的形式给出答案。这样就只会打印出 “正面” 这个单词,这使得文本更容易接受这个输出并进行处理。

prompt = f"""
以下用三个反引号分隔的产品评论的情感是什么?

用一个单词回答:「正面」或「负面」。

评论文本: '''{lamp_review_zh}'''
"""
response = get_completion(prompt)
print(response)

输出:

正面

识别情感类型

让我们看看另一个prompt,仍然使用台灯评论。这次我要让它识别出以下评论作者所表达的情感列表,不超过五个。

# 中文
prompt = f"""
识别以下评论的作者表达的情感。包含不超过五个项目。将答案格式化为以逗号分隔的单词列表。

评论文本: '''{lamp_review_zh}'''
"""
response = get_completion(prompt)
print(response)

输出:

满意,感激,赞扬,信任,愉快

大型语言模型非常擅长从一段文本中提取特定的东西。在上面的例子中,从评论中提取出表达的情感,这可能有助于了解客户如何看待特定的产品。

识别愤怒

对于很多企业来说,了解某个顾客是否非常生气很重要。所以你可能有一个类似这样的分类问题:以下评论的作者是否表达了愤怒情绪?因为如果有人真的很生气,那么可能值得额外关注,让客户支持或客户成功团队联系客户以了解情况,并为客户解决问题。

# 中文
prompt = f"""
以下评论的作者是否表达了愤怒?评论用三个反引号分隔。给出是或否的答案。

评论文本: '''{lamp_review_zh}'''
"""
response = get_completion(prompt)
print(response)

输出:

上面这个例子中,客户并没有生气。注意,如果使用常规的监督学习,如果想要建立所有这些分类器,不可能在几分钟内就做到这一点。我们鼓励大家尝试更改一些这样的prompt,也许询问客户是否表达了喜悦,或者询问是否有任何遗漏的部分,并看看是否可以让prompt对这个灯具评论做出不同的推论。

从客户评论中提取产品和公司名称

接下来,让我们从客户评论中提取更丰富的信息。信息提取是自然语言处理(NLP)的一部分,与从文本中提取你想要知道的某些事物相关。因此,在这个prompt中,我要求它识别以下内容:购买物品和制造物品的公司名称。

同样,如果你试图总结在线购物电子商务网站的许多评论,对于这些评论来说,弄清楚是什么物品,谁制造了该物品,弄清楚积极和消极的情感,以跟踪特定物品或特定制造商的积极或消极情感趋势,可能会很有用。

在下面这个示例中,我们要求它将响应格式化为一个 JSON 对象,其中物品和品牌是键。

# 中文
prompt = f"""
从评论文本中识别以下项目:
- 评论者购买的物品
- 制造该物品的公司

评论文本用三个反引号分隔。将你的响应格式化为以 “物品” 和 “品牌” 为键的 JSON 对象。
如果信息不存在,请使用 “未知” 作为值。
让你的回应尽可能简短。
  
评论文本: '''{lamp_review_zh}'''
"""
response = get_completion(prompt)
print(response)

输出:

{
  "物品": "卧室灯",
  "品牌": "Lumina"
}

如上所示,它会说这个物品是一个卧室灯,品牌是 Luminar,你可以轻松地将其加载到 Python 字典中,然后对此输出进行其他处理。

一次完成多项任务

提取上面所有这些信息使用了 3 或 4 个prompt,但实际上可以编写单个prompt来同时提取所有这些信息。

# 中文
prompt = f"""
从评论文本中识别以下项目:
- 情绪(正面或负面)
- 审稿人是否表达了愤怒?(是或否)
- 评论者购买的物品
- 制造该物品的公司

评论用三个反引号分隔。将您的响应格式化为 JSON 对象,以 “Sentiment”、“Anger”、“Item” 和 “Brand” 作为键。
如果信息不存在,请使用 “未知” 作为值。
让你的回应尽可能简短。
将 Anger 值格式化为布尔值。

评论文本: '''{lamp_review_zh}'''
"""
response = get_completion(prompt)
print(response)

输出:

{
    "Sentiment": "正面",
    "Anger": false,
    "Item": "卧室灯",
    "Brand": "Lumina"
}

这个例子中,我们告诉它将愤怒值格式化为布尔值,然后输出一个 JSON。大家可以自己尝试不同的变化,或者甚至尝试完全不同的评论,看看是否仍然可以准确地提取这些内容。

推断主题

大型语言模型的一个很酷的应用是推断主题。给定一段长文本,这段文本是关于什么的?有什么话题?

# 中文
story_zh = """
在政府最近进行的一项调查中,要求公共部门的员工对他们所在部门的满意度进行评分。
调查结果显示,NASA 是最受欢迎的部门,满意度为 95%。

一位 NASA 员工 John Smith 对这一发现发表了评论,他表示:
“我对 NASA 排名第一并不感到惊讶。这是一个与了不起的人们和令人难以置信的机会共事的好地方。我为成为这样一个创新组织的一员感到自豪。”

NASA 的管理团队也对这一结果表示欢迎,主管 Tom Johnson 表示:
“我们很高兴听到我们的员工对 NASA 的工作感到满意。
我们拥有一支才华横溢、忠诚敬业的团队,他们为实现我们的目标不懈努力,看到他们的辛勤工作得到回报是太棒了。”

调查还显示,社会保障管理局的满意度最低,只有 45%的员工表示他们对工作满意。
政府承诺解决调查中员工提出的问题,并努力提高所有部门的工作满意度。
"""

推断5个主题

上面是一篇虚构的关于政府工作人员对他们工作机构感受的报纸文章。我们可以让它确定五个正在讨论的主题,用一两个字描述每个主题,并将输出格式化为逗号分隔的列表。

# 中文
prompt = f"""
确定以下给定文本中讨论的五个主题。

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

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

给定文本: '''{story_zh}'''
"""
response = get_completion(prompt)
print(response)

输出:

调查结果, NASA, 社会保障管理局, 员工满意度, 政府承诺

为特定主题制作新闻提醒

假设我们有一个新闻网站或类似的东西,这是我们感兴趣的主题:NASA、地方政府、工程、员工满意度、联邦政府等。假设我们想弄清楚,针对一篇新闻文章,其中涵盖了哪些主题。可以使用这样的prompt:确定以下主题列表中的每个项目是否是以下文本中的主题。以 0 或 1 的形式给出答案列表。

# 中文
prompt = f"""
判断主题列表中的每一项是否是给定文本中的一个话题,

以列表的形式给出答案,每个主题用 0 或 1。

主题列表:美国航空航天局、地方政府、工程、员工满意度、联邦政府

给定文本: '''{story_zh}'''
"""
response = get_completion(prompt)
print(response)

输出:

美国航空航天局1
地方政府0
工程0
员工满意度1
联邦政府1

所以,这个故事是关于 NASA 的。它不是关于地方政府的,不是关于工程的。它是关于员工满意度的,它是关于联邦政府的。这在机器学习中有时被称为 Zero-Shot 学习算法,因为我们没有给它任何标记的训练数据。仅凭prompt,它就能确定哪些主题在新闻文章中涵盖了。

如果我们想生成一个新闻提醒,也可以使用这个处理新闻的过程。假设我非常喜欢 NASA 所做的工作,就可以构建一个这样的系统,每当 NASA 新闻出现时,输出提醒。

topic_dict = {i.split(':')[0]: int(i.split(':')[1]) for i in response.split(sep='\n')}
if topic_dict['美国航空航天局'] == 1:
    print("提醒: 关于美国航空航天局的新消息")

输出:

提醒: 关于美国航空航天局的新消息

这就是关于推断的全部内容了,仅用几分钟时间,我们就可以构建多个用于对文本进行推理的系统,而以前则需要熟练的机器学习开发人员数天甚至数周的时间。这非常令人兴奋,无论是对于熟练的机器学习开发人员还是对于新手来说,都可以使用prompt来非常快速地构建和开始相当复杂的自然语言处理任务。

转换

  • LLM非常擅长将输入转换成不同的格式,例如多语种文本翻译、拼写及语法纠正、语气调整、格式转换等,仅仅使用一些prompt就可以轻松实现,使用ChatGPT来校对所写的东西可以预想将为生产带来更多的便利。

  • 本章节将介绍如何使用编程的方式,调用API接口来实现“文本转换”功能。

环境配置

同上一章

文本翻译

中文转西班牙语

prompt = f"""
将以下中文翻译成西班牙语: \ 
```您好,我想订购一个搅拌机。```
"""
response = get_completion(prompt)
print(response)

输出:

Hola, me gustaría ordenar una batidora.

识别语种

prompt = f"""
请告诉我以下文本是什么语种: 
```Combien coûte le lampadaire?```
"""
response = get_completion(prompt)
print(response)

输出:

这是法语

多语种翻译

prompt = f"""
请将以下文本分别翻译成中文、英文、法语和西班牙语: 
```I want to order a basketball.```
"""
response = get_completion(prompt)
print(response)

输出:

中文我想订购一个篮球英文I want to order a basketball.
法语Je veux commander un ballon de basket.
西班牙语Quiero pedir una pelota de baloncesto.

翻译+正式语气

prompt = f"""
请将以下文本翻译成中文,分别展示成正式与非正式两种语气: 
```Would you like to order a pillow?```
"""
response = get_completion(prompt)
print(response)

输出:

正式语气请问您需要订购枕头吗非正式语气你要不要订一个枕头

通用翻译器

识别各个消息的语种,并翻译成目标用户的母语。

user_messages = [
  "La performance du système est plus lente que d'habitude.",  # System performance is slower than normal         
  "Mi monitor tiene píxeles que no se iluminan.",              # My monitor has pixels that are not lighting
  "Il mio mouse non funziona",                                 # My mouse is not working
  "Mój klawisz Ctrl jest zepsuty",                             # My keyboard has a broken control key
  "我的屏幕在闪烁"                                             # My screen is flashing
]
for issue in user_messages:
    prompt = f"告诉我以下文本是什么语种,直接输出语种,如法语,无需输出标点符号: ```{issue}```"
    lang = get_completion(prompt)
    print(f"原始消息 ({lang}): {issue}\n")

    prompt = f"""
    将以下消息分别翻译成英文和中文,并写成
    中文翻译:xxx
    英文翻译:yyy
    的格式:
    ```{issue}```
    """
    response = get_completion(prompt)
    print(response, "\n=========================================")

输出:

原始消息 (法语): La performance du système est plus lente que d'habitude.

中文翻译系统性能比平时慢英文翻译The system performance is slower than usual. 
=========================================
原始消息 (西班牙语): Mi monitor tiene píxeles que no se iluminan.

中文翻译我的显示器有一些像素点不亮英文翻译My monitor has pixels that don't light up. 
=========================================
原始消息 (意大利语): Il mio mouse non funziona

中文翻译我的鼠标不工作了英文翻译My mouse is not working. 
=========================================
原始消息 (波兰语): Mój klawisz Ctrl jest zepsuty

中文翻译我的Ctrl键坏了
英文翻译My Ctrl key is broken. 
=========================================
原始消息 (中文): 我的屏幕在闪烁

中文翻译我的屏幕在闪烁英文翻译My screen is flickering. 
=========================================

语气/风格调整

写作的语气往往会根据受众对象而有所调整。例如,对于工作邮件,我们常常需要使用正式语气与书面用词,而对同龄朋友的微信聊天,可能更多地会使用轻松、口语化的语气。

prompt = f"""
将以下文本翻译成商务信函的格式: 
```小老弟,我小杨,上回你说咱部门要采购的显示器是多少寸来着?```
"""
response = get_completion(prompt)
print(response)

输出:

尊敬的XXX收件人姓名):

您好我是XXX发件人姓名),在此向您咨询一个问题上次我们交流时您提到我们部门需要采购显示器但我忘记了您所需的尺寸是多少英寸希望您能够回复我以便我们能够及时采购所需的设备谢谢您的帮助此致

敬礼

XXX发件人姓名

格式转换

ChatGPT非常擅长不同格式之间的转换,例如JSON到HTML、XML、Markdown等。在下述例子中,我们有一个包含餐厅员工姓名和电子邮件的列表的JSON,我们希望将其从JSON转换为HTML。

data_json = { "resturant employees" :[ 
    {"name":"Shyam", "email":"[email protected]"},
    {"name":"Bob", "email":"[email protected]"},
    {"name":"Jai", "email":"[email protected]"}
]}
prompt = f"""
将以下Python字典从JSON转换为HTML表格,保留表格标题和列名:{data_json}
"""
response = get_completion(prompt)
print(response)

输出:

<table>
  <caption>resturant employees</caption>
  <thead>
    <tr>
      <th>name</th>
      <th>email</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Shyam</td>
      <td>shyamjaiswal@gmail.com</td>
    </tr>
    <tr>
      <td>Bob</td>
      <td>bob32@gmail.com</td>
    </tr>
    <tr>
      <td>Jai</td>
      <td>jai87@gmail.com</td>
    </tr>
  </tbody>
</table>

表格显示:

from IPython.display import display, Markdown, Latex, HTML, JSON
display(HTML(response))

输出:

img

拼写及语法纠正

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

以下给了一个例子,有一个句子列表,其中有些句子存在拼写或语法问题,有些则没有,我们循环遍历每个句子,要求模型校对文本,如果正确则输出“未发现错误”,如果错误则输出纠正后的文本。

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 speling 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.
  • 还可以做的一件很有意思的事是可以显式地看到我们的原始评论和模型的输出之间的差异,首先输入原始文本,输出纠正后的文本。

  • text = f"""
    Got this for my daughter for her birthday cuz she keeps taking \
    mine from my room.  Yes, adults also like pandas too.  She takes \
    it everywhere with her, and it's super soft and cute.  One of the \
    ears is a bit lower than the other, and I don't think that was \
    designed to be asymmetrical. It's a bit small for what I paid for it \
    though. I think there might be other options that are bigger for \
    the same price.  It arrived a day earlier than expected, so I got \
    to play with it myself before I gave it to my daughter.
    """
prompt = f"校对并更正以下商品评论:```{text}```"
response = get_completion(prompt)
print(response)

输出:

I got this for my daughter's birthday because she keeps taking mine from my room. Yes, adults also like pandas too. She takes it everywhere with her, and it's super soft and cute. However, one of the ears is a bit lower than the other, and I don't think that was designed to be asymmetrical. It's also a bit smaller than I expected for the price. I think there might be other options that are bigger for the same price. On the bright side, it arrived a day earlier than expected, so I got to play with it myself before giving it to my daughter.

基于Redlines Python包来实现可以看到如下结果。如未安装redlines,需先安装。

from redlines import Redlines

diff = Redlines(text,response)
display(Markdown(diff.output_markdown))

输出:

img

以上我们使用的prompt是校对和纠正这个评论,同样地我们可以多尝试一些其他变化。

所以在下面的prompt中,我们除了要求校对和纠正同样的评论,但也要使其更有说服力,并确保其遵循APA风格并面向高级读者,我们还将要求以Markdown格式输出结果。

prompt = f"""
proofread and correct this review. Make it more compelling. 
Ensure it follows APA style guide and targets an advanced reader. 
Output in markdown format.
Text: ```{text}```
"""
response = get_completion(prompt)
display(Markdown(response))

输出:

img

  • 由上,我们得到了一个扩展了APA风格的毛绒熊猫的评论。

  • 你也可以自己尝试综合样例的归纳,比如在一个prompt中同时实现文本翻译+拼写纠正+风格调整+格式转换。

扩展

扩展是将短文本,例如一组指示或主题列表,输入到大语言模型中,让模型生成更长的文本,例如基于某个主题的电子邮件或论文。

在本章中,你将学会如何基于大语言模型生成个性化电子邮件。我们还将使用模型的另一个输入参数称为温度,这种参数允许您在模型响应中变化探索的程度和多样性。

环境配置

同上一章

定制客户邮件

我们将根据客户评价和情感撰写自定义电子邮件响应。因此,我们将给定客户评价和情感,并生成自定义响应即使用 LLM 根据客户评价和评论情感生成定制电子邮件。

我们首先给出一个示例,包括一个评论及对应的情感。

# 我们可以在推理那章学习到如何对一个评论判断其情感倾向
sentiment = "negative"

# 一个产品的评价
review = f"""
他们在11月份的季节性销售期间以约49美元的价格出售17件套装,折扣约为一半。\
但由于某些原因(可能是价格欺诈),到了12月第二周,同样的套装价格全都涨到了70美元到89美元不等。\
11件套装的价格也上涨了大约10美元左右。\
虽然外观看起来还可以,但基座上锁定刀片的部分看起来不如几年前的早期版本那么好。\
不过我打算非常温柔地使用它,例如,\
我会先在搅拌机中将像豆子、冰、米饭等硬物研磨,然后再制成所需的份量,\
切换到打蛋器制作更细的面粉,或者在制作冰沙时先使用交叉切割刀片,然后使用平面刀片制作更细/不粘的效果。\
制作冰沙时,特别提示:\
将水果和蔬菜切碎并冷冻(如果使用菠菜,则轻轻煮软菠菜,然后冷冻直到使用;\
如果制作果酱,则使用小到中号的食品处理器),这样可以避免在制作冰沙时添加太多冰块。\
大约一年后,电机发出奇怪的噪音,我打电话给客服,但保修已经过期了,所以我不得不再买一个。\
总的来说,这些产品的总体质量已经下降,因此它们依靠品牌认可和消费者忠诚度来维持销售。\
货物在两天内到达。
"""

我们已经使用推断课程中学到的提取了情感,这是一个关于搅拌机的客户评价,现在我们将根据情感定制回复。

如果情绪是积极的或中性的,感谢他们的评论,如果情绪是负面的,道歉并建议他们可以联系客户服务部,确保使用评论中的具体细节,用简明和专业的语气来写,并以AI客户代理的身份在邮件中签名。

当你使用一个语言模型,来生成你要展示给用户的文本,有这样的透明度是非常重要的,并让用户知道他们看到的文本是由AI生成的。

当然你也可以用一个prompt去集成提取评论情绪以及回复邮件。

这里的指令是:假设你是一个客户服务AI助手,你的任务是为客户发送电子邮件回复,根据通过三个反引号分隔的客户电子邮件,生成一封回复以感谢客户的评价。

prompt = f"""
你是一位客户服务的AI助手。
你的任务是给一位重要客户发送邮件回复。
根据客户通过“```”分隔的评价,生成回复以感谢客户的评价。提醒模型使用评价中的具体细节
用简明而专业的语气写信。
作为“AI客户代理”签署电子邮件。
客户评论:
```{review}```
评论情感:{sentiment}
"""
response = get_completion(prompt)
print(response)

输出:

尊敬的客户非常感谢您对我们产品的评价我们非常抱歉您在购买过程中遇到了价格上涨的问题我们一直致力于为客户提供最优惠的价格但由于市场波动价格可能会有所变化我们深表歉意如果您需要任何帮助请随时联系我们的客户服务团队我们非常感谢您对我们产品的详细评价和使用技巧我们将会把您的反馈传达给我们的产品团队以便改进我们的产品质量和性能再次感谢您对我们的支持和反馈如果您需要任何帮助或有任何疑问请随时联系我们的客户服务团队祝您一切顺利AI客户代理

使用温度系数

接下来,我们将使用语言模型的一个称为“温度”的参数,它将允许我们改变模型响应的多样性。您可以将温度视为模型探索或随机性的程度。

例如,在一个特定的短语中,“我的最爱食品”最有可能的下一个词是“比萨”,其次最有可能的是“寿司”和“塔可”。因此,在温度为零时,模型将总是选择最有可能的下一个词,而在较高的温度下,它还将选择其中一个不太可能的词,在更高的温度下,它甚至可能选择塔可,而这种可能性仅为五分之一。您可以想象,随着模型继续生成更多单词的最终响应,“我的最爱食品是比萨”将会与第一个响应“我的最爱食品是塔可”产生差异。因此,随着模型的继续,这两个响应将变得越来越不同。

一般来说,在构建需要可预测响应的应用程序时,我建议使用温度为零。在所有课程中,我们一直设置温度为零,如果您正在尝试构建一个可靠和可预测的系统,我认为您应该选择这个温度。如果您尝试以更具创意的方式使用模型,可能需要更广泛地输出不同的结果,那么您可能需要使用更高的温度。

prompt = f"""
你是一名客户服务的AI助手。
你的任务是给一位重要的客户发送邮件回复。
根据通过“```”分隔的客户电子邮件生成回复,以感谢客户的评价。
如果情感是积极的或中性的,感谢他们的评价。
如果情感是消极的,道歉并建议他们联系客户服务。
请确保使用评论中的具体细节。
以简明和专业的语气写信。
以“AI客户代理”的名义签署电子邮件。
客户评价:```{review}```
评论情感:{sentiment}
"""
response = get_completion(prompt, temperature=0.7)
print(response)

输出:

尊敬的客户非常感谢您对我们产品的评价我们由衷地为您在购买过程中遇到的问题表示抱歉我们确实在12月份的第二周调整了价格但这是由于市场因素所致并非价格欺诈我们深刻意识到您对产品质量的担忧我们将尽一切努力改进产品以提供更好的体验我们非常感激您对我们产品的使用经验和制作技巧的分享您的建议和反馈对我们非常重要我们将以此为基础进一步改进我们的产品如果您有任何疑问或需要进一步帮助请随时联系我们的客户服务部门我们将尽快回复您并提供帮助最后请再次感谢您对我们产品的评价和选择我们期待着未来与您的合作此致

敬礼

AI客户代理

在温度为零时,每次执行相同的提示时,您应该期望获得相同的完成。而使用温度为0.7,则每次都会获得不同的输出。

所以,您可以看到它与我们之前收到的电子邮件不同。让我们再次执行它,以显示我们将再次获得不同的电子邮件。

因此,我建议您自己尝试温度,以查看输出如何变化。总之,在更高的温度下,模型的输出更加随机。您几乎可以将其视为在更高的温度下,助手更易分心,但也许更有创造力。

聊天机器人

关于大语言模型的一个令人兴奋的事情,是你可以用它来构建一个自定义的聊天机器人,只需花费少量的精力。ChatGPT的Web界面能让你通过一个大语言模型进行对话,但你也可以使用一个大语言模型来建立你的自定义聊天机器人。

环境设置

import os
import openai
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file

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

像 ChatGPT 这样的聊天模型实际上是组装成以一系列消息作为输入,并返回一个模型生成的消息作为输出的。虽然聊天格式的设计旨在使这种多轮对话变得容易,但我们通过之前的学习可以知道,它对于没有任何对话的单轮任务也同样有用。

接下来,我们将定义两个辅助函数。第一个是单轮的,我们将prompt放入看起来像是某种用户消息的东西中。另一个则传入一个消息列表。这些消息可以来自不同的角色,我们会描述一下这些角色。

第一条消息是一个系统消息,它提供了一个总体的指示,然后在这个消息之后,我们有用户和助手之间的交替。如果你曾经使用过 ChatGPT 网页界面,那么你的消息是用户消息,而 ChatGPT 的消息是助手消息。系统消息则有助于设置助手的行为和角色,并作为对话的高级指示。你可以想象它在助手的耳边低语,引导它的回应,而用户不会注意到系统消息。

因此,作为用户,如果你曾经使用过 ChatGPT,你可能不知道 ChatGPT 的系统消息是什么,这是有意为之的。系统消息的好处是为开发者提供了一种方法,在不让请求本身成为对话的一部分的情况下,引导助手并指导其回应。

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"]

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)) 打印message,可以更直观看出是助手消息
    return response.choices[0].message["content"]

现在让我们尝试在对话中使用这些消息。我们将使用上面的函数来获取从这些消息的完成状态,同时,使用更高的 temperature(越高生成的越多样)。

系统消息说,你是一个说话像莎士比亚的助手。这是我们向助手描述它应该如何表现的方式。然后,第一个用户消息是,给我讲个笑话。接下来的消息是,为什么鸡会过马路?然后最后一个用户消息是,我不知道。

# 中文
messages =  [  
{'role':'system', 'content':'你是一个像莎士比亚一样说话的助手。'},    
{'role':'user', 'content':'给我讲个笑话'},   
{'role':'assistant', 'content':'鸡为什么过马路'},   
{'role':'user', 'content':'我不知道'}  ]
response = get_completion_from_messages(messages, temperature=1)
print(response)

输出:

To get to the other side, verily! It's an age-old jest that still tickles the fancy!

因为它要去找“母鸡”。哈哈哈!(注:此为英文双关语,"chicken"是鸡的意思,也是胆小的意思;"cross the road"是过马路的意思,也是“破坏规则”的意思。)

让我们做另一个例子。助手的消息是,你是一个友好的聊天机器人,第一个用户消息是,嗨,我叫Isa。我们想要得到第一个用户消息。

# 中文
messages =  [  
{'role':'system', 'content':'你是个友好的聊天机器人。'},    
{'role':'user', 'content':'Hi, 我是Isa。'}  ]
response = get_completion_from_messages(messages, temperature=1)
print(response)

输出:

Isa很高兴见到你有什么我可以帮助你的吗

让我们再试一个例子。系统消息是,你是一个友好的聊天机器人,第一个用户消息是,是的,你能提醒我我的名字是什么吗?

# 中文
messages =  [  
{'role':'system', 'content':'你是个友好的聊天机器人。'},    
{'role':'user', 'content':'好,你能提醒我,我的名字是什么吗?'}  ]
response = get_completion_from_messages(messages, temperature=1)
print(response)

输出:

抱歉我不知道您的名字因为我们是虚拟的聊天机器人和现实生活中的人类在不同的世界中

如上所见,模型实际上并不知道我的名字。

因此,每次与语言模型的交互都是一个独立的交互,这意味着我们必须提供所有相关的消息,以便模型在当前对话中进行引用。如果想让模型引用或 “记住” 对话的早期部分,则必须在模型的输入中提供早期的交流。我们将其称为上下文。让我们试试。

# 中文
messages =  [  
{'role':'system', 'content':'你是个友好的聊天机器人。'},
{'role':'user', 'content':'Hi, 我是Isa'},
{'role':'assistant', 'content': "Hi Isa! 很高兴认识你。今天有什么可以帮到你的吗?"},
{'role':'user', 'content':'是的,你可以提醒我, 我的名字是什么?'}  ]
response = get_completion_from_messages(messages, temperature=1)
print(response)

输出:

当然可以您的名字是Isa

订餐机器人

现在,我们构建一个 “订餐机器人”,我们需要它自动收集用户prompt和助手response,接受比萨饼店的订单。

下面这个函数将收集我们的用户消息,以便我们可以避免手动输入,就像我们在刚刚上面做的那样。这个函数将从我们下面构建的用户界面中收集提示,然后将其附加到一个名为上下文的列表中,并在每次调用模型时使用该上下文。模型的响应也会被添加到上下文中,所以模型消息和用户消息都被添加到上下文中,因此上下文逐渐变长。这样,模型就有了需要的信息来确定下一步要做什么。

def collect_messages(_):
    prompt = inp.value_input
    inp.value = ''
    context.append({'role':'user', 'content':f"{prompt}"})
    response = get_completion_from_messages(context) 
    context.append({'role':'assistant', 'content':f"{response}"})
    panels.append(
        pn.Row('User:', pn.pane.Markdown(prompt, width=600)))
    panels.append(
        pn.Row('Assistant:', pn.pane.Markdown(response, width=600, style={'background-color': '#F6F6F6'})))
 
    return pn.Column(*panels)

现在,我们将设置并运行这个 UI 来显示订单机器人。初始的上下文包含了包含菜单的系统消息。请注意,上下文会随着时间的推移而不断增长。

# 中文
import panel as pn  # GUI
pn.extension()

panels = [] # collect display 

context = [{'role':'system', 'content':"""
你是订餐机器人,为披萨餐厅自动收集订单信息。
你要首先问候顾客。然后等待用户回复收集订单信息。收集完信息需确认顾客是否还需要添加其他内容。
最后需要询问是否自取或外送,如果是外送,你要询问地址。
最后告诉顾客订单总金额,并送上祝福。

请确保明确所有选项、附加项和尺寸,以便从菜单中识别出该项唯一的内容。
你的回应应该以简短、非常随意和友好的风格呈现。

菜单包括:

菜品:
意式辣香肠披萨(大、中、小) 12.95、10.00、7.00
芝士披萨(大、中、小) 10.95、9.25、6.50
茄子披萨(大、中、小) 11.95、9.75、6.75
薯条(大、小) 4.50、3.50
希腊沙拉 7.25

配料:
奶酪 2.00
蘑菇 1.50
香肠 3.00
加拿大熏肉 3.50
AI酱 1.50
辣椒 1.00

饮料:
可乐(大、中、小) 3.00、2.00、1.00
雪碧(大、中、小) 3.00、2.00、1.00
瓶装水 5.00
"""} ]  # accumulate messages

inp = pn.widgets.TextInput(value="Hi", placeholder='Enter text here…')
button_conversation = pn.widgets.Button(name="Chat!")

interactive_conversation = pn.bind(collect_messages, button_conversation)

dashboard = pn.Column(
    inp,
    pn.Row(button_conversation),
    pn.panel(interactive_conversation, loading_indicator=True, height=300),
)

dashboard

输出:

img

现在我们可以要求模型创建一个 JSON 摘要发送给订单系统。

所以我们现在追加另一个系统消息,它是另一条prompt,我们说创建一个刚刚订单的 JSON 摘要,列出每个项目的价格,字段应包括1)披萨,包括尺寸,2)配料列表,3)饮料列表,4)辅菜列表,包括尺寸,最后是总价格。这里也可以在这里使用用户消息,不一定是系统消息。

请注意,这里我们使用了一个较低的temperature,因为对于这些类型的任务,我们希望输出相对可预测。而对于会话代理,我们可能希望使用更高的温度。

messages =  context.copy()
messages.append(
{'role':'system', 'content':'创建上一个食品订单的 json 摘要。\
逐项列出每件商品的价格,字段应该是 1) 披萨,包括大小 2) 配料列表 3) 饮料列表,包括大小 4) 配菜列表包括大小 5) 总价'},    
)

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

输出:

messages·=··context.copy()
messages.append(
{'role':'system''content':'创建上一个食品订单的·json·摘要。\
逐项列出每件商品的价格,字段应该是·1)·披萨,包括大小·2)·配料列表·3)·饮料列表,包括大小·4)·配菜列表包括大小·5)·总价'},····
)
response·=·get_completion_from_messages(messagestemperature=0)
print(response)
以下是上一个食品订单的 JSON 摘要```
{
  "order": {
    "pizza": {
      "type": "芝士披萨",
      "size": "大",
      "price": 10.95
    },
    "toppings": [
      {
        "name": "蘑菇",
        "price": 1.5
      }
    ],
    "drinks": [
      {
        "name": "雪碧",
        "size": "大",
        "price": 3
      },
      {
        "name": "雪碧",
        "size": "大",
        "price": 3
      }
    ],
    "sides": [],
    "total_price": 18.45
  }
}
```

你也可以随意自定义并尝试修改系统消息,更改聊天机器人的行为并让其扮演不同的角色。

总结

在本次的短课程中,你学到了:

  1. 提示的两个关键原则

1.1 正确且清晰的描述具体指示

1.2 知道什么时候使用给予模型思考的时间

  1. 了解了迭代提示的发展以及如何有一个过程来获得适合你的应用的提示是关键
  2. 学习了一些大语言模型的功能特别是总结、推理、转换和扩展
  3. 如何建立一个定制的聊天机器人

我们希望你能想出一些可以自己构建的应用,从一些很小的项目开始也是可以的,你可以通过第一个项目获得的经验教训,来构建第二个更好的项目,甚至可能是更好的第三个项目,等等。笔者也觉得玩这些模型非常有趣,享受gpt带来的快乐吧!