Building RailPulse AI: Deploying an Open-Source Railway Dispatch Agent with Gemma and Google Cloud Run
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
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
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.