DEV Community

Cover image for Introduction to Using Generative AI Models: Create Your Own Chatbot πŸ€–πŸ’¬
Lotfi
Lotfi

Posted on

Introduction to Using Generative AI Models: Create Your Own Chatbot πŸ€–πŸ’¬

Find the final source code produced by this article here.

Introduction

The aim of this article is to guide you step by step in creating an application that interacts with generative AI models. We will start by setting up a Python development environment, following best practices. Then, we will explain the basics of interacting with generative AI model as well as the fundamental principles of the Langchain framework. Once these foundations are laid, we will implement a simple chatbot using the OpenAI API and Langchain. Finally, we will integrate this chatbot into a graphical interface with Streamlit, to offer a smooth and interactive user experience.

Prerequisites

To follow this article, you will need the following:

  • Some knowledge of algorithms and Python programming.
  • Python version 3.11.9.
  • An OpenAI account to use their API.

We will use OpenAI models for this tutorial, but you can eventually replace them with other similar models according to your preferences.

Preparing the Work Environment

If you are already comfortable with Python, you can skip this section.

Creating the Project

Start by creating a directory for our project:

$ mkdir my_awesome_ai_project
Enter fullscreen mode Exit fullscreen mode

Then, navigate into it:

$ cd my_awesome_ai_project
Enter fullscreen mode Exit fullscreen mode

Installing the Appropriate Python Version with Pyenv

Pyenv is an essential tool for managing multiple Python versions on your machine. It allows you to install and switch between different Python versions easily, which is particularly useful when working on multiple projects requiring distinct versions.

To install Pyenv, follow the instructions provided on the Pyenv GitHub repository.

After installing Pyenv, use it to install the Python version required for this project:

$ pyenv install 3.11.9
Enter fullscreen mode Exit fullscreen mode

Then, to ensure that this version is used by default in your project, create a .python-version file at the root of the project directory with the chosen version:

3.11.9
Enter fullscreen mode Exit fullscreen mode

This will ensure that all Python commands executed in this directory use the correct version.

To verify that everything is working correctly, run the following command in the project directory:

$ python --version
Enter fullscreen mode Exit fullscreen mode

You should then see the correct Python version displayed.

A Virtual Environment

To start your project well, it is essential to set up a Python virtual environment. But what exactly is a Python virtual environment? It's an isolated space that allows you to manage dependencies specific to each project, without interfering with those of other projects on your machine. This ensures a clean and secure working environment, where each project uses the specific library versions it needs.

To create a virtual environment in our project, run the following command at the project root:

python -m venv .venv
Enter fullscreen mode Exit fullscreen mode

This command creates a .venv folder at the project root, which represents the virtual environment.

To activate our virtual environment, simply enter the following command:

source .venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

On Windows, use this command:

.venv\Scripts\activate.bat
Enter fullscreen mode Exit fullscreen mode

To deactivate the virtual environment, simply type the following command:

deactivate
Enter fullscreen mode Exit fullscreen mode

Dependency Management in a Python Project

In the following part of this article, you will need to add external dependencies to your project, meaning you will install packages in your virtual environment. To do this, you will use the following command:

pip install <package_name>
Enter fullscreen mode Exit fullscreen mode

For example, to install the requests library, you would type:

pip install requests
Enter fullscreen mode Exit fullscreen mode

Once you have installed all your dependencies, it is important to document them in a file that can be used later to recreate the environment exactly. For this, you will use the command:

pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

This command will generate a requirements.txt file containing the complete list of installed packages and their exact versions. This file is essential for sharing your project with other developers or deploying your application, as it will allow anyone to recreate the environment by running the following command:

pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

This ensures that everyone working on the project is using the same package versions, preventing issues related to compatibility or version differences.

The "Hello World!"

Now that our project is properly configured, we will implement a small test program, the famous "Hello World". Create a main.py file at the project root with the following content:

def main():
    # The main() function represents the core of our program.
    # This is where we define the main actions to execute.
    print("Hello World!")

# The following condition checks if this script is run directly.
# If so, it calls the main() function. If the script is imported
# as a module in another file, this block will not be executed unless
# explicitly called.
if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

To run this script, launch the following command:

python main.py
Enter fullscreen mode Exit fullscreen mode

Running the script should display the phrase: "Hello World!".

And there you have it! We are now ready to start. πŸ˜„

OpenAI API

If you plan to use a model other than those offered by OpenAI, you can skip this section.

In this part, we will see how to obtain an OpenAI API key, which will allow us to interact with OpenAI's APIs.

Start by creating an account on the OpenAI website and log in to your account via this link: here.

Once authenticated, access the Dashboard/API Keys section and create a new API key. Copy the generated key, as we will need it later.

It's important to note that using this API is paid πŸ’΅. However, for the purposes of this article, a minimal amount will suffice to cover the essential needs and perform the proposed tests.

Environment Variable

The value of this API key is confidential and should never appear directly in your source code. To follow best practices, you will store it in an environment variable, which will allow the script to access it easily and securely.

To manage environment variables, you will use the dotenv package. Install it with the following command:

pip install python-dotenv
Enter fullscreen mode Exit fullscreen mode

Don't forget to activate the Python virtual environment before running the pip install command. This will install the package in the project's virtual environment.

Once the package is installed, create a .env file at the project root. This file will contain the value of your API key:

OPENAI_API_KEY="<YOUR_API_KEY>"
Enter fullscreen mode Exit fullscreen mode

If you are using a versioning system such as Git, make sure to add the .env file to your .gitignore to avoid versioning it.

To access the environment variable in the script, simply use the load_dotenv function at the beginning. Let's add this logic to the main.py file:

import os
from dotenv import load_dotenv

# Load environment variables from the .env file
load_dotenv()

def main():
    # Retrieve the OpenAI API key from environment variables
    openai_api_key: str = os.environ.get("OPENAI_API_KEY")

    # Display the API key (for testing)
    print(openai_api_key)

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

The script should display the value of your OpenAI key.

First Contact

To interact with the OpenAI API, you will install the openai package:

pip install openai
Enter fullscreen mode Exit fullscreen mode

We will use this package to call the OpenAI API and generate a haiku using the gpt-4o-mini model. Let's modify our main.py file as follows:

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

def main():
    openai_api_key = os.environ.get("OPENAI_API_KEY")

    # Configure the OpenAI client with the API key
    client = OpenAI(api_key=openai_api_key)

    # Create a request to generate a haiku from the gpt-4o-mini model
    completion = client.chat.completions.create(
        # The model to use for generating the response, here "gpt-4o-mini"
        model="gpt-4o-mini",
        # The messages sent to the model, in list form. Each message has a "role" (here "user")
        # and a "content", which is the text we send to the model.
        messages=[
            {"role": "user", "content": "write a haiku about ai"}
        ],
        # The temperature parameter adjusts the creativity of the response. A value close to 0
        # gives more deterministic responses, while a higher value (up to 1)
        # allows for more diversity and improvisation in the response.
        temperature=0.7
    )

    # Display the response generated by the model
    print(completion.choices[0].message.content)

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

By running this script, the OpenAI API will generate a haiku about artificial intelligence, which the program will then display in the console.

We have just made a simple call to the OpenAI API to generate a haiku. This basic example allows us to familiarize ourselves with the API and understand how to interact with OpenAI models. In the rest of this article, we will explore more advanced interactions with the API using Langchain.

LangChain

If theory gives you a headache 🀯, feel free to skip this section and go directly to the part where we implement our chatbot!

In this chapter, we will explore the Langchain library and explain its basic principles.

Why Use LangChain

Langchain is a powerful library that facilitates the integration of generative AI models into complex process chains. This library is particularly useful when you need to manage multiple steps in an interaction between your applications and AI models.

By using LangChain, you can not only optimize the management of your interactions with AI APIs, but also create more modular workflows, allowing you to add new functionalities without complicating your code. Moreover, LangChain implements many tools that simplify our work and prevent us from reinventing the wheel, thus allowing us to focus on adding specific value to our applications.

Principle

LangChain is based on a simple principle: it allows chaining multiple processing steps with AI models in a fluid and organized manner. The goal is to create chains (hence the name "LangChain") that link AI models, data flows, and functionalities in a modular way.

To better understand how LangChain works, we will go through its key concepts step by step, putting them into practice with Python code examples:

1. Loaders

Loaders are used to import and manage data from different sources (files, APIs, etc.). This allows for quickly integrating external data into your workflows, thus optimizing the enrichment of AI model responses.

Code Example:

from langchain_community.document_loaders import TextLoader  

# Load a text file  
loader = TextLoader('my_file.txt')  

# Load the documents  
documents = loader.load()  

# Display the number of loaded documents  
print(f"Number of loaded documents: {len(documents)}")  

# Display the content of the first document  
print(f"Document content: {documents[0].page_content}")
Enter fullscreen mode Exit fullscreen mode

Here, we use TextLoader to load the content of my_file.txt and import it as documents exploitable by LangChain.

2. Prompts

Prompts are the instructions you send to the AI model. With LangChain, you can customize these prompts to precisely match what you expect from the model.

Code Example:

from langchain.prompts import PromptTemplate  

# Create a prompt template  
template = "Translate the following text into English: {text}"  

prompt_template = PromptTemplate(  
    input_variables=["text"],  
    template=template,  
)  

# Generate the prompt with a variable  
my_prompt = prompt_template.format(text="Bonjour, comment allez-vous ?")  

print(my_prompt)
Enter fullscreen mode Exit fullscreen mode

Here, we create a PromptTemplate that takes a text variable and generates a prompt by inserting this variable into the template.

3. LLMs (Large Language Models)

LLMs are large language models like OpenAI's GPT. LangChain directly integrates these models, allowing you to call them to generate responses.

Code Example:

import os  
from dotenv import load_dotenv  
from langchain_openai.llms import OpenAI  

load_dotenv()  

llm = OpenAI(openai_api_key=os.environ['OPENAI_API_KEY'])  

# Generate a response from a prompt  
response = llm.invoke(my_prompt)  

print(response)
Enter fullscreen mode Exit fullscreen mode

Here, we initialize an OpenAI LLM model using our API key, then we use the previously generated prompt to get a response.

4. Chains

Chains allow you to link multiple steps in the same workflow. You can thus build more complex workflows by combining prompts, models, and other functionalities.

Code Example:

# Create a chain that combines the prompt and the LLM model  
chain = prompt_template | llm  

# Execute the chain with an input  
result = chain.invoke({"text": "Bonjour, comment allez-vous ?"})  

print(result)
Enter fullscreen mode Exit fullscreen mode

Here, we create a chain that combines our prompt and the LLM model, then we execute the chain with a specific input to obtain the result.

5. Memory

Memory in LangChain offers the possibility to manage context in a conversation by keeping track of previous messages. This allows for creating smarter chatbots or assistants, capable of remembering past interactions to offer a more coherent user experience.

Code Example:

from langchain_community.chat_message_histories import ChatMessageHistory  
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage  

# Create an instance of ChatMessageHistory to track the conversation message history  
history = ChatMessageHistory()  

# Add a system message to the history, which defines the assistant's behavior  
history.add_message(SystemMessage(content="You are a helpful assistant. Answer all questions to the best of your ability."))  

# Add a human message to the history, representing the user's input in the conversation  
history.add_message(HumanMessage(content="Hi!"))  

# Add an AI message to the history, representing the assistant's response  
history.add_message(AIMessage(content="Hello! How can I help you?"))  

# Display the list of messages in the conversation history  
print(history.messages)
Enter fullscreen mode Exit fullscreen mode

Here, ChatMessageHistory is used to store the history of exchanges. Each message from the system, user, or AI is added to this history, thus preserving the context of the conversation for future interactions.

By combining these elements, LangChain allows you to build complex and modular workflows, seamlessly integrating AI models. Whether it's for processing data, generating text, or creating intelligent conversational assistants, LangChain offers a flexible structure for your AI projects.

Creating a Chatbot

We finally arrive at the long-awaited part! Here, we will put into practice what we've seen previously to implement a simple chatbot using the LangChain library and OpenAI API.

Note: If you wish to use another model, you can consult the official LangChain documentation to learn how to change it easily.

Installing Dependencies

Let's start by installing the necessary packages by executing the following command:

pip install langchain langchain_openai langchain-community
Enter fullscreen mode Exit fullscreen mode

Modifying the main.py file

Next, let's modify the main.py file to look like this:

import os  
from dotenv import load_dotenv  
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder  
from langchain_core.messages import SystemMessage  
from langchain_community.chat_message_histories import ChatMessageHistory  
from langchain_openai import ChatOpenAI

# Load environment variables from a .env file
load_dotenv()

# Retrieve the OpenAI API key from environment variables
openai_api_key = os.getenv("OPENAI_API_KEY")  

# Create a conversation prompt template with messages
prompt = ChatPromptTemplate.from_messages(
   [        
       # System message to define the assistant's role  
       SystemMessage("You are a helpful assistant. Answer all questions to the best of your ability."),  
       # Placeholder for messages to be filled dynamically  
       MessagesPlaceholder(variable_name="messages"),  
   ]  
)  

# Initialize the GPT-4 model with the API key and set the temperature to adjust creativity  
llm = ChatOpenAI(api_key=openai_api_key, model="gpt-4o-mini", temperature=0.7)

# Create a processing chain combining the prompt and the LLM model  
chain = prompt | llm  

# Create a chat message history to track the conversation  
chat_history = ChatMessageHistory()  

# Main function that manages the conversation with the assistant  
def main():  
   # Infinite loop to maintain a continuous conversation with the user 
   while True:  
       # Ask for user input  
       user_input = input("You: ")  

       # Add the user's message to the conversation history  
       chat_history.add_user_message(user_input)  

       # Use the model to generate a response based on the message history  
       response = chain.invoke({"messages": chat_history.messages})  

       # Display the assistant's response  
       print("Assistant:", response.content)  

       # Add the assistant's response to the message history  
       chat_history.add_ai_message(response.content)  


# Entry point of the program, calls the main function
if __name__ == "__main__":  
   main()
Enter fullscreen mode Exit fullscreen mode

By running our main.py script, we will be able to interact with the chatbot.

Bonus 🎁: Graphical Interface

In this bonus part, we will see how to add a simple and elegant graphical interface to interact with the chatbot in a more user-friendly way.

We will use the Streamlit library to quickly create a functional graphical interface.

But before that, we're going to make some modifications to our source code to better structure it.

Code Refactoring

Creating the chatbot_core module

We will structure our code by creating a new file chatbot_core.py that will contain the core of the chatbot as a module:

import os
from dotenv import load_dotenv  
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder  
from langchain_core.messages import SystemMessage  
from langchain_community.chat_message_histories import ChatMessageHistory  
from langchain_openai import ChatOpenAI  


class ChatbotCore:  
    def __init__(  
            self,  
            openai_api_key=None,  
            system_prompt="You are a helpful assistant. Answer all questions to the best of your ability.",  
            model="gpt-4o-mini",  
            temperature=0.7,  
    ):  
        if openai_api_key is None:  
            load_dotenv()  
            openai_api_key = os.getenv("OPENAI_API_KEY")  
        self.api_key = openai_api_key  

        self.prompt = ChatPromptTemplate.from_messages(  
            [                SystemMessage(system_prompt),  
                MessagesPlaceholder(variable_name="messages"),  
            ]  
        )  
        # Configure the language model  
        self.llm = ChatOpenAI(  
            api_key=self.api_key, model=model, temperature=temperature  
        )  

        self.chain = self.prompt | self.llm  

        # Initialize conversation history  
        self.chat_history = ChatMessageHistory()  

    def send_message(self, message):  
        """  
        Send a message to the chatbot and receive the response.  
        Args:
            message (str): The message to send to the chatbot.  
        Returns:
            str: The chatbot's response.
        """
        self.chat_history.add_user_message(message)  
        response = self.chain.invoke({"messages": self.chat_history.messages})  
        self.chat_history.add_ai_message(response.content)  
        return response.content  

    def get_history(self):  
        """  
        Access the conversation history.  
        Returns:
            list: A list of conversation messages.
        """
        return self.chat_history.messages
Enter fullscreen mode Exit fullscreen mode

We have encapsulated the chatbot logic in a ChatbotCore class that exposes useful methods for interacting with the chatbot.

Updating the main.py file

Here's how to use this class in main.py:

from chatbot_core import (ChatbotCore)

def main():  
    chatbot_core = ChatbotCore()

    while True:  
        user_input = input("You: ")  
        response = chatbot_core.send_message(user_input)  
        print("Assistant:", response)  


if __name__ == "__main__":  
    main()
Enter fullscreen mode Exit fullscreen mode

Now that we have clean and modular source code, let's start the graphical interface part.

Implementing the Graphical Interface

Let's install Streamlit with the following command:

pip install streamlit
Enter fullscreen mode Exit fullscreen mode

Then, create a chatbot_interface.py file to implement the interface:

import streamlit as st
from chatbot_core import ChatbotCore

# Define the application title in the Streamlit interface
st.title("πŸ’¬ Chatbot")

# Add a caption under the title to describe the application
st.caption("πŸš€ A Streamlit chatbot powered by OpenAI")

# Check if a 'chatbot' object exists in the session state, otherwise create a new one
if "chatbot" not in st.session_state:
    # Create an instance of ChatbotCore in the session state
    st.session_state.chatbot = ChatbotCore()

# Display the chatbot's message history (user and assistant messages)
for msg in st.session_state.chatbot.get_history():
    # Display a message depending on whether it's from a user or an assistant
    st.chat_message("user" if msg.type == "human" else "assistant").write(msg.content)

# Check if the user has entered a message in the interface
if prompt := st.chat_input():
    # Display the user's message in the interface
    st.chat_message("user").write(prompt)

    # Send the message to the chatbot instance and get the response
    response = st.session_state.chatbot.send_message(prompt)

    # Display the chatbot's response in the interface
    st.chat_message("assistant").write(response)
Enter fullscreen mode Exit fullscreen mode

To launch the graphical interface, run this command:

streamlit run chatbot.py
Enter fullscreen mode Exit fullscreen mode

Conclusion

This guide has allowed you to discover the basics of generative AI models and create an interactive chatbot. To go further, Retrieval-augmented generation (RAG) and AI Agents represent a promising evolution of what we have achieved here. These technologies can enrich interactions and offer even more sophisticated solutions. Additionally, you can also explore how to deploy your Streamlit application to make it accessible online.

We hope this article has inspired you to continue exploring and developing AI-based applications. Good luck!

Top comments (2)

Collapse
 
techwiz7777 profile image
Morpheus Ia

This article is honestly super clear, and it’s exactly what a beginner like me needs. it’s really helpful and well-explained. Great job!

Collapse
 
lotfi profile image
Lotfi

Thank you πŸ™