前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《ChatGPT Prompt Engineering for Developers》学习笔记

《ChatGPT Prompt Engineering for Developers》学习笔记

作者头像
口仆
发布2023-08-28 17:44:27
4740
发布2023-08-28 17:44:27
举报

本笔记是 deeplearning.ai 最近推出的短期课程《ChatGPT Prompt Engineering for Developers》的学习总结。

1 引言

总的来说,当前有两类大语言模型(LLM):「基础 LLM」「指令微调 LLM」

基础 LLM 基于大量文本数据训练而成,核心思想为预测一句话的下一个单词(即词语接龙)。基于语料的限制,有时会返回不符合预期的结果(如上图所示)。

指令微调 LLM 基于人类设定的指令(格式化的输入与输出)对基础 LLM 进行微调训练,使其能够返回更加有用且无害的结果。当前还会使用 RLHF(基于人类反馈的强化学习)来进一步提升模型效果,此处不做展开。

本课程旨在介绍如何基于 ChatGPT 官方 API 编写 Prompt(即指令),来驱动模型更好地完成各种各样的任务。

2 指南

总的来说,Prompt 的编写有两大原则:

  • 原则一:编写明确而清晰的指令
  • 原则二:给模型足够的时间思考

下面将结合具体的代码来对这两条原则进行详细说明。在这之前,首先给出本课程中所使用的模型调用函数的代码(略去了从环境变量获取 key 的部分):

代码语言:javascript
复制
import openai

openai.api_key = 'xxx'

def get_completion(prompt, model="gpt-3.5-turbo"):
    messages = [{"role": "user", "content": prompt}]
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=0, # this is the degree of randomness of the model's output
    )
    return response.choices[0].message["content"]

2.1 编写明确而清晰的指令

对于这一原则,课程中给出了四条具体的策略来实践该原则:

「策略 1:使用分隔符,避免 Prompt 入侵」

我们可以通过分隔符来标识仅需要 ChatGPT 进行阅读的文本,防止其被解析为指令(即 Prompt 入侵)。常用的分隔符包括:三个引号、三个反引号、三个破折号等,ChatGPT 对于这些分隔符的区分并不是很敏感(例如在 Prompt 中说明使用反引号分隔,但实际用的是引号,并不会影响模型输出)。

具体的代码示例如下(Prompt 不作翻译,可以自行替换为中文):

代码语言:javascript
复制
text = f"""
内容省略(可添加任意内容)
"""
prompt = f"""
Summarize the text delimited by triple backticks \
into a single sentence.
```{text}```
"""

response = get_completion(prompt)
print(response)

「策略 2:要求模型提供结构化输出」

对于开发者来说,要求模型提供诸如 HTML 或 JSON 的格式化输出,有利于构建更加健壮的应用。具体的代码示例如下:

代码语言:javascript
复制
prompt = f"""
Generate a list of three made-up book titles along \
with their authors and genres. 
Provide them in JSON format with the following keys: 
book_id, title, author, genre.
"""

response = get_completion(prompt)
print(response)

「策略 3:让模型进行条件判断」

对于较复杂的 Prompt,模型生成结果的时间可能会较长,同时可能浪费大量的 token。为了规避不必要的 API 调用消耗,我们可以在 Prompt 中包含一定的条件判断逻辑,来帮助模型在不满足条件时提前终止运算,直接返回结果。具体的代码示例如下:

代码语言:javascript
复制
text_1 = f"""
如何把大象放进冰箱?\
首先打开冰箱的门,\
然后把大象放进去,\
最后关上冰箱门。
"""
prompt = f"""
You will be provided with text delimited by triple quotes. 
If it contains a sequence of instructions, \
re-write those instructions in the following format:

Step 1 - ...
Step 2 - …
…
Step N - …

If the text does not contain a sequence of instructions, \
then simply write \"No steps provided.\"

```{text_1}```
"""

response = get_completion(prompt)
print("Completion for Text 1:")
print(response)

「策略 4:少量(Few-shot)训练提示」

有时候,对于某些任务,我们需要为模型提供少量完成该任务的成功示例,来帮助模型更好地理解并完成该任务。具体的代码示例如下:

代码语言:javascript
复制
prompt = f"""
Your task is to answer in a consistent style.

<child>: Teach me about patience.

<grandparent>: The river that carves the deepest \
valley flows from a modest spring; the \
grandest symphony originates from a single note; \
the most intricate tapestry begins with a solitary thread.

<child>: Teach me about love.
"""

response = get_completion(prompt)
print(response)

2.2 给模型足够的时间思考

这一原则的意义是:对于较为复杂的任务,如果不进行拆解,要求模型在短时间内通过少量词语进行回答,则可能导致结果的不准确。课程中给出了两条具体的策略来实践该原则:

「策略 1:指定完成任务所需的步骤」

该策略即通过 Prompt 拆解任务步骤降低复杂度,以帮助模型更好地完成任务。具体的代码示例如下(省略了 text 的内容,指定输出格式):

代码语言:javascript
复制
prompt = f"""
Your task is to perform the following actions: 
1 - Summarize the following text delimited by 
  <> with 1 sentence.
2 - Translate the summary into Chinese.
3 - List each name in the Chinese summary.
4 - Output a json object that contains the 
  following keys: chinese_summary, num_names.

Use the following format:
Text: <text to summarize>
Summary: <summary>
Translation: <summary translation>
Names: <list of names in Chinese summary>
Output JSON: <json with summary and num_names>

Text: <{text}>
"""

response = get_completion(prompt)
print("\nCompletion for prompt 2:")
print(response)

「策略 2:提示模型在匆忙得出结论之前思考自己的解决方案」

通过提示模型在回答之前思考自己的解决方案,有时可以得到更好的结果。课程中给出了一个解数学题的案例,如果没有提示模型首先尝试解题,则模型会判断学生的解法是正确的,但是如果提示模型首先自己推导解题过程再进行判断,则其会得出学生的解法是错误的结论。具体的代码如下(比较长):

代码语言:javascript
复制
prompt = f"""
Your task is to determine if the student's solution \
is correct or not.
To solve the problem do the following:
- First, work out your own solution to the problem. 
- Then compare your solution to the student's solution \ 
and evaluate if the student's solution is correct or not. 
Don't decide if the student's solution is correct until 
you have done the problem yourself.

Use the following format:
Question:
'''
question here
'''
Student's solution:
'''
student's solution here
'''
Actual solution:
'''
steps to work out the solution and your solution here
'''
Is the student's solution the same as actual solution \
just calculated:
'''
yes or no
'''
Student grade:
'''
correct or incorrect
'''

Question:
'''
I'm building a solar power installation and I need help \
working out the financials. 
- Land costs $100 / square foot
- I can buy solar panels for $250 / square foot
- I negotiated a contract for maintenance that will cost \
me a flat $100k per year, and an additional $10 / square \
foot
What is the total cost for the first year of operations \
as a function of the number of square feet.
'''
Student's solution:
'''
Let x be the size of the installation in square feet.
Costs:
1. Land cost: 100x
2. Solar panel cost: 250x
3. Maintenance cost: 100,000 + 100x
Total cost: 100x + 250x + 100,000 + 100x = 450x + 100,000
'''
Actual solution:
"""

response = get_completion(prompt)
print(response)

该代码的输出结果为:

代码语言:javascript
复制
Let x be the size of the installation in square feet.

Costs:
1. Land cost: 100x
2. Solar panel cost: 250x
3. Maintenance cost: 100,000 + 10x

Total cost: 100x + 250x + 100,000 + 10x = 360x + 100,000

Is the student's solution the same as actual solution just calculated:
No

Student grade:
Incorrect

PS:课程中在多行文本中使用反斜杠来代替换行符,保证原始文本中没有换行,但是其在反斜杠添加了空格,会导致换行依旧存在,需要去除空格(实际对模型来说并没有影响,只是影响展示)

此外,本节还提到了模型的一种局限性:「幻觉」(Hallucination)。幻觉指 LLM 模型对于知识的边界性有时把握不足,可能会出现一本正经地胡说八道的情况。为了减少幻觉,我们可以提示模型首先收集相关信息,基于这些信息来回答问题。

3 迭代

编写 Prompt 是一个持续迭代的过程,通过对模型返回结果的分析,不断地修改 Prompt,我们可以最终得到较为满意的输出。同时,对 Prompt 的修改需要遵循上一章节提到的原则。

4 摘要

我们可以使用 ChatGPT 来辅助生成对长文本的总结,以帮助我们在短时间内获取更多的信息。具体来说,通过模型执行摘要任务有以下两个注意点:

4.1 细化需求

通过 Prompt 来细化摘要的具体目的与关注点,可以获得更加精准的摘要。具体的代码示例如下:

代码语言:javascript
复制
prompt = f"""
Your task is to generate a short summary of a product \
review from an ecommerce site to give feedback to the \
Shipping deparmtment. 

Summarize the review below in Chinese, delimited by triple 
backticks, in at most 30 words, and focusing on any aspects \
that mention shipping and delivery of the product. 

Review: '''{prod_review}'''
"""

response = get_completion(prompt)
print(response)

4.2 关键字差异

如果在 Prompt 中使用关键字「总结」(summarize),虽然模型会基于 Prompt 返回对应的总结,但其中通常会包含一些其他的信息;而如果使用关键字「提取」(extract),则模型会专注于提取在 prompt 中所提示的范围,返回更加精准的摘要。具体的代码示例如下:

代码语言:javascript
复制
prompt = f"""
Your task is to extract relevant information from \
a product review from an ecommerce site to give \
feedback to the Shipping department. The extracted result \
should be translated into Chinese.

From the review below delimited by triple quotes \
extract the information relevant to shipping and \
delivery. Limit to 30 words. 

Review: '''{prod_review}'''
"""

response = get_completion(prompt)
print(response)

5 推理

ChatGPT 非常适合用来对文本进行推理,对于传统 NLP 方法来说需要大量步骤(例如数据收集、打标签、训练、测试等)才能完成的任务,ChatGPT 可以通过 Prompt 轻松完成。本节将列举两种主要的推理任务:「情感分析」「信息提取」

5.1 情感分析

我们可以通过多种方式来指导 ChatGPT 分析一段文字中的情感,例如:

「识别积极/消极」

代码语言:javascript
复制
prompt = f"""
What is the sentiment of the following product review, 
which is delimited with triple backticks?

Give your answer as a single word, either "positive" \
or "negative".

Review text: '''{lamp_review}'''
"""

response = get_completion(prompt)
print(response)

「识别多种情感」

代码语言:javascript
复制
prompt = f"""
Identify a list of emotions that the writer of the \
following review is expressing. Include no more than \
five items in the list. Format your answer as a list of \
lower-case words separated by commas.

Review text: '''{lamp_review}'''
"""

response = get_completion(prompt)
print(response)

「识别特定情感」

代码语言:javascript
复制
prompt = f"""
Is the writer of the following review expressing anger?\
The review is delimited with triple backticks. \
Give your answer as either yes or no.

Review text: '''{lamp_review}'''
"""
response = get_completion(prompt)
print(response)

5.2 信息提取

我们可以通过不同的 Prompt 指导 ChatGPT 提取不同维度的信息,例如:

「提取标签(实体)信息」

代码语言:javascript
复制
prompt = f"""
Identify the following items from the review text: 
- Item purchased by reviewer
- Company that made the item

The review is delimited with triple backticks. \
Format your response as a JSON object with \
"Item" and "Brand" as the keys. 
If the information isn't present, use "unknown" \
as the value.
Make your response as short as possible.
  
Review text: '''{lamp_review}'''
"""

response = get_completion(prompt)
print(response)

「提取主题信息」

对于主题提取,ChatGPT 可以直接提取多个主题:

代码语言:javascript
复制
prompt = f"""
Determine five topics that are being discussed in the \
following text, which is delimited by triple backticks.

Make each item one or two words long. 

Format your response as a list of items separated by commas.

Text sample: '''{story}'''
"""

response = get_completion(prompt)
print(response)

也可以给定主题列表,判断输入文本中包含了哪些特定主题(该方式也被称为 Zero Shot 训练):

代码语言:javascript
复制
topic_list = [
    "nasa", "local government", "engineering", 
    "employee satisfaction", "federal government"
]

prompt = f"""
Determine whether each item in the following list of \
topics is a topic in the text below, which
is delimited with triple backticks.

Give your answer as list with 0 or 1 for each topic.\

List of topics: {", ".join(topic_list)}

Text sample: '''{story}'''
"""

response = get_completion(prompt)
print(response)

6 转换

本节将介绍如何使用 ChatGPT 来进行文本转换任务,例如翻译、拼写与语法检查、语气调整、格式转换等,主要以代码示例为主。

6.1 翻译

ChatGPT 基于多种语言进行训练,具有较强的翻译能力,例如:

「翻译多种语言」

代码语言:javascript
复制
prompt = f"""
Translate the following  text to French and Spanish
and English pirate: \
'''I want to order a basketball'''
"""

response = get_completion(prompt)
print(response)

「识别当前语言」

代码语言:javascript
复制
prompt = f"""
Tell me which language this is: 
'''Combien coûte le lampadaire?'''
"""

response = get_completion(prompt)
print(response)

6.2 语气调整

基于不同的读者(观众),输出的文字可能有较大差异。ChatGPT 可以基于 Prompt 产生不同的语调,例如:

代码语言:javascript
复制
prompt = f"""
Translate the following from slang to a business letter: 
'Dude, This is Joe, check out this spec on this standing lamp.'
"""

response = get_completion(prompt)
print(response)

6.3 格式转换

ChatGPT 支持多种格式的转换,Prompt 应该清楚地描述输入与输出格式,例如:

代码语言:javascript
复制
data_json = { "resturant employees" :[ 
    {"name":"Shyam", "email":"shyamjaiswal@gmail.com"},
    {"name":"Bob", "email":"bob32@gmail.com"},
    {"name":"Jai", "email":"jai87@gmail.com"}
]}

prompt = f"""
Translate the following python dictionary from JSON to an HTML \
table with column headers and title: {data_json}
"""

response = get_completion(prompt)
print(response)

6.4 文字检查

ChatGPT 还可以用来检查拼写和语法,帮助你改写文字(写文章必备),例如:

「普通校对与改写」

代码语言:javascript
复制
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"proofread and correct this review: ```{text}```"
response = get_completion(prompt)
print(response)

课程中还使用了 redlines 包来查看改写前后的差异:

代码语言:javascript
复制
from redlines import Redlines

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

「基于特定风格的校对与改写」

代码语言:javascript
复制
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))

7 扩展

ChatGPT 可以用来对文字进行扩展(将短文本变成长文本),本节将给出一个使用 ChatGPT 自动回复 email 的示例,并介绍对扩展来说很重要的一个 API 参数:「温度」(Temperature)

7.1 扩展示例

下面的例子给出了基于用户的评价生成自动回复 email 的 Prompt:

代码语言:javascript
复制
prompt = f"""
You are a customer service AI assistant.
Your task is to send an email reply in Chinese to a valued customer.
Given the customer email delimited by ```, \
Generate a reply to thank the customer for their review.
If the sentiment is positive or neutral, thank them for \
their review.
If the sentiment is negative, apologize and suggest that \
they can reach out to customer service. 
Make sure to use specific details from the review.
Write in a concise and professional tone.
Sign the email as `AI customer agent`.
Customer review: '''{review}'''
Review sentiment: {sentiment}
"""

response = get_completion(prompt)
print(response)

实际上情感可以由 ChatGPT 自动提取,无需指定。此外,为了防止不好的用途,课程中建议明确表明上述内容是由 AI 生成的。

7.2 温度参数

温度参数可以用来控制模型输出的随机性,具体规则如下:

  • 温度越低,模型的输出越稳定(更保守)
  • 温度越高,模型的输出越随机(更具有创造力)

在不同的场景下,通过对温度参数的控制,可以得到更加合适的输出(温度参数通常设置在 0-2 之间)。

8 聊天机器人

本节将介绍如何使用 ChatGPT 构建一个聊天机器人,其关键在于如何为模型提供「上下文信息」。对模型来说,每一轮对话都是独立的,我们需要在接口中传入之前对话的历史信息。对于接口来说,总共存在三种角色,分别是:

  • System 角色:用于设定模型的身份,约束模型行为,放置于一轮对话的最开头
  • User 角色:当前对话的用户,提供输入信息
  • Assistant 角色:生成回复的模型

历史信息需要明确地表明每一条消息所对应的角色,如下图所示:

为了实现历史信息的传入,需要修改之前的模型调用函数:

代码语言:javascript
复制
def get_completion_from_messages(messages, model="gpt-3.5-turbo", temperature=0):
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=temperature, # this is the degree of randomness of the model's output
    )
#     print(str(response.choices[0].message))
    return response.choices[0].message["content"]

基于上述函数,可以手动实现历史消息的传入:

代码语言:javascript
复制
messages =  [  
{'role':'system', 'content':'You are friendly chatbot.'},
{'role':'user', 'content':'Hi, my name is Isa'},
{'role':'assistant', 'content': "Hi Isa! It's nice to meet you. \
Is there anything I can help you with today?"},
{'role':'user', 'content':'Yes, you can remind me, What is my name?'}  ]
response = get_completion_from_messages(messages, temperature=1)
print(response)

为了实现一个聊天机器人,我们需要支持对于对话历史消息的自动收集,课程中基于 panel 包实现了一个点菜机器人,通过精心设置的 System Prompt 实现了自动化点菜与价格计算,具体的代码此处不作展开。

这里给出一个更加通用的聊天机器人 demo,基于 Streamlit 实现,可以自由设置 System Prompt,同时对于对话历史消息进行了自动收集与上传:

代码语言:javascript
复制
import streamlit as st
import openai

st.markdown('# ChatGPT demo')


def init_session():
    if 'round' not in st.session_state:
        print("重置轮数")
        st.session_state['round'] = 1

    if 'question' not in st.session_state:
        print("重置问题记录")
        st.session_state['question'] = []

    if 'answer' not in st.session_state:
        print("重置回答记录")
        st.session_state['answer'] = []


init_session()
code_input = st.text_input('请输入使用密钥')
system_input = st.text_input('请设定 ChatGPT 角色(用于生成符合角色的回答)', "你是一个有用的AI助手")
latest_input = st.text_area('请输入内容')

openai.api_key = code_input

if st.button('发送'):
    st.session_state['question'].append(latest_input)

    message_list = []
    dic_q_item = {"role": "system", "content": system_input}
    message_list.append(dic_q_item)
    for n in range(st.session_state['round']):
        # 第 n 轮,有 n 个问题,n - 1 个答案待组装
        if n - 1 >= 0:
            dic_a_item = {"role": "assistant", "content": st.session_state['answer'][n - 1]}
            message_list.append(dic_a_item)
        dic_q_item = {"role": "user", "content": st.session_state['question'][n]}
        message_list.append(dic_q_item)

    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        max_tokens=1000,
        user="wzy",
        messages=message_list,
        temperature=0
    )

    answer = completion.choices[0].message['content']
    st.session_state['answer'].append(answer)

    col1, col2 = st.columns(2)

    for i in range(st.session_state['round']):
        with col1:
            st.text('User')
            st.write('  ' + st.session_state['question'][i])
            st.text(' ')
            st.write(' ')
        with col2:
            st.text(' ')
            st.write(' ')
            st.text('ChatGPT')
            st.write('  ' + st.session_state['answer'][i])

    st.session_state['round'] += 1

if st.button('清空对话记录'):
    st.session_state.clear()

9 总结

以下为本课程核心知识点的思维导图总结:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 口仆 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 引言
  • 2 指南
    • 2.1 编写明确而清晰的指令
      • 2.2 给模型足够的时间思考
      • 3 迭代
      • 4 摘要
        • 4.1 细化需求
          • 4.2 关键字差异
          • 5 推理
            • 5.1 情感分析
              • 5.2 信息提取
              • 6 转换
                • 6.1 翻译
                  • 6.2 语气调整
                    • 6.3 格式转换
                      • 6.4 文字检查
                      • 7 扩展
                        • 7.1 扩展示例
                          • 7.2 温度参数
                          • 8 聊天机器人
                          • 9 总结
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档