Skip to content

Natural language bus arrival queries for Singapore using LangGraph, Gemini, and LTA DataMall

Spkap/TransitGPT

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TransitGPT

LangGraph-based conversational agent for Singapore bus queries using real-time LTA DataMall APIs

Table of Contents

  1. Project Overview
  2. Architecture & Workflow Design
  3. Key Features
  4. Assumptions Made
  5. Setup Instructions
  6. Usage
  7. Deployment Considerations

Project Overview

Deliverables

This project demonstrates:

  • Part 1: Transport query agent using LangGraph for workflow orchestration
  • Part 2: 10-user simulation with results analysis

The agent converts natural language queries (e.g., "When is the next bus at Marina Bay Sands?") into real-time bus arrival information via Singapore's LTA DataMall APIs.

Tech Stack

Component Technology Purpose
Framework LangGraph v0.2+ Agent orchestration
LLM Google Gemini 2.0 Flash Lite Function calling
APIs LTA DataMall BusArrival v3 Real-time data
Validation Pydantic Type safety
Language Python 3.11+ Async/await patterns

Architecture & Workflow Design

System Architecture

graph TB
    User[User Query] --> Agent[LangGraph Agent]
    Agent --> Tool1[find_bus_stop_code]
    Agent --> Tool2[get_bus_arrival_times]
    Tool1 --> Cache[(Bus Stop Cache<br/>5000+ locations)]
    Tool2 --> LTA[LTA DataMall API]
    LTA --> Response[Formatted Response]
    Cache --> Response

Loading

LangGraph Workflow

flowchart LR
    A[User Query] --> B[Agent Node]
    B --> C{should_continue?}
    C -->|Has tool_calls| D[Tool Node]
    C -->|No tool_calls| E[Final Response]
    D --> B

Loading

Why LangGraph?

Chosen over simpler frameworks for:

  • Explicit Control: StateGraph makes decisions transparent (vs black-box agents)
  • State Management: Built-in message accumulation across tool calls
  • Conditional Routing: Intelligent loop management via should_continue() edge
  • Production Maturity: Async execution, error boundaries, scalability

Agent Components

Nodes (Processing Units):

  • agent_node: Gemini 2.0 reasoning for tool selection
  • tool_node: Executes landmark resolution or bus arrivals

Edges (Flow Control):

  • Conditional: Routes based on presence of tool calls
  • Return: Loops tool results back to agent

State:

  • AgentState: TypedDict with operator.add for message accumulation
  • Preserves full conversation context

Tool Selection Logic

Two-tool architecture:

  1. find_bus_stop_code: Converts landmarks to 5-digit codes
    • Fuzzy matching: "Bugis Junction" → "Bugis Jct" → code 03211
    • Multi-strategy search: exact → normalized → keyword
  2. get_bus_arrival_times: Fetches real-time arrivals
    • Optional service filtering (e.g., "bus 196 only")
    • GPS-tracked arrival times with seat availability

Decision Flow

graph TD
    A[User Query] --> B{Query Type?}
    B -->|"Direct Code<br/>(e.g., stop 01012)"| C[get_bus_arrival_times]
    B -->|"Landmark Name<br/>(e.g., Marina Bay)"| D[find_bus_stop_code]
    B -->|"Service + Code<br/>(e.g., bus 196 at 01012)"| C
    B -->|"Service + Landmark<br/>(e.g., bus 196 at Marina Bay)"| D
    D --> E[Returns Bus Stop Code]
    E --> C
    C --> F[Returns Real-time Arrivals]
    F --> G[Natural Language Response]

Loading

Example Flow

Scenario: Multi-step query

User: "Bus times at Marina Bay Sands"
Step 1: Agent detects landmark name (no bus stop code)
Step 2: Call find_bus_stop_code("Marina Bay Sands")
        → Returns: "04169"
Step 3: Call get_bus_arrival_times("04169")
        → Returns: [Bus 133: 3min (SEA), Bus 502A: 7min (SEA)]
Step 4: Generate response: "Bus 133 in 3 mins, Bus 502A in 7 mins"

Scenario: Direct code

User: "Arrivals at stop 77009"
Step 1: Agent detects 5-digit code
Step 2: Call get_bus_arrival_times("77009")
        → Returns: Real-time data
Step 3: Format and respond

Key Features

1. Natural Language Understanding

  • Handles colloquial location names
  • Fuzzy matching for naming variations
  • Extracts parameters (stop code, service number, landmark)

2. Real-time Data Integration

  • Live GPS-tracked bus arrivals (LTA updates every 20 seconds)
  • Seat availability status (SEA/SDA/LSD)
  • Service-specific filtering when requested

3. Robust Error Handling

  • Pydantic validation for API responses
  • Retry logic with exponential backoff (tenacity)
  • Graceful degradation for API failures
  • Clear error messages for invalid inputs

4. Multi-User Testing

  • 10 diverse test scenarios (Part 2 deliverable)
  • Covers: direct codes, landmarks, service filtering, error cases
  • 100% appropriate response handling achieved

Assumptions Made

API & Data

  • Valid LTA API key with sufficient quota (5000 calls/month free tier)
  • LTA updates bus positions every 20 seconds
  • Stable JSON response schemas per LTA documentation
  • 5-digit bus stop code format consistent

User Behavior

  • English language queries
  • Geographic scope: Singapore public transport only
  • Query intent: Bus arrival information
  • Operating hours: 5:30 AM - 12:30 AM SGT

Technical

  • Gemini 2.0 Flash Lite provides reliable function calling
  • Stable internet connectivity (2-5 second acceptable latency)
  • Conversation stays within model's context window
  • Python 3.11+ environment available

Setup Instructions

Prerequisites

Installation

  1. Clone Repository

    git clone <repository-url>
    cd singapore-transport-agent
  2. Virtual Environment

    python -m venv venv
    source venv/bin/activate  # Windows: venv\Scripts\activate
  3. Install Dependencies

    pip install -r requirements.txt
  4. Configure Environment

    Create .env file:

    LTA_API_KEY=your_lta_api_key_here
    GOOGLE_API_KEY=your_google_api_key_here
    TZ=Asia/Singapore
  5. Verify Setup

    python -c "
    import os
    from dotenv import load_dotenv
    load_dotenv()
    assert os.getenv('LTA_API_KEY'), 'Missing LTA API key'
    assert os.getenv('GOOGLE_API_KEY'), 'Missing Google API key'
    print('✅ Environment configured')
    "

Usage

Running the Agent

  1. Start Jupyter Notebook

    jupyter notebook Transit.ipynb
  2. Execute Cells Sequentially

    • Run from top to bottom
    • First execution caches 5000+ bus stops (2-3 minutes)
    • Subsequent runs use cached data
  3. View Results

    • Cell 7 contains 10-user simulation
    • Results table shows query responses
    • Success metrics displayed at end

Example Queries

Landmark-based:

  • "Show me bus arrivals at Marina Bay Sands"
  • "Next bus at Raffles Hotel"
  • "Buses at Suntec City"

Service-specific:

  • "When is bus 196 arriving at stop 01012?"
  • "Service 175 at Marina Bay Sands"

Direct codes:

  • "Arrivals at stop 77009"
  • "Check bus times for 04169"

Response Time

  • Cached landmarks: 2-3 seconds
  • New landmarks: 5-8 seconds
  • Direct codes: 3-5 seconds

Deployment Considerations

LangGraph Agent Deployment

Core Stack:

  • API Layer: FastAPI (async endpoints)
  • Agent: LangGraph + Gemini 2.0 Flash Lite
  • Storage: Redis (conversation state + bus stop cache)
  • Platform: Docker containerized

Deployment Options:

  • LangGraph Cloud: Managed deployment, easiest for prototypes
  • AWS: ECS Fargate + ElastiCache Redis
  • Google Cloud: Cloud Run + Memorystore
  • Azure: Container Apps + Azure Cache for Redis

Scaling Considerations

The Stateful Challenge:

LangGraph agents maintain conversation context across requests - naive scaling breaks this.

Solution:

  • Store conversation state in Redis per user (session:{user_id})
  • Session TTL: 30 minutes
  • Async processing for concurrent requests

Performance Optimization:

  • Cache bus stop data (24hr TTL, ~2MB for 5000 stops)
  • Reuse HTTP connections to external APIs
  • Rate limiting respects LTA quota (5000 calls/month)

Monitoring AI Agents

Agent-Specific Metrics:

  • Tool call frequency and success rates
  • Agent loop iterations (detect infinite loops)
  • LLM token usage and costs

Observability Tools:

  • LangSmith: Trace agent workflows, debug tool decisions
  • Logging: JSON logs with user correlation IDs
  • Alerts: Trigger when LTA API fails (agent cannot function)

Security & Privacy

API Key Protection:

LTA_API_KEY = os.getenv("LTA_API_KEY")        # Never hardcode
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")  # Environment variables

About

Natural language bus arrival queries for Singapore using LangGraph, Gemini, and LTA DataMall

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published