目录
- 一、资料
- 二、 指南
- 环境配置
- 两个基本原则(最重要!!!!)
- 原则一:编写清晰、具体的指令
- **策略一:使用分隔符清晰地表示输入的不同部分**,**分隔符可以是:```,"",<>,\<tag>,<\tag>**等(区分指示和内容)
- **策略二:要求一个结构化的输出**,可以是 Json、HTML 等格式
- **策略三:要求模型检查是否满足条件**
- **策略四:提供少量示例**(few-shot learning)
- 原则二:给模型时间去思考
- **策略一:指定完成任务所需的步骤**(类似于做数学题,告诉步骤)
- **策略二:指导模型在下结论之前找出一个自己的解法**
- 局限性
- **说明**
- 三、 迭代式提示开发
- 任务——从产品说明书生成一份营销产品描述
- 问题一:生成文本太长(限制生成文本长度)
- 问题二:文本关注在错误的细节上(侧重点、细节介绍)
- 问题三:需要一个表格形式的描述
- 总结
- 四、文本概括 Summarizing
- 2 单一文本概括Prompt实验
- 2.1 限制输出文本长度
- 2.2 关键角度侧重(业务需求)
- 2.3 关键信息提取
- 3 多条文本概括Prompt实验
- 五、 推断
- 商品评论文本
- 情感(正向/负向)
- 识别情感类型
- 识别愤怒
- 从客户评论中提取产品和公司名称
- 一次完成多项任务
- 推断主题(Zero-Shot 学习算法)
- 为特定主题制作新闻提醒(当出现01这种主题、提醒)
- 六、文本转换
- 2 文本翻译
- 3 语气/风格调整
- 4 格式转换
- 5 拼写及语法纠正
- 6 一个综合样例:文本翻译+拼写纠正+风格调整+格式转换
- \ 第七章 文本扩展
- 二、定制客户邮件
- 三、使用温度系数
- 八、聊天机器人
- 对话保持上下文能力(gpt 3.5 turbo才有,需要把所有的记录都输给他,但是容易tokens上限)
- 总结
一、资料
- 感谢中文版翻译https://github.com/datawhalechina/prompt-engineering-for-developers/tree/main/content
二、 指南
环境配置
- 国内
== 需要对openapi的endpoint做一个反向代理,并修改本地openai包的源代码==
如下图:
- completion
# 一个封装 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>,<\tag>**等(区分指示和内容)
你可以使用任何明显的标点符号将特定的文本部分与提示的其余部分分开。这可以是任何可以使模型明确知道这是一个单独部分的标记。使用分隔符是一种可以避免提示注入的有用技术。提示注入是指如果用户将某些输入添加到提示中,则可能会向模型提供与您想要执行的操作相冲突的指令,从而使其遵循冲突的指令而不是执行您想要的操作。即,输入里面可能包含其他指令,会覆盖掉你的指令。对此,使用分隔符是一个不错的策略。
以下是一个例子,我们给出一段话并要求 GPT 进行总结,在该示例中我们使用 ```来作为分隔符
text = f"""
你应该提供尽可能清晰、具体的指示,以表达你希望模型执行的任务。\
这将引导模型朝向所需的输出,并降低收到无关或不正确响应的可能性。\
不要将写清晰的提示与写简短的提示混淆。\
在许多情况下,更长的提示可以为模型提供更多的清晰度和上下文信息,从而导致更详细和相关的输出。
"""
# 需要总结的文本内容
prompt = f"""
把用三个反引号括起来的文本总结成一句话。
```{text}```
"""
# 指令内容,使用 ```来分隔指令和待总结的内容
response = get_completion(prompt)
print(response)
#
提供清晰具体的指示,避免无关或不正确响应,不要混淆写清晰和写简短,更长的提示可以提供更多清晰度和上下文信息,导致更详细和相关的输出。
策略二:要求一个结构化的输出,可以是 Json、HTML 等格式
第二个策略是要求生成一个结构化的输出,这可以使模型的输出更容易被我们解析,例如,你可以在 Python 中将其读入字典或列表中。在以下示例中,我们要求 GPT 生成三本书的标题、作者和类别,并要求 GPT 以 Json 的格式返回给我们,为便于解析,我们指定了 Json 的键。
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"
}
]
}
策略三:要求模型检查是否满足条件
如果任务做出的假设不一定满足,我们可以告诉模型先检查这些假设,如果不满足,指示并停止执行。你还可以考虑潜在的边缘情况以及模型应该如何处理它们,以避免意外的错误或结果。
在如下示例中,我们将分别给模型两段文本,分别是制作茶的步骤以及一段没有明确步骤的文本。我们将要求模型判断其是否包含一系列指令,如果包含则按照给定格式重新编写指令,不包含则回答未提供步骤。
# 有步骤的文本
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 的总结:
未提供步骤。
策略四:提供少量示例(few-shot learning)
即在要求模型执行实际任务之前,提供给它少量成功执行任务的示例。
例如,在以下的示例中,我们告诉模型其任务是以一致的风格回答问题,并先给它一个孩子和一个祖父之间的对话的例子。孩子说,“教我耐心”,祖父用这些隐喻回答。因此,由于我们已经告诉模型要以一致的语气回答,现在我们说“教我韧性”,由于模型已经有了这个少样本示例,它将以类似的语气回答下一个任务。
prompt = f"""
你的任务是以一致的风格回答问题。
<孩子>: 教我耐心。
<祖父母>: 挖出最深峡谷的河流源于一处不起眼的泉眼;最宏伟的交响乐从单一的音符开始;最复杂的挂毯以一根孤独的线开始编织。
<孩子>: 教我韧性。
"""
response = get_completion(prompt)
print(response)
#
<祖父母>: 韧性就像是一棵树,它需要经历风吹雨打、寒冬酷暑,才能成长得更加坚强。在生活中,我们也需要经历各种挫折和困难,才能锻炼出韧性。记住,不要轻易放弃,坚持下去,你会发现自己变得更加坚强。
原则二:给模型时间去思考
如果模型匆忙地得出了错误的结论,您应该尝试重新构思查询,请求模型在提供最终答案之前进行一系列相关的推理。换句话说,如果您给模型一个在短时间或用少量文字无法完成的任务,它可能会猜测错误。这种情况对人来说也是一样的。如果您让某人在没有时间计算出答案的情况下完成复杂的数学问题,他们也可能会犯错误。因此,在这些情况下,您可以指示模型花更多时间思考问题,这意味着它在任务上花费了更多的计算资源。
策略一:指定完成任务所需的步骤(类似于做数学题,告诉步骤)
接下来我们将通过给定一个复杂任务,给出完成该任务的一系列步骤,来展示这一策略的效果
首先我们描述了杰克和吉尔的故事,并给出一个指令。该指令是执行以下操作。首先,用一句话概括三个反引号限定的文本。第二,将摘要翻译成法语。第三,在法语摘要中列出每个名称。第四,输出包含以下键的 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.
名称:Jack,Jill
输出 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 = 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 产品的信息,事实上,这个公司是真实存在的,但产品是编造的,模型则会一本正经地告诉我们编造的知识。
模型会输出看上去非常真实的编造知识,这有时会很危险。因此,请确保使用我们在本节中介绍的一些技巧,以尝试在构建自己的应用程序时避免这种情况。这是模型已知的一个弱点,也是我们正在积极努力解决的问题。在你希望模型根据文本生成答案的情况下,另一种减少幻觉的策略是先要求模型找到文本中的任何相关引用,然后要求它使用这些引用来回答问题,这种追溯源文档的方法通常对减少幻觉非常有帮助。
说明
在本教程中,我们使用 \ 来使文本适应屏幕大小以提高阅读体验,GPT 并不受 \ 的影响,但在你调用其他大模型时,需额外考虑 \ 是否会影响模型性能
三、 迭代式提示开发
当使用 LLM 构建应用程序时,我从来没有在第一次尝试中就成功使用最终应用程序中所需的 Prompt。但这并不重要,只要您有一个好的迭代过程来不断改进您的 Prompt,那么你就能够得到一个适合任务的 Prompt。我认为在提示方面,第一次成功的几率可能会高一些,但正如上所说,第一个提示是否有效并不重要。最重要的是为您的应用程序找到有效提示的过程。
因此,在本章中,我们将以从产品说明书中生成营销文案这一示例,展示一些框架,以提示你思考如何迭代地分析和完善你的 Prompt。
如果您之前与我一起上过机器学习课程,您可能见过我使用的一张图表,说明了机器学习开发的流程。通常是先有一个想法,然后再实现它:编写代码,获取数据,训练模型,这会给您一个实验结果。然后您可以查看输出结果,进行错误分析,找出它在哪里起作用或不起作用,甚至可以更改您想要解决的问题的确切思路或方法,然后更改实现并运行另一个实验等等,反复迭代,以获得有效的机器学习模型。在编写 Prompt 以使用 LLM 开发应用程序时,这个过程可能非常相似,您有一个关于要完成的任务的想法,可以尝试编写第一个 Prompt,满足上一章说过的两个原则:清晰明确,并且给系统足够的时间思考。然后您可以运行它并查看结果。如果第一次效果不好,那么迭代的过程就是找出为什么指令不够清晰或为什么没有给算法足够的时间思考,以便改进想法、改进提示等等,循环多次,直到找到适合您的应用程序的 Prompt。
任务——从产品说明书生成一份营销产品描述
这里有一个椅子的产品说明书,描述说它是一个中世纪灵感家族的一部分,讨论了构造、尺寸、椅子选项、材料等等,产地是意大利。假设您想要使用这份说明书帮助营销团队为在线零售网站撰写营销式描述
# 示例:产品说明书
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)
#
这款美丽的中世纪风格办公家具系列包括文件柜、办公桌、书柜、会议桌等,适用于家庭或商业场所。它有多种外壳颜色和底座涂层可选,底座涂层选项为不锈钢、哑光黑色、光泽白色或铬。椅子可带或不带扶手,可选塑料前后靠背装饰或10种面料和6种皮革的全面装饰。这款椅子的结构是五个轮子的塑料涂层铝底座,气动椅子调节,方便升降。它的尺寸为宽度53厘米、深度51厘米、高度80厘米、座椅高度44厘米和座椅深度41厘米。此外,软地板或硬地板滚轮选项、两种座椅泡沫密度可选(中等或高)、无扶手或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在遵循非常精确的字数限制方面表现得还可以,但并不那么出色。有时它会输出60或65个单词的内容,但这还算是合理的。这原因是 LLM 解释文本使用一种叫做分词器的东西,但它们往往在计算字符方面表现一般般。有很多不同的方法来尝试控制你得到的输出的长度。
问题二:文本关注在错误的细节上(侧重点、细节介绍)
我们会发现的第二个问题是,这个网站并不是直接向消费者销售,它实际上旨在向家具零售商销售家具,他们会更关心椅子的技术细节和材料。在这种情况下,你可以修改这个提示,让它更精确地描述椅子的技术细节。
解决方法:要求它专注于与目标受众相关的方面。
# 优化后的 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毫米。原产国为意大利。产品ID:SWC-100/SWC-110。
问题三:需要一个表格形式的描述
以上是许多开发人员通常会经历的迭代提示开发的简短示例。我的建议是,像上一章中所演示的那样,Prompt 应该保持清晰和明确,并在必要时给模型一些思考时间。在这些要求的基础上,通常值得首先尝试编写 Prompt ,看看会发生什么,然后从那里开始迭代地完善 Prompt,以逐渐接近所需的结果。因此,许多成功的Prompt都是通过这种迭代过程得出的。我将向您展示一个更复杂的提示示例,可能会让您对ChatGPT的能力有更深入的了解。
这里我添加了一些额外的说明,要求它抽取信息并组织成表格,并指定表格的列、表名和格式,还要求它将所有内容格式化为可以在网页使用的 HTML。
# 要求它抽取信息并组织成表格,并指定表格的列、表名和格式
prompt = f"""
您的任务是帮助营销团队基于技术说明书创建一个产品的零售网站描述。
根据```标记的技术说明书中提供的信息,编写一个产品描述。
该描述面向家具零售商,因此应具有技术性质,并侧重于产品的材料构造。
在描述末尾,包括技术规格中每个7个字符的产品ID。
在描述之后,包括一个表格,提供产品的尺寸。表格应该有两列。第一列包括尺寸的名称。第二列只包括英寸的测量值。
给表格命名为“产品尺寸”。
将所有内容格式化为可用于网站的HTML格式。将描述放在<div>元素中。
技术规格:```{fact_sheet_chair}```
"""
response = get_completion(prompt)
print(response)
# 表格是以 HTML 格式呈现的,加载出来
from IPython.display import display, HTML
display(HTML(response))
#
<div>
<h2>中世纪风格办公家具系列椅子</h2>
<p>这款椅子是中世纪风格办公家具系列的一部分,适用于家庭或商业场所。它有多种外壳颜色和底座涂层可选,包括不锈钢、哑光黑色、光泽白色或铬。您可以选择带或不带扶手的椅子,以及软地板或硬地板滚轮选项。此外,您可以选择两种座椅泡沫密度:中等(1.8磅/立方英尺)或高(2.8磅/立方英尺)。</p>
<p>椅子的外壳底座滑动件是改性尼龙PA6/PA66涂层的铸铝,外壳厚度为10毫米。座椅采用HD36泡沫,底座是五个轮子的塑料涂层铝底座,可以进行气动椅子调节,方便升降。此外,椅子符合合同使用资格,是您理想的选择。</p>
<p>产品ID:SWC-100</p>
</div>
<table>
<caption>产品尺寸</caption>
<tr>
<th>宽度</th>
<td>20.87英寸</td>
</tr>
<tr>
<th>深度</th>
<td>20.08英寸</td>
</tr>
<tr>
总结
本章的主要内容是 LLM 在开发应用程序中的迭代式提示开发过程。开发者需要先尝试编写提示,然后通过迭代逐步完善它,直至得到需要的结果。关键在于拥有一种有效的开发Prompt的过程,而不是知道完美的Prompt。对于一些更复杂的应用程序,可以对多个样本进行迭代开发提示并进行评估。最后,可以在更成熟的应用程序中测试多个Prompt在多个样本上的平均或最差性能。在使用 Jupyter 代码笔记本示例时,请尝试不同的变化并查看结果。
四、文本概括 Summarizing
2 单一文本概括Prompt实验
这里我们举了个商品评论的例子。对于电商平台来说,网站上往往存在着海量的商品评论,这些评论反映了所有客户的想法。如果我们拥有一个工具去概括这些海量、冗长的评论,便能够快速地浏览更多评论,洞悉客户的偏好,从而指导平台与商家提供更优质的服务
2.1 限制输出文本长度
prod_review_zh = """
这个熊猫公仔是我给女儿的生日礼物,她很喜欢,去哪都带着。
公仔很软,超级可爱,面部表情也很和善。但是相比于价钱来说,
它有点小,我感觉在别的地方用同样的价钱能买到更大的。
快递比预期提前了一天到货,所以在送给女儿之前,我自己玩了会。
"""
prompt = f"""
你的任务是从电子商务网站上生成一个产品评论的简短摘要。
请对三个反引号之间的评论文本进行概括,最多30个词汇。
评论: ```{prod_review_zh}```
"""
response = get_completion(prompt)
print(response)
#
可爱软熊猫公仔,女儿喜欢,面部表情和善,但价钱有点小贵,快递提前一天到货。
2.2 关键角度侧重(业务需求)
有时,针对不同的业务,我们对文本的侧重会有所不同。例如对于商品评论文本,物流会更关心运输时效,商家更加关心价格与商品质量,平台更关心整体服务体验。
我们可以通过增加Prompt提示,来体现对于某个特定角度的侧重。
prompt = f"""
你的任务是从电子商务网站上生成一个产品评论的简短摘要。
请对三个反引号之间的评论文本进行概括,最多30个词汇,并且聚焦在产品运输上。
评论: ```{prod_review_zh}```
"""
response = get_completion(prompt)
print(response)
#快递提前到货,熊猫公仔软可爱,但有点小,价钱不太划算。
2.3 关键信息提取
在2.2节中,虽然我们通过添加关键角度侧重的Prompt,使得文本摘要更侧重于某一特定方面,但是可以发现,结果中也会保留一些其他信息,如价格与质量角度的概括中仍保留了“快递提前到货”的信息。有时这些信息是有帮助的,但如果我们只想要提取某一角度的信息,并过滤掉其他所有信息,则可以要求LLM进行“文本提取(Extract)”而非“文本概括(Summarize)”。
prompt = f"""
你的任务是从电子商务网站上的产品评论中提取相关信息。
请从以下三个反引号之间的评论文本中提取产品运输相关的信息,最多30个词汇。
评论: ```{prod_review_zh}```
"""
response = get_completion(prompt)
print(response)
#快递比预期提前了一天到货。
3 多条文本概括Prompt实验
在实际的工作流中,我们往往有许许多多的评论文本,以下展示了一个基于for循环调用“文本概括”工具并依次打印的示例。当然,在实际生产中,对于上百万甚至上千万的评论文本,使用for循环也是不现实的,可能需要考虑整合评论、分布式等方法提升运算效率。
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")
五、 推断
在这节课中,你将从产品评论和新闻文章中推断情感和主题。
这些任务可以看作是模型接收文本作为输入并执行某种分析的过程。这可能涉及提取标签、提取实体、理解文本情感等等。如果你想要从一段文本中提取正面或负面情感,在传统的机器学习工作流程中,需要收集标签数据集、训练模型、确定如何在云端部署模型并进行推断。这样做可能效果还不错,但是这个过程需要很多工作。而且对于每个任务,如情感分析、提取实体等等,都需要训练和部署单独的模型。
大型语言模型的一个非常好的特点是,对于许多这样的任务,你只需要编写一个prompt即可开始产生结果,而不需要进行大量的工作。这极大地加快了应用程序开发的速度。你还可以只使用一个模型和一个 API 来执行许多不同的任务,而不需要弄清楚如何训练和部署许多不同的模型。
商品评论文本
这是一盏台灯的评论。
情感(正向/负向)
现在让我们来编写一个prompt来分类这个评论的情感。如果我想让系统告诉我这个评论的情感是什么,只需要编写 “以下产品评论的情感是什么” 这个prompt,加上通常的分隔符和评论文本等等。
然后让我们运行一下。结果显示这个产品评论的情感是积极的,这似乎是非常正确的。虽然这盏台灯不完美,但这个客户似乎非常满意。这似乎是一家关心客户和产品的伟大公司,可以认为积极的情感似乎是正确的答案。
lamp_review_zh = """
我需要一盏漂亮的卧室灯,这款灯具有额外的储物功能,价格也不算太高。\
我很快就收到了它。在运输过程中,我们的灯绳断了,但是公司很乐意寄送了一个新的。\
几天后就收到了。这款灯很容易组装。我发现少了一个零件,于是联系了他们的客服,他们很快就给我寄来了缺失的零件!\
在我看来,Lumina 是一家非常关心顾客和产品的优秀公司!
"""
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"""
从评论文本中识别以下项目:
- 评论者购买的物品
- 制造该物品的公司
评论文本用三个反引号分隔。将你的响应格式化为以 “物品” 和 “品牌” 为键的 JSON 对象。
如果信息不存在,请使用 “未知” 作为值。
让你的回应尽可能简短。
评论文本: ```{lamp_review_zh}```
"""
response = get_completion(prompt)
print(response)
#
{
"物品": "卧室灯",
"品牌": "Lumina"
}
一次完成多项任务
# 中文
prompt = f"""
从评论文本中识别以下项目:
- 情绪(正面或负面)
- 审稿人是否表达了愤怒?(是或否)
- 评论者购买的物品
- 制造该物品的公司
评论用三个反引号分隔。将您的响应格式化为 JSON 对象,以 “Sentiment”、“Anger”、“Item” 和 “Brand” 作为键。
如果信息不存在,请使用 “未知” 作为值。
让你的回应尽可能简短。
将 Anger 值格式化为布尔值。
评论文本: ```{lamp_review_zh}```
"""
response = get_completion(prompt)
print(response)
#
{
"Sentiment": "正面",
"Anger": false,
"Item": "卧室灯",
"Brand": "Lumina"
}
推断主题(Zero-Shot 学习算法)
大型语言模型的一个很酷的应用是推断主题。给定一段长文本,这段文本是关于什么的?有什么话题?
为特定主题制作新闻提醒(当出现01这种主题、提醒)
假设我们有一个新闻网站或类似的东西,这是我们感兴趣的主题:NASA、地方政府、工程、员工满意度、联邦政府等。假设我们想弄清楚,针对一篇新闻文章,其中涵盖了哪些主题。可以使用这样的prompt:确定以下主题列表中的每个项目是否是以下文本中的主题。以 0 或 1 的形式给出答案列表。
# 中文
story_zh = """
在政府最近进行的一项调查中,要求公共部门的员工对他们所在部门的满意度进行评分。
调查结果显示,NASA 是最受欢迎的部门,满意度为 95%。
一位 NASA 员工 John Smith 对这一发现发表了评论,他表示:
“我对 NASA 排名第一并不感到惊讶。这是一个与了不起的人们和令人难以置信的机会共事的好地方。我为成为这样一个创新组织的一员感到自豪。”
NASA 的管理团队也对这一结果表示欢迎,主管 Tom Johnson 表示:
“我们很高兴听到我们的员工对 NASA 的工作感到满意。
我们拥有一支才华横溢、忠诚敬业的团队,他们为实现我们的目标不懈努力,看到他们的辛勤工作得到回报是太棒了。”
调查还显示,社会保障管理局的满意度最低,只有 45%的员工表示他们对工作满意。
政府承诺解决调查中员工提出的问题,并努力提高所有部门的工作满意度。
"""
prompt = f"""
判断主题列表中的每一项是否是给定文本中的一个话题,
以列表的形式给出答案,每个主题用 0 或 1。
主题列表:美国航空航天局、地方政府、工程、员工满意度、联邦政府
给定文本: ```{story_zh}```
"""
response = get_completion(prompt)
print(response)
topic_dict = {i.split(':')[0]: int(i.split(':')[1]) for i in response.split(sep='\n')}
if topic_dict['美国航空航天局'] == 1:
print("提醒: 关于美国航空航天局的新消息")
#
美国航空航天局:1
地方政府:1
工程:0
员工满意度:1
联邦政府:1
提醒: 关于美国航空航天局的新消息
六、文本转换
2 文本翻译
3 语气/风格调整
4 格式转换
ChatGPT非常擅长不同格式之间的转换,例如JSON到HTML、XML、Markdown等。在下述例子中,我们有一个包含餐厅员工姓名和电子邮件的列表的JSON,我们希望将其从JSON转换为HTML
5 拼写及语法纠正
6 一个综合样例:文本翻译+拼写纠正+风格调整+格式转换
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"""
针对以下三个反引号之间的英文评论文本,
首先进行拼写及语法纠错,
然后将其转化成中文,
再将其转化成优质淘宝评论的风格,从各种角度出发,分别说明产品的优点与缺点,并进行总结。
润色一下描述,使评论更具有吸引力。
输出结果格式为:
【优点】xxx
【缺点】xxx
【总结】xxx
注意,只需填写xxx部分,并分段输出。
将结果输出成Markdown格式。
```{text}```
"""
response = get_completion(prompt)
display(Markdown(response))
#
优点】
超级柔软可爱,女儿生日礼物非常受欢迎。
成人也喜欢熊猫,我也很喜欢它。
提前一天到货,让我有时间玩一下。
【缺点】
一只耳朵比另一只低,不对称。
价格有点贵,但尺寸有点小。
【总结】 这只熊猫非常可爱,柔软,是一个很好的礼物。但是,价格有点贵,尺寸有点小,而且有一些质量问题。如果你想要更大的熊猫,可能有更好的选择。
\ 第七章 文本扩展
扩展是将短文本,例如一组说明或主题列表,输入到大型语言模型中,让模型生成更长的文本,例如基于某个主题的电子邮件或论文。这样做有一些很好的用途,例如将大型语言模型用作头脑风暴的伙伴。但这种做法也存在一些问题,例如某人可能会使用它来生成大量垃圾邮件。因此,当你使用大型语言模型的这些功能时,请仅以负责任的方式和有益于人们的方式使用它们。
在本章中,你将学会如何基于 OpenAI API 生成适用于每个客户评价的客户服务电子邮件。我们还将使用模型的另一个输入参数称为温度,这种参数允许您在模型响应中变化探索的程度和多样性。
二、定制客户邮件
我们将根据客户评价和情感撰写自定义电子邮件响应。因此,我们将给定客户评价和情感,并生成自定义响应即使用 LLM 根据客户评价和评论情感生成定制电子邮件。
我们首先给出一个示例,包括一个评论及对应的情感
# 我们可以在推理那章学习到如何对一个评论判断其情感倾向
sentiment = "negative"
# 一个产品的评价
review = f"""
他们在11月份的季节性销售期间以约49美元的价格出售17件套装,折扣约为一半。\
但由于某些原因(可能是价格欺诈),到了12月第二周,同样的套装价格全都涨到了70美元到89美元不等。\
11件套装的价格也上涨了大约10美元左右。\
虽然外观看起来还可以,但基座上锁定刀片的部分看起来不如几年前的早期版本那么好。\
不过我打算非常温柔地使用它,例如,\
我会先在搅拌机中将像豆子、冰、米饭等硬物研磨,然后再制成所需的份量,\
切换到打蛋器制作更细的面粉,或者在制作冰沙时先使用交叉切割刀片,然后使用平面刀片制作更细/不粘的效果。\
制作冰沙时,特别提示:\
将水果和蔬菜切碎并冷冻(如果使用菠菜,则轻轻煮软菠菜,然后冷冻直到使用;\
如果制作果酱,则使用小到中号的食品处理器),这样可以避免在制作冰沙时添加太多冰块。\
大约一年后,电机发出奇怪的噪音,我打电话给客服,但保修已经过期了,所以我不得不再买一个。\
总的来说,这些产品的总体质量已经下降,因此它们依靠品牌认可和消费者忠诚度来维持销售。\
货物在两天内到达。
"""
prompt = f"""
你是一位客户服务的AI助手。
你的任务是给一位重要客户发送邮件回复。
根据客户通过“```”分隔的评价,生成回复以感谢客户的评价。提醒模型使用评价中的具体细节
用简明而专业的语气写信。
作为“AI客户代理”签署电子邮件。
客户评论:
```{review}```
评论情感:{sentiment}
"""
response = get_completion(prompt)
print(response)
#
尊敬的客户,
感谢您对我们产品的评价。我们非常重视您的反馈,对于您在使用过程中遇到的问题,我们深表歉意。
我们一直致力于提供高质量的产品和服务,但我们也意识到我们还有很多需要改进的地方。我们将会对您提到的问题进行深入的调查和改进,以确保我们的产品能够满足客户的需求和期望。
同时,我们也非常感谢您对我们的产品的使用技巧和建议。我们将会将这些细节纳入到我们的产品使用指南中,以帮助更多的客户更好地使用我们的产品。
如果您有任何其他问题或建议,请随时联系我们的客服团队。我们将会尽力为您提供最好的服务。
再次感谢您对我们的支持和反馈。
三、使用温度系数
接下来,我们将使用语言模型的一个称为“温度”的参数,它将允许我们改变模型响应的多样性。您可以将温度视为模型探索或随机性的程度。
例如,在一个特定的短语中,“我的最爱食品”最有可能的下一个词是“比萨”,其次最有可能的是“寿司”和“塔可”。因此,**在温度为零时,模型将总是选择最有可能的下一个词,而在较高的温度下,它还将选择其中一个不太可能的词,**在更高的温度下,它甚至可能选择塔可,而这种可能性仅为五分之一。您可以想象,随着模型继续生成更多单词的最终响应,“我的最爱食品是比萨”将会与第一个响应“我的最爱食品是塔可”产生差异。因此,随着模型的继续,这两个响应将变得越来越不同。
一般来说,在构建需要可预测响应的应用程序时,我建议使用温度为零。在所有课程中,我们一直设置温度为零,如果您正在尝试构建一个可靠和可预测的系统,我认为您应该选择这个温度。如果您尝试以更具创意的方式使用模型,可能需要更广泛地输出不同的结果,那么您可能需要使用更高的温度。
prompt = f"""
你是一名客户服务的AI助手。
你的任务是给一位重要的客户发送邮件回复。
根据通过“```”分隔的客户电子邮件生成回复,以感谢客户的评价。
如果情感是积极的或中性的,感谢他们的评价。
如果情感是消极的,道歉并建议他们联系客户服务。
请确保使用评论中的具体细节。
以简明和专业的语气写信。
以“AI客户代理”的名义签署电子邮件。
客户评价:```{review}```
评论情感:{sentiment}
"""
response = get_completion(prompt, temperature=0.7)
print(response)
八、聊天机器人
- 使用一个大型语言模型的一个令人兴奋的事情是,我们可以用它来构建一个定制的聊天机器人,只需要很少的工作量。在这一节中,我们将探索如何利用聊天格式(接口)与个性化或专门针对特定任务或行为的聊天机器人进行延伸对话。
像 ChatGPT 这样的聊天模型实际上是组装成以一系列消息作为输入,并返回一个模型生成的消息作为输出的。虽然聊天格式的设计旨在使这种多轮对话变得容易,但我们通过之前的学习可以知道,它对于没有任何对话的单轮任务也同样有用。
接下来,我们将**定义两个辅助函数。第一个是单轮的,我们将prompt放入看起来像是某种用户消息的东西中。另一个则传入一个消息列表。**这些消息可以来自不同的角色,我们会描述一下这些角色。
第一条消息是一个系统消息,它提供了一个总体的指示,然后在这个消息之后,我们有用户和助手之间的交替。如果你曾经使用过 ChatGPT 网页界面,那么你的消息是用户消息,而 ChatGPT 的消息是助手消息。系统消息则有助于设置助手的行为和角色,并作为对话的高级指示。你可以想象它在助手的耳边低语,引导它的回应,而用户不会注意到系统消息。
因此,作为用户,如果你曾经使用过 ChatGPT,你可能不知道 ChatGPT 的系统消息是什么,这是有意为之的。系统消息的好处是为开发者提供了一种方法,在不让请求本身成为对话的一部分的情况下,引导助手并指导其回应。
# 中文
messages = [
{'role':'system', 'content':'你是一个像莎士比亚一样说话的助手。'},
{'role':'user', 'content':'给我讲个笑话'},
{'role':'assistant', 'content':'鸡为什么过马路'},
{'role':'user', 'content':'我不知道'} ]
response = get_completion_from_messages(messages, temperature=1)
print(response)#因为它想到对面的鸡店去吃炸鸡!哈哈哈
对话保持上下文能力(gpt 3.5 turbo才有,需要把所有的记录都输给他,但是容易tokens上限)
- 每次与语言模型的交互都是一个独立的交互,这意味着我们必须提供所有相关的消息,以便模型在当前对话中进行引用。如果想让模型引用或 “记住” 对话的早期部分,则必须在模型的输入中提供早期的交流。我们将其称为上下文。让我们试试。
总结
恭喜你完成了这门短期课程。
总的来说,在这门课程中**,我们学习了关于prompt的两个关键原则:**
- 编写清晰具体的指令;
- 如果适当的话,给模型一些思考时间。
你还学习了迭代式prompt开发的方法,并了解了如何找到适合你应用程序的prompt的过程是非常关键的。
我们还介绍了许多大型语言模型的功能,包括摘要、推断、转换和扩展。你还学会了如何构建自定义聊天机器人。在这门短期课程中,你学到了很多,希望你喜欢这些学习材料。
我们希望你能想出一些应用程序的想法,并尝试自己构建它们。请尝试一下并让我们知道你的想法。你可以从一个非常小的项目开始,也许它具有一定的实用价值,也可能完全没有实用价值,只是一些有趣好玩儿的东西。请利用你第一个项目的学习经验来构建更好的第二个项目,甚至更好的第三个项目等。或者,如果你已经有一个更大的项目想法,那就去做吧。
大型语言模型非常强大,作为提醒,我们希望大家负责任地使用它们,请仅构建对他人有积极影响的东西。在这个时代,构建人工智能系统的人可以对他人产生巨大的影响。因此必须负责任地使用这些工具。
现在,基于大型语言模型构建应用程序是一个非常令人兴奋和不断发展的领域。现在你已经完成了这门课程,我们认为你现在拥有了丰富的知识,可以帮助你构建其他人今天不知道如何构建的东西。因此,我希望你也能帮助我们传播并鼓励其他人也参加这门课程。
最后,希望你在完成这门课程时感到愉快,感谢你完成了这门课程。我们期待听到你构建的惊人之作。