This post is my submission for DEV Education Track: Build Multi-Agent Systems with ADK.
What I Built
GeoGuard AI – a multi-agent geological intelligence system that automates terrain risk assessment.
The problem: geological hazard analysis (landslides, slope instability) usually requires multiple domain experts (geologists, climatologists) and manual synthesis. GeoGuard AI uses three specialized agents to replicate this collaborative workflow: a Hazard Agent, a Climate Agent, and an Orchestrator Agent.

Given a location (e.g., Nanga Parbat – Higher Himalayan Syntaxis), the system independently analyzes slope stability, climate trends, and then combines both to identify compounding risks – like how rising temperatures and rain-on-snow events can destabilize a "moderate" slope into a high-risk zone.

The result is a fast, explainable, and modular AI system that demonstrates real-world agentic collaboration.
Cloud Run Embed
⚠️ Note on execution environment
The agents were successfully executed during development. Later, the original cloud execution environment became restricted due to project permission and billing limitations.
The architecture, code, and multi-agent logic remain fully validated.
Your Agents
GeoGuard AI uses a supervised, hierarchical multi-agent pattern built with Google ADK.
| Agent | Role | Specialization |
|---|---|---|
| OrchestratorAgent | Manager | Receives user request, delegates tasks, synthesizes final report. Does not perform analysis itself. |
| HazardAgent | Geologist | Evaluates slope gradients, lithology, structural discontinuities. Uses a landslide_tool for deterministic risk calculation. |
| ClimateAgent | Climatologist | Analyzes temperature anomalies (High-Elevation Amplification), rainfall trends, and monsoon penetration. |
How they work together:
- User submits a target location →
OrchestratorAgentinitializes context. -
ClimateAgentandHazardAgentrun in parallel (orchestrated by the parent agent). - Each returns structured output (risk level + explanation).
-
OrchestratorAgentcombines both outputs to identify compounding effects – e.g., “High Climate Risk + Moderate Hazard = Volatile environment.”
Code snippet – Hazard Agent with tool:
from google.adk.agent import Agent
hazard_agent = Agent(
name="HazardAgent",
description="Evaluates geological hazards such as landslides and terrain instability.",
ROLE: Geological hazard specialist.
RULES: Do not analyze climate factors.
OUTPUT FORMAT: Hazard Level (Low/Moderate/High) + Explanation + Key Factors
"""
)
@hazard_agent.tool
def landslide_tool(slope: float, rainfall: float):
if slope > 30 and rainfall > 100:
return "High Landslide Risk"
return "Moderate Risk"
Code snippet – Climate Agent with tool:
from google.adk.agent import Agent
climate_agent = Agent(
name="ClimateAgent",
description="Analyzes climate conditions influencing geological hazards.",
model="gemini-1.5-pro",
instructions="""
ROLE:
Climate analysis specialist.
RESPONSIBILITIES:
- Evaluate rainfall trends
- Assess temperature anomalies
- Determine climate amplification effects
RULES:
- Avoid geological interpretation.
- Focus only on climate influence.
OUTPUT FORMAT:
Climate Risk Level:
Explanation:
"""
)
Tool 1: Calculate High-Elevation Amplification (HEA)
@climate_agent.tool
def high_elevation_amplification_tool(
current_temp: float,
historic_temp: float,
elevation: float
) -> str:
"""
Returns climate risk level based on temperature anomaly amplified by elevation.
"""
anomaly = current_temp - historic_temp
amplification = anomaly * (1 + elevation / 5000) # simplified model
if amplification > 2.5:
return "High Climate Risk: Extreme temperature anomaly"
elif amplification > 1.0:
return "Moderate Climate Risk: Notable warming trend"
else:
return "Low Climate Risk: Stable thermal regime"
Tool 2: Evaluate rainfall-induced risk (monsoon / rain-on-snow)
@climate_agent.tool
def rainfall_risk_tool(annual_rainfall: float, rain_on_snow_events: int) -> str:
"""
Assesses risk from precipitation changes.
"""
if annual_rainfall > 1200 and rain_on_snow_events > 3:
return "High Climate Risk (Rain-on-snow hazard)"
elif annual_rainfall > 800 or rain_on_snow_events > 1:
return "Moderate Climate Risk"
return "Low Climate Risk"
Code snippet – orchestrator Agent with tool:
from google.adk.agent import Agent
orchestrator = Agent(
name="OrchestratorAgent",
description="Coordinates communication between all specialized agents.",
model="gemini-1.5-pro",
instructions="""
ROLE:
Manage workflow between agents.
RESPONSIBILITIES:
- Receive user request
- Delegate tasks
- Combine results
RULES:
- Do not perform analysis directly.
- Use agents collaboratively.
- Maintain session context.
"""
)
Tool 1: Delegate to HazardAgent
@orchestrator.tool
def call_hazard_agent(location: str, slope_angle: float, lithology: str) -> str:
"""
Simulate calling HazardAgent – in practice, invoke the agent.
Returns hazard level and key factors.
"""
# This would be a real agent call in production
if slope_angle > 30:
return f"Hazard assessment for {location}: High Risk (steep slope {slope_angle}° on {lithology})"
else:
return f"Hazard assessment for {location}: Moderate Risk (slope {slope_angle}°, {lithology})"
Tool 2: Delegate to ClimateAgent
@orchestrator.tool
def call_climate_agent(location: str, temp_anomaly: float) -> str:
"""
Simulate calling ClimateAgent.
"""
if temp_anomaly > 1.5:
return f"Climate assessment for {location}: High Risk (anomaly +{temp_anomaly}°C)"
else:
return f"Climate assessment for {location}: Moderate Risk (anomaly +{temp_anomaly}°C)"
Tool 3: Synthesize both reports and identify compounding effects
@orchestrator.tool
def synthesize_risk(hazard_output: str, climate_output: str) -> str:
"""
Combine agent outputs to produce final recommendation.
"""
risk_level = "CRITICAL" if ("High" in hazard_output and "High" in climate_output) else "ELEVATED" if ("Moderate" in hazard_output and "High" in climate_output) else "MANAGEABLE"
return f"""
Final Synthesis:
- Hazard: {hazard_output}
- Climate: {climate_output}
- Compounding Risk Level: {risk_level}
- Recommendation: {'Immediate monitoring of rain-on-snow events and slope pore-water pressure' if risk_level == 'CRITICAL' else 'Routine observation'}
"""
Tool 4: Validate session context (prevent drift)
@orchestrator.tool
def check_context_integrity(user_target: str, expected_target: str) -> bool:
"""
Ensures the conversation hasn't shifted targets unexpectedly.
"""
return user_target.strip().lower() == expected_target.strip().lower()
Key Learnings
Separation of concerns prevents hallucination
Telling the Climate Agent to avoid geological interpretation and the Hazard Agent to ignore climate factors forced each agent to stay in its lane. This dramatically improved output quality.The orchestrator pattern is powerful but subtle
The Orchestrator Agent doesn't need a complex model – it just needs clear instructions to delegate and combine. Its "integrity" (no drift in reasoning chains) was surprisingly easy to maintain with good prompt boundaries.Tool use replaces guesswork
Instead of asking Gemini to "estimate landslide risk", I gave the Hazard Agent a simple landslide_tool with deterministic logic. This is a great pattern for any numeric or rule-based calculation.Real-world constraints are real
Everything worked perfectly in development, but cloud execution was later blocked by billing/permission limits.Monitoring agent health matters
During testing, the Climate Agent caused a token bottleneck (>4s queue) due to high temperature anomaly sampling. This showed that even well-designed agents need performance monitoring – not just accuracy.































