feat: add comprehensive Google ADK tutorials and example agents for memory management and callbacks

- Introduced multiple tutorials covering memory agents, including in-memory and persistent conversation agents, with detailed README documentation.
- Implemented agent lifecycle callbacks and LLM interaction callbacks to enhance monitoring and control over agent execution.
- Added examples for tool execution callbacks, demonstrating how to track tool usage and performance.
- Updated requirements for new tutorials and removed outdated dependencies from previous versions.
This commit is contained in:
Shubhamsaboo 2025-08-03 20:22:10 -05:00
parent 53dfb652bf
commit 54ecde998c
87 changed files with 1815 additions and 7 deletions

View file

@ -93,6 +93,7 @@ A curated collection of **Awesome LLM apps built with RAG, AI Agents, Multi-agen
* [🎨 AI Game Design Agent Team](advanced_ai_agents/multi_agent_apps/agent_teams/ai_game_design_agent_team/)
* [👨‍⚖️ AI Legal Agent Team (Cloud & Local)](advanced_ai_agents/multi_agent_apps/agent_teams/ai_legal_agent_team/)
* [💼 AI Recruitment Agent Team](advanced_ai_agents/multi_agent_apps/agent_teams/ai_recruitment_agent_team/)
* [🏠 AI Real Estate Agent Team](advanced_ai_agents/multi_agent_apps/agent_teams/ai_real_estate_agent_team)
* [👨‍💼 AI Services Agency (CrewAI)](advanced_ai_agents/multi_agent_apps/agent_teams/ai_services_agency/)
* [👨‍🏫 AI Teaching Agent Team](advanced_ai_agents/multi_agent_apps/agent_teams/ai_teaching_agent_team/)
* [💻 Multimodal Coding Agent Team](advanced_ai_agents/multi_agent_apps/agent_teams/multimodal_coding_agent_team/)

View file

@ -0,0 +1,5 @@
google-adk>=1.9.0
streamlit>=1.47.1
python-dotenv>=1.1.1
sqlalchemy>=2.0.0
asyncio

View file

@ -0,0 +1,3 @@
# If using Gemini via Google AI Studio
GOOGLE_GENAI_USE_VERTEXAI=False
GOOGLE_API_KEY="your-api-key"

View file

@ -0,0 +1,233 @@
# 6.1 Agent Lifecycle Callbacks
This tutorial demonstrates how to use `before_agent_callback` and `after_agent_callback` to monitor agent execution lifecycle.
## 🎯 Learning Objectives
- Understand agent lifecycle callbacks
- Learn how to monitor agent execution timing
- See how to share state between callbacks
- Practice implementing performance monitoring
## 📁 Project Structure
```
6_1_agent_lifecycle_callbacks/
├── agent.py # Agent with lifecycle callbacks
├── app.py # Streamlit web interface
├── requirements.txt # Python dependencies
└── README.md # This file
```
## 🔧 Setup
1. **Install dependencies:**
```bash
pip install -r requirements.txt
```
2. **Set up API key:**
```bash
# Create .env file
echo "GOOGLE_API_KEY=your_api_key_here" > .env
```
## 🚀 Running the Demo
### Command Line Demo
```bash
python agent.py
```
### Web Interface
```bash
streamlit run app.py
```
## 🧠 Core Concept: Agent Lifecycle Monitoring
Agent lifecycle callbacks allow you to monitor the beginning and end of agent execution, providing visibility into when agents start and complete their tasks.
### **Agent Lifecycle Flow**
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User Input │───▶│ Agent Start │───▶│ Agent End │
│ │ │ Callback │ │ Callback │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Agent Logic │ │ Performance │
│ Execution │ │ Metrics │
└─────────────────┘ └─────────────────┘
```
### **Callback Execution Timeline**
```
Timeline: ──────────────────────────────────────────────────────────▶
User Message
┌─────────────────┐
│ before_agent │ ← Records start time, agent info
│ _callback │
└─────────────────┘
┌─────────────────┐
│ Agent Logic │ ← Core agent processing
│ Execution │
└─────────────────┘
┌─────────────────┐
│ after_agent │ ← Calculates duration, logs completion
│ _callback │
└─────────────────┘
Response to User
```
## 📖 Code Walkthrough
### **1. Callback Functions**
The callbacks work in pairs to monitor the complete agent lifecycle:
**Before Callback (`before_agent_callback`):**
- Records execution start timestamp
- Stores start time in session state for after callback
- Logs agent execution start (agent name, time)
- Returns `None` to allow normal execution
**After Callback (`after_agent_callback`):**
- Retrieves start time from session state
- Calculates total execution duration
- Logs completion with performance metrics
- Returns `None` to use original result
### **2. State Management Between Callbacks**
```
Session State Flow:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ before_callback │───▶│ Session State │───▶│ after_callback │
│ stores: │ │ │ │ retrieves: │
│ - start_time │ │ - request_start │ │ - start_time │
│ │ │ _time │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### **3. Agent Setup**
The agent is configured with both lifecycle callbacks:
- `before_agent_callback`: Monitors agent execution start
- `after_agent_callback`: Monitors agent execution completion
- Uses `InMemoryRunner` for proper callback triggering
## 🧪 Testing Examples
### **Example Output Format**
```
🚀 Agent LifecycleDemoAgent started at 19:15:30
⏰ Start time: 2024-01-15 19:15:30
✅ Agent LifecycleDemoAgent completed
⏱️ Duration: 1.23s
⏰ End time: 2024-01-15 19:15:31
📊 Performance: 1.23s | LifecycleDemoAgent
```
### **What Each Metric Tells You**
- **🚀 Start time**: When the agent began processing
- **✅ Completion time**: When the agent finished processing
- **⏱️ Duration**: Total execution time in seconds
- **📊 Performance**: Formatted performance summary
## 🔍 Key Concepts
### **Agent Lifecycle Monitoring**
- **Execution Start**: Track when agents begin processing
- **Execution End**: Track when agents complete their tasks
- **Performance Timing**: Calculate total execution duration
- **State Sharing**: Pass timing data between callbacks
### **CallbackContext**
- **agent_name**: Name of the agent being executed
- **invocation_id**: Unique identifier for this execution
- **state**: Session state that persists between callbacks
### **State Management**
- Use `callback_context.state.to_dict()` to get current state
- Use `callback_context.state.update()` to modify state
- State is shared between before and after callbacks
## 🎯 Use Cases
- **Performance Monitoring**: Track execution times
- **Logging**: Record agent activities
- **Analytics**: Collect usage statistics
- **Debugging**: Monitor agent behavior
- **Custom Logic**: Add pre/post processing
## 🚨 Common Mistakes
1. **Forgetting to await session creation:**
```python
# ❌ Wrong
session_service.create_session(...)
# ✅ Correct
await session_service.create_session(...)
```
2. **Using wrong callback signature:**
```python
# ❌ Wrong
def after_agent_callback(context, result):
# ✅ Correct
def after_agent_callback(callback_context: CallbackContext):
```
3. **Not using InMemoryRunner:**
```python
# ❌ Wrong - callbacks won't trigger
agent.run(message)
# ✅ Correct
runner.run_async(...)
```
## ⚠️ Critical Implementation Note
**Event Loop Completion**: The `after_agent_callback` will not trigger if you break the event loop immediately upon receiving `is_final_response()`.
**Correct Pattern**: Allow the event loop to complete naturally:
```python
# ❌ Wrong - breaks loop early, after_agent_callback won't run
if event.is_final_response() and event.content:
response_text = event.content.parts[0].text.strip()
break # This prevents after_agent_callback from running
# ✅ Correct - let loop complete naturally
if event.is_final_response() and event.content:
response_text = event.content.parts[0].text.strip()
# Don't break - let the loop complete to ensure callbacks run
```
This is a known ADK behavior where breaking the loop early prevents cleanup callbacks from executing.
## 🔗 Next Steps
- Try Tutorial 6.2: LLM Interaction Callbacks
- Experiment with state management between callbacks
- Add custom logging or analytics
- Implement performance alerts for slow responses

View file

@ -0,0 +1,122 @@
import os
import asyncio
from datetime import datetime
from typing import Optional
from google.adk.agents import LlmAgent
from google.adk.agents.callback_context import CallbackContext
from google.adk.runners import InMemoryRunner
from google.genai import types
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# --- 1. Define the Callback Functions ---
def before_agent_callback(callback_context: CallbackContext) -> Optional[types.Content]:
"""Callback before agent execution starts"""
agent_name = callback_context.agent_name
start_time = datetime.now()
print(f"🚀 Agent {agent_name} started at {start_time.strftime('%H:%M:%S')}")
print(f"⏰ Start time: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
print() # Add spacing
# Store start time in state for after callback
current_state = callback_context.state.to_dict()
current_state["start_time"] = start_time.isoformat()
callback_context.state.update(current_state)
return None
def after_agent_callback(callback_context: CallbackContext) -> Optional[types.Content]:
"""Callback after agent execution completes"""
agent_name = callback_context.agent_name
current_state = callback_context.state.to_dict()
# Get start time from state
start_time_str = current_state.get("start_time")
if start_time_str:
start_time = datetime.fromisoformat(start_time_str)
end_time = datetime.now()
duration = end_time - start_time
duration_seconds = duration.total_seconds()
print(f"✅ Agent {agent_name} completed")
print(f"⏱️ Duration: {duration_seconds:.2f}s")
print(f"⏰ End time: {end_time.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"📊 Performance: {duration_seconds:.2f}s | {agent_name}")
print() # Add spacing
return None
# --- 2. Setup Agent with Callbacks ---
llm_agent_with_callbacks = LlmAgent(
name="agent_lifecycle_demo_agent",
model="gemini-2.5-flash",
instruction="You are a helpful assistant. Respond to user questions clearly and concisely.",
description="An LLM agent demonstrating lifecycle callbacks for monitoring",
before_agent_callback=before_agent_callback,
after_agent_callback=after_agent_callback
)
# --- 3. Setup Runner and Sessions ---
runner = InMemoryRunner(agent=llm_agent_with_callbacks, app_name="agent_lifecycle_callback_demo")
async def run_agent(message: str) -> str:
"""Run the agent with the given message"""
user_id = "demo_user"
session_id = "demo_session"
# Get the bundled session service
session_service = runner.session_service
# Get or create session
session = await session_service.get_session(
app_name="agent_lifecycle_callback_demo",
user_id=user_id,
session_id=session_id
)
if not session:
session = await session_service.create_session(
app_name="agent_lifecycle_callback_demo",
user_id=user_id,
session_id=session_id,
state={"conversation_history": []}
)
# Create user content
user_content = types.Content(
role='user',
parts=[types.Part(text=message)]
)
# Run agent and get response
response_text = ""
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=user_content
):
if event.is_final_response() and event.content:
response_text = event.content.parts[0].text.strip()
# Don't break here - let the loop complete naturally to ensure after_agent_callback runs
return response_text
# --- 4. Execute ---
if __name__ == "__main__":
print("\n" + "="*50 + " Agent Lifecycle Callbacks Demo " + "="*50)
# Test messages
test_messages = [
"Hello, how are you?"
]
async def test_agent():
for i, message in enumerate(test_messages, 1):
print(f"\n--- Test {i}: {message} ---")
response = await run_agent(message)
print(f"🤖 Response: {response}")
asyncio.run(test_agent())

View file

@ -0,0 +1,165 @@
#!/usr/bin/env python3
"""
Streamlit App for Agent Lifecycle Callbacks Demo
"""
import streamlit as st
import asyncio
from agent import llm_agent_with_callbacks, runner
from google.genai import types
# Page configuration
st.set_page_config(
page_title="Agent Lifecycle Callbacks Demo",
page_icon="🔄",
layout="wide"
)
# Title and description
st.title("🔄 Agent Lifecycle Callbacks Demo")
st.markdown("""
This demo shows how to use `before_agent_callback` and `after_agent_callback` to monitor agent execution.
Watch the console output to see the callback timing information.
""")
# Sidebar
with st.sidebar:
st.header("📊 Callback Information")
st.markdown("""
**Before Callback:**
- Records start time
- Logs agent execution start
**After Callback:**
- Calculates execution duration
- Logs completion time
""")
st.header("🔧 Technical Details")
st.markdown("""
- Uses `InMemoryRunner` for session management
- Callbacks receive `CallbackContext` with agent info
- State is shared between callbacks via session
""")
# Main chat interface
st.header("💬 Chat with Agent")
# Define the get_response function
async def get_response(prompt_text: str) -> str:
"""Run agent with the given prompt"""
user_id = "demo_user"
session_id = "demo_session"
# Get the bundled session service
session_service = runner.session_service
# Get or create session
session = await session_service.get_session(
app_name="agent_lifecycle_callback_demo",
user_id=user_id,
session_id=session_id
)
if not session:
session = await session_service.create_session(
app_name="agent_lifecycle_callback_demo",
user_id=user_id,
session_id=session_id
)
# Create user content
user_content = types.Content(
role='user',
parts=[types.Part(text=prompt_text)]
)
# Run agent and get response
response_text = ""
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=user_content
):
if event.is_final_response() and event.content:
response_text = event.content.parts[0].text.strip()
# Don't break - let the loop complete to ensure callbacks run
return response_text
# Initialize chat history
if "messages" not in st.session_state:
st.session_state.messages = []
# Display chat messages
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Chat input
if prompt := st.chat_input("Ask me anything..."):
# Add user message to chat history
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# Add assistant response to chat history
with st.chat_message("assistant"):
message_placeholder = st.empty()
# Show loading message
message_placeholder.markdown("🤔 Thinking...")
# Get response
response = asyncio.run(get_response(prompt))
# Update placeholder with response
message_placeholder.markdown(response)
# Add assistant response to chat history
st.session_state.messages.append({"role": "assistant", "content": response})
# Quick test buttons
st.markdown("---")
st.header("⚡ Quick Tests")
col1, col2, col3 = st.columns(3)
with col1:
if st.button("👋 Greeting Test"):
with st.chat_message("user"):
st.markdown("Hello, how are you?")
with st.chat_message("assistant"):
with st.spinner("🤖 Agent is processing..."):
response = asyncio.run(get_response("Hello, how are you?"))
st.markdown(response)
with col2:
if st.button("🧮 Math Test"):
with st.chat_message("user"):
st.markdown("What's 2 + 2?")
with st.chat_message("assistant"):
with st.spinner("🤖 Agent is processing..."):
response = asyncio.run(get_response("What's 2 + 2?"))
st.markdown(response)
with col3:
if st.button("😄 Joke Test"):
with st.chat_message("user"):
st.markdown("Tell me a short joke")
with st.chat_message("assistant"):
with st.spinner("🤖 Agent is processing..."):
response = asyncio.run(get_response("Tell me a short joke"))
st.markdown(response)
# Clear chat button
if st.button("🗑️ Clear Chat"):
st.session_state.messages = []
st.rerun()
# Footer
st.markdown("---")
st.markdown("""
<div style='text-align: center; color: #666;'>
<p>Check the console/terminal for callback timing information</p>
</div>
""", unsafe_allow_html=True)

View file

@ -0,0 +1,3 @@
google-adk>=1.9.0
streamlit>=1.47.1
python-dotenv>=1.1.1

View file

@ -0,0 +1,3 @@
# If using Gemini via Google AI Studio
GOOGLE_GENAI_USE_VERTEXAI=False
GOOGLE_API_KEY="your-api-key"

View file

@ -0,0 +1,218 @@
# 6.2 LLM Interaction Callbacks
This tutorial demonstrates how to use `before_model_callback` and `after_model_callback` to monitor LLM requests and responses.
## 🎯 Learning Objectives
- Understand LLM interaction callbacks
- Learn how to monitor LLM requests and responses
- Track token usage and response times
- Estimate API costs
- Monitor LLM performance metrics
## 📁 Project Structure
```
6_2_llm_interaction_callbacks/
├── agent.py # Agent with LLM interaction callbacks
├── app.py # Streamlit web interface
├── requirements.txt # Python dependencies
└── README.md # This file
```
## 🔧 Setup
1. **Install dependencies:**
```bash
pip install -r requirements.txt
```
2. **Set up API key:**
```bash
# Create .env file
echo "GOOGLE_API_KEY=your_api_key_here" > .env
```
## 🚀 Running the Demo
### Command Line Demo
```bash
python agent.py
```
### Web Interface
```bash
streamlit run app.py
```
## 🧠 Core Concept: LLM Interaction Monitoring
LLM interaction callbacks allow you to monitor the communication between your agent and the underlying language model, providing insights into requests, responses, and performance metrics.
### **LLM Interaction Flow**
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User Input │───▶│ LLM Request │───▶│ LLM Response │
│ │ │ Callback │ │ Callback │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Model API │ │ Token Usage │
│ (Gemini) │ │ & Performance │
└─────────────────┘ └─────────────────┘
```
### **Callback Execution Timeline**
```
Timeline: ──────────────────────────────────────────────────────────▶
User Message
┌─────────────────┐
│ before_model │ ← Records start time, model info
│ _callback │
└─────────────────┘
┌─────────────────┐
│ LLM API Call │ ← Actual request to Gemini
│ (Gemini 2.5) │
└─────────────────┘
┌─────────────────┐
│ after_model │ ← Calculates duration, tokens, cost
│ _callback │
└─────────────────┘
Response to User
```
## 📖 Code Walkthrough
### **1. Callback Functions**
The callbacks work in pairs to monitor the complete LLM interaction:
**Before Callback (`before_model_callback`):**
- Extracts model information from `llm_request`
- Records request timestamp
- Stores data in session state for after callback
- Logs request details (model, time, agent)
**After Callback (`after_model_callback`):**
- Retrieves stored request data from session state
- Calculates response duration
- Extracts token usage from `llm_response.usage_metadata`
- Estimates API costs based on token count
- Logs performance metrics
### **2. State Management Between Callbacks**
```
Session State Flow:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ before_callback │───▶│ Session State │───▶│ after_callback │
│ stores: │ │ │ │ retrieves: │
│ - start_time │ │ - llm_request_ │ │ - start_time │
│ - model │ │ time │ │ - model │
│ - prompt_length │ │ - llm_model │ │ - prompt_length │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### **3. Agent Setup**
The agent is configured with both callbacks:
- `before_model_callback`: Monitors request initiation
- `after_model_callback`: Monitors response completion
- Uses `InMemoryRunner` for proper callback triggering
## 🧪 Testing Examples
### **Example Output Format**
```
🤖 LLM Request to gemini-2.5-flash
⏰ Request time: 19:15:30
📋 Agent: llm_monitor_agent
📝 LLM Response from gemini-2.5-flash
⏱️ Duration: 1.45s
🔢 Tokens: 156
💰 Estimated cost: $0.0004
```
### **What Each Metric Tells You**
- **⏰ Request time**: When the LLM request was initiated
- **⏱️ Duration**: Total time from request to response
- **🔢 Tokens**: Total tokens consumed (input + output)
- **💰 Estimated cost**: Approximate API cost based on token usage
## 🔍 Key Concepts
### **LLM Request Monitoring**
- **Model Information**: Track which model is being used
- **Timing**: Record request timestamps
- **State Management**: Store request data for response analysis
### **LLM Response Monitoring**
- **Response Time**: Calculate duration from request to response
- **Token Usage**: Track total tokens consumed
- **Cost Estimation**: Approximate API costs
### **Usage Metadata**
- **Token Count**: `llm_response.usage_metadata.total_token_count`
- **Model Information**: Available in request and response
- **Timing Data**: Stored in session state between callbacks
## 🎯 Use Cases
- **Performance Monitoring**: Track LLM response times
- **Cost Management**: Monitor API usage and costs
- **Quality Assurance**: Analyze prompt and response patterns
- **Debugging**: Troubleshoot LLM interaction issues
- **Analytics**: Collect usage statistics and metrics
## 🚨 Common Mistakes
1. **Incorrect callback signatures:**
```python
# ❌ Wrong
def before_model_callback(context, model, prompt):
# ✅ Correct
def before_model_callback(callback_context: CallbackContext, llm_request):
```
2. **Wrong token extraction:**
```python
# ❌ Wrong
tokens = llm_response.usage_metadata.get('total_token_count')
# ✅ Correct
tokens = getattr(llm_response.usage_metadata, 'total_token_count', 0)
```
3. **Not using InMemoryRunner:**
```python
# ❌ Wrong - callbacks won't trigger
agent.run(message)
# ✅ Correct
runner.run_async(...)
```
## 🔗 Next Steps
- Try Tutorial 6.3: Tool Execution Callbacks
- Experiment with different cost estimation models
- Add response quality metrics
- Implement rate limiting and quota management
- Create custom analytics dashboards

View file

@ -0,0 +1,179 @@
#!/usr/bin/env python3
"""
LLM Interaction Callbacks Demo
Simple agent that demonstrates LLM request/response monitoring
"""
import os
from datetime import datetime
from typing import Optional
from google.adk.agents import LlmAgent
from google.adk.agents.callback_context import CallbackContext
from google.adk.runners import InMemoryRunner
from google.genai import types
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
def before_model_callback(callback_context: CallbackContext, llm_request) -> Optional[types.Content]:
"""Callback before LLM request is made"""
agent_name = callback_context.agent_name
request_time = datetime.now()
# Extract model and prompt from llm_request
model = getattr(llm_request, 'model', 'unknown')
# Extract full prompt text from llm_request contents
prompt_text = "unknown"
if hasattr(llm_request, 'contents') and llm_request.contents:
for content in llm_request.contents:
if hasattr(content, 'parts') and content.parts:
for part in content.parts:
if hasattr(part, 'text') and part.text:
prompt_text = part.text
break
if prompt_text != "unknown":
break
print(f"🤖 LLM Request to {model}")
print(f"⏰ Request time: {request_time.strftime('%H:%M:%S')}")
print(f"📋 Agent: {agent_name}")
print() # Add spacing
# Store request info in state for after callback
current_state = callback_context.state.to_dict()
current_state["llm_request_time"] = request_time.isoformat()
current_state["llm_model"] = model
current_state["llm_prompt_length"] = len(prompt_text)
callback_context.state.update(current_state)
# Return None to allow normal execution
return None
def after_model_callback(callback_context: CallbackContext, llm_response) -> Optional[types.Content]:
"""Callback after LLM response is received"""
agent_name = callback_context.agent_name
current_state = callback_context.state.to_dict()
# Extract response info
response_text = str(llm_response) if llm_response else 'unknown'
model = current_state.get("llm_model", "unknown")
# Extract token count from usage_metadata
tokens = 0
if llm_response and hasattr(llm_response, 'usage_metadata') and llm_response.usage_metadata:
tokens = getattr(llm_response.usage_metadata, 'total_token_count', 0)
# Get request time from state
request_time_str = current_state.get("llm_request_time")
if request_time_str:
request_time = datetime.fromisoformat(request_time_str)
duration = datetime.now() - request_time
duration_seconds = duration.total_seconds()
else:
duration_seconds = 0
print(f"📝 LLM Response from {model}")
print(f"⏱️ Duration: {duration_seconds:.2f}s")
print(f"🔢 Tokens: {tokens}")
# Calculate estimated cost for Gemini 2.5 Flash
# Pricing: $2.50 per 1M output tokens (including thinking tokens)
cost_per_1k_output = 0.0025 # $2.50 per 1M = $0.0025 per 1K
estimated_cost = (tokens / 1000) * cost_per_1k_output
print(f"💰 Estimated cost: ${estimated_cost:.4f}")
print() # Add spacing
# Return None to use the original response
return None
# Create agent with LLM callbacks
root_agent = LlmAgent(
name="llm_monitor_agent",
model="gemini-2.5-flash",
description="Agent with LLM interaction monitoring",
instruction="""
You are a helpful assistant with LLM monitoring.
Your role is to:
- Provide clear, informative responses
- Keep responses concise but comprehensive
- Demonstrate the LLM callback system
The system will automatically track:
- Your requests to the LLM model
- Response times and token usage
- Estimated API costs
Focus on being helpful while showing the monitoring capabilities.
""",
before_model_callback=before_model_callback,
after_model_callback=after_model_callback
)
# Create runner for agent execution
runner = InMemoryRunner(agent=root_agent, app_name="llm_monitor_app")
async def run_agent(message: str) -> str:
"""Run the agent with the given message"""
user_id = "demo_user"
session_id = "demo_session"
# Get the bundled session service
session_service = runner.session_service
# Get or create session
session = await session_service.get_session(
app_name="llm_monitor_app",
user_id=user_id,
session_id=session_id
)
if not session:
session = await session_service.create_session(
app_name="llm_monitor_app",
user_id=user_id,
session_id=session_id,
state={"conversation_history": []}
)
# Create user content
user_content = types.Content(
role='user',
parts=[types.Part(text=message)]
)
# Run agent and get response
response_text = ""
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=user_content
):
if event.is_final_response() and event.content:
response_text = event.content.parts[0].text.strip()
# Don't break here - let the loop complete naturally to ensure callbacks run
return response_text
if __name__ == "__main__":
import asyncio
# Test the agent
print("🧪 Testing LLM Interaction Callbacks")
print("=" * 50)
test_messages = [
"Explain quantum computing in simple terms",
"Write a short poem about AI",
"What are the benefits of renewable energy?"
]
async def test_agent():
for message in test_messages:
print(f"\n🤖 User: {message}")
response = await run_agent(message)
print(f"🤖 Agent: {response}")
print("-" * 50)
asyncio.run(test_agent())

View file

@ -0,0 +1,127 @@
#!/usr/bin/env python3
"""
Streamlit App for LLM Interaction Callbacks Demo
"""
import streamlit as st
import sys
import os
import asyncio
from agent import run_agent
# Page configuration
st.set_page_config(
page_title="LLM Interaction Callbacks",
page_icon="🤖",
layout="wide"
)
# Title and description
st.title("🤖 LLM Interaction Callbacks Demo")
st.markdown("""
This demo shows how to monitor LLM requests and responses using callbacks.
Watch the console output to see detailed LLM interaction tracking!
""")
# Sidebar with information
with st.sidebar:
st.header("📊 LLM Monitoring")
st.markdown("""
**Request Callback**: Triggered when LLM request is sent
- Logs model name and prompt
- Records request timestamp
- Tracks prompt length
**Response Callback**: Triggered when LLM response is received
- Calculates response duration
- Tracks token usage
- Estimates API costs
""")
# Main chat interface
st.header("💬 Chat with LLM Monitor")
# Initialize chat history
if "messages" not in st.session_state:
st.session_state.messages = []
# Display chat messages
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Chat input
if prompt := st.chat_input("Ask me something..."):
# Add user message to chat
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# Get agent response
with st.chat_message("assistant"):
with st.spinner("🤖 LLM is processing..."):
response = asyncio.run(run_agent(prompt))
st.markdown(response)
# Add assistant response to chat
st.session_state.messages.append({"role": "assistant", "content": response})
# Quick test buttons
st.markdown("---")
st.header("⚡ Quick Tests")
col1, col2, col3 = st.columns(3)
with col1:
if st.button("🔬 Science Test"):
with st.chat_message("user"):
st.markdown("Explain quantum computing in simple terms")
with st.chat_message("assistant"):
with st.spinner("🤖 LLM is processing..."):
response = asyncio.run(run_agent("Explain quantum computing in simple terms"))
st.markdown(response)
with col2:
if st.button("📝 Poetry Test"):
with st.chat_message("user"):
st.markdown("Write a short poem about AI")
with st.chat_message("assistant"):
with st.spinner("🤖 LLM is processing..."):
response = asyncio.run(run_agent("Write a short poem about AI"))
st.markdown(response)
with col3:
if st.button("🌍 Environment Test"):
with st.chat_message("user"):
st.markdown("What are the benefits of renewable energy?")
with st.chat_message("assistant"):
with st.spinner("🤖 LLM is processing..."):
response = asyncio.run(run_agent("What are the benefits of renewable energy?"))
st.markdown(response)
# Clear chat button
if st.button("🗑️ Clear Chat History"):
st.session_state.messages = []
st.rerun()
# Information about callbacks
st.markdown("---")
st.header("📋 LLM Callback Output")
st.markdown("""
**Check your console/terminal** to see the LLM interaction output:
```
🤖 LLM Request to gemini-2.5-flash
Request time: 10:30:15
📋 Agent: llm_monitor_agent
📝 LLM Response from gemini-2.5-flash
Duration: 1.45s
🔢 Tokens: 156
💰 Estimated cost: $0.0004
```
""")
# Footer
st.markdown("---")
st.markdown("*Watch the console output to see LLM interaction callbacks in action!*")

View file

@ -0,0 +1,3 @@
google-adk>=1.9.0
streamlit>=1.47.1
python-dotenv>=1.1.1

View file

@ -0,0 +1,3 @@
# If using Gemini via Google AI Studio
GOOGLE_GENAI_USE_VERTEXAI=False
GOOGLE_API_KEY="your-api-key"

View file

@ -0,0 +1,254 @@
# 🎯 Tutorial 6.3: Tool Execution Callbacks
## 🎯 What You'll Learn
- **Before Tool Callbacks**: Monitor when tools begin execution
- **After Tool Callbacks**: Track tool completion and results
- **Tool Context**: Understand how tool execution is monitored
## 🧠 Core Concept: Tool Execution Monitoring
Tool execution callbacks allow you to monitor when agents use tools, track their execution lifecycle, and analyze the results. This provides visibility into how agents interact with external systems and APIs.
### **Tool Execution Flow**
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Tool Call │───▶│ Before Tool │───▶│ Tool Execution │
│ (Agent) │ │ Callback │ │ (External) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ After Tool │ │ Tool Result │
│ Callback │ │ (Agent) │
└─────────────────┘ └─────────────────┘
```
### **Callback Execution Timeline**
```
Time → 0ms 5ms 10ms 15ms 20ms 25ms
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
[Tool] [Before] [Exec] [After] [Result]
Call Callback Start Callback Return
```
### **Use Cases**
- **Execution Monitoring**: Track when tools start and complete
- **Parameter Validation**: Check tool inputs before execution
- **Result Logging**: Record tool outputs and errors
- **Debugging**: Understand tool execution patterns
- **Analytics**: Monitor which tools are used most
## 🚀 Tutorial Overview
In this tutorial, we'll create an agent with tool execution callbacks that:
- Uses a simple calculator tool
- Monitors tool execution start and end
- Tracks tool parameters and results
- Provides detailed tool usage visibility
## 📁 Project Structure
```
6_3_tool_execution_callbacks/
├── README.md # This file
├── requirements.txt # Dependencies
├── agent.py # Agent with tool callbacks
└── app.py # Streamlit interface
```
## 🎯 Learning Objectives
By the end of this tutorial, you'll understand:
- ✅ **Before Tool Callbacks**: How to monitor tool execution start
- ✅ **After Tool Callbacks**: How to track tool completion
- ✅ **Tool Context**: How to access tool and agent information
- ✅ **FunctionTool**: How to properly register tools with callbacks
- ✅ **Callback Integration**: How to integrate callbacks with agents
## 🚀 Getting Started
### **Setup**
1. **Install dependencies**: `pip install -r requirements.txt`
2. **Set up environment**: Create `.env` with `GOOGLE_API_KEY=your_key`
3. **Run the app**: `streamlit run app.py`
### **Test the Agent**
```bash
# Run the Streamlit app
streamlit run app.py
# Try these test messages:
- "Calculate 15 + 27"
- "What is 100 divided by 4?"
- "Multiply 8 by 12"
```
## 🔧 Key Concepts
### **1. Before Tool Callback**
- **Trigger**: When tool execution begins
- **Parameters**: `tool`, `args`, `tool_context`
- **Use Cases**: Log tool usage, validate parameters, record start
### **2. After Tool Callback**
- **Trigger**: When tool execution completes
- **Parameters**: `tool`, `args`, `tool_context`, `tool_response`
- **Use Cases**: Log results, handle errors, provide feedback
### **3. Tool Context**
- **Agent Information**: Access `tool_context.agent_name`
- **State Management**: Use `tool_context.state` for data sharing
- **Tool Details**: Access tool information via `tool.name`
## 🔍 Testing Examples
### **Basic Tool Usage**
```
User: "Calculate 15 + 27"
🔧 Tool calculator_tool started
📝 Parameters: {'operation': 'add', 'a': 15.0, 'b': 27.0}
📋 Agent: tool_execution_demo_agent
✅ Tool calculator_tool completed
⏱️ Duration: 0.0012s
📄 Result: 15 + 27 = 42
```
### **Error Handling**
```
User: "What is 10 divided by 0?"
🔧 Tool calculator_tool started
📝 Parameters: {'operation': 'divide', 'a': 10.0, 'b': 0.0}
📋 Agent: tool_execution_demo_agent
✅ Tool calculator_tool completed
⏱️ Duration: 0.0008s
📄 Result: Error: Division by zero
```
## 🎯 What Each Metric Tells You
### **Before Tool Callback Output**
- **🔧 Tool Name**: Which tool is being executed
- **📝 Parameters**: Input parameters passed to the tool
- **📋 Agent**: Which agent is using the tool
### **After Tool Callback Output**
- **✅ Completion Status**: Tool execution completed successfully
- **⏱️ Duration**: How long the tool took to execute
- **📄 Result**: The output or result from the tool
## 🎯 Critical Implementation Notes
### **FunctionTool Requirement**
Tools must be wrapped with `FunctionTool` for callbacks to work:
```python
# ✅ Correct - Use FunctionTool
calculator_function_tool = FunctionTool(func=calculator_tool)
agent = LlmAgent(tools=[calculator_function_tool], ...)
# ❌ Incorrect - Raw function won't trigger callbacks
agent = LlmAgent(tools=[calculator_tool], ...)
```
### **Callback Signatures**
Use the correct parameter order for tool callbacks:
```python
# ✅ Correct signatures
def before_tool_callback(tool: BaseTool, args: dict, tool_context: ToolContext):
pass
def after_tool_callback(tool: BaseTool, args: dict, tool_context: ToolContext, tool_response: any):
pass
```
### **Event Loop Completion**
Don't break the event loop immediately after `is_final_response()`:
```python
# ✅ Do this - allows callbacks to complete
if event.is_final_response() and event.content:
response_text = event.content.parts[0].text.strip()
# Don't break - let the loop complete naturally
```
## 🎯 Advanced Patterns
### **Multiple Tools**
Register multiple tools with the same callbacks:
```python
def weather_tool(city: str) -> str:
return f"Weather in {city}: Sunny, 25°C"
def calculator_tool(operation: str, a: float, b: float) -> str:
# ... implementation
# Register multiple tools
weather_function_tool = FunctionTool(func=weather_tool)
calculator_function_tool = FunctionTool(func=calculator_tool)
agent = LlmAgent(
name="multi_tool_agent",
model="gemini-2.5-flash",
tools=[calculator_function_tool, weather_function_tool],
before_tool_callback=before_tool_callback,
after_tool_callback=after_tool_callback
)
```
### **Parameter Validation**
Implement validation in before_tool_callback:
```python
def before_tool_callback(tool: BaseTool, args: dict, tool_context: ToolContext):
tool_name = tool.name
# Validate calculator tool parameters
if tool_name == "calculator_tool":
if "operation" not in args:
print("⚠️ Warning: Missing operation parameter")
if "a" not in args or "b" not in args:
print("⚠️ Warning: Missing numeric parameters")
print(f"🔧 Tool {tool_name} started")
print(f"📝 Parameters: {args}")
return None
```
### **Result Modification**
Modify tool results in after_tool_callback:
```python
def after_tool_callback(tool: BaseTool, args: dict, tool_context: ToolContext, tool_response: any):
tool_name = tool.name
# Add context to calculator results
if tool_name == "calculator_tool" and "result" in tool_response:
operation = args.get("operation", "unknown")
tool_response["context"] = f"Performed {operation} operation"
print(f"✅ Tool {tool_name} completed")
print(f"📄 Result: {tool_response}")
return tool_response # Return modified response
```
## 🔗 Next Steps
After completing this tutorial, you'll be ready for:
- **[Advanced Tool Patterns](../advanced_tool_patterns/README.md)** - Complex tool architectures
- **[Custom Tool Development](../custom_tools/README.md)** - Building custom tools
- **[Tool Integration](../tool_integration/README.md)** - Integrating external APIs
## 📚 Additional Resources
- [Google ADK Tool Callbacks](https://google.github.io/adk-docs/callbacks/types-of-callbacks/#tool-execution-callbacks)
- [Tool Development Guide](https://google.github.io/adk-docs/tools/)
- [FunctionTool Documentation](https://google.github.io/adk-docs/tools/function-tools/)

View file

@ -0,0 +1,150 @@
import os
import asyncio
import time
from datetime import datetime
from typing import Optional, Dict, Any
from google.adk.agents import LlmAgent
from google.adk.runners import InMemoryRunner
from google.adk.tools import BaseTool, FunctionTool
from google.adk.tools.tool_context import ToolContext
from google.genai import types
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
def calculator_tool(operation: str, a: float, b: float) -> str:
"""Simple calculator tool with basic operations"""
if operation == "add":
return f"{a} + {b} = {a + b}"
elif operation == "subtract":
return f"{a} - {b} = {a - b}"
elif operation == "multiply":
return f"{a} × {b} = {a * b}"
elif operation == "divide":
if b == 0:
return "Error: Division by zero"
return f"{a} ÷ {b} = {a / b}"
else:
return f"Unknown operation: {operation}"
# Create FunctionTool from the calculator function
calculator_function_tool = FunctionTool(func=calculator_tool)
def before_tool_callback(tool: BaseTool, args: Dict[str, Any], tool_context: ToolContext) -> Optional[Dict[str, Any]]:
"""Callback before tool execution starts"""
agent_name = tool_context.agent_name
tool_name = tool.name
start_time = time.time()
print(f"🔧 Tool {tool_name} started")
print(f"📝 Parameters: {args}")
print(f"📋 Agent: {agent_name}")
print() # Add spacing
# Store start time in tool_context state for after callback
current_state = tool_context.state.to_dict()
current_state["tool_start_time"] = start_time
tool_context.state.update(current_state)
# Return None to allow normal execution
return None
def after_tool_callback(tool: BaseTool, args: Dict[str, Any], tool_context: ToolContext, tool_response: Any) -> Optional[Any]:
"""Callback after tool execution completes"""
agent_name = tool_context.agent_name
tool_name = tool.name
current_state = tool_context.state.to_dict()
# Get start time from state and calculate duration
start_time = current_state.get("tool_start_time")
if start_time:
end_time = time.time()
duration_seconds = end_time - start_time
print(f"✅ Tool {tool_name} completed")
print(f"⏱️ Duration: {duration_seconds:.4f}s")
print(f"📄 Result: {tool_response}")
print() # Add spacing
else:
print(f"✅ Tool {tool_name} completed")
print(f"📄 Result: {tool_response}")
print() # Add spacing
# Return None to use the original tool response
return None
# --- 2. Setup Agent with Tool Callbacks ---
llm_agent_with_tool_callbacks = LlmAgent(
name="tool_execution_demo_agent",
model="gemini-2.5-flash",
instruction="You are a helpful assistant with calculator tools. When users ask for calculations, use the calculator_tool with appropriate parameters and provide clear explanations of the results.",
description="An LLM agent demonstrating tool execution callbacks for monitoring",
tools=[calculator_function_tool],
before_tool_callback=before_tool_callback,
after_tool_callback=after_tool_callback
)
# --- 3. Setup Runner and Sessions ---
runner = InMemoryRunner(agent=llm_agent_with_tool_callbacks, app_name="tool_execution_callback_demo")
async def run_agent(message: str) -> str:
"""Run the agent with the given message"""
user_id = "demo_user"
session_id = "demo_session"
# Get the bundled session service
session_service = runner.session_service
# Get or create session
session = await session_service.get_session(
app_name="tool_execution_callback_demo",
user_id=user_id,
session_id=session_id
)
if not session:
session = await session_service.create_session(
app_name="tool_execution_callback_demo",
user_id=user_id,
session_id=session_id,
state={"conversation_history": []}
)
# Create user content
user_content = types.Content(
role='user',
parts=[types.Part(text=message)]
)
# Run agent and get response
response_text = ""
async for event in runner.run_async(
user_id=user_id,
session_id=session_id,
new_message=user_content
):
if event.is_final_response() and event.content:
response_text = event.content.parts[0].text.strip()
# Don't break - let the loop complete naturally to ensure callbacks run
return response_text
# --- 4. Execute ---
if __name__ == "__main__":
print("\n" + "="*50 + " Tool Execution Callbacks Demo " + "="*50)
# Test messages
test_messages = [
"Calculate 15 + 27",
"What is 100 divided by 4?",
"Multiply 8 by 12",
"What is 50 minus 23?"
]
async def test_agent():
for i, message in enumerate(test_messages, 1):
print(f"\n--- Test {i}: {message} ---")
response = await run_agent(message)
print(f"🤖 Response: {response}")
asyncio.run(test_agent())

View file

@ -0,0 +1,147 @@
#!/usr/bin/env python3
"""
Streamlit App for Tool Execution Callbacks Demo
"""
import streamlit as st
import sys
import os
import asyncio
from agent import run_agent
# Page configuration
st.set_page_config(
page_title="Tool Execution Callbacks",
page_icon="🔧",
layout="wide"
)
# Title and description
st.title("🔧 Tool Execution Callbacks Demo")
st.markdown("""
This demo shows how to monitor tool execution using callbacks.
Watch the console output to see detailed tool execution tracking!
""")
# Sidebar with information
with st.sidebar:
st.header("📊 Tool Execution Monitoring")
st.markdown("""
**Before Tool Callback**
- Triggered when a tool starts execution
- Logs tool name and input parameters
- Records agent name
- Stores start time for duration tracking
**After Tool Callback**
- Triggered when a tool finishes execution
- Logs tool result
- Calculates and displays execution duration
- Handles errors (e.g., division by zero)
""")
st.markdown("---")
st.markdown("### 🧮 Available Tools")
st.markdown("""
**Calculator Tool**:
- Addition: `add`
- Subtraction: `subtract`
- Multiplication: `multiply`
- Division: `divide`
""")
# Main chat interface
st.header("💬 Chat with Tool Monitor")
# Initialize chat history
if "messages" not in st.session_state:
st.session_state.messages = []
# Display chat messages
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Chat input
if prompt := st.chat_input("Ask me to calculate something..."):
# Add user message to chat
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# Get agent response
with st.chat_message("assistant"):
with st.spinner("🔧 Tool is executing..."):
response = asyncio.run(run_agent(prompt))
st.markdown(response)
# Add assistant response to chat
st.session_state.messages.append({"role": "assistant", "content": response})
# Quick test buttons
st.markdown("---")
st.header("⚡ Quick Tests")
col1, col2, col3 = st.columns(3)
with col1:
if st.button(" Addition Test"):
test_message = "Calculate 15 + 27"
st.session_state.messages.append({"role": "user", "content": test_message})
with st.chat_message("user"):
st.markdown(test_message)
with st.chat_message("assistant"):
with st.spinner("🔧 Tool is executing..."):
response = asyncio.run(run_agent(test_message))
st.markdown(response)
st.session_state.messages.append({"role": "assistant", "content": response})
with col2:
if st.button("➗ Division Test"):
test_message = "What is 100 divided by 4?"
st.session_state.messages.append({"role": "user", "content": test_message})
with st.chat_message("user"):
st.markdown(test_message)
with st.chat_message("assistant"):
with st.spinner("🔧 Tool is executing..."):
response = asyncio.run(run_agent(test_message))
st.markdown(response)
st.session_state.messages.append({"role": "assistant", "content": response})
with col3:
if st.button("❌ Error Test"):
test_message = "Calculate 10 divided by 0"
st.session_state.messages.append({"role": "user", "content": test_message})
with st.chat_message("user"):
st.markdown(test_message)
with st.chat_message("assistant"):
with st.spinner("🔧 Tool is executing..."):
response = asyncio.run(run_agent(test_message))
st.markdown(response)
st.session_state.messages.append({"role": "assistant", "content": response})
# Clear chat button
if st.button("🗑️ Clear Chat History"):
st.session_state.messages = []
st.rerun()
# Information about callbacks
st.markdown("---")
st.header("📋 Tool Callback Output")
st.markdown("""
**Check your console/terminal** to see the tool execution output:
```
🔧 Tool calculator_tool started
📝 Parameters: {'operation': 'add', 'a': 15.0, 'b': 27.0}
📋 Agent: tool_execution_demo_agent
Tool calculator_tool completed
Duration: 0.0012s
📄 Result: 15 + 27 = 42
```
""")
# Footer
st.markdown("---")
st.markdown("*Watch the console output to see tool execution callbacks in action!*")

View file

@ -0,0 +1,3 @@
google-adk>=1.9.0
streamlit>=1.47.1
python-dotenv>=1.1.1

View file

@ -0,0 +1,189 @@
# 🎯 Tutorial 6: Callbacks
## 🎯 What You'll Learn
- **Agent Lifecycle Callbacks**: Monitor agent creation, initialization, and cleanup
- **LLM Interaction Callbacks**: Track model requests, responses, and token usage
- **Tool Execution Callbacks**: Monitor tool calls, parameters, and results
## 🧠 Core Concept: Callbacks
Callbacks are functions that get executed at specific points during agent execution, allowing you to monitor, log, and control the agent's behavior without modifying the core logic.
### **Callback Flow Diagram**
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Agent Start │───▶│ LLM Request │───▶│ Tool Execution │
│ Callback │ │ Callback │ │ Callback │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Agent End │ │ LLM Response │ │ Tool Result │
│ Callback │ │ Callback │ │ Callback │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### **Why Use Callbacks?**
- **Monitoring**: Track agent performance and behavior
- **Logging**: Record interactions for debugging and analysis
- **Control**: Modify behavior based on specific events
- **Integration**: Connect agents to external systems
- **Debugging**: Understand what's happening inside the agent
## 🚀 Tutorial Overview
This tutorial covers three essential callback patterns in Google ADK:
1. **Agent Lifecycle Callbacks**: Monitor agent creation, initialization, and cleanup events
2. **LLM Interaction Callbacks**: Track model requests, responses, and token usage
3. **Tool Execution Callbacks**: Monitor tool calls, parameters, and execution results
Each sub-tutorial provides simple, focused examples that demonstrate specific callback patterns.
## 📁 Project Structure
```
6_callbacks/
├── README.md # This file - concept explanation
├── 6_1_agent_lifecycle_callbacks/ # Agent lifecycle monitoring
│ ├── README.md # Lifecycle callback patterns
│ ├── agent.py # Agent with lifecycle callbacks
│ ├── app.py # Streamlit interface
│ └── requirements.txt # Dependencies
├── 6_2_llm_interaction_callbacks/ # LLM request/response tracking
│ ├── README.md # LLM callback patterns
│ ├── agent.py # Agent with LLM callbacks
│ ├── app.py # Streamlit interface
│ └── requirements.txt # Dependencies
└── 6_3_tool_execution_callbacks/ # Tool execution monitoring
├── README.md # Tool callback patterns
├── agent.py # Agent with tool callbacks
├── app.py # Streamlit interface
└── requirements.txt # Dependencies
```
## 🎯 Learning Objectives
By the end of this tutorial, you'll understand:
- ✅ **Callback Fundamentals**: How callbacks work in Google ADK
- ✅ **Lifecycle Monitoring**: Track agent creation, initialization, and cleanup
- ✅ **LLM Tracking**: Monitor model requests, responses, and performance
- ✅ **Tool Monitoring**: Track tool execution and results
- ✅ **Practical Applications**: Real-world use cases for callbacks
- ✅ **Debugging Techniques**: Use callbacks for troubleshooting
## 🚀 Getting Started
### **Prerequisites**
- Python 3.11+
- Google AI Studio API key
- Basic understanding of Google ADK (Tutorials 1-5)
### **Setup**
1. **Get API Key**: Visit [Google AI Studio](https://aistudio.google.com/)
2. **Create .env file**: Add `GOOGLE_API_KEY=your_key_here`
3. **Install dependencies**: `pip install -r requirements.txt`
### **Run Tutorials**
```bash
# Agent Lifecycle Callbacks
cd 6_1_agent_lifecycle_callbacks
streamlit run app.py
# LLM Interaction Callbacks
cd ../6_2_llm_interaction_callbacks
streamlit run app.py
# Tool Execution Callbacks
cd ../6_3_tool_execution_callbacks
streamlit run app.py
```
## 🔧 Callback Patterns
### **1. Agent Lifecycle Callbacks**
```python
def on_agent_start(agent_name: str):
print(f"🚀 Agent {agent_name} started")
def on_agent_end(agent_name: str, result: str):
print(f"✅ Agent {agent_name} completed: {result}")
# Register callbacks
agent = LlmAgent(
name="my_agent",
model="gemini-2.5-flash",
on_start=on_agent_start,
on_end=on_agent_end
)
```
### **2. LLM Interaction Callbacks**
```python
def on_llm_request(model: str, prompt: str):
print(f"🤖 LLM Request to {model}: {prompt[:50]}...")
def on_llm_response(model: str, response: str, tokens: int):
print(f"📝 LLM Response from {model}: {tokens} tokens")
# Register callbacks
agent = LlmAgent(
name="my_agent",
model="gemini-2.5-flash",
on_llm_request=on_llm_request,
on_llm_response=on_llm_response
)
```
### **3. Tool Execution Callbacks**
```python
def on_tool_start(tool_name: str, params: dict):
print(f"🔧 Tool {tool_name} started with params: {params}")
def on_tool_end(tool_name: str, result: str):
print(f"✅ Tool {tool_name} completed: {result}")
# Register callbacks
agent = LlmAgent(
name="my_agent",
model="gemini-2.5-flash",
tools=[my_tool],
on_tool_start=on_tool_start,
on_tool_end=on_tool_end
)
```
## 🎯 Use Cases
### **Monitoring & Analytics**
- Track agent performance metrics
- Monitor token usage and costs
- Analyze tool usage patterns
- Debug agent behavior
### **Logging & Debugging**
- Log all agent interactions
- Debug tool execution issues
- Monitor LLM response quality
- Track error patterns
### **Integration & Control**
- Connect to external monitoring systems
- Implement custom error handling
- Add authentication and validation
- Control agent behavior dynamically
## 🔗 Next Steps
After completing this tutorial, you'll be ready for:
- **[Advanced Agent Patterns](../advanced_patterns/README.md)** - Complex agent architectures
- **[Production Deployment](../deployment/README.md)** - Deploying agents to production
- **[Custom Tools](../custom_tools/README.md)** - Building custom tools and integrations
## 📚 Additional Resources
- [Google ADK Documentation](https://google.github.io/adk-docs/)
- [Callback API Reference](https://google.github.io/adk-docs/api-reference/python/)
- [Best Practices Guide](https://google.github.io/adk-docs/best-practices/)

View file

@ -43,7 +43,12 @@ This crash course covers the essential concepts of Google ADK through hands-on t
- **[5.1 In-Memory Conversation](./5_memory_agent/5_1_in_memory_conversation/README.md)** - Basic session management
- **[5.2 Persistent Conversation](./5_memory_agent/5_2_persistent_conversation/README.md)** - Database storage with SQLite
6. **More tutorials coming soon!**
6. **[6_callbacks](./6_callbacks/README.md)** - Callback patterns and monitoring
- **[6.1 Agent Lifecycle Callbacks](./6_callbacks/6_1_agent_lifecycle_callbacks/README.md)** - Monitor agent creation and cleanup
- **[6.2 LLM Interaction Callbacks](./6_callbacks/6_2_llm_interaction_callbacks/README.md)** - Track model requests and responses
- **[6.3 Tool Execution Callbacks](./6_callbacks/6_3_tool_execution_callbacks/README.md)** - Monitor tool calls and results
7. **More tutorials coming soon!**
## 🛠️ Prerequisites

View file

@ -1,5 +0,0 @@
google-adk>=0.1.0
streamlit>=1.28.0
python-dotenv>=1.0.0
sqlalchemy>=2.0.0
asyncio