Build an Eco Lifestyle Agent with watsonx

Build a full-stack AI chatbot using IBM watsonx.ai and IBM Bob IDE.

Introduction

30 Second Summary

Living sustainably is something most people want to do, but figuring out the right product swaps, recycling rules, or how much carbon your daily commute produces takes research most of us never get around to.

In this project, you will build an Eco Lifestyle Agent powered by IBM Granite on watsonx.ai, then create a custom chat interface using IBM Bob as your AI coding partner.

What You'll Build

Picture typing "What should I use instead of plastic wrap?" into your browser and getting back a personalized, practical answer from your own AI agent in seconds.

By the end of this project, you'll have:

  • A deployed AI agent on watsonx.ai that gives personalized advice about sustainable products, carbon footprints, eco habits, and recycling.
  • A full-stack chat application where you type eco-living questions in your browser and see real-time responses from IBM Granite running in the cloud.
  • Hands-on experience using IBM Bob's Agent mode to generate an entire Flask web app from natural language prompts.
  • Secret Mission: Add conversation memory so EcoBot remembers what you discussed earlier in the chat.

Are there any prerequisites?

You need Python 3.10+ installed, an IBM Cloud account with the watsonx.ai free trial active, and IBM Bob IDE installed with its free trial active (30-day trial with 40 Bobcoins).

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters.

Set Up Your Environment

Your Eco Lifestyle Agent will run on IBM Cloud's watsonx.ai service. For your code to reach watsonx.ai, it needs two credentials: an API key to prove your identity, and a project ID to tell watsonx.ai where to route the request.

In this step, you will grab both credentials from IBM Cloud, set up your project folder in IBM Bob IDE, and install the Python packages your app needs.

In this step, get ready to:
  • Generate your IBM Cloud API key and copy your watsonx.ai project ID.
  • Create your project folder and environment file in IBM Bob IDE.
  • Install Flask and requests, then verify everything works.
Get your watsonx.ai API key and project ID

IBM Cloud uses IAM (Identity and Access Management) to control access to its services. You need an API key that your Python code will exchange for a temporary token each time it calls watsonx.ai.

  • Open your browser and navigate to https://cloud.ibm.com.
  • Sign in with your IBM Cloud account.
  • In the top menu bar, click Manage.
  • Select Access (IAM) from the dropdown.
  • In the left sidebar, click API keys.
  • Click Create an IBM Cloud API key.
  • Enter a name like ecobot-key and an optional description.
  • Click Create.
  • Click Copy or Download to save your API key somewhere safe.

Your API key is only shown once

IBM Cloud will not display your API key again after you close this dialog. If you lose it, you will need to create a new one. Paste it into a temporary note or text file now so you have it for the next substep.

Now grab your watsonx.ai project ID.

  • Navigate to https://dataplatform.cloud.ibm.com to open the watsonx.ai console.
  • Open an existing watsonx.ai project, or click New project to create one.
  • Inside your project, click the Manage tab.
  • Find the project ID in the General information area.
  • Copy the project ID and save it alongside your API key.

What is a project ID?

Every watsonx.ai project has a unique identifier. When your code calls the text generation API, it passes this ID so watsonx.ai knows which project's resources and models to use.

Set up IBM Bob IDE and create your project folder

With your credentials saved, you need a place to write code. IBM Bob IDE is an AI-powered coding environment with built-in Agent, Plan, and Ask modes that can generate code from natural language descriptions.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type IBM Bob and press Enter to open it.

You should see IBM Bob's welcome screen with the file explorer on the left and the editor area in the center.

  • Open the integrated terminal in IBM Bob by clicking Terminal in the top menu and selecting New Terminal.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir ecobot
cd ecobot
  • Confirm the folder exists by running:
pwd

You should see a path ending in ecobot, confirming you are inside your new project folder.

Now create your environment file to store your credentials.

  • Create the .env file by running:
touch .env
  • Open .env in the IBM Bob editor by clicking it in the file explorer sidebar.
  • Paste the following content into .env, replacing the placeholder values with the API key and project ID you saved earlier:
WATSONX_API_KEY=your-api-key-here
WATSONX_PROJECT_ID=your-project-id-here
WATSONX_URL=https://us-south.ml.cloud.ibm.com

What does this file do?

The .env file stores configuration values outside your code. WATSONX_API_KEY is your IBM Cloud credential. WATSONX_PROJECT_ID tells the API which project to use. WATSONX_URL is the base address of the watsonx.ai service in the US South region.

Keeping secrets in .env prevents you from accidentally committing them to version control.

  • Save .env.
  • Confirm the file was created by running:
ls -a

You should see .env listed in the output.

✔️ Awesome, I've got everything!

Great. Double check you have saved .env with your real API key and project ID filled in.

ⓧ I'd like to double check the full code

Your .env file should look exactly like this (with your actual values in place of the placeholders):

WATSONX_API_KEY=your-api-key-here
WATSONX_PROJECT_ID=your-project-id-here
WATSONX_URL=https://us-south.ml.cloud.ibm.com

Now install the two Python packages your app needs.

  • Install Flask and requests by running:
pip install flask requests

You should see pip download and install both packages, finishing with a success message.

Seeing a permission error or command not found?

If pip is not found, try pip3 instead. On some systems the command is version-specific.

If you see a permission error, add --user to the end of the command to install packages locally.

Still stuck? Help me install Flask and requests in IBM Bob's terminal.

Verify everything works

You have your credentials saved and packages installed. Time to confirm Python sees everything correctly.

  • Check your Python version by running:
python --version

You should see Python 3.10 or higher printed in the terminal.

Seeing an older version or command not found?

Try python3 --version instead. On macOS, python may point to an older system install while python3 points to the version you installed.

If neither works, revisit the Python installation steps from the prerequisites. Help me fix my Python version.

Before you run the final check, what do you think will happen when Python tries to import Flask and requests?

  • Verify both packages are importable by running:
python -c "import flask; import requests; print('Ready!')"

You should see Ready! printed with no errors. This confirms both packages installed correctly and Python can find them.

Seeing ModuleNotFoundError?

This means pip installed the packages for a different Python version than the one you just ran. Try running the same command with python3 instead of python. Make sure both the pip and python commands point to the same installation.

Need help? Help me fix ModuleNotFoundError for flask or requests.

  • Confirm your .env file has all three values filled in (not still showing placeholders) by running:
cat .env

You should see your actual API key, project ID, and the URL https://us-south.ml.cloud.ibm.com printed.

Your environment is ready. You have credentials, a project folder, and working packages. Next up, you will design your Eco Lifestyle Agent's personality directly in the watsonx.ai Prompt Lab.

Design and Test Your Eco Lifestyle Agent

Your environment is ready, and your credentials are stored safely in your .env file. Now you need the most important ingredient for any AI agent: a quality prompt that shapes how the model responds.

Before writing any code, you can prototype your agent directly in the watsonx.ai Prompt Lab. This lets you iterate on instructions, test different questions, and see results instantly without touching a single line of Python.

In this step, get ready to:
  • Select IBM Granite as your foundation model and configure generation settings.
  • Write a system prompt that transforms Granite into an eco lifestyle advisor.
  • Test the prompt with diverse sustainability questions and verify response quality.
Open the Prompt Lab and select IBM Granite

The Prompt Lab is the playground where you can test prompts against any foundation model available in watsonx.ai. You need to select your model and configure how creative and verbose it should be.

  • In your watsonx.ai project, click the Assets tab.
  • Click New asset, then select the Chat and build prompts with foundation models tile.
  • The Prompt Lab opens with a default model selected.
  • Click the model dropdown at the top of the Prompt Lab.
  • Click View all foundation models to see every available model.
  • Search for and select ibm/granite-3-8b-instruct.
  • Click Select model to confirm your choice.

Why IBM Granite?

IBM Granite is IBM's family of open-source AI models. The ibm/granite-3-8b-instruct variant is tuned to follow instructions carefully, which makes it ideal for a task-specific agent like EcoBot.

It is included in the watsonx.ai free trial at no cost.

  • In the model parameters panel on the right side, locate the Temperature slider and set it to 0.7.
  • Set Max tokens to 500.

What do temperature and max tokens control?

Temperature controls randomness. A value of 0.7 gives responses that are creative enough to sound natural but consistent enough to stay on topic.

Max tokens sets a ceiling on response length. 500 tokens is roughly 375 words, which gives EcoBot enough room to provide detailed advice without rambling.

You should now see ibm/granite-3-8b-instruct displayed in the model selector, with your temperature and token settings visible in the parameters panel.

Write the eco lifestyle system prompt

A system prompt is a set of instructions that shapes how the model behaves for every conversation. Without one, the model gives generic answers. With a focused system prompt, it becomes a specialist.

  • In the Prompt Lab, locate the Instruction field at the top of the prompt editor.
  • Paste the following system prompt into the Instruction field:
You are EcoBot, a friendly and knowledgeable Eco Lifestyle Agent. You help users live more sustainably by:

1. Recommending eco-friendly product alternatives
2. Estimating carbon footprints for daily activities
3. Suggesting sustainable habits and lifestyle changes
4. Providing recycling and waste reduction guidance

Always be specific, practical, and encouraging. When recommending products, mention why they are more sustainable. When discussing carbon footprint, provide approximate numbers when possible. Keep responses concise but informative.

What does this system prompt do?

The first line gives the model an identity and personality. The numbered list defines four specific capabilities so the model knows its scope.

The final paragraph sets quality standards: be specific, use numbers, stay concise. These constraints prevent vague, generic responses and keep EcoBot focused on actionable advice.

Your Instruction field should now contain the full EcoBot system prompt.

Test with multiple eco questions

A prompt is only as good as its outputs. You need to test across EcoBot's four capabilities to confirm it gives practical, relevant responses.

  • In the chat input area at the bottom of the Prompt Lab, type the following product question and press Enter or click Generate:
What's a sustainable alternative to paper towels?

You should see a response recommending specific alternatives like reusable cloths or bamboo paper towels, with reasons why each is more sustainable.

  • Test a carbon footprint question by typing:
How much CO2 does a 10-mile car commute produce?

EcoBot should respond with approximate numbers and suggest lower-carbon alternatives like cycling or public transit.

  • Test a recycling question by typing:
Can I recycle pizza boxes?

EcoBot should explain the grease-contamination issue and suggest composting as an alternative.

Adjust if responses fall short

If responses are too vague, add more specific instructions to your system prompt. For example, you could add "Always include at least one specific product name or brand" or "When estimating CO2, use kg as the unit."

Before you run the final verification, what kind of response do you expect from an eco-lifestyle question about plastic wrap alternatives?

  • Type this final test question into the Prompt Lab:
What are some eco-friendly alternatives to plastic wrap?

You should see a detailed, practical response listing alternatives like beeswax wraps, silicone lids, or reusable containers, with explanations of why each is more sustainable than plastic wrap.

Getting vague or off-topic responses?

Check that your system prompt is in the Instruction field, not the main chat input area. The Instruction field persists across all messages while the chat input is for individual questions.

Confirm your model is set to ibm/granite-3-8b-instruct and not a different Granite variant. Different model sizes produce different quality levels.

Help me fix my EcoBot responses.

Your Eco Lifestyle Agent is producing quality responses across all four capability areas. Next up, you will break EcoBot free from the Prompt Lab by calling it programmatically through the watsonx.ai REST API.

Call Your Agent from the Command Line

Your EcoBot agent works in the Prompt Lab. You tested it with multiple eco-lifestyle questions and got solid answers back from IBM Granite.

The problem is that the agent is trapped inside IBM's web dashboard. Nobody else can use it, and you cannot build a custom interface on top of it. To build a real chat frontend, you first need to prove you can reach the same model programmatically via the REST API. That is exactly what this step does.

In this step, get ready to:
  • Learn how IBM Cloud authenticates API requests with IAM token exchange.
  • Write a test script using IBM Bob that calls watsonx.ai from the command line.
  • Run the script and see an eco-lifestyle response printed in your terminal.
Understand the API authentication flow

IBM Cloud does not let you call the watsonx.ai model directly with your API key. Instead, it uses a two-step process called IAM token exchange.

  • Your API key is sent to IBM's IAM endpoint, which returns a temporary Bearer token.
  • You include that Bearer token in the Authorization header when calling the text generation endpoint.

What are the two endpoints?

The token endpoint is https://iam.cloud.ibm.com/identity/token. You POST your API key here and receive back a short-lived access token.

The text generation endpoint is https://us-south.ml.cloud.ibm.com/ml/v1/text/generation?version=2025-02-11. You POST your prompt and model configuration here, along with the Bearer token, and receive the model's generated text.

Write a test script with IBM Bob

Now that you understand the authentication flow, you will use IBM Bob's Code mode to generate a Python script that performs both steps automatically.

  • In IBM Bob's chat panel, select Code from the mode selector dropdown (or type /code in the chat input).
  • Enter the following prompt in the chat input:
Create a new file called test_api.py in the current project folder. Write a Python script that:
1. Reads WATSONX_API_KEY, WATSONX_PROJECT_ID, and WATSONX_URL from environment variables with fallback defaults
2. Defines a get_iam_token() function that POSTs to https://iam.cloud.ibm.com/identity/token with grant_type urn:ibm:params:oauth:grant-type:apikey
3. Defines a test_generation() function that calls the watsonx.ai text generation endpoint with the ibm/granite-3-8b-instruct model, using a prompt about reducing plastic waste
4. Prints progress messages and the generated response
5. Uses an if __name__ == "__main__" block to run test_generation()

What happens when you send this prompt?

Bob will propose creating a new test_api.py file and show you the code it wants to write. You will need to click Approve to let Bob create the file.

Bob may generate code that is slightly different from the reference below. That is fine as long as the key structure matches: environment variable loading, a token exchange function, and a generation function that builds the correct prompt format.

  • Click Approve when Bob proposes to create the file.
  • Review the generated code in the editor. Confirm it has a get_iam_token() function and a test_generation() function.

✔️ I'll try it first

Review the code Bob generated. Make sure it includes both functions and uses the ibm/granite-3-8b-instruct model ID. If anything looks off, compare against the reference in the other tab.

ⓧ Let me check my work

Your test_api.py should contain these three sections. If Bob generated something different, replace the contents with this code.

Section 1: Imports and credentials.

import os
import requests

WATSONX_API_KEY = os.environ.get("WATSONX_API_KEY", "your-api-key-here")
WATSONX_PROJECT_ID = os.environ.get("WATSONX_PROJECT_ID", "your-project-id-here")
WATSONX_URL = os.environ.get("WATSONX_URL", "https://us-south.ml.cloud.ibm.com")

Section 2: The IAM token exchange function.

def get_iam_token():
    token_url = "https://iam.cloud.ibm.com/identity/token"
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    data = {
        "grant_type": "urn:ibm:params:oauth:grant-type:apikey",
        "apikey": WATSONX_API_KEY
    }
    response = requests.post(token_url, headers=headers, data=data)
    response.raise_for_status()
    return response.json()["access_token"]

Section 3: The test generation function and entry point.

def test_generation():
    print("Getting IAM token...")
    token = get_iam_token()
    print("Token obtained successfully!")

    url = f"{WATSONX_URL}/ml/v1/text/generation?version=2025-02-11"

    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
        "Accept": "application/json"
    }

    prompt = """<|system|>
You are EcoBot, a friendly eco lifestyle assistant.
<|user|>
What are 3 simple ways to reduce plastic waste at home?
<|assistant|>
"""
    body = {
        "input": prompt,
        "parameters": {
            "max_new_tokens": 300,
            "temperature": 0.7,
            "stop_sequences": ["<|user|>", "<|endoftext|>"]
        },
        "model_id": "ibm/granite-3-8b-instruct",
        "project_id": WATSONX_PROJECT_ID
    }

    print("Calling watsonx.ai API...")
    response = requests.post(url, headers=headers, json=body)
    response.raise_for_status()

    result = response.json()
    generated_text = result["results"][0]["generated_text"]
    print("\nEcoBot says:")
    print(generated_text.strip())


if __name__ == "__main__":
    test_generation()

Before running the script, you need to export your credentials as environment variables so the script can read them.

  • In IBM Bob's integrated terminal, export your credentials by running these commands (replace the placeholder values with your real credentials from the .env file):
export WATSONX_API_KEY=your-api-key-here
export WATSONX_PROJECT_ID=your-project-id-here
export WATSONX_URL=https://us-south.ml.cloud.ibm.com

Why export instead of hardcoding?

Environment variables keep credentials out of your source code. The os.environ.get() calls in the script read these values at runtime, so your API key never appears directly in test_api.py.

Before you run the script, predict what you expect to see. Will the script succeed and print eco-lifestyle tips, or will it fail?

  • Run your test script:
python test_api.py

You should see three progress messages followed by EcoBot's response:

Getting IAM token...
Token obtained successfully!
Calling watsonx.ai API...

EcoBot says:
<practical tips about reducing plastic waste>

The exact tips will vary on each run, but you should see a multi-sentence answer with specific, actionable suggestions about reducing plastic waste.

Troubleshoot common errors

If your script threw an error instead of printing tips, check the error message against these common causes.

Seeing an error?

  • 401 Unauthorized means your API key is incorrect or was not exported properly. Double-check the value you exported matches the key in your .env file.
  • 404 Not Found means your project ID is incorrect or the model ID is not available in your region. Verify WATSONX_PROJECT_ID matches the ID from your watsonx.ai project's Manage tab.
  • ConnectionError means WATSONX_URL is wrong. Confirm it is set to https://us-south.ml.cloud.ibm.com (no trailing slash).

If the script succeeds, confirm the response JSON contains a results array with generated_text inside it. That is the structure your Flask app will parse in the next step.

help me debug my watsonx.ai API call

Your API connection is proven. You can reach IBM Granite from a Python script and get eco-lifestyle responses back. Next up, you will build a proper chat frontend so anyone can talk to your agent through a browser.

Build the Chat Frontend with IBM Bob

Your API connection to watsonx.ai works, but a Python script in the terminal is not a chat experience.

In this step, you will use IBM Bob's Code mode to generate a complete Flask web application with a chat interface. You describe what you want in plain English, Bob writes the code, and you review it before saving.

In this step, get ready to:
  • Generate a Flask app with a homepage route and a chat endpoint.
  • Generate a green-themed chat interface with HTML, CSS, and JavaScript.
  • Run the app and verify the UI loads in your browser.
Generate the Flask app with IBM Bob

You need a Flask app with two routes: one to serve the chat page, and one to receive messages from the browser. Bob can generate this entire structure from a single prompt.

  • In IBM Bob, click the mode selector dropdown to the left of the chat input and select Code mode (or type /code in the chat).
  • Enter this prompt in the Bob chat interface:
Create a Flask app called app.py with a route that serves an HTML chat page at / and a POST endpoint at /chat that accepts JSON with a message field and returns a JSON response. Use render_template for the homepage. Include environment variable loading for WATSONX_API_KEY, WATSONX_PROJECT_ID, and WATSONX_URL with os.environ.get and fallback defaults. Make the /chat endpoint return a placeholder JSON response for now.
  • Bob will ask for permission to create the file. Click Approve to let Bob write app.py to your project folder.
  • Review the generated code. It should match the structure below:
import os
import requests
from flask import Flask, render_template, request, jsonify

app = Flask(__name__)

# Load credentials from environment variables
WATSONX_API_KEY = os.environ.get("WATSONX_API_KEY", "your-api-key-here")
WATSONX_PROJECT_ID = os.environ.get("WATSONX_PROJECT_ID", "your-project-id-here")
WATSONX_URL = os.environ.get("WATSONX_URL", "https://us-south.ml.cloud.ibm.com")


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/chat", methods=["POST"])
def chat():
    data = request.get_json()
    user_message = data.get("message", "")

    if not user_message:
        return jsonify({"error": "No message provided"}), 400

    return jsonify({"response": "Chat endpoint placeholder"})


if __name__ == "__main__":
    app.run(debug=True, port=5000)

What does this code do?

  • The / route serves an HTML template. Flask looks for templates in a templates/ folder by default.
  • The /chat endpoint accepts POST requests with a JSON body containing a message field. For now it returns a placeholder string.
  • The environment variables match the values you already have in your .env file from the previous step.
  • If Bob's output differs slightly from the reference above, adjust it so the route names, function names, and return values match exactly.
  • Save app.py.

Flask needs a templates folder to find HTML files. Without it, the / route will crash.

  • Create the templates folder by running this command in IBM Bob's terminal:
mkdir templates
  • Confirm the folder was created by running:
ls

You should see app.py, templates, test_api.py, and .env listed in the output.

Don't see the templates folder?

  • Make sure your terminal is inside the ecobot folder. Run pwd to check your current directory.
  • If you are in a different folder, run cd ~/Desktop/ecobot to get back.

Still stuck? Help me create the templates folder in my ecobot project

Generate the chat interface HTML

Now you need the actual chat page that users will see. This includes a header, a scrollable message area, a text input, and JavaScript to send messages to your /chat endpoint.

  • In Bob's chat interface (still in Code mode), enter this prompt:
Create a chat interface HTML file at templates/index.html with a green eco theme (use #2d6a4f as the primary green). Include:
- A header with the title "EcoBot" and subtitle "Your Eco Lifestyle Agent - Ask about sustainable products, carbon footprint, recycling, and more"
- A scrollable message area with a welcome message from the bot
- An input field with placeholder "Ask about eco-friendly living..." and a Send button
- JavaScript that sends messages to /chat via fetch POST with JSON body {message: text}, shows "Thinking..." while waiting, and displays the response. Disable the send button while waiting.
  • Bob will ask for permission to create the file. Click Approve to let Bob write templates/index.html.

What should the generated HTML contain?

Review Bob's output and confirm these key elements are present:

  • A .header div with background-color: #2d6a4f containing an h1 that says "EcoBot".
  • A .chat-container div with overflow-y: auto for scrollable messages.
  • A welcome message div with class message bot already inside the chat container.
  • An input field with id="messageInput" and a button with id="sendButton".
  • A sendMessage() function that calls fetch("/chat", ...) with a JSON body.
  • If any of the key elements above are missing or named differently, adjust the generated file to match.
  • Save templates/index.html.

Before you run the app, what do you think will happen when you type a message and click Send?

  • Start the Flask app by running this command in IBM Bob's terminal:
python app.py

You should see output including Running on http://127.0.0.1:5000 in the terminal.

  • Open your web browser and navigate to http://localhost:5000.

You should see a green-themed chat interface with "EcoBot" in the header, a welcome message from the bot, an input field, and a Send button.

  • Type Hello in the input field and click Send.

You see "Thinking..." appear briefly, then the response "Chat endpoint placeholder" shows up. That placeholder proves your frontend is successfully talking to your Flask backend.

Page not loading?

  • If you see a TemplateNotFound error in the terminal, confirm index.html is inside the templates folder (not in the ecobot root).
  • If the page loads but sending a message shows "Sorry, I could not connect to the server", check that python app.py is still running in your terminal.
  • If port 5000 is already in use, stop the other process or change the port in app.py.

Still stuck? Help me get my Flask EcoBot chat page to load in the browser

✔️ Awesome, I've got everything!

Great. Double check you have saved both app.py and templates/index.html, and that the Flask server is still running.

ⓧ I'd like to double check the full code

Here is what app.py should look like at this point:

import os
import requests
from flask import Flask, render_template, request, jsonify

app = Flask(__name__)

WATSONX_API_KEY = os.environ.get("WATSONX_API_KEY", "your-api-key-here")
WATSONX_PROJECT_ID = os.environ.get("WATSONX_PROJECT_ID", "your-project-id-here")
WATSONX_URL = os.environ.get("WATSONX_URL", "https://us-south.ml.cloud.ibm.com")


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/chat", methods=["POST"])
def chat():
    data = request.get_json()
    user_message = data.get("message", "")

    if not user_message:
        return jsonify({"error": "No message provided"}), 400

    return jsonify({"response": "Chat endpoint placeholder"})


if __name__ == "__main__":
    app.run(debug=True, port=5000)

For templates/index.html, confirm these sections are present in order:

  • A <style> block with CSS for .header, .chat-container, .message, and .input-area using greens (#2d6a4f, #1b4332, #d8f3dc).
  • A .header div containing <h1>EcoBot</h1> and a subtitle paragraph.
  • A #chatContainer div with a welcome .message.bot div inside.
  • An .input-area with #messageInput and #sendButton.
  • A <script> block with addMessage() and sendMessage() functions that call fetch("/chat", ...).

Your frontend is live and communicating with the Flask backend. Next up, you will wire the /chat endpoint to watsonx.ai so every message gets answered by IBM Granite.

Connect the Frontend to watsonx.ai

Your chat interface looks polished and professional. But if you type a question right now, all you get back is the word Chat endpoint placeholder.

The /chat route in your Flask app is not connected to watsonx.ai yet. In this step, you will wire the backend so every message typed in the browser travels to IBM Granite and comes back as a real sustainability response.

In this step, get ready to:
  • Add a system prompt, IAM token exchange, and text generation function to your Flask app.
  • Wire the /chat route to call watsonx.ai with error handling.
  • Test the full end-to-end chat experience with real eco questions.
Add the watsonx.ai integration code

Your test_api.py already proved the API connection works. Now you need the same logic inside app.py so the Flask server can call watsonx.ai on behalf of the browser.

  • In app.py, find the line that says WATSONX_URL = os.environ.get("WATSONX_URL", "https://us-south.ml.cloud.ibm.com").
  • Add the following system prompt constant directly below it:
SYSTEM_PROMPT = """You are EcoBot, a friendly and knowledgeable Eco Lifestyle Agent. You help users live more sustainably by:

1. Recommending eco-friendly product alternatives
2. Estimating carbon footprints for daily activities
3. Suggesting sustainable habits and lifestyle changes
4. Providing recycling and waste reduction guidance

Always be specific, practical, and encouraging. When recommending products, mention why they are more sustainable. When discussing carbon footprint, provide approximate numbers when possible. Keep responses concise but informative."""

What does this code do?

SYSTEM_PROMPT defines EcoBot's personality and expertise. Every time a user sends a message, this prompt gets sent alongside it to guide the model's response style.

The numbered list gives the model specific capabilities to draw from. The final paragraph sets the tone: concise, practical, and encouraging.

  • Save app.py.
  • Check your terminal where Flask is running. You should see it automatically reload without any error messages.

Flask not reloading?

Make sure your Flask app was started with debug=True (it is in the code from the previous step). If the terminal shows a SyntaxError, check that the triple-quoted string has matching """ at both the start and end.

help me fix a SyntaxError in my SYSTEM_PROMPT string in app.py

  • Now add the IAM token exchange function below SYSTEM_PROMPT by pasting this code:
def get_iam_token():
    # Exchange your API key for a temporary Bearer token
    token_url = "https://iam.cloud.ibm.com/identity/token"
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    data = {
        "grant_type": "urn:ibm:params:oauth:grant-type:apikey",
        "apikey": WATSONX_API_KEY
    }
    response = requests.post(token_url, headers=headers, data=data)
    response.raise_for_status()
    return response.json()["access_token"]

What does this code do?

IBM Cloud does not accept raw API keys in request headers. Instead, you exchange your key at the IAM token endpoint for a short-lived Bearer token. This token is what authenticates your actual generation request.

This is the same pattern your test_api.py uses. Keeping it as a separate function means both the test script and the Flask app share the same authentication flow.

  • Save app.py.
  • Check your terminal again. Flask reloads without errors.

Next, you need the function that actually sends your user's message to the Granite model and returns the response.

  • Add the first part of the generate_response function below get_iam_token:
def generate_response(user_message):
    # Get a fresh token for this request
    token = get_iam_token()

    # Build the watsonx.ai text generation endpoint URL
    url = f"{WATSONX_URL}/ml/v1/text/generation?version=2025-02-11"

    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
        "Accept": "application/json"
    }

    # Format the prompt using Granite's chat template
    prompt = f"""<|system|>
{SYSTEM_PROMPT}
<|user|>
{user_message}
<|assistant|>
"""

What does this code do?

The function starts by getting a fresh IAM token, then builds the API URL with the required version parameter. The headers include the Bearer token for authentication.

The prompt variable uses special tags (<|system|>, <|user|>, <|assistant|>) to structure the conversation for the Granite model. The system instructions come first, followed by the user's actual question, then the assistant tag signals the model to generate its answer.

  • Continue the generate_response function by adding this code directly below the prompt (still inside the function):
    # Configure the API request body
    body = {
        "input": prompt,
        "parameters": {
            "max_new_tokens": 500,
            "temperature": 0.7,
            "stop_sequences": ["<|user|>", "<|endoftext|>"]
        },
        "model_id": "ibm/granite-3-8b-instruct",
        "project_id": WATSONX_PROJECT_ID
    }

    # Send the request and extract the generated text
    response = requests.post(url, headers=headers, json=body)
    response.raise_for_status()

    result = response.json()
    generated_text = result["results"][0]["generated_text"]
    return generated_text.strip()

What does this code do?

The body dict tells watsonx.ai which model to use (ibm/granite-3-8b-instruct), how creative to be (temperature: 0.7), and when to stop generating (at <|user|> or <|endoftext|> tokens).

After the POST request, the response JSON contains a results array. You grab the first result's generated_text and strip any trailing whitespace before returning it.

  • Save app.py.
  • Check your terminal. Flask reloads without errors, confirming the function syntax is valid.

Seeing an IndentationError?

The second code block (starting with body = {) must be indented one level inside the generate_response function. Make sure both code blocks use the same indentation style (4 spaces per level).

help me fix an IndentationError in my generate_response function

Now you have all the helper functions. Time to wire them into the /chat route.

  • In app.py, scroll down to the chat() function.
  • Find this line:
    return jsonify({"response": "Chat endpoint placeholder"})
  • Replace it with this code that calls your generate_response function:
    response_text = generate_response(user_message)
    return jsonify({"response": response_text})
  • Save app.py.
  • Flask reloads in the terminal.

Before you test, what do you think will happen when you type an eco question into the chat now?

  • Switch to your browser at http://localhost:5000 and refresh the page.
  • Type What are some eco-friendly alternatives to plastic wrap? and click Send.

After a few seconds, EcoBot responds with practical sustainability advice. The message travels from your browser to Flask, from Flask to watsonx.ai, and back again.

Seeing an error in the chat?

If EcoBot says something like API error: 401, your API key in the .env file is incorrect or you are not loading it into your environment. Make sure you exported the variables in your terminal before starting Flask, or use python-dotenv to load them automatically.

If you see API error: 404, double-check your project ID matches the one from your watsonx.ai project's Manage tab.

help me debug why my watsonx.ai API call is returning an error in my Flask app

Add error handling and loading state

Your chat works, but if watsonx.ai is temporarily unavailable or your token expires mid-session, the app crashes with an unhandled exception. Proper error handling returns a friendly message instead.

  • In app.py, find the two lines you just added inside the chat() function:
    response_text = generate_response(user_message)
    return jsonify({"response": response_text})
  • Replace those two lines with this error-handling version:
    try:
        response_text = generate_response(user_message)
        return jsonify({"response": response_text})
    except requests.exceptions.HTTPError as e:
        return jsonify({"error": f"API error: {e.response.status_code}"}), 500
    except Exception as e:
        return jsonify({"error": str(e)}), 500

What does this code do?

The try/except block catches two types of failures. HTTPError catches specific API errors (like 401 or 404) and returns the status code in the message. The generic Exception catch handles anything else (network timeouts, JSON parse errors) and returns the error text.

Your frontend JavaScript already shows these error messages to the user. It also already displays a Thinking... indicator while waiting for the response, so you do not need to add any loading-state code.

  • Save app.py.
  • Switch to your browser and type another question to confirm the chat still works with the error handling in place.

Chat stopped responding after the edit?

Check your terminal for a SyntaxError or IndentationError. The try: and except blocks must be indented at the same level (one tab or 4 spaces inside the chat() function).

help me fix the indentation in my Flask /chat route's try/except block

Test the full experience

Your EcoBot is fully connected. Time to put it through its paces with different types of eco questions to confirm the system prompt shapes responses correctly.

  • In the chat interface, type: What should I use instead of plastic bags? and send it.
  • Confirm EcoBot suggests specific alternatives and explains why they are more sustainable.
  • Type: How much CO2 does flying from NYC to LA produce? and send it.
  • Confirm EcoBot provides an approximate number for the carbon footprint.
  • Type: How do I recycle electronics? and send it.
  • Confirm the response includes practical guidance about e-waste recycling options.

Before you run this final check, what do you expect EcoBot to say about reducing your carbon footprint?

  • Type: What are 3 easy ways to reduce my carbon footprint? and send it.

EcoBot responds with specific, practical sustainability tips within a few seconds. All four test questions produced relevant, actionable answers shaped by your system prompt.

What should I see?

Each response should be friendly and encouraging in tone. Product questions should name specific alternatives. Carbon footprint questions should include approximate numbers. Recycling questions should provide actionable next steps. If any responses feel vague or off-topic, you can refine the SYSTEM_PROMPT constant in app.py and save to see the change immediately.

✔️ Awesome, I've got everything!

Great. Double check you have saved app.py and your Flask server is running without errors in the terminal.

ⓧ I'd like to double check the full code

Scroll through your app.py and confirm the following sections are present, in order:

  • Imports (os, requests, flask modules).
  • app = Flask(__name__) and the three environment variable lines.
  • SYSTEM_PROMPT triple-quoted string defining EcoBot's personality.
  • get_iam_token() function exchanging the API key for a Bearer token.
  • generate_response(user_message) function building the prompt and calling the watsonx.ai endpoint.
  • @app.route("/") serving the HTML template.
  • @app.route("/chat", methods=["POST"]) calling generate_response inside a try/except block.
  • if __name__ == "__main__": block running the app on port 5000.

Your Eco Lifestyle Agent is fully operational. Every eco question you type in the browser travels through Flask to IBM Granite and returns as personalized sustainability advice. Next up, you will push this further in the Secret Mission by giving EcoBot memory across a conversation.

Secret mission

Add Conversation Memory

Right now EcoBot forgets everything between messages. If you ask "What about recycling those?" after discussing plastic bottles, it has no idea what "those" refers to. Can you give EcoBot a memory so it maintains context across a conversation?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs on free trials for both watsonx.ai and IBM Bob, so there are no ongoing costs within the trial limits. The Flask app only runs when you start it locally.

Resources you used:

  • IBM watsonx.ai project (free trial, no ongoing cost within trial limits).
  • IBM Bob IDE (free trial, 40 Bobcoins for 30 days).
  • Local ecobot project folder with Flask application files.
  • Python packages: flask and requests.

Keep everything running

No action needed. Choose this if you are still actively building or plan to extend EcoBot further.

  • Your watsonx.ai project persists in IBM Cloud. It costs nothing when idle within the free trial.
  • IBM Bob IDE stays installed on your machine. Your 30-day trial with 40 Bobcoins continues counting down.
  • The Flask app only runs when you start it with python app.py. No background processes remain after you close the terminal.

Pause - I'll come back to this later

Shut down running processes to free up memory. Your files and cloud project stay intact for when you return.

  • Stop the Flask app by pressing Ctrl+C in the terminal where it is running.
  • Close IBM Bob IDE.
  • Your watsonx.ai project stays active in IBM Cloud but costs nothing when idle.
  • Your local ecobot folder remains on your Desktop for next time.

Delete - I don't want to use this again

Remove all project resources and start fresh. Follow each step below to clean up completely.

  • Delete the watsonx.ai project: log into IBM Cloud, click the Navigation menu, then select Projects. Click the Actions icon next to your project and choose Delete project. Enter the project name to confirm, then click Delete.
  • Remove the IBM Bob IDE application. On macOS, drag IBM Bob from your Applications folder to the Trash. On Windows, open Settings, select Apps, find IBM Bob, and click Uninstall.
  • Delete the local project folder by running this command in your terminal:
cd ~/Desktop
rm -rf ecobot
  • Uninstall the Python packages by running:
pip uninstall flask requests
  • When prompted to confirm each uninstall, type y and press Enter.

Nice Work!

Nice Work!

You just built a full-stack AI chatbot from scratch. Your Eco Lifestyle Agent runs on IBM Granite via watsonx.ai, serves a custom green-themed chat interface, and answers real sustainability questions with practical advice.

You've learned how to:

  • Built a custom Eco Lifestyle Agent powered by IBM Granite on watsonx.ai with specialized sustainability instructions.
  • Learned IBM Cloud IAM authentication and REST API integration for watsonx.ai.
  • Used IBM Bob's AI-assisted coding to generate a complete Flask web application with a chat interface.
  • Connected a custom frontend to a cloud AI backend via API, proving full-stack AI application development.
  • Secret Mission: Added conversation memory so EcoBot maintains context across multi-turn conversations.

Ready to quiz yourself?