
Introduction
In an era where health consciousness is at the forefront and the pursuit of a balanced lifestyle has become a universal aspiration, nutrition undeniably stands as a central pillar.
However, the intricate nature of dietary plans and the overwhelming wealth of nutritional data often serve as obstacles in our path to achieving this balance. A common instance of such a scenario is faced by individuals with diabetes, who require consistent and accurate nutritional guidance to manage their blood sugar levels effectively. Wouldn’t it be transformative to have access to a personalized nutritional assistant at our digital disposal?
In this context, leveraging technology to assist in nutritional guidance becomes not just beneficial but essential. By integrating cutting-edge Artificial Intelligence (AI) with a comprehensive nutritional database, it’s possible to create a powerful tool that can assist individuals in their health journey.
The code for this project is on this GitHub repository: link
The Project Overview
The core of the project involves building a chatbot, named FitBot, which is powered by OpenAI‘s functions and is based on the ReAct (Reasoning and Acting) framework (Figure 1).
It is designed to deliver nutritional information and advice, achieving this by interpreting user’s dietary habits and integrating an API of nutritional data.
Technical Approach
By leveraging the ReAct framework, FitBot maintains the interactive nature of a conversation and can provide a detailed explanation for each piece of advice it gives. It also connects with an external nutritional database, ensuring accurate and up-to-date dietary advice.

Behind the Scenes with FitBot
FitBot combines OpenAI’s GPT-4 capabilities with ReAct’s dynamic processing to understand dietary queries, suggest suitable alternatives, and deliver personalized advice. It does this while maintaining a conversational tone, making nutritional advice accessible and engaging.
What sets FitBot apart is its capability to interface with an external nutritional database. This allows FitBot to provide precise and updated information to the users, ensuring the advice given is reliable and based on accurate data.
In the upcoming sections, we’ll delve into the code and see how FitBot’s components are developed and interact with each other, providing a comprehensive look at the inner workings of this innovative project.
Building FitBot: The Code Explained
This project has four main scripts used for processing data and displaying it in a UI:
- fitness_agent.py: This file contains the
FitnessAgent
class, which leverages OpenAI functions to enable the functionalities required by FitBot. - chatbot.py: This file contains the code for the user interface of FitBot, which is implemented using the Gradio library.
- agents.py: This file contains the
Agent
class, which is used to handle conversations with the OpenAI API. This code is based on this script developed by James Briggs in the funkagent library. - parser.py: This file contains the code for parsing functions docstrings into OpenAI’s functions descriptions.
Defining our functions
To create a chatbot that can provide accurate and useful nutritional and fitness advice, we needed to consider what information would be most valuable to the end user. This is the reason behind the implementation of the functions explained earlier.
get_nutritional_info
: This function is crucial for any fitness-oriented chatbot. People often lack clear information about the nutritional content of the foods they eat. By using the Nutrition endpoint from API Ninjas to fetch real-time nutritional data for various food items, we can help users make informed dietary decisions. The data returned can include details like calories, protein, carbs, fats, and more, offering a comprehensive look at the nutritional profile of a food item.
def get_nutritional_info(self, query: str) -> dict:
"""Fetches the nutritional information for a specific food item
:param query: The food item to get nutritional info for
:return: The nutritional information of the food item
"""
api_url = 'https://api.api-ninjas.com/v1/nutrition?query={}'.format(query)
response = requests.get(api_url, headers={'X-Api-Key': self.nut_api_key})
if response.status_code == requests.codes.ok:
return response.json() # Use json instead of text for a more structured data
else:
return {"Error": response.status_code, "Message": response.text}
calculate_bmr
: The Basal Metabolic Rate (BMR) is a key figure in understanding an individual’s metabolism. It is the amount of energy expended while at rest and is closely related to one’s age, weight, height, and sex. The ability to calculate BMR gives the chatbot a baseline to help users understand how many calories their bodies need to function, even without any physical activity.
def calculate_bmr(weight: float, height: float, age: int, gender: str, equation: str = 'mifflin_st_jeor') -> float:
"""Calculates the Basal Metabolic Rate (BMR) for a person
:param weight: The weight of the person in kg
:param height: The height of the person in cm
:param age: The age of the person in years
:param gender: The gender of the person ('male' or 'female')
:param equation: The equation to use for BMR calculation ('harris_benedict' or 'mifflin_st_jeor')
:return: The BMR of the person
"""
if equation.lower() == 'mifflin_st_jeor':
if gender.lower() == 'male':
return (10 * weight) + (6.25 * height) - (5 * age) + 5
else: # 'female'
return (10 * weight) + (6.25 * height) - (5 * age) - 161
else: # 'harris_benedict'
if gender.lower() == 'male':
return 88.362 + (13.397 * weight) + (4.799 * height) - (5.677 * age)
else: # 'female'
return 447.593 + (9.247 * weight) + (3.098 * height) - (4.330 * age)
calculate_tdee
: Understanding one’s Total Daily Energy Expenditure (TDEE) is vital for creating a personalized diet or exercise plan. TDEE takes into account not only BMR but also the calories burned during daily activities and exercise. Knowing their TDEE can help users plan their diet and exercise routines more effectively, to either maintain, lose or gain weight.
def calculate_tdee(bmr: float, activity_level: str) -> float:
"""Calculates the Total Daily Energy Expenditure (TDEE) for a person
:param bmr: The BMR of the person
:param activity_level: The activity level of the person
('sedentary', 'lightly_active', 'moderately_active', 'very_active', 'super_active')
:return: The TDEE of the person
"""
activity_factors = {
'sedentary': 1.2,
'lightly_active': 1.375,
'moderately_active': 1.55,
'very_active': 1.725,
'super_active': 1.9,
}
return bmr * activity_factors.get(activity_level, 1)
calculate_ibw
: Knowing the Ideal Body Weight (IBW) can provide users with a target weight that is considered healthy for their height and gender. While IBW is not a perfect measure (it doesn’t consider factors like muscle mass), it does give users a general idea of where their weight should be for optimal health.
def calculate_ibw(height: float, gender: str) -> float:
"""Calculates the Ideal Body Weight (IBW)
:param height: The height of the person in inches
:param gender: The gender of the person ("male" or "female")
:return: The Ideal Body Weight in kg
"""
if gender.lower() == 'male':
if height <= 60: # 5 feet = 60 inches
return 50
else:
return 50 + 2.3 * (height - 60)
elif gender.lower() == 'female':
if height <= 60:
return 45.5
else:
return 45.5 + 2.3 * (height - 60)
else:
raise ValueError("Invalid gender. Expected 'male' or 'female'.")
calculate_bmi
: Body Mass Index (BMI) is a simple calculation using a person’s height and weight. The formula is BMI = kg/m^2 where kg is a person’s weight in kilograms and m^2 is their height in meters squared. BMI does not measure body fat directly, but research has shown that BMI is moderately correlated with more direct measures of body fat. It provides a helpful gauge to understand if a person is underweight, at a healthy weight, overweight, or obese.
def calculate_bmi(weight: float, height: float) -> float:
"""Calculates the Body Mass Index (BMI) for a person
:param weight: The weight of the person in kg
:param height: The height of the person in cm
:return: The BMI of the person
"""
height_meters = height / 100 # convert cm to meters
bmi = weight / (height_meters ** 2)
return round(bmi, 2) # round to 2 decimal places for readability
Building the Agent: Wrapping the Functions
With the essential functions established, our next step is to integrate these into our chatbot agent. This encapsulation enables the bot to leverage the functions and provide relevant, accurate responses based on user queries.
Here’s how you can create the agent:
# Instantiate the agent
fitness_agent = FitnessAgent(openai_api_key, nut_api_key)
You can check the functions it is enriched with:
# You can view the processed function instructions
print(fitness_agent.functions)
Output:
[
{
"name":"get_nutritional_info",
"description":"Fetches the nutritional information for a specific food item",
"parameters":{
"type":"object",
"properties":{
"query":{
"description":"The food item to get nutritional info for",
"type":"string"
}
}
},
"required":[
"query"
]
},
{
"name":"calculate_bmr",
"description":"Calculates the Basal Metabolic Rate (BMR) for a person",
"parameters":{
"type":"object",
"properties":{
"weight":{
"description":"The weight of the person in kg",
"type":"number"
},
"height":{
"description":"The height of the person in cm",
"type":"number"
},
"age":{
"description":"The age of the person in years",
"type":"integer"
},
"gender":{
"description":"The gender of the person ('male' or 'female')",
"type":"string"
},
"equation":{
"description":"The equation to use for BMR calculation ('harris_benedict' or 'mifflin_st_jeor')",
"type":"string"
}
}
},
"required":[
"weight",
"height",
"age",
"gender",
"equation"
]
},
{
"name":"calculate_tdee",
"description":"Calculates the Total Daily Energy Expenditure (TDEE) for a person",
"parameters":{
"type":"object",
"properties":{
"bmr":{
"description":"The BMR of the person",
"type":"number"
},
"activity_level":{
"description":"The activity level of the person",
"type":"string"
}
}
},
"required":[
"bmr",
"activity_level"
]
}
]
Engaging with the Agent: User Conversations
Having encapsulated our defined functions within the FitnessAgent, we can now simulate a conversation with our bot. Using the ask()
method of the FitnessAgent class, we can easily feed user queries into our bot and print out the responses it generates.
For example, let’s ask the bot for some nutritional information about a common food item:
# Define a question
user_input = "What is the nutritional value of a banana?"
# Get raw chat response
response = fitness_agent.ask(user_input)
# Print final response
print(response['choices'][0]['message']['content'])
Output:
A 100-gram serving of banana typically contains:
- Calories: 89.4
- Total Fat: 0.3 grams, of which Saturated Fat is 0.1 grams
- Protein: 1.1 grams
- Sodium: 1 milligram
- Potassium: 22 milligrams
- Cholesterol: 0 milligram
- Total Carbohydrates: 23.2 grams, of which Dietary Fiber is 2.6 grams and Sugars are 12.3 grams
These values may vary based on the exact size and ripeness of the banana. Also note, bananas are a good source of dietary potassium and vitamin C.
In this example, FitBot effectively uses the get_nutritional_info()
function to fetch and display the nutritional value of a banana.
The FitBot can handle more complex requests as well. For instance, it can calculate a person’s Total Daily Energy Expenditure (TDEE) if provided with the required data:
# Define a question
user_input = "What is the TDEE of a 30-year-old man, who is 180 cm tall, weighs 80 kg, and exercises 3 times a week?"
# Get raw chat response
response = fitness_agent.ask(user_input)
# Print final response
print(response['choices'][0]['message']['content'])
Output:
The Total Daily Energy Expenditure (TDEE) of a 30-year-old man who is 180 cm tall, weighs 80 kg, and exercises 3 times a week with "moderate" exercise according to the Harris-Benedict equation would be approximately 2574 calories a day. This is a rough estimate and individual results could vary based on multiple factors like metabolism speed, specific physical activity, and more.
Here's a simple breakdown of the process:
1. Calculating Basal Metabolic Rate (BMR) - This is the number of calories a person would burn just by existing, without any activity.
For men, the Harris-Benedict equation to calculate BMR is
BMR = 88.362 + (13.397 * weight in kg) + (4.799 * height in cm) - (5.677 * age in years)
For the given values, the BMR comes to around 1783 calories.
2. Adjusting for Activity - The BMR can thus be adjusted for physical activity level using the following multipliers:
- Sedentary (little or no exercise): BMR * 1.2
- Lightly active (light exercise/sports 1-3 days/week): BMR * 1.375
- Moderately active (moderate exercise/sports 3-5 days/week): BMR * 1.55
- Very active (hard exercise/sports 6-7 days a week): BMR * 1.725
- Super active (very hard exercise/physical job & exercise 2x/day): BMR * 1.9
For someone who exercises 3 times a week, we could take the multiplier to be at 1.55 (Moderately active).
Hence, TDEE = BMR * Activity Multiplier = 1783 * 1.55 = approximately 2764 calories.
Note: The actual total caloric needs may be slightly higher or lower as this calculation doesn't take into account muscle to fat ratio, specific types of activities, etc.
Let’s compare this output with vanilla ChatGPT based on GPT-4:
TDEE, or Total Daily Energy Expenditure, represents the total number of calories a person needs to consume in a day to maintain their current weight, considering their physical activity level.
We first need to calculate the Basal Metabolic Rate (BMR), which is the amount of energy expended while at rest. There are several equations to calculate BMR, one of the commonly used ones is the Mifflin-St. Jeor equation:
For men: BMR = 10 * weight(kg) + 6.25 * height(cm) - 5 * age(y) + 5
So, for your case:
BMR = 10 * 80kg + 6.25 * 180cm - 5 * 30y + 5
BMR = 800 + 1125 - 150 + 5 = 1780 kcal/day
This is the amount of calories needed to keep the body functioning while at rest.
To include physical activity in the equation, we multiply the BMR by the Physical Activity Level (PAL). For someone who exercises 3 times a week, a common PAL is 1.375 (moderate exercise).
So, TDEE = BMR * PAL
TDEE = 1780 kcal/day * 1.375 = 2447.5 kcal/day
So, a 30-year-old man who is 180 cm tall, weighs 80 kg, and exercises 3 times a week would need approximately 2448 calories per day to maintain his current weight. Please note this is just an estimate; actual caloric needs can vary based on many factors, including genetics and body composition. Always consult a healthcare provider or a registered dietitian for personalized advice.
Integrating Chatbot with User Interface
Now that we have built our fitness agent and equipped it with useful features, we want to present it in an easy-to-use interface.
For this, we use Gradio, a Python library that allows us to create shareable web-based user interfaces swiftly and conveniently. In this section, we’ll walk you through integrating our chatbot with a Gradio user interface.
Here’s the overall structure of our interface:
def main():
openai_api_key = gr.components.Textbox(
lines=1,
label="Enter OpenAI API Key",
type="password",
)
nut_api_key = gr.components.Textbox(
lines=1,
label="Enter Nutrition API Key",
type="password",
)
question = gr.components.Textbox(
lines=3,
label="Enter your message",
)
output_history = gr.outputs.HTML(
label="Updated Conversation",
)
inputs = [
openai_api_key,
nut_api_key,
question,
]
iface = gr.Interface(
fn=partial(get_response),
inputs=inputs,
outputs=[output_history],
title="Fitness Agent",
description="A simple chatbot using a Fitness Agent and Gradio with conversation history",
allow_flagging=False,
)
iface.launch()
if __name__ == "__main__":
main()
This is our main
function, the entry point of our script. We start by creating text boxes for the user to input their OpenAI API key and Nutrition API key. The keys are set to the type password
to hide the input. Next, we provide a text box for the user to ask their question. The response from the bot will be displayed as HTML in an area labelled "Updated Conversation".
The inputs and output are then passed to the Gradio interface, which is launched when the script is run.
The get_response
function interacts with the fitness agent:
def get_response(openai_api_key, nut_api_key, user_input, action=None):
set_openai_api_key(openai_api_key)
set_nut_api_key(nut_api_key)
fitness_agent = FitnessAgent(openai_api_key, nut_api_key)
# Get raw chat response
fitness_agent.ask(user_input)
memory = fitness_agent.agent.chat_history
# Iterate through messages in ChatMessageHistory and format the output
updated_conversation = '<div style="background-color: hsl(30, 100%, 30%); color: white; padding: 5px; margin-bottom: 10px; text-align: center; font-size: 1.5em;">Chat History</div>'
logger.info(memory)
for i, message in enumerate(memory):
if i != 0:
if message['role'] == 'user':
prefix = "User: "
background_color = "hsl(0, 0%, 40%)" # Dark grey background
text_color = "hsl(0, 0%, 100%)" # White text
else:
prefix = "Chatbot: "
background_color = "hsl(0, 0%, 95%)" # White background
text_color = "hsl(0, 0%, 0%)" # Black text
updated_conversation += f'<div style="color: {text_color}; background-color: {background_color}; margin: 5px; padding: 5px;">{prefix}{message["content"]}</div>'
return updated_conversation
In get_response
, we set the OpenAI and Nutrition API keys using the functions set_openai_api_key
and set_nut_api_key
, and then initialize our fitness agent. We then call the agent’s ask
method with the user’s question and store the conversation history. Each message in the conversation history is then formatted as an HTML string and added to updated_conversation
. This HTML string is returned and displayed in the Gradio interface.
A Glimpse of the Resulting Interface
After integrating the essential calculations and the conversation logic, and encapsulating all of this into a visually pleasing user interface with Gradio, we have our FitBot ready to interact with!
Here’s how the final interface looks:

On the interface, you’ll notice three input boxes where you can enter the necessary keys for OpenAI and the API Ninjas Nutrition endpoint, as well as the user message to the Fitness Agent.
Summary
This article detailed the creation of FitBot, a comprehensive Fitness Agent using OpenAI’s GPT-4, a powerful AI model capable of understanding and responding to complex user queries.
We started by building functions to calculate key health metrics like the Basal Metabolic Rate (BMR), Total Daily Energy Expenditure (TDEE), and Body Mass Index (BMI). These calculations form the base for the Fitness Agent’s ability to provide accurate and tailored fitness and nutritional advice.
Next, we incorporated an integration with a Nutrition endpoint from API Ninjas. This allowed the Fitness Agent to access and provide accurate nutritional information, a crucial component for any comprehensive fitness and diet plan.
We then showcased how to construct a conversation logic to make the Fitness Agent more interactive. It’s capable of handling a conversational flow, enabling it to answer varied user queries and guide users on their fitness journey effectively.
Finally, we encapsulated all these functionalities into a visually appealing user interface using Gradio. The result is a Fitness Agent that is not only intelligent but also user-friendly, offering clear and comprehensive advice in an easily digestible format.
TL;DR: In this article, we built FitBot, a Fitness Agent using OpenAI’s GPT-4, capable of providing personalized fitness and nutrition advice. We implemented functions for calculating key health metrics (BMR, TDEE, BMI), integrated with a Nutrition API for accurate dietary information, and wrapped it all in a user-friendly interface using Gradio. This project showcases the power of AI in health and fitness, simplifying complex calculations, offering personalized advice, and delivering it all through an appealing user interface.
Thank you for reading!
- Follow me on Linkedin!