Author: Robina Mirbahar
AI/ML Google Developer Expert (GDE) | Google Cloud Champion Innovator
GitHub Blueprint: RobinaMirbahar/railpulse-gemma-cloudrun
YouTube Walkthrough: @RobinaCodesAI

Robina Mirbahar presenting RailPulse AI, Gemma 4, and Cloud Run at DevFest Baku 2026, ADA University.
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 do not 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."""
pass
def check_corridor_signals_and_wind(corridor_id: str) -> dict:
"""Query wayside signal states (Green/Yellow/Red) and track anemometers."""
pass
def calculate_schedule_recovery(train_id: str, current_delay_min: int) -> dict:
"""Calculate speed profiles needed to recover schedule without exceeding corridor limits."""
pass
When an operator asks:
"Train 102 is approaching the Absheron corridor 14 minutes behind schedule. Can we accelerate to recover time?"
Gemma does not 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 Does Not 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 ADK 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 and 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:
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 and Video Walkthrough
The complete codebase, Streamlit operator dashboard, Dockerfile, and Google ADK implementation are open-source and ready to clone:
- GitHub Repository: https://github.com/RobinaMirbahar/railpulse-gemma-cloudrun
- YouTube Channel: https://www.youtube.com/@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!
