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
Then, navigate into it:
$ cd my_awesome_ai_project
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
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
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
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
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
On Windows, use this command:
.venv\Scripts\activate.bat
To deactivate the virtual environment, simply type the following command:
deactivate
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>
For example, to install the requests library, you would type:
pip install requests
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
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
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()
To run this script, launch the following command:
python main.py
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
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>"
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()
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
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()
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}")
Here, we use
TextLoader
to load the content ofmy_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)
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)
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)
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)
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
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()
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
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()
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
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)
To launch the graphical interface, run this command:
streamlit run chatbot.py
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)
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!
Thank you π