Using ChatGPT to Create a Customised Chatbot

In this project we will use ChatGPT to utilize its chat format to have extended conversations with chatbots personalized or specialized for specific tasks or behaviors.
natural-language-processing
deep-learning
openai
prompt-engineering
Author

Pranath Fernando

Published

May 7, 2023

1 Introduction

Large language models such as ChatGPT can generate text responses based on a given prompt or input. Writing prompts allow users to guide the language model’s output by providing a specific context or topic for the response. This feature has many practical applications, such as generating creative writing prompts, assisting in content creation, and even aiding in customer service chatbots.

For example, a writing prompt such as “Write a short story about a time traveler who goes back to the medieval period” could lead the language model to generate a variety of unique and creative responses. Additionally, prompts can be used to generate more specific and relevant responses for tasks such as language translation or summarization. In these cases, the prompt would provide information about the desired output, such as the language to be translated or the key points to be included in the summary. Overall, prompts provide a way to harness the power of large language models for a wide range of practical applications.

However, creating effective prompts for large language models remains a significant challenge, as even prompts that seem similar can produce vastly different outputs.

In my previous article, we looked at how to use ChatGPT to generate customer service emails that are tailored to each customer’s review.

In this article, we will look at how to use ChatGPT to utilize its chat format to have extended conversations with chatbots personalized or specialized for specific tasks or behaviors.

2 Setup

2.1 Load the API key and relevant Python libaries.

First we need to load certain python libs and connect the OpenAi api.

The OpenAi api library needs to be configured with an account’s secret key, which is available on the website.

You can either set it as the OPENAI_API_KEY environment variable before using the library: !export OPENAI_API_KEY='sk-...'

Or, set openai.api_key to its value:

import openai
openai.api_key = "sk-..."
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')

2.2 Helper functions

We will use OpenAI’s gpt-3.5-turbo model and the chat completions endpoint.

We’re going to define two helper functions. If you kind of look at get_completion(), though, you’ll see that we give a prompt, but then kind of inside the function, what we’re actually doing is inserting this prompt into what appears to be some sort of user message. And the reason for this is that the ChatGPT model is a chat model, trained to accept a stream of messages as input and output a message that was generated by the model. The assistant message is the output, and the user message serves as kind of the input.

Because of this, we’re actually going to use the second helper function and pass in a list of messages rather than kind of giving it one prompt and obtaining one completion. I’ll go over those because these messages might come in a variety of various forms from those jobs. So, for illustration’s sake, below is a sample message list.

As a result, the initial message is a system message that serves as a general instruction. Following this message, the user and the assistant take turns speaking. And something like this would keep happening. Your messages are the user messages if you’ve ever used ChatGPT’s web interface, and ChatGPT’s messages are the assistant messages.

Therefore, the system message serves as a form of high-level directive for the dialogue and helps to establish the assistant’s behaviours and identity. So, without the user being aware of the system message, it can be compared to whispering in the assistant’s ear and kind of directing its responses.

In other words, if you’ve ever used ChatGPT, it’s likely that you have no idea what is contained in the system message. The system message has the advantage of giving you, the developer, a means to frame the dialogue without including the request itself in it. Therefore, you can sort of direct the assistant, whisper in its ear, and direct its responses without the user being aware of it.

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

3 Customised Chatbots

One of the fascinating aspects of a large language model is that it can be used to quickly and easily create a personalised chatbot. You can hold a conversation using a large language model through ChatGPT’s online interface, which is designed to be conversational. But one of the great things is that you can create a custom chatbot that can serve as an AI order taker for a restaurant or a large language model to play the part of an AI customer support agent.

4 Message Completion

So, we will make use of our second helper function to extract the completion from the messages. A bigger temperature is also being used, to allow more variety (and so less consistancy) in the models responses.

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

To get to the other side, of course!

messages =  [  
{'role':'system', 'content':'You are friendly chatbot.'},    
{'role':'user', 'content':'Hi, my name is Isa'}  ]
response = get_completion_from_messages(messages, temperature=1)
print(response)
Output

Hello Isa! It’s nice to meet you. How are you doing today?

messages =  [  
{'role':'system', 'content':'You are friendly chatbot.'},    
{'role':'user', 'content':'Yes,  can you remind me, What is my name?'}  ]
response = get_completion_from_messages(messages, temperature=1)
print(response)
Output

I’m sorry, but as a chatbot, I don’t have access to your name. Could you please tell me your name so I can address you properly?

So we can see it does’nt know the name.

This highlights that each discussion you have with a language model is a separate interaction, and you must supply the model with all pertinent messages for it to use in the conversation at hand. The prior exchanges must be included in the model’s input if you want the model to reference or, quote unquote, remember earlier sections of a dialogue. This will be referred to as the context from here on.

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)
Output

Your name is Isa!

5 OrderBot

We can automate the collection of user prompts and assistant responses to build a OrderBot. The OrderBot will take orders at a pizza restaurant.

We’re going to automate the gathering of user requests and assistant responses in order to develop this chatbot, which we’re going to call orderbot. First, we’re going to define this helper function, which will collect our user messages so we can avoid typing them in by hand. It will gather prompts from a user interface that will be built below, append them to a list called context, and then call the model each time with that context.

Once the model answer has been included, the context will then also include the model message, the user message, and so forth. As a result, the context will continue to expand.

The model will then have the data it requires to decide what to do next. The context is shown here, and it contains the system message that contains the menu. Take note that we’ll use the same context each time we use the language model, and that the context is growing over time. Now we’ll set up and operate this type of UI to display the order bot.

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)
import panel as pn  # GUI
pn.extension()

panels = [] # collect display 

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

This brings up an interface to enable us to have an interactive conversation which will look like this.

I’m going to say hi and request a pizza in the chat. And the assistant responds, “Great, what pizza would you like to order?” Pizza with pepperoni, cheese and eggplant is on the menu. What is their cost? We have the prices, great, good. A medium eggplant pizza is what I’m feeling right now.

So as you can see, we could kind of continue this dialogue. Let’s take a closer look at what we’ve written in the system message. You are an automated system that takes orders for a pizza business, called an order bot. After introducing yourself and taking the customer’s order, you ask whether the order is for pickup or delivery.

After collecting the complete order, you should summarise it and ask the customer one last time if they would like to add anything else. You can request an address if it’s a delivery. You then receive the payout. For the purpose of clearly identifying each item from the menu, be sure to specify all extras, alternatives, and sizes. You make a quick, polite, and conversational response. The menu is comprised of, and then this is the menu.

The assistant then asks if we want any toppings, which we had sort of requested in an assistant message. Therefore, I believe we don’t need any further toppings. Things, for sure. Do you have any other items we could order? Let’s go get some water, hmm. in fact, fries. Large or small? And this is fantastic because we kind of asked the assistance to clarify extras and sides in the system message.

So now that we have the discussion, we can ask the model to generate a JSON summary that we can send to the order system. So we are now appending another system message, which is an instruction, and we are saying create a JSON summary of the previous food order, itemise the price for each item, the fields should be one pizza, include side, two lists of toppings, three lists of drinks, four lists of sides, and finally the total price. A user message may alternatively be used in this place; a system message is not required.

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 '},    

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

Sure, here’s a JSON summary of the order:

{
  "pizza": [
    {
      "type": "pepperoni",
      "size": "large",
      "price": 12.95
    },
    {
      "type": "cheese",
      "size": "medium",
      "price": 9.25
    }
  ],
  "toppings": [
    {
      "type": "extra cheese",
      "price": 2.00
    },
    {
      "type": "mushrooms",
      "price": 1.50
    }
  ],
  "drinks": [
    {
      "type": "coke",
      "size": "large",
      "price": 3.00
    },
    {
      "type": "sprite",
      "size": "small",
      "price": 1.00
    }
  ],
  "sides": [
    {
      "type": "fries",
      "size": "large",
      "price": 4.50
    }
  ],
  "total_price": 35.20
}

Because we want the results from these kinds of operations to be rather predictable, you’ll also see that in this instance we’re choosing a lower temperature. In this scenario, I might use a lower temperature since you might want the output to be a little bit more predictable for a customer’s assistant chatbot as well.

You might want to use a higher temperature for a conversational agent, but you might also want to do so in this case. The summary of our order is presented here, and if we wanted, we could submit it to the order system.

6 Acknowledgements

I’d like to express my thanks to the wonderful ChatGPT Prompt Engineering for Developers Course by DeepLearning.ai and OpenAI - which i completed, and acknowledge the use of some images and other materials from the course in this article.

Subscribe