Notes from DeepLearning.AI and OpenAI’s ChatGPT Prompt Engineering for Developers course

Anurag Chatterjee
9 min readMay 1, 2023

--

The recent free course published by DeepLearning.AI and OpenAI on prompt engineering for developers is a very good course as it allows developers to access the OpenAI API by providing their own API key within a notebook environment. The course content is easy to follow and is a good starting point for most developers wanting to get their hands dirty on prompt engineering.

Here are some of my notes from the course content.

Introduction

Source: Course content

… for most practical applications today, we would recommend most people instead focus on instruction tuned LLMs which are easier to use and also because of the work of OpenAI and other LLM companies becoming safer and more aligned. So this course will focus on best practices for instruction tuned LLMs Which is what we recommend you use for most of your applications.

So when you use an instruction tuned LLMs, think of giving instructions to another person. Say someone that’s smart but doesn’t know the specifics of your task. So when an LLMs doesn’t work, sometimes it’s because the instructions weren’t clear enough.

Principles of prompting

  1. Write clear and specific instructions
  • Clear =/= short

Avoid prompt injection by providing delimiters to to clearly indicate distinct parts of the input. Delimiters can be anything like ```, “””, <>, etc.

  • Ask for structured output like HTML and JSON
  • Check whether conditions are satisfied. Check assumptions required to do the task.
  • Few-shot prompting. Give successful examples of completing tasks. Then ask model to perform the task.

2. Give the model time to “think”

Allow the model to spend more computational effort on a task. Think of the scenario where a person is asked to solve a complex maths problem without providing them enough time.

  • Specify the steps required to complete a task. Also ask for the output in a specific format.
  • Instruct the model to work out its own solution before rushing to a conclusion

Model limitations

Model does not know the boundary of its knowledge well and might hallucinate answers where the statements that sound plausible but are not true.

One way to reduce hallucinations is by first finding the relevant information and quotes from the text then answer the questions based on the relevant information by having a way to trace the answer back to the source document.

Iterative prompt development

Most machine learning models do not work the first time. What matters in the process of getting the prompts to work for our application. A framework like the below that works for machine learning development can be also followed for prompt development.

Source: Course content

Some common issues and their solutions might be as below.

  • The generated text is too long. Limit the number of words/sentences/characters.
  • Text focuses on the wrong details. Ask it to focus on the aspects that are relevant to the intended audience.
  • Description needs a table of dimensions. Ask it to extract information and organise it in a table.

Iterative process summary

  • Try something
  • Analyse where the result does not give what you want
  • Clarify instructions, give more time to think
  • Refine prompts with a batch of examples to get the average or worst case performance in the case of mature applications to get incremental improvement

Summarising use cases

Programatically summarise articles.

  • Summarise with a word/sentence/character limit
  • Summarise with a focus on the audience for whom the summary is relevant
  • Try “extract” instead of “summarise” in case the summary includes topics that are not related to the topic of focus.

Inferring use cases

Rather than having separate models for different NLP tasks use LLMs to solve them.

Sample code inspired by the course content to extract whether a customer is delighted and then extract why they are delighted based on the text

import openai
import os

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

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

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

def get_prompt(review):
return f"""
Format the output as a JSON with 2 keys: delighted, extract.\
delighted is a boolean value indicating whether the customer is delighted \
based on the review.\
extract is the extract of the review that shows why the customer expresses \
delight.
Just keep the text from within the review and do not add anything on top of \
that.\
In case the delight value is false then keep the extract as empty.

Format the value of delighted as a boolean and keep the extract within \
1 to 2 sentences.
The review is delimited with triple backticks. \


Review text: '''{review}'''
"""


happy_review = """
Needed a nice lamp for my bedroom, and this one had \
additional storage and not too high of a price point. \
Got it fast. The string to our 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. I had a missing part, so I contacted their \
support and they very quickly got me the missing piece! \
Lumina seems to me to be a great company that cares \
about their customers and products!!
"""

response = get_completion(get_prompt(happy_review))
print(response)

# Output
"""
{
"delighted": true,
"extract": "Lumina seems to me to be a great company that cares about their customers and products!"
}
"""

angry_review = """
I just needed a working lamp for working at night.
I don't know where to start about my journey of immense pain \
with Baca lighting.
From the horrible products to the pathetic customer service. \
Considering I paid $30 for a lamp it is my mistake to have trusted \
their marketing material and should have just gone to an Ikea and \
got it myself.
I would really ask others to avoid Baca at all costs and would ask \
them to provide me a refund again and stop making promises to their customers.
"""

response = get_completion(get_prompt(angry_review))
print(response)
# Output
"""
{
"delighted": false,
"extract": ""
}
"""

We can also write a single prompt to identify the sentiment expressed, emotion expressed, item purchased and company that made the item from a e-commerce review.

prompt = f"""
Identify the following items from the review text:
- Sentiment (positive or negative)
- Is the reviewer expressing anger? (true or false)
- Item purchased by reviewer
- Company that made the item

The review is delimited with triple backticks. \
Format your response as a JSON object with \
"Sentiment", "Anger", "Item" and "Brand" as the keys.
If the information isn't present, use "unknown" \
as the value.
Make your response as short as possible.
Format the Anger value as a boolean.

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

We can check whether a news article talks about certain topics using a LLM as a zero-shot learning algorithm since no training data was provided.

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 a JSON where they key is the name of \
the topic from the list and the value is a boolean indicating \
whether the news article is about that topic.\

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

Text sample: '''{story}'''
"""
response = get_completion(prompt)
print(response)

# Output
"""
{
"nasa": true,
"local government": false,
"engineering": false,
"employee satisfaction": true,
"federal government": true
}
"""

Transforming use cases

Use LLMs for text transformation tasks such as language translation, spelling and grammar checking, tone adjustment and format conversion.

Expanding use cases

Generate a larger piece of text from a small piece of text. Can be used for both good and bad. E.g. generate an email from a piece of text. Here we can modify the “temperature” to adjust the degree of randomness of the model. At higher temperatures the assistant is more distractible but maybe more creative.

prompt = f"""
You are a customer service AI assistant.
Your task is to send an email reply 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, temperature=0.7)
print(response)
Source: course content

Building Chatbots using LLMs

The Chat format consists of 3 roles. The system role which is like an initial setup or whisper into the ears of the assistant. The user is the person interacting with the assistant. The user is not aware of the system message and it is used to guide the assistant such that the assistant has the desired behaviour.

Source: course content
# Here messages is a list of messages following the above format
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"]

messages = [
{'role':'system', 'content':'You are an assistant that speaks like Shakespeare.'},
{'role':'user', 'content':'tell me a joke'},
{'role':'assistant', 'content':'Why did the chicken cross the road'},
{'role':'user', 'content':'I don\'t know'}
]

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

# Output
"""
{
"content": "To get to the other side, good sir.",
"role": "assistant"
}
To get to the other side, good sir.
"""

Each conversation with a language model is a standalone interaction which means we must provide all relevant messages for the model to draw from in the current conversation. For the model to recollect from earlier information, we must provide “context”.

Pizza shop “OrderBot”

A powerful Chatbot in action that can take orders for a pizza shop.

import panel as pn  # GUI
pn.extension()

panels = [] # collect display

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)


context = [ {'role':'system', 'content':"""
You are OrderBot, an automated service to collect orders for a pizza restaurant. \
You first greet the customer, then collects the order, \
and then asks if it's a pickup or delivery. \
You wait to collect the entire order, then summarize it and check for a final \
time if the customer wants to add anything else. \
If it's a delivery, you ask for an address. \
Finally you collect the payment.\
Make sure to clarify all options, extras and sizes to uniquely \
identify the item from the menu.\
You respond in a short, very conversational friendly style. \
The menu includes \
pepperoni pizza 12.95, 10.00, 7.00 \
cheese pizza 10.95, 9.25, 6.50 \
eggplant pizza 11.95, 9.75, 6.75 \
fries 4.50, 3.50 \
greek salad 7.25 \
Toppings: \
extra cheese 2.00, \
mushrooms 1.50 \
sausage 3.00 \
canadian bacon 3.50 \
AI sauce 1.50 \
peppers 1.00 \
Drinks: \
coke 3.00, 2.00, 1.00 \
sprite 3.00, 2.00, 1.00 \
bottled water 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
messages =  context.copy()
messages.append(
{'role':'system', 'content':'create a json summary of the previous food order. Itemize the price for each item\
The fields should be 1) pizza, include size 2) list of toppings 3) list of drinks, include size 4) list of sides include size 5)total price '},
)
#The fields should be 1) pizza, price 2) list of toppings 3) list of drinks, include size include price 4) list of sides include size include price, 5)total price '},

# Keep the temperature lower here to have higher predictability at this stage
response = get_completion_from_messages(messages, temperature=0)
print(response)
Pizza shop OrderBot in action!

Conclusion

The course covers the basics of prompt engineering and how to get started building applications powered by LLMs very well. It also reminds us that LLMs are very powerful and should be used responsibly to build things that will have a positive impact.

I will highly recommend to take the course and try out the notebooks in the interactive sessions!

--

--

Anurag Chatterjee
Anurag Chatterjee

Written by Anurag Chatterjee

I am an experienced professional who likes to build solutions to real-world problems using innovative technologies and then share my learnings with everyone.

Responses (1)