Building a Custom Chatbot with ChatGPT from Scratch

Learn how to build a custom chatbot from scratch using ChatGPT and streamline your operations with 24/7 customer service.

Published on:

January 12, 2024

In today's digital age, chatbots have become a ubiquitous presence in many businesses and industries, providing an effective means of automating customer service, streamlining operations, and enhancing user experiences. With the increasing popularity of messaging apps and the growing demand for instant communication, chatbots have emerged as a valuable tool for businesses looking to engage with customers in real-time. 

A chatbot is one such tool. Unlike customer care executives, chatbots are available 24/7 and can stay consistently error-free while maintaining efficiency.

A Commercial Perspective.

Chatbots aren't just fun and games, they can also have a huge impact on a business's bottom line! By automating repetitive tasks and handling customer inquiries around the clock, chatbots can help businesses save time and money, while improving customer satisfaction and retention. Plus, with the ability to handle multiple conversations at once, chatbots can increase efficiency and productivity, allowing employees to focus on more complex tasks that require human interaction. So, whether you're a small business owner or a multinational corporation, incorporating a chatbot into your operations could be the smartest decision you make all year! 

Source

However, building a chatbot can be a daunting task, requiring a deep understanding of conversational design, natural language processing, and programming. In this e-book, we will provide a comprehensive guide on how to build a chatbot from scratch, covering everything from the basics of chatbot design to the technical aspects of programming and deployment. Whether you're a business owner looking to integrate a chatbot into your operations or a developer interested in building your own chatbot, this e-book will provide you with the knowledge and skills you need to create a successful chatbot that can enhance your business operations and delight your customers.

The Development Trends

Chatbots have come a long way since their inception. In the early days, building a chatbot meant writing a series of rules and decision trees that would dictate the bot's responses to user inputs. While this approach can still be effective, it has its limitations. With the advent of artificial intelligence (AI), chatbots have become more sophisticated, capable of understanding natural language and generating responses that are more human-like. And now, with the emergence of generative AI, chatbots are poised to become even more advanced, with the ability to generate creative and engaging responses in real-time. 

Traditional Rule-Based

Rule-based chatbots are the most basic form of a chatbot, relying on a set of predetermined rules and decision trees to generate responses to user inputs. While this approach can be effective for simple tasks, such as providing information or answering frequently asked questions, it has its limitations. Rule-based chatbots are rigid and inflexible, and they can struggle to understand complex user queries or adapt to changing circumstances. However, they are relatively easy to build and require minimal technical expertise, making them a good option for small businesses or organizations with limited resources.

AI-Powered

AI-powered chatbots are designed to mimic human conversation, using natural language processing (NLP) and machine learning algorithms to understand user inputs and generate responses. This approach allows chatbots to handle more complex queries and provide more personalized and engaging interactions with users. However, AI-powered chatbots require a significant amount of data and technical expertise to build and maintain. They also have a higher risk of errors or biases, as they rely on data to learn and make decisions.

Generative AI-Powered

Generative AI chatbots are the latest advancement in chatbot technology. Unlike rule-based or AI-powered chatbots, generative AI chatbots can create original and creative responses, making them feel much more natural than the other two could ever be. However, generative AI chatbots require a massive amount of data and computing power to build and operate from scratch, thanks to open-source libraries like huggingface such cutting-edge technology is now easily accessible to a lot of people. Additionally, there are concerns around the ethical implications of using generative AI chatbots, as they have the potential to produce misleading or harmful content if not properly trained or supervised.

A step-by-step tutorial on how to build a Generative - AI powered chatbot using OpenAI’s APIs.

Why OpenAI?

There’re a lot of other models that are capable of being a good core to a chatbot. But the collection of pre-trained models that are available at OpenAI’s disposal is second to none. It allows us to experiment with different models and see what's working. Additionally, OpenAI offers a range of powerful tools and APIs that can make building a chatbot much easier and more efficient. For example, OpenAI's ChatGPT is one of the most, if not the most advanced natural language processing (NLP) model around, capable of generating human-like responses to a wide range of prompts. By building a chatbot with ChatGPT at its core, you will be in a good position to create engaging and natural conversations with your users.

Available APIs

Here’s a list of available APIs from OpenAI. We proceed with the latest of the lot, gpt-3.5-turbo, because of the power and pricing.

Getting your hands dirty

Find your OpenAI API key here.

!pip install openai -q


import pandas as pd
from openai.embeddings_utils import get_embedding, cosine_similarity
import numpy as np
import openai
import os
import pandas as pd


openai.api_key = " " # Enter your OpenAI API key here.

Because this is a prototype, we’ll be using data from a bunch of text files as our knowledge database. When it comes to production, you might be better of with actual databases since they offer better security and speed of retrieval.

csvout = {}


files = os.listdir()
print(files)
files = [i for i in files if '.txt' in i] 


for filename in files:
   with open(filename) as myfile:
    df = pd.DataFrame([], columns=['line'])
    data = ''
    for line in myfile:
        line = line.replace('\n', ' ')
        data += line
    csvout[filename[:-4]] = data


df = pd.DataFrame(csvout.items(), columns=['Title', 'text_scrap'])
df.to_csv("master_df.csv", index=False)


df["embedding"] = df.text_scrap.apply(lambda x: get_embedding(x, engine='text-embedding-ada-002'))
df["embedding"] = df.embedding.apply(np.array)

This piece of code extracts the information from each txt file and creates a single csv file that has the columns which contain the Title of the files, the Content in the file, and the corresponding Embeddings. If you happen to be dealing with large volumes of data, Vector Databases like Pinecone and Weaviate are your best bets since they’re faster and built specifically to handle high-dimensional data like vectors.

The motivation behind computing embeddings is that embeddings are a way of representing words or phrases as vectors of numbers. These vectors are designed to capture the semantic meaning of the words or phrases, allowing machines to understand better the relationships between different words and their context in a sentence.

Embeddings are necessary for machines because traditional methods of representing words such as one-hot vectors or frequency counts do not capture the underlying semantic meaning of the words. For example, the word "dog" and "puppy" may have different representations in a traditional vector space model, even though they are closely related in meaning. This can make it difficult for machines to understand the context and meaning of words in a sentence. On the other hand, embeddings are designed to capture the semantic meaning of words, making it easier for machines to understand the relationships between different words and their context in a sentence. For example, using embeddings, the words "dog" and "puppy" would have similar vector representations, reflecting their close semantic relationship.

We make use of text-embedding-ada-002, OpenAI’s latest embedding model, to create embeddings.

def search_content(df, product_description):
   embedding = get_embedding(product_description,
engine='text-embedding-ada-002')
   df['score'] = df.embedding.apply(lambda x: cosine_similarity(x, embedding))
   df.sort_values('score', ascending=True, inplace=True)
   return df.loc[df['score'].idxmax()]["text_scrap"]

Once you’ve computed the embeddings it’s time to put them to test. The function above computes the embeddings for an incoming message, computes the Cosine Similarities with each of the embeddings that were previously computed for our text files, and returns the content in the text file with the highest Cosine Similarity.

If you’re wondering what Cosine Similarity is, it is a metric used to measure the similarity between two vectors of word embeddings. Specifically, cosine similarity measures the cosine of the angle between two vectors in a high-dimensional vector space, with values ranging from -1 (indicating complete dissimilarity) to 1 (indicating complete similarity).

model_engine = "gpt-3.5-turbo"


def get_response(prompt):
    
    response = openai.ChatCompletion.create(
    model=model_engine,
    messages= prompt
  )
    
    # Extract the response from the response object
    response_text = response['choices'][0]['message']['content']
    chatbot_response = response_text.strip()
    
    return chatbot_response

Now we choose the model (model_engine) we’d want to power our chatbot with. There are multiple tweakings that OpenAI allows you to do their models, check out their documentation for more information. 

If you’ve played around with ChatGPT or Generative AI in general, you’d understand the importance of prompting. Prompting is crucial in generative AI because it provides the initial input or direction for the AI to generate outputs. By giving a specific prompt or context, the AI can focus its attention on generating outputs that are relevant to the given prompt. In addition, prompting can be used to fine-tune the model's output to fit specific requirements. For instance, a language model used for automated text generation in customer service may be prompted with specific keywords or phrases to ensure that the generated text is relevant to the customer's query. It is this ability of Generative AI that makes them interesting.

prompt = [
          { "role":"system", "content": 
        """Act as an advanced chatbot. As an advanced chatbot, your primary goal is to assist users to the best of your ability without being repetitive. 
 In order to effectively assist users, it is important to not be very detailed and thorough in your responses. 
 Give the user a short and concise response."""}

Here’s a sample text that we’d be prompting our chatbot with. It is important that you try multiple prompts and see how the model is responding to them before finalizing the final prompt.

while True:
        user_input = input("> ")
        if user_input == "exit":
            break
        external_knowledge = search_content(df, user_input, n=1)
        context = {"role": "user", "content": external_knowledge}
        prompt.append(context)
        convo = {"role": "user", "content": user_input}
        prompt.append(convo)
        chatbot_response = get_response(prompt)
        print(f"Chatbot: {chatbot_response}")
        response = {"role": "assistant", "content": chatbot_response}
        prompt.append(response)

Alright, now we get to the good part. Let’s put together everything that we have built so far. The relevant text content is given to the model based on the input given by the user. Since we’ve prompted the model to stay concise with its responses, the model summarizes the massive chunks of text extracted from the database. 

Building an Interface

One quick and easy method to build an interface for the chatbot that we’ve just created is streamlit. There’re other tools that help you realize this, like Gradio. We’ll be sticking to streamlit for this example.

!pip install streamlit -q
!pip install streamlit-chat -q
import streamlit as st
import pandas as pd
from streamlit_chat import message
import openai
from openai.embeddings_utils import get_embedding, cosine_similarity
import numpy as np




openai.api_key = " "
model_engine = "gpt-3.5-turbo"


df = pd.read_csv('master_df.csv')


df["embedding"] = df.text_scrap.apply(lambda x: get_embedding(x, engine='text-embedding-ada-002'))
df["embedding"] = df.embedding.apply(np.array)










def search_content(df, product_description, n=3, pprint=True):
   embedding = get_embedding(product_description, engine='text-embedding-ada-002')
   df['score'] = df.embedding.apply(lambda x: cosine_similarity(x, embedding))
   res = df.sort_values('score', ascending=False)
   return res['text_scrap'][0]
 


def get_response(prompt):
    
    response = openai.ChatCompletion.create(
    model=model_engine,
    messages= prompt
  )
    
    # Extract the response from the response object
    response_text = response['choices'][0]['message']['content']
    chatbot_response = response_text.strip()
    
    return chatbot_response










st.set_page_config(
    page_title="The ChatBot",
    page_icon=":robot:"
)
st.header("Streamlit Chat")






if 'generated' not in st.session_state:
    st.session_state['generated'] = []


if 'past' not in st.session_state:
    st.session_state['past'] = []




def get_text():
    input_text = st.text_input("You: ","Hello, how are you?", key="input")
    return input_text 






user_input = get_text()


prompt = [
          { "role":"system", "content": 
        """Act as an advanced chatbot. As an advanced chatbot, your primary goal is to assist users to the best of your ability without being repetitive. 
 In order to effectively assist users, it is important to not be very detailed and thorough in your responses. 
 Give the user a short and concise response."""}
      ]


if user_input:
        external_knowledge = search_content(df, user_input, n=1)
        context = {"role": "user", "content": external_knowledge}
        prompt.append(context)
        convo = {"role": "user", "content": user_input}
        prompt.append(convo)
        output = get_response(prompt)
        response = {"role": "assistant", "content": output}
        prompt.append(response)


        st.session_state.past.append(user_input)
        st.session_state.generated.append(output)






if st.session_state['generated']:


    for i in range(len(st.session_state['generated'])-1, -1, -1):


        message(st.session_state['past'][i],avatar_style='adventurer', is_user=True, key=str(i) + '_user')
        
        message(st.session_state["generated"][i], avatar_style="fun-emoji", key=str(i))

Here’s the complete program. In order to run the bot, you’ll need to save this program as a python file and run the following command.

streamlit run .py

What are other viable alternatives to OpenAI?

Huggingface is one solid alternative to OpenAI. It has a plethora of pre-trained models to choose from, and the process of training and fine-tuning is hassle-free. 

What lies ahead?

Chatbots like ChatGPT have demonstrated the potential of AI, and it appears that we have only scratched the surface. As AI models become more complex, their abilities will undoubtedly expand, and with the emergence of multi-modal AI and Artificial General Intelligence, we may soon see AI that is proficient in several domains out of the box. However, as we contemplate the potential of AI, we must also exercise caution to safeguard individuals' privacy and ensure that the models are responsible. As a continuation, we’ll create a follow-up dealing with a building chatbot based on large documents. Stay tuned!