Gen AI Intensive Course Capstone Submission (2025Q1)

The StyleBot-3000

Introduction

I recently completed my capstone project as part of Google’s 5-Day Gen AI Intensive Course through Kaggle. This course focuses on the latest developments in generative AI, highlighting the capabilities of Google's Gemini multimodal family of models. Over the course of five days, we explored everything from the basics of LLMs and prompt engineering to embeddings, AI agents, domain-specific models like Med-PaLM, and MLOps for generative AI using Vertex AI.

Each day layered on new skills, from understanding how embeddings power vector searches to building smarter, more autonomous AI agents. It was a fast, deep dive into where GenAI is today, and to where it's heading.

In this post, I’ll walk through some key learnings from my project, and where I see these tools opening exciting new doors for developers, researchers, and businesses alike.

Resources

 
 

The Concept

Will the StyleBot-3000 make all your styling dreams come true? Probably not.

Will it show you how to connect an agentic AI chatbot to a SQL database? Absolutely.

This project isn’t really about fashion - it’s about bringing together two powerful engineering ideas: building a chatbot that’s autonomous enough to scan a database and respond to users naturally.

On Day 3 of the course, we explored function calling with the Gemini API, which showed us how to give Gemini programmatic access to a SQL database. In a separate workbook, we also built an agentic chatbot using LangGraph, designed to let users place a coffee order through casual conversation. However, unlike in the first workbook, the chatbot’s responses were hardcoded - they didn’t pull from a real database.

Right away, I saw the potential in combining these two ideas. By giving a conversational agent real-time access to structured data, we could create a flexible, intelligent frontend for nearly any data-driven application.

Getting Started

This workbook assumes you have a Google API key linked to your Kaggle account. If you haven’t set that up yet, check out the Day 1 - Prompting workbook for a walkthrough on how to register your API key.

Whenever I build a program, I like to include a clear “go” button - a simple protocol for getting everything up and running. In this case, you can copy the workbook to your Kaggle account by clicking “Copy & Edit” in the upper right. Then, just hit “Run All” and scroll down to the second-to-last cell.

Once the chatbot loads (it can take a few minutes), you’ll be able to interact with it until you either press the letter ‘q‘ or the StateGraph reaches 100 levels of recursion.

In the very last cell, I’ve included a number of example prompts that you are welcome to try.

Workbook Walkthrough

In this section, I’ll walk through each cell of the StyleBot-3000 Kaggle workbook at a summary level, giving a brief overview of its purpose. The line item numbers correspond to the cell numbers in the notebook.

  1. Environment Setup: Basic preamble to help prevent package conflicts, based on the template from the second Day 3 workbook.

  2. API Key Registration: Make the Google API key available in the environment.

  3. Load SQLite: Import SQLite to manage the database locally.

  4. Build the Product Database: Create a simple SQLite database of products.

  5. Connect to the Database: Programmatically load a connection to the new database.

  6. Prompt Engineering: Define the general behavior of the chatbot, including instructions on when to call certain functions. Also sets up the welcome message.

  7. Build the Toolset: Create the set of tools the chatbot can use during conversation.

  8. Conversation Class: Set up a class to store and manage the full chat history.

  9. Bind Tools to the LLM: Make the available tools accessible by binding them to the LLM.

  10. Create Nodes: Define the nodes that will be used in the final LangGraph (StateGraph).

  11. Build the LangGraph: Assemble the full LangGraph (StateGraph) for the chatbot.

  12. Configure and Launch the Chatbot: Set up and run the chatbot, making it ready for user interaction.

  13. Example Prompts: Provide sample prompts to test and interact with the chatbot.

Detailed Walkthrough & Thoughts

First and foremost…

I’m a data guy. I love data - truly. Anytime I can access a database programmatically, I get happy. When I first started this project, I wanted to build a full-blown inventory management system. However, I quickly ran into a number of issues that were ultimately caused by a combination of poor prompt engineering and inadequate debug reporting.

So, I decided to scale the project back to more familiar territory. I know how to manipulate data in my sleep. We had also learned how to give Gemini programmatic access to data and how to build an agentic chatbot. By, “agentic” I mean that the program can decide which action to take next, without hardcoding specific paths. In other words, we give the chatbot tools and rules, but let it choose when (and how) to use them.

The Data

I started by coding a simple dataset of clothing, shoes, and accessories. I intentionally structured it so that the bot would need to “work” a bit to correctly assess the inventory. For example, there’s a red shirt categorized for women and a green shirt categorized for men. Both genders have blue shirts, but all accessories are unisex.

I also purposely left gaps in the inventory to challenge the model’s reasoning, forcing it to navigate ambiguity rather than being spoon-fed complete data. Each product included descriptions, giving the model more material to evaluate and respond appropriately.

The Behavior

With the database handled, the biggest challenge quickly revealed itself: prompt engineering.

I’ve been an avid LLM user for a while, so I initially assumed prompting would be straightforward. I couldn’t have been more wrong. About 70% of the time, unexpected behavior traced directly back to flaws in the main behavioral prompt. Sometimes, contradictory instructions would cause the model to “fight itself” for the correct response. Other times, the model would hallucinate entire inventories that didn’t exist. Tightening the prompts too much, however, made the model overly rigid - it would reject valid user input because it couldn’t find a literal match.

I wanted the bot to only pull from the database, but still have the creativity to, say, recommend a silver bracelet when someone mentioned they liked “shiny things.” After a lot of iteration, I found a working balance.

One major breakthrough was front-loading a unique set of categories. In the behavioral prompt, I instruct the bot to first pull the unique product categories before doing anything else (see “Before issuing your first recommendation query…” in the behavioral prompt). This reduced the memory overhead problems associated with pre-loading the entire dataset, while giving the bot a referenceable framework to narrow down user requests at the same time. After adding this step, accuracy and recommendation quality improved significantly.

Another helpful discovery was limiting the bot to only one constraint during database searches. Early debugging showed the bot would either get way too specific (e.g., constraining by type, fit, and color all at once) or overly literal (e.g., searching specifically for "shiny," which is not a valid option among type, fit, or color). In both cases, the queries returned too little data for the model to work with. By intentionally handicapping the search process to one constraint at a time, the bot had a wider pool of results to consider, striking a much better balance between relevance and flexibility.

The Tools

Building the toolset for the model was genuinely refreshing. I’m a heavy user of docstrings in my own coding, and seeing them used here - not just for documentation, but as functional instruction for the model - was really quite incredible.

In one workbook (Day 3 - Building an agent with LangGraph), it was fascinating to see tools defined as essentially as stubs, using just docstrings to explain their purpose, and then watch the model conceptually incorporate them at the node level during conversation. The implication here is mind-blowing: we can simply describe a function's behavior, and the model handles the rest.

Two small notes here:

  • I kept the list_tables() tool for posterity. It was useful as a design reference when building other tools, even though it wasn’t strictly necessary used in the chatbot’s final workflow.

  • One major debugging challenge was realizing that the database connection didn’t operate correctly when defined earlier in the program (cell 5). I left the original connection setup intact, just in case other parts of the program referenced it - though the active database connection now happens inside the tool definitions themselves. I tracked down this issue by using the immensely helpful verbose output from the Day 3 - Function Calling with the Gemini API workbook (see the "Errors & Troubleshooting" section below).

Putting it All Together: The LangGraph

An Agentic Chatbot Built with LangGraph

Building the LangGraph was very straightforward - and even comes with some visualization capabilities (see right).

The structure here is highly reminiscent of AnimGraph State Machines in Unreal Engine’s PaperZD plug-in: each node decides if and when to move to another state and can only transition along its connected edges.

This setup is what corrals the chatbot’s autonomy into its designed purpose. The chatbot can only perform a limited set of actions - respond to and prompt the user, use a tool, or exit the program - because these are the only actions defined within the LangGraph space it operates in.

While this LangGraph is one node simpler than the original chatbot graph presented in the course, it’s important to note that this chatbot now has access to a full-blown database, complete with the tools to query and analyze it.

A quick comment on the nodes themselves: in most of the other workbooks, a retry helper was introduced to handle disconnects and rate limit errors. During my experimentation, I encountered a few rate threshold exceptions (HTTP 429 errors). To stay consistent with the previous examples, I researched how to add retry functionality in the event of common errors.

This retry capability is applied at the node level (see retry=RetryPolicy() in cell 11) and is attached specifically to the toolset and chatbot nodes.

Errors & Troubleshooting

Throughout the creation of this chatbot, the verbose output from the Day 3 - Function Calling with the Gemini API workbook was an immense help. It provided a detailed look at what the chatbot was doing, how it was reasoning through each step, and any errors it encountered. This was how I discovered the database connection issue mentioned earlier, and was a key factor in having everything click into place.

As I became more familiar with the workflow, I scaled back the level of debug output. The original verbose logging - while extremely useful for troubleshooting - cluttered the final code and wasn’t necessary for regular operation. In the final version of the workbook, I kept only the abbreviated debug lines (commented out), which can easily be re-enabled if deeper troubleshooting is needed later.

Conclusion

Building the StyleBot-3000 was a rewarding exercise - not because the final product is flashy, but because of everything I learned along the way.

Working with agentic models, real databases, and tools like LangGraph gave me a firsthand appreciation for how powerful, but also how tricky, designing AI workflows can be. I expected most of the challenges to be technical, but instead, the biggest hurdles came from prompt engineering and finding the right balance between freedom and structure inside the model’s behavior.

This project, and the course that enabled it’s creation, reinforced learnings I’ll take into every future AI build: clear structure, flexible design, and good debugging practices are just as important as good ideas. It also reminded me that sometimes, scaling back a project to its essentials isn’t a step backward - it’s the path to actually making it work.

There's so much potential in combining conversational agents with real data access, and this project only scratched the surface. I'm excited to keep exploring what’s possible.