My Capstone project for Google GenAI course

veggie-recipe-generator Image credit: Generated using GPT-4o.

How I built Generative AI Agent for Veg Recipe generator with LangGraph and Gemini

My Problem with finding best recipes

Lately I have been spending lot of time browsing Internet or youtube cooking videos to find perfect recipe for cooking specific vegetables. Sometimes I over boil broccoli and sometimes I spend time figuring out how to cook Mexican version of bell peppers dish. During these times I wonder how great it would be to have a magic tool which could answer my queries directly.

How Generative AI agent saved my day?

Recently I took this hands-on “5-day Gen AI Intensive Course with Google” and thought this might be a good way to solve my problem with recipes.

This is the definition from Google’s whitepaper:

“Generative AI agents are applications that strive to achieve goals by perceiving their environment and acting upon it using available tools. They extend the capabilities of standalone Generative AI models by integrating reasoning, logic, and access to external information.”

To put it simply: LLMs cannot interact with the external world or act on the external events. GenAI agents use LLMs, reasoning, logic and external tools to achieve the objectives set by the user.

This fits perfectly with my personal problem I mentioned about finding recipes. If I set the objective of generating recipes for a given vegetable and if I add tools to get recipes for such vegetables then the GenAI agent will reason about my request, use logic to find the recipes using perfect tool and format it in a human-readable format and present it to me.

So, I decided to create a GenAI Agent for Veggie Recipe Generator which helps me with recipes for the vegetables I want. In this capstone project, I have used mock recipes to demonstrate what I have learnt in this course.

Demonstrates three GenAI capabilities

This project uses Google gemini-2.0-flash API and LangGraph.

It will be demonstrating these GenAI capabilities using :

  • Generative AI agents: Using LangGraph to create multi-step conversational flow.
  • Structured output: Making the LLM return information in a predictable human-readable format.
  • Few-shot prompting: Guiding LLM’s behavior with clear examples.

The Solution

This solution uses an agent built with LangGraph and powered by Google Gemini API. It follows defined graph to manage the conversation.

  1. It greets the user.
  2. It understands the user’s request for a vegetable recipe.
  3. It checks if the vegetable is one it knows (Carrot, Broccoli, etc.).
  4. If valid, it uses a “tool” (a simulated function) to look up the recipe.
  5. If invalid, it politely informs the user of its limitations.
  6. It presents the recipe (if found) in a clear, structured format.
  7. It asks the user if they want another recipe or want to quit.
  8. It loops back or exits based on the user’s response.

The Code

State Management

LangGraph uses state dictionary to pass between nodes. Here is it’s structure.

from typing import Annotated, List, Literal, TypedDict as TypeHint
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langchain_core.messages import BaseMessage

### Define the structure for the recipe data returned by the tool
class Recipe(TypeHint):
    title: str
    ingredients: List[str]
    instructions: str

class RecipeState(TypedDict):
    """State representing the user's recipe conversation."""
    messages: Annotated[List[BaseMessage], add_messages] # Conversation history
    finished: bool # Flag to end the conversation

Guiding LLM using few-shot prompting and structured output

The core instructions tell the AI its role, limitations, how to use tools, and crucially, how to format the structured output it receives from the tool. We include examples (few-shot prompting) to make these instructions clearer:

# Snippet from RECIPEBOT_SYSINT
RECIPEBOT_SYSINT = (
    "system",
    "You are RecipeBot... You can provide recipes ONLY for... Carrot, Broccoli, Spinach, Potato, Bell Pepper.\\n\\n"
    "When the user asks for a recipe... you MUST use the 'get_recipe' tool...\\n"
    "The 'get_recipe' tool will return structured information with 'title', 'ingredients', and 'instructions'.\\n"
    "When you receive the recipe from the tool, present it clearly to the user in this format:\\n"
    "Okay, here is a recipe for [Vegetable Name]:\\n"
    "**[Recipe Title]**\\n"
    "Ingredients:\\n- [Ingredient 1]\\n- [Ingredient 2]\\n...\\n"
    "Instructions:\\n [Instructions text]\\n\\n"
    "If the user asks for a vegetable not on the list ..., politely tell them you only have recipes for those listed vegetables, and ask if they want one of those instead.\n\n"
    "After providing a recipe... ALWAYS ask the user if they would like another recipe... or if they want to quit.\\n\\n"
    "Example Interactions:\\n"
    "Human: Can I get a recipe for carrots?\\n"
    "AI: <tool_call>get_recipe(vegetable='carrot')</tool_call>\\n"
    "Tool: {'title': 'Simple Roasted Carrots', 'ingredients': ['1lb chopped carrots', ...], 'instructions': 'Toss chopped carrots...'}\\n"
    "AI: Okay, here is a recipe for Carrot:\\n**Simple Roasted Carrots**\\nIngredients:\\n- 1lb chopped carrots\\n...\\nInstructions:\\nToss chopped carrots...\\nWould you like another recipe... or would you like to quit?\\n"
    "Human: Do you have one for Zucchini?\\n"
    "AI: I'm sorry, I only have recipes for Carrot, Broccoli, Spinach, Potato, and Bell Pepper. Would you like one for those?\\n"
    # ... more examples ...
)

get_recipe tool

We define a Python function decorated with LangChain’s @tool. This function simulates looking up a recipe and returns it as a structured Python dictionary (Recipe), not just a block of text.

from langchain_core.tools import tool
from typing import Dict, Any, Optional

# Mock Recipe Data (Structured)
MOCK_RECIPES: Dict[str, Recipe] = {
    "carrot": {"title": "Simple Roasted Carrots", "ingredients": [...], "instructions": "..."},
    "broccoli": {"title": "Steamed Broccoli with Lemon", "ingredients": [...], "instructions": "..."},
    # ... other recipes
}

@tool
def get_recipe(vegetable: str) -> Optional[Recipe]: # Returns structured Recipe or None
    """Provides a simple structured vegetarian recipe (title, ingredients, instructions) for the specified vegetable. Only works for Carrot, Broccoli, Spinach, Potato, Bell Pepper."""
    recipe_data = MOCK_RECIPES.get(vegetable.lower())
    if recipe_data:
        return recipe_data
    else:
        return None

Returning structured output from the tool allows the chatbot node to reliably extract and format the title, ingredients, and instructions according to the system prompt’s rules. Currently we are returning hardcoded mock data.

Building the graph with LangGraph

We combine the nodes which perform actions, edges which define flow between the nodes and conditional logic to when to call a tool? or When to quit?

from langgraph.graph import StateGraph, START, END

# --- Build Graph ---
# print("Building graph...") # Debug
graph_builder = StateGraph(RecipeState)

# Add nodes
graph_builder.add_node("chatbot", chatbot_node)
graph_builder.add_node("human", human_node)
graph_builder.add_node("tools", tool_node)

# Define entry point
graph_builder.add_edge(START, "chatbot")

# Define edges with conditional routing
graph_builder.add_conditional_edges(
    "chatbot",
    route_logic,
    {
        "tools": "tools",
        "human": "human",
    }
)
graph_builder.add_conditional_edges(
    "human",
    exit_logic,
    {
        "chatbot": "chatbot",
        END: END
    }
)

# Tool node always returns to chatbot
graph_builder.add_edge("tools", "chatbot")

# Compile the graph
recipe_graph = graph_builder.compile()

Kaggle notebook

If you want to see the code or would like to run it, you can find the jupyter notebook on Kaggle here.

Feel free to give me your feedback!

Limitations

  1. Currently get_recipes tool uses MOCK_RECIPES dictionary hardcoded. In the future, it would be great to search the Internet or use RAG to search from an embedding vector database.
  2. Limited it to only 5 vegetables to keep it simple. It could be expanded to include list of ingredients and also allergies and some ingredients to avoid.
  3. Current chat box is very basic. It would be great to add voice input.
  4. Add more tools to find supermarkets to buy the ingredients.

Conclusion

This capstone project demonstrates how combining the conversational power of Google’s Gemini model with the structured workflow capabilities of LangGraph allows for the creation of useful and interactive AI agents. By leveraging few-shot prompting and handling structured tool outputs within an agentic framework, I have solved my recipe issue. There is a lot more one can build with Generative AI Agents.

Follow me

If you are new to my posts, I regularly post about AWS, EKS, Kubernetes and Cloud computing related topics. Do follow me in LinkedIn and visit my dev.to posts. You can find all my previous blog posts in my blog