DEV Community

Cover image for GeoGuard AI– a multi-agent geological intelligence system that automates terrain risk assessment.
Muhammad Yasin Khan
Muhammad Yasin Khan

Posted on

GeoGuard AI– a multi-agent geological intelligence system that automates terrain risk assessment.

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.

Hazard Agent (Geology Specialist)

from google.adk.agent import Agent

hazard_agent = Agent(
    name="HazardAgent",
    description="Evaluates geological hazards such as landslides and terrain instability.",
    model="gemini-1.5-pro",
    instructions="""
    ROLE:
    Geological hazard specialist.

    RESPONSIBILITIES:
    - Analyze slope instability
    - Evaluate landslide susceptibility
    - Identify terrain risks

    RULES:
    - Do not analyze climate factors.
    - Use scientific reasoning.
    - Provide clear risk classification.

    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"
Enter fullscreen mode Exit fullscreen mode

Climate Agent (Atmospheric Specialist)

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: Low | Moderate | High
    Explanation:
    """
)

@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)

    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"

@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"
Enter fullscreen mode Exit fullscreen mode

Orchestrator Agent (Manager)

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.
    """
)

@orchestrator.tool
def call_hazard_agent(location: str, slope_angle: float, lithology: str) -> str:
    """Simulate calling HazardAgent – returns hazard level."""
    if slope_angle > 30:
        return f"Hazard: High Risk (slope {slope_angle}° on {lithology})"
    return f"Hazard: Moderate Risk (slope {slope_angle}°, {lithology})"

@orchestrator.tool
def call_climate_agent(location: str, temp_anomaly: float) -> str:
    """Simulate calling ClimateAgent."""
    if temp_anomaly > 1.5:
        return f"Climate: High Risk (anomaly +{temp_anomaly}°C)"
    return f"Climate: Moderate Risk (anomaly +{temp_anomaly}°C)"

@orchestrator.tool
def synthesize_risk(hazard_output: str, climate_output: str) -> str:
    """Combine agent outputs and identify compounding effects."""
    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_output}
    - {climate_output}
    - Compounding Risk Level: {risk_level}
    - Recommendation: {'Immediate monitoring of rain-on-snow events and pore-water pressure' if risk_level == 'CRITICAL' else 'Routine observation'}
    """
Enter fullscreen mode Exit fullscreen mode

How They Work Together

  1. User submits a target location → OrchestratorAgent initializes context.
  2. ClimateAgent and HazardAgent run in parallel (orchestrated by the parent agent).
  3. Each returns structured output (risk level + explanation).
  4. OrchestratorAgent combines both outputs using synthesize_risk to identify compounding effects – e.g., “High Climate Risk + Moderate Hazard = Elevated risk environment.”

Key Learnings

  1. 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.

  2. The orchestrator pattern is powerful but subtle
    The OrchestratorAgent 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.

  3. Tool use replaces guesswork
    Instead of asking Gemini to "estimate high‑elevation amplification", I gave the Climate Agent a deterministic high_elevation_amplification_tool. This is a great pattern for any numeric or rule‑based calculation.

  4. Real‑world constraints are real
    Everything worked perfectly in development, but cloud execution was later blocked by billing/permission limits. This taught me to always design agents that can run locally or be easily redeployed – and to document that clearly.

  5. 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.

Top comments (0)