Skip to content

Instantly share code, notes, and snippets.

@vincentkoc
Last active October 9, 2025 19:52
Show Gist options
  • Select an option

  • Save vincentkoc/638f11fcb00d0351473be5a8705d4c08 to your computer and use it in GitHub Desktop.

Select an option

Save vincentkoc/638f11fcb00d0351473be5a8705d4c08 to your computer and use it in GitHub Desktop.
Google Cloud/Gemini ADK + Opik for Agent ADK
# Google API Key
# Get your key from: https://ai.google.dev/
GOOGLE_API_KEY=your_google_api_key_here
# Opik Configuration (Optional - will prompt for configuration if not set)
# Get your key from: https://www.comet.com/
# OPIK_API_KEY=your_opik_api_key_here
# OPIK_WORKSPACE=your_workspace_name

Recipe Agent Demo with Google ADK and Opik

A multi-agent system demonstration using Google's Agent Development Kit (ADK) with Comet Opik observability integration. This project showcases how to build intelligent, traceable AI agents for recipe suggestions and research with a beautiful CLI interface powered by Rich.

Features

  • 🤖 Multi-Agent Architecture: Sequential workflow with specialized agents
  • 🔍 Recipe Research: Intelligent agents that suggest and research recipes
  • 📊 Full Observability: Complete tracing and monitoring via Comet Opik
  • 🛠️ Tool Integration: Custom tools for database search and nutritional analysis
  • Google Gemini: Powered by Google's latest Gemini 2.0 Flash model
  • 🎨 Beautiful CLI: Rich-powered interface with colors, panels, and markdown rendering

Architecture

The system consists of a sequential multi-agent workflow:

  1. RecipePipeline (SequentialAgent)

    • Ensures both agents run in order
    • Passes state between agents
    • Guarantees complete workflow execution
  2. RecipeSuggester (Agent)

    • Analyzes provided ingredients
    • Generates creative recipe suggestions
    • Provides cooking instructions and timing
    • Outputs to recipe state key
  3. RecipeResearcher (Agent)

    • Accesses suggested recipe from state
    • Uses tools to gather nutritional information
    • Researches recipe background and context
    • Suggests variations and cooking tips
    • Outputs to research state key
  4. RecipeMasterAgent (Root Agent)

    • Delegates to RecipePipeline
    • Synthesizes information from both agents
    • Provides final formatted response

Setup

Prerequisites

Installation

  1. Clone or download this repository

  2. Install dependencies:

    pip install -r requirements.txt
  3. Configure environment variables:

    cp .env.example .env

    Edit .env and add your API keys:

    GOOGLE_API_KEY=your_google_api_key_here
    
  4. Configure Opik (Optional - will prompt if not configured):

    opik configure

    Or set environment variables in .env:

    OPIK_API_KEY=your_opik_api_key_here
    OPIK_WORKSPACE=your_workspace_name
    

Usage

Run the agent:

python recipe_agent.py

Example Session

╭───────────────────────────────────────────────────────────╮
│         Recipe Agent Demo                                 │
│  Powered by Google ADK with Opik Observability           │
╰───────────────────────────────────────────────────────────╯

Enter ingredients (comma-separated): chicken, garlic, lemon

╭─ 🍳 Recipe Suggester ─────────────────────────────────────╮
│                                                           │
│  Recipe: Lemon Garlic Roasted Chicken                    │
│                                                           │
│  Ingredients:                                             │
│  • 1 whole chicken                                        │
│  • 4 cloves garlic, minced                                │
│  • 2 lemons, juiced and zested                            │
│  ...                                                      │
╰───────────────────────────────────────────────────────────╯

╭─ 🔬 Recipe Researcher ────────────────────────────────────╮
│                                                           │
│  Nutritional Information:                                │
│  • High in protein                                        │
│  • Contains vitamin C from lemon                          │
│                                                           │
│  Cultural Background:                                     │
│  This classic preparation is common in Mediterranean...   │
╰───────────────────────────────────────────────────────────╯

╭─ 📖 Final Recipe ─────────────────────────────────────────╮
│                                                           │
│  [Complete synthesized recipe with all details]           │
│                                                           │
╰───────────────────────────────────────────────────────────╯

✅ Done! Check your Opik dashboard for detailed traces.

Observability with Opik

All agent interactions are automatically traced and logged to Comet Opik, providing:

  • Agent execution traces: See exactly how agents delegate and communicate
  • Token usage tracking: Monitor LLM API consumption and costs
  • Performance metrics: Analyze response times and bottlenecks
  • Error tracking: Debug issues with detailed error information
  • Tool usage logs: Track when and how tools are called

View traces in your Opik dashboard at: https://www.comet.com/

Project Structure

google_adk_recipies/
├── recipe_agent.py          # Main agent implementation
├── requirements.txt         # Python dependencies
├── .env.example            # Environment variable template
├── .env                    # Your local config (create this)
└── README.md              # This file

Agent Features

Custom Tools

The recipe researcher agent uses custom tools:

  • search_recipe_database: Simulates database queries for recipe information
  • get_nutritional_info: Provides nutritional analysis for ingredients

Callbacks Integration

All agents are instrumented with Opik callbacks:

  • before_agent_callback / after_agent_callback: Track agent invocations
  • before_model_callback / after_model_callback: Monitor model calls
  • before_tool_callback / after_tool_callback: Track tool usage

Configuration

Model Selection

The agents use Google's Gemini model by default. You can modify the model in recipe_agent.py:

model="gemini-2.0-flash-exp"

Available models:

  • gemini-2.0-flash-exp (default, fastest)
  • gemini-2.0-flash
  • gemini-1.5-pro
  • gemini-1.5-flash

Agent System Instructions

Each agent has a specialized system instruction that defines its role and behavior. You can customize these in recipe_agent.py to change agent behavior.

Troubleshooting

API Key Issues

If you see authentication errors:

  1. Verify your GOOGLE_API_KEY is set correctly in .env
  2. Check that your API key is valid at https://ai.google.dev/

Opik Connection Issues

If traces aren't appearing in Opik:

  1. Run opik configure to set up your credentials
  2. Verify you're logged into the correct workspace
  3. Check that opik_tracer.flush() is called at the end of execution

Import Errors

If you see import errors:

  1. Ensure you've installed all dependencies: pip install -r requirements.txt
  2. Verify you're using Python 3.9 or higher: python --version
  3. Consider using a virtual environment

Advanced Usage

Adding New Agents

To add new agents to the sequential workflow:

new_agent = Agent(
    name="NewAgent",
    model="gemini-2.0-flash-exp",
    instruction="Your instructions here",
    output_key="new_agent_output",
    before_agent_callback=opik_tracer.before_agent_callback,
    after_agent_callback=opik_tracer.after_agent_callback,
    before_model_callback=opik_tracer.before_model_callback,
    after_model_callback=opik_tracer.after_model_callback,
    before_tool_callback=opik_tracer.before_tool_callback,
    after_tool_callback=opik_tracer.after_tool_callback,
)

# Add to sequential pipeline
recipe_pipeline = SequentialAgent(
    name="RecipePipeline",
    sub_agents=[recipe_suggester, recipe_researcher, new_agent],
    description="Extended pipeline with new agent."
)

Creating Custom Tools

Define new tools as Python functions (no decorator needed):

def my_custom_tool(param: str) -> str:
    """
    Tool description for the LLM.

    Args:
        param: Parameter description

    Returns:
        Result description
    """
    # Your tool logic here
    return result

# Add to agent's tools list
agent = Agent(
    name="AgentWithTools",
    model="gemini-2.0-flash-exp",
    instruction="...",
    tools=[my_custom_tool, another_tool]
)

Key Technologies

  • Google ADK: Agent Development Kit for building multi-agent systems
  • Comet Opik: Observability and tracing for AI agents
  • Rich: Beautiful terminal formatting and output
  • Google Gemini: Google's latest generative AI models

Resources

What's Included

  • ✅ Multi-agent sequential workflow with SequentialAgent
  • ✅ Custom tools for recipe research and nutrition
  • ✅ Full Opik observability integration
  • ✅ Beautiful Rich CLI interface
  • ✅ State management between agents using output_key
  • ✅ Error handling and graceful degradation
  • ✅ Environment-based configuration

License

This project is provided as-is for demonstration purposes.

Contributing

Feel free to submit issues, fork the repository, and create pull requests for any improvements.

---
# Opik Online Evaluation Configuration for Recipe Agent
# This file defines evaluation criteria for assessing recipe quality in real-time
evaluations:
- name: recipe_taste_appeal
description: "Evaluates the recipe's potential taste appeal and flavor combinations from a professional chef's perspective"
type: llm_judge
model: gpt-4
prompt: |
You are a professional chef and food critic. Evaluate the suggested recipe's potential taste appeal and flavor combinations. Consider:
- Ingredient harmony and balance
- Seasoning and flavor depth
- Cultural authenticity (if applicable)
- Overall gastronomic appeal
Rate on a scale of 1-10 and explain your rating with specific culinary insights.
INPUT:
{{input}}
OUTPUT:
{{output}}
EVALUATION FORMAT:
Taste Score: [1-10]
Reasoning: [One clear sentence focusing on flavor profile and culinary merit]
scoring:
type: numeric
min: 1
max: 10
metadata:
category: taste
importance: high
- name: recipe_complexity_accessibility
description: "Evaluates the recipe's practicality and ease of execution for home cooks"
type: llm_judge
model: gpt-4
prompt: |
You are an experienced cooking instructor who specializes in home cooking. Evaluate the recipe's practicality and ease of execution. Consider:
- Ingredient accessibility
- Required cooking skills
- Time commitment
- Equipment needs
- Clear instructions
Rate the complexity level and time estimate.
INPUT:
{{input}}
OUTPUT:
{{output}}
EVALUATION FORMAT:
Complexity Level: [Beginner/Intermediate/Advanced]
Time Estimate: [Quick (<30min)/Medium (30-60min)/Extended (>60min)]
Response example should be complexity + time, i.e: Intermediate 40mins
scoring:
type: categorical
categories:
- "Beginner Quick"
- "Beginner Medium"
- "Beginner Extended"
- "Intermediate Quick"
- "Intermediate Medium"
- "Intermediate Extended"
- "Advanced Quick"
- "Advanced Medium"
- "Advanced Extended"
metadata:
category: accessibility
importance: high
- name: recipe_overall_quality
description: "Evaluates the overall quality and completeness of the recipe response"
type: llm_judge
model: gpt-4
prompt: |
You are an expert recipe developer and cookbook editor. Evaluate the overall quality and completeness of the recipe response. Consider:
- Ingredient specifications
- Step-by-step clarity
- Technical accuracy
- Safety considerations
- Serving size/yield information
- Additional tips/variations provided
Rate on a scale of 1-10 and explain your comprehensive assessment.
INPUT:
{{input}}
OUTPUT:
{{output}}
EVALUATION FORMAT:
Quality Score: [1-10]
Only give the quality score back
scoring:
type: numeric
min: 1
max: 10
metadata:
category: quality
importance: critical
- name: recipe_safety_check
description: "Checks for food safety considerations and warnings"
type: llm_judge
model: gpt-4
prompt: |
You are a food safety expert. Review the recipe for any food safety concerns or missing safety information. Consider:
- Proper cooking temperatures
- Food handling instructions
- Allergen warnings
- Storage information
- Cross-contamination risks
INPUT:
{{input}}
OUTPUT:
{{output}}
EVALUATION FORMAT:
Safety Rating: [Safe/Minor Concerns/Major Concerns]
Issues: [List any specific safety concerns or "None identified"]
scoring:
type: categorical
categories:
- "Safe"
- "Minor Concerns"
- "Major Concerns"
metadata:
category: safety
importance: critical
- name: recipe_completeness
description: "Checks if the recipe contains all necessary components"
type: heuristic
rules:
- check: contains_ingredients
description: "Recipe must include an ingredients list"
weight: 30
- check: contains_instructions
description: "Recipe must include step-by-step instructions"
weight: 30
- check: contains_timing
description: "Recipe should include prep and cook times"
weight: 15
- check: contains_servings
description: "Recipe should specify serving size or yield"
weight: 10
- check: contains_tips
description: "Recipe should include helpful tips or variations"
weight: 10
- check: contains_nutritional_info
description: "Recipe should include nutritional information or considerations"
weight: 5
scoring:
type: percentage
min: 0
max: 100
metadata:
category: completeness
importance: high
"""
Recipe Agent Demo with Google ADK and Opik Integration
=======================================================
A multi-agent system for recipe suggestions and research using Google's Agent Development Kit
with Comet Opik observability integration.
Agents:
- RecipeSuggesterAgent: Suggests recipes based on provided ingredients
- RecipeResearchAgent: Researches additional context and information about recipes
"""
import os
import asyncio
import warnings
from dotenv import load_dotenv
from google.adk.agents import Agent, SequentialAgent
from google.adk.runners import InMemoryRunner
from google.genai import types
from opik.integrations.adk import OpikTracer
from rich.console import Console
from rich.panel import Panel
from rich.markdown import Markdown
from rich.live import Live
from rich.spinner import Spinner
# Suppress warnings for cleaner output
warnings.filterwarnings('ignore')
# Initialize Rich console
console = Console()
# Load environment variables
load_dotenv()
# Verify API key is set
if not os.getenv("GOOGLE_API_KEY"):
raise ValueError("GOOGLE_API_KEY not found in environment variables. Please set it in .env file.")
# Remove GEMINI_API_KEY if both are set to avoid warnings
if os.getenv("GOOGLE_API_KEY") and os.getenv("GEMINI_API_KEY"):
os.environ.pop("GEMINI_API_KEY", None)
# Initialize Opik tracer with configuration
opik_tracer = OpikTracer(
name="recipe-agent-system",
tags=["recipe", "demo", "multi-agent"],
metadata={
"environment": "development",
"version": "1.0.0"
},
project_name="google-adk-recipes"
)
# Define tools for recipe research
def search_recipe_database(recipe_name: str) -> str:
"""
Search a recipe database for detailed information.
Args:
recipe_name: The name of the recipe to search for
Returns:
Information about the recipe
"""
# Simulated database search - in production, this would query a real database
return f"Database results for '{recipe_name}': Found multiple variations with preparation times ranging from 30-60 minutes. Popular cooking methods include baking, grilling, and stir-frying."
def get_nutritional_info(ingredients: list[str]) -> str:
"""
Get nutritional information for a list of ingredients.
Args:
ingredients: List of ingredient names
Returns:
Nutritional information summary
"""
# Simulated nutritional lookup - in production, this would use a nutrition API
return f"Nutritional analysis for {len(ingredients)} ingredients: High in protein and complex carbohydrates. Contains essential vitamins and minerals."
# Create the Recipe Suggester Agent
recipe_suggester = Agent(
name="RecipeSuggester",
model="gemini-2.0-flash-exp",
instruction="""You are a creative recipe suggester agent. Your role is to:
1. Analyze the provided ingredients
2. Suggest ONE creative and practical recipe that uses those ingredients
3. Provide clear, step-by-step cooking instructions
4. Include estimated preparation and cooking times
IMPORTANT: Do not ask follow-up questions. Always provide a complete recipe suggestion immediately based on the ingredients given.
Be creative but practical, and consider common cooking techniques.""",
output_key="recipe",
before_agent_callback=opik_tracer.before_agent_callback,
after_agent_callback=opik_tracer.after_agent_callback,
before_model_callback=opik_tracer.before_model_callback,
after_model_callback=opik_tracer.after_model_callback,
before_tool_callback=opik_tracer.before_tool_callback,
after_tool_callback=opik_tracer.after_tool_callback,
)
# Create the Recipe Research Agent with tools
recipe_researcher = Agent(
name="RecipeResearcher",
model="gemini-2.0-flash-exp",
instruction="""You are a recipe research specialist. Your role is to:
1. Look at the recipe that was suggested (check the session state for 'recipe')
2. Research additional context about that specific recipe using the available tools
3. Provide historical or cultural background
4. Use get_nutritional_info tool to get nutritional analysis
5. Suggest variations and substitutions
6. Offer tips for best results
IMPORTANT:
- Do not ask follow-up questions. Always provide complete research and information immediately.
- Use BOTH available tools (search_recipe_database and get_nutritional_info) to gather comprehensive information
- Provide your findings directly in a detailed format.""",
output_key="research",
tools=[search_recipe_database, get_nutritional_info],
before_agent_callback=opik_tracer.before_agent_callback,
after_agent_callback=opik_tracer.after_agent_callback,
before_model_callback=opik_tracer.before_model_callback,
after_model_callback=opik_tracer.after_model_callback,
before_tool_callback=opik_tracer.before_tool_callback,
after_tool_callback=opik_tracer.after_tool_callback,
)
# Create a Sequential Workflow Agent to ensure both agents run
recipe_pipeline = SequentialAgent(
name="RecipePipeline",
sub_agents=[recipe_suggester, recipe_researcher],
description="Executes recipe suggestion followed by research in sequence."
)
# Create the Root Agent (orchestrator) that uses the sequential pipeline
root_agent = Agent(
name="RecipeMasterAgent",
model="gemini-2.0-flash-exp",
instruction="""You are the Recipe Master Agent. Your role is to coordinate recipe creation.
When a user provides ingredients:
1. Delegate to the RecipePipeline agent which will automatically run both RecipeSuggester and RecipeResearcher in sequence
2. The pipeline will provide you with both the recipe (from RecipeSuggester) and research (from RecipeResearcher)
3. Synthesize the information from both into a comprehensive, well-formatted final response
Your final response should include:
- Recipe name and description
- Complete ingredient list
- Step-by-step instructions with timing
- Nutritional information from research
- Cultural/historical context
- Tips and variations
IMPORTANT: Do not ask follow-up questions. Always provide a complete, well-formatted response.""",
sub_agents=[recipe_pipeline],
before_agent_callback=opik_tracer.before_agent_callback,
after_agent_callback=opik_tracer.after_agent_callback,
before_model_callback=opik_tracer.before_model_callback,
after_model_callback=opik_tracer.after_model_callback,
before_tool_callback=opik_tracer.before_tool_callback,
after_tool_callback=opik_tracer.after_tool_callback,
)
def run_agent_sync(user_prompt: str):
"""Synchronous function to run the agent with a user prompt."""
async def _run():
# Create runner with root agent
runner = InMemoryRunner(
agent=root_agent,
app_name="recipe-agent-demo"
)
# Create session
user_id = "user_1"
session = await runner.session_service.create_session(
app_name="recipe-agent-demo",
user_id=user_id
)
# Create content message
content = types.Content(
role='user',
parts=[types.Part(text=user_prompt)]
)
# Run the agent and collect response
console.print()
async for event in runner.run_async(
user_id=user_id,
session_id=session.id,
new_message=content
):
# Print event content as it arrives with agent step indicators
if hasattr(event, 'content') and event.content:
if hasattr(event, 'author') and event.author:
author = event.author
# Print the content with rich formatting
if hasattr(event.content, 'parts') and event.content.parts:
for part in event.content.parts:
if hasattr(part, 'text') and part.text:
# Determine panel style based on agent
if author == "RecipeSuggester":
title = "🍳 Recipe Suggester"
style = "green"
elif author == "RecipeResearcher":
title = "🔬 Recipe Researcher"
style = "blue"
elif author == "RecipeMasterAgent":
title = "📖 Final Recipe"
style = "bold magenta"
else:
title = author
style = "white"
# Display as a panel
console.print(Panel(
Markdown(part.text),
title=title,
border_style=style,
padding=(1, 2)
))
# Run the async function in a new event loop
asyncio.run(_run())
def main():
"""Main function to run the recipe agent demo."""
console.print(Panel.fit(
"[bold cyan]Recipe Agent Demo[/bold cyan]\n"
"Powered by Google ADK with Opik Observability",
border_style="cyan"
))
console.print()
# Get ingredients from user
ingredients = console.input("[bold yellow]Enter ingredients (comma-separated):[/bold yellow] ").strip()
if not ingredients:
console.print("[red]No ingredients provided. Exiting.[/red]")
return
console.print()
with console.status("[bold green]Preparing your recipe...", spinner="dots"):
pass
# Create the user prompt
user_prompt = f"I have these ingredients: {ingredients}. Please suggest a complete recipe with all details, and include nutritional research and background information about the dish."
# Run the agent
try:
run_agent_sync(user_prompt)
console.print()
console.print("[bold green]✅ Done! Check your Opik dashboard for detailed traces.[/bold green]")
except Exception as e:
console.print(f"\n[bold red]❌ Error occurred:[/bold red] {str(e)}")
console.print("[yellow]Please check your configuration and try again.[/yellow]")
import traceback
traceback.print_exc()
finally:
# Ensure all traces are flushed to Opik
console.print()
with console.status("[cyan]Flushing traces to Opik...", spinner="dots"):
opik_tracer.flush()
console.print("[green]✓ Traces sent successfully![/green]")
if __name__ == "__main__":
main()
google-adk>=0.1.0
google-genai>=0.1.0
opik>=0.1.0
python-dotenv>=1.0.0
rich>=13.0.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment