Author: Robina Mirbahar
AI/ML Google Developer Expert (GDE) | Google Cloud Champion Innovator
GitHub Blueprint: RobinaMirbahar/railpulse-gemma-cloudrun
YouTube Tutorial: Watch Full Hands-On Video Lab
Most artificial intelligence tutorials show you how to build chatbots that write poems, summarize PDFs, or generate boilerplate code.
Those demos are fun, but they don't reflect the harsh reality of engineering in mission-critical industries.
When you deploy AI to manage physical operations, like freight trains moving across thousands of kilometers, a generic chatbot is not just useless; it can be dangerous. A hallucinated speed limit or a delayed signal alert can lead to catastrophic hardware failure or derailment.
At DevFest Baku 2026 at ADA University, I wanted to show developers how to build an enterprise-grade AI system that solves a real operational problem. We built and deployed RailPulse AI, an autonomous, safety-critical railway dispatch and live-tracking agent powered by Google's open-weights Gemma models, the Google Agent Development Kit (ADK), and Google Cloud Run.
In this article, I will walk you through the complete architecture, how we used function calling with deterministic safety interlocks, and how you can run open-weights agents serverlessly in production.
The Real-World Engineering Problem
Railway corridors run on tight margins. Dispatchers must continuously monitor:
- Real-Time Telemetry: GPS coordinates, axle bearing temperatures, and locomotive velocity.
- Corridor Weather Extremes: Sudden crosswinds along coastal or mountain tracks.
- Schedule Recovery: Balancing delays against passenger connection deadlines and single-track passing sidings.
When an emergency happens, dispatchers do not have time to sift through fragmented SCADA dashboards and thousand-line logs. They need an intelligent system that aggregates live telemetry, cross-references signaling rules, and synthesizes clear, actionable dispatch advisories.
Crucially, the system cannot hallucinate. It must combine the natural reasoning of an LLM with the deterministic predictability of hard-coded safety rules.
The RailPulse AI Architecture
Here is the high-level system design we implemented in RobinaMirbahar/railpulse-gemma-cloudrun:
┌─────────────────────────────────────────────────────────────┐ │ Streamlit Operator Cockpit │ │ Interactive SVG Gauges • Multi-Language (EN/AZ/RU) │ └──────────────────────────────┬──────────────────────────────┘ │ HTTPS / JSON ▼ ┌─────────────────────────────────────────────────────────────┐ │ Google Cloud Run Service │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Google ADK Agent Orchestrator │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────────┐ │ │ │ │ │ Gemma Model │ ◄───► │ Tool Declarations │ │ │ │ │ │ (Open-Weights) │ │ Function Calling │ │ │ │ │ └─────────────────┘ └──────────┬──────────┘ │ │ │ └───────────────────────────────────────┼─────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Deterministic Safety Interlocks (Python) │ │ │ │ • Hard Wind Speed Limits (Wind > 65 km/h -> 80) │ │ │ │ • Absolute Signal Overrides (Red Signal = STOP) │ │ │ └───────────────────────────────────────┬─────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Pydantic Structured Advisory Schema │ │ │ │ `RailDispatchAdvisory` Validation │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ 1. Grounding the Agent: Real-World Tools via Function Calling
Rather than letting Gemma guess railway conditions, we gave the agent access to dedicated telemetry tools using Google ADK function calling:
def get_live_train_telemetry(train_id: str) -> dict: """Fetch live GPS, speed, bearing temperatures, and locomotive health.""" ... def check_corridor_signals_and_wind(corridor_id: str) -> dict: """Query wayside signal states (Green/Yellow/Red) and track anemometers.""" ... def calculate_schedule_recovery(train_id: str, current_delay_min: int) -> dict: """Calculate speed profiles needed to recover schedule without exceeding corridor limits.""" ... When an operator asks:
"Train 102 is approaching the Absheron corridor 14 minutes behind schedule. Can we accelerate to recover time?"
Gemma doesn't just guess. It autonomously calls check_corridor_signals_and_wind(corridor="Absheron") and get_live_train_telemetry(train_id="102") to gather factual state before generating a response.
2. Deterministic Safety Interlocks: Why the LLM Doesn't Make the Final Safety Call
In physical systems, you never give an LLM sole authority over life-safety decisions.
If the model experiences a temporary context confusion, it might suggest maintaining 140 km/h in a 90 km/h windstorm. To prevent this, we built a two-tier architecture:
- Tier 1 (Cognitive Reasoning): Gemma analyzes the situation, extracts the context, and proposes a schedule recovery plan.
- Tier 2 (Deterministic Guardrails): A hard-coded Python interlock inspects the output against physical invariants before delivering the advisory:
class SafetyInterlock: MAX_HIGH_WIND_SPEED_KMH = 80.0 HIGH_WIND_THRESHOLD_KMH = 65.0 @classmethod def enforce_guardrails(cls, advisory: RailDispatchAdvisory, wind_speed: float, signal_state: str) -> RailDispatchAdvisory: # Rule 1: High Wind Speed Clamp if wind_speed >= cls.HIGH_WIND_THRESHOLD_KMH: if advisory.recommended_speed_kmh > cls.MAX_HIGH_WIND_SPEED_KMH: advisory.recommended_speed_kmh = cls.MAX_HIGH_WIND_SPEED_KMH advisory.safety_flags.append( f"GUARDRAIL ENFORCED: Wind speed {wind_speed} km/h exceeds threshold. Speed clamped to 80 km/h." ) # Rule 2: Absolute Red Signal Override if signal_state == "RED": advisory.recommended_speed_kmh = 0.0 advisory.action_required = "EMERGENCY_STOP" advisory.safety_flags.append("CRITICAL: Red signal detected. Immediate stop enforced.") return advisory This guarantees that even if an operator or a prompt edge case attempts to push the model beyond safe boundaries, the mathematical and physical guardrails cannot be bypassed.
3. Strict Output Contracts with Pydantic
To ensure the downstream operator dashboard never crashes due to unstructured freeform text, all agent advisories are validated against a strict schema:
from pydantic import BaseModel, Field from typing import List, Optional class RailDispatchAdvisory(BaseModel): train_id: str recommended_speed_kmh: float = Field(..., ge=0.0, le=160.0) delay_delta_minutes: int signal_status: str action_required: str advisory_summary: str safety_flags: List[str] = [] confidence_score: float = Field(..., ge=0.0, le=1.0) If the agent’s response does not conform to this structure, the pipeline rejects it before it ever reaches the dispatcher cockpit.
4. Packaging and Deploying on Google Cloud Run
To make RailPulse AI truly production-ready, we containerized the application with Docker and deployed it to Google Cloud Run.
Why Cloud Run for AI Agents?
- Serverless Economics: Rail telematics spikes during train arrivals and yard movements, with quiet periods in between. Cloud Run scales dynamically from zero to handle spikes and scales back down when traffic subsides.
- Air-Gapped & Sovereign Edge Ready: Because Gemma is an open-weights model, you can run inference entirely inside your own private VPC without sending sensitive operational telemetry to third-party proprietary APIs.
- One-Command Deployment:
# Build and deploy directly to Cloud Run gcloud run deploy railpulse-agent \ --source . \ --platform managed \ --region europe-west1 \ --allow-unauthenticated \ --memory 4Gi \ --cpu 2 5. Localized Operator Experience: Multi-Language Dispatch (EN / AZ / RU)
In international logistics corridors like the Trans-Caspian International Transport Route, operators speak multiple languages.
Because Gemma possesses strong multilingual representations, RailPulse AI seamlessly provides telemetry readouts, safety flags, and advisory summaries in English, Azerbaijani (Azərbaycan dili), and Russian (Русский), allowing local dispatchers to interact with the system in their native language while maintaining uniform safety semantics under the hood.
Key Lessons from Building RailPulse AI
Building this project and presenting it at DevFest Baku reinforced three fundamental principles for building agentic AI:
- Agents are More Than Prompts: A production agent is 20% prompt engineering and 80% systems engineering: tool definitions, schema validation, network resilience, and error handling.
- Deterministic Guardrails are Mandatory: In physical, industrial, or medical applications, never let an LLM have unchecked authority. Pair cognitive reasoning with deterministic code interlocks.
- Open-Weights Models are Production Ready: With models like Gemma and serverless platforms like Google Cloud Run, individual developers and enterprise teams can build custom, private, and fully auditable AI systems without paying thousands of dollars in proprietary API costs.
Explore the Code
The complete codebase, Streamlit operator dashboard, Dockerfile, and Google ADK implementation are open-source and ready to clone:
⭐ GitHub Repository: github.com/RobinaMirbahar/railpulse-gemma-cloudrun
📺 Follow the channel for hands-on AI tutorials: @RobinaCodesAI
Special thanks to the GDG Baku team, ADA University, and all the incredible participants at DevFest Baku 2026 for making this hands-on workshop such a rewarding experience!
Are you building autonomous agents for real-world IoT or operations? Let's connect in the comments or on LinkedIn and GitHub!
#AIAgents #GoogleCloud #Gemma #DevFestBaku #CloudRun #IoT #OpenSource #SoftwareEngineering #Python #DevFest2026

