Friday, 1 August 2025

AI-Powered Train Ticket Booking Agent with .NET MAUI

 This solution outlines how to build an intelligent train ticket booking agent using .NET MAUI that works both online and offline, with machine learning capabilities to improve suggestions over time.

Solution Architecture

  1. .NET MAUI Frontend: Cross-platform mobile application

  2. Local AI/ML Engine: ONNX runtime with pre-trained models

  3. Offline Data Storage: SQLite database for user preferences and historical data

  4. Online Fallback: Integration with open-source LLMs (like Llama.cpp)

Note :  ONNX (Open Neural Network Exchange) Runtime is a high-performance inference engine for machine learning models. Here's how to integrate it into your .NET MAUI train booking agent for offline AI capabilities.

Implementation Steps

1. Set up the .NET MAUI Project

bash
dotnet new maui -n TrainBookingAgent
cd TrainBookingAgent

2. Add Required NuGet Packages

bash
dotnet add package Microsoft.ML
dotnet add package Microsoft.ML.OnnxRuntime
dotnet add package SQLiteNetExtensions
dotnet add package LlamaSharp (for online LLM fallback)

3. Core Components

a. Machine Learning Model for Personalized Recommendations

Create a recommendation engine that learns from user behavior:

csharp
// RecommendationModel.cs
public class RecommendationModel
{
    private MLContext _mlContext;
    private ITransformer _model;
    private PredictionEngine<UserPreference, TravelRecommendation> _predictionEngine;

    public void TrainModel(List<UserPreference> trainingData)
    {
        _mlContext = new MLContext();
        
        var dataView = _mlContext.Data.LoadFromEnumerable(trainingData);
        
        var pipeline = _mlContext.Transforms.Conversion.MapValueToKey("Label")
            .Append(_mlContext.Transforms.Concatenate("Features", 
                nameof(UserPreference.DepartureTimePreference),
                nameof(UserPreference.SeatPreference),
                nameof(UserPreference.PreferredClass)))
            .Append(_mlContext.Transforms.NormalizeMinMax("Features"))
            .Append(_mlContext.MulticlassClassification.Trainers.SdcaMaximumEntropy())
            .Append(_mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"));

        _model = pipeline.Fit(dataView);
        _predictionEngine = _mlContext.Model.CreatePredictionEngine<UserPreference, TravelRecommendation>(_model);
    }

    public TravelRecommendation Predict(UserPreference input)
    {
        return _predictionEngine.Predict(input);
    }
    
    public void SaveModel(string filePath)
    {
        _mlContext.Model.Save(_model, null, filePath);
    }
    
    public void LoadModel(string filePath)
    {
        _model = _mlContext.Model.Load(filePath, out _);
        _predictionEngine = _mlContext.Model.CreatePredictionEngine<UserPreference, TravelRecommendation>(_model);
    }
}

b. Local Database for Offline Operation

csharp
// DatabaseService.cs
public class DatabaseService
{
    private SQLiteAsyncConnection _database;

    public async Task InitializeAsync()
    {
        if (_database != null) return;
        
        _database = new SQLiteAsyncConnection(Path.Combine(FileSystem.AppDataDirectory, "bookingdata.db3"));
        await _database.CreateTableAsync<BookingHistory>();
        await _database.CreateTableAsync<UserPreference>();
    }

    public async Task SaveBookingHistory(BookingHistory history)
    {
        await InitializeAsync();
        await _database.InsertAsync(history);
    }

    public async Task<List<BookingHistory>> GetBookingHistory()
    {
        await InitializeAsync();
        return await _database.Table<BookingHistory>().ToListAsync();
    }

    public async Task UpdateUserPreferences(UserPreference preference)
    {
        await InitializeAsync();
        var existing = await _database.Table<UserPreference>().FirstOrDefaultAsync();
        if (existing != null)
        {
            preference.Id = existing.Id;
            await _database.UpdateAsync(preference);
        }
        else
        {
            await _database.InsertAsync(preference);
        }
    }
}

c. AI Agent with Online/Offline Capabilities

csharp
// BookingAgent.cs
public class BookingAgent
{
    private readonly DatabaseService _dbService;
    private readonly RecommendationModel _recommendationModel;
    private bool _isOnline;
    private readonly ILlamaApi _llamaApi; // For online LLM
    
    public BookingAgent(DatabaseService dbService, RecommendationModel recommendationModel)
    {
        _dbService = dbService;
        _recommendationModel = recommendationModel;
        
        // Initialize LLAMA (open-source model) for online use
        var @params = new ModelParams("llama-model.bin")
        {
            ContextSize = 2048,
            Seed = 0,
            GpuLayerCount = 5
        };
        _llamaApi = new LlamaApi(@params);
    }

    public async Task<BookingResponse> BookTicket(BookingRequest request)
    {
        try
        {
            // Check connectivity
            _isOnline = Connectivity.NetworkAccess == NetworkAccess.Internet;
            
            if (_isOnline)
            {
                // Try to use online AI for better results
                var prompt = $"Create a train booking for {request.Departure} to {request.Destination} " +
                             $"on {request.TravelDate:yyyy-MM-dd}. " +
                             $"User preferences: {request.Preferences}. " +
                             $"Return as JSON with fields: trains, times, prices, recommendations.";
                
                var response = await _llamaApi.GetResponseAsync(prompt);
                var onlineResult = JsonSerializer.Deserialize<BookingResponse>(response);
                
                // Save this interaction for offline learning
                await SaveInteraction(request, onlineResult);
                
                return onlineResult;
            }
            else
            {
                // Offline mode - use local ML model
                var history = await _dbService.GetBookingHistory();
                var recommendation = _recommendationModel.Predict(request.Preferences);
                
                return new BookingResponse
                {
                    Trains = history
                        .Where(h => h.Departure == request.Departure && h.Destination == request.Destination)
                        .OrderByDescending(h => h.Rating)
                        .Take(3)
                        .ToList(),
                    Recommendation = recommendation
                };
            }
        }
        catch (Exception ex)
        {
            // Fallback to basic offline logic
            return OfflineFallback(request);
        }
    }

    private async Task SaveInteraction(BookingRequest request, BookingResponse response)
    {
        // Save to local DB for future offline use
        var history = new BookingHistory
        {
            Departure = request.Departure,
            Destination = request.Destination,
            TravelDate = request.TravelDate,
            SelectedTrain = response.Trains.FirstOrDefault()?.TrainNumber,
            Rating = 0 // Will be updated when user provides feedback
        };
        
        await _dbService.SaveBookingHistory(history);
        
        // Update user preferences
        await _dbService.UpdateUserPreferences(request.Preferences);
        
        // Retrain model periodically
        if (DateTime.Now.Day % 7 == 0) // Retrain weekly
        {
            await RetrainModel();
        }
    }

    private async Task RetrainModel()
    {
        var history = await _dbService.GetBookingHistory();
        var trainingData = history.Select(h => new UserPreference
        {
            DepartureTimePreference = h.DepartureTime.Hours,
            SeatPreference = h.SeatType,
            PreferredClass = h.Class,
            Rating = h.Rating
        }).ToList();
        
        _recommendationModel.TrainModel(trainingData);
        
        // Save updated model
        var modelPath = Path.Combine(FileSystem.AppDataDirectory, "recommendationModel.zip");
        _recommendationModel.SaveModel(modelPath);
    }
}

4. MAUI UI Implementation

MainPage.xaml

xml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TrainBookingAgent.MainPage">
    
    <ScrollView>
        <VerticalStackLayout Spacing="20" Padding="30">
            
            <Label Text="Train Booking Agent" FontSize="24" HorizontalOptions="Center"/>
            
            <Frame BorderColor="LightGray" CornerRadius="10">
                <VerticalStackLayout Spacing="15">
                    <Entry x:Name="DepartureEntry" Placeholder="Departure City"/>
                    <Entry x:Name="DestinationEntry" Placeholder="Destination City"/>
                    <DatePicker x:Name="TravelDatePicker"/>
                    
                    <Picker x:Name="ClassPicker" Title="Preferred Class">
                        <Picker.Items>
                            <x:String>First Class</x:String>
                            <x:String>Second Class</x:String>
                            <x:String>Sleeper</x:String>
                        </Picker.Items>
                    </Picker>
                    
                    <Button x:Name="BookButton" Text="Find Trains" 
                            Clicked="OnBookClicked" BackgroundColor="#512BD4" TextColor="White"/>
                </VerticalStackLayout>
            </Frame>
            
            <ActivityIndicator x:Name="LoadingIndicator" IsVisible="False"/>
            
            <CollectionView x:Name="ResultsCollection" IsVisible="False">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <Frame BorderColor="LightGray" CornerRadius="10" Padding="15">
                            <VerticalStackLayout>
                                <Label Text="{Binding TrainNumber}" FontSize="18" FontAttributes="Bold"/>
                                <Label Text="{Binding DepartureTime, StringFormat='Departs: {0:hh:mm tt}'}"/>
                                <Label Text="{Binding ArrivalTime, StringFormat='Arrives: {0:hh:mm tt}'}"/>
                                <Label Text="{Binding Price, StringFormat='Price: {0:C}'}"/>
                                <Button Text="Book Now" BackgroundColor="#512BD4" TextColor="White"
                                        CommandParameter="{Binding .}"
                                        Clicked="OnBookTrainClicked"/>
                            </VerticalStackLayout>
                        </Frame>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
            
            <Label x:Name="OfflineLabel" Text="Offline Mode - Showing Local Recommendations" 
                   IsVisible="False" FontAttributes="Italic" TextColor="Gray"/>
            
        </VerticalStackLayout>
    </ScrollView>
    
</ContentPage>

MainPage.xaml.cs

csharp
public partial class MainPage : ContentPage
{
    private readonly BookingAgent _bookingAgent;
    
    public MainPage()
    {
        InitializeComponent();
        
        var dbService = new DatabaseService();
        var recommendationModel = new RecommendationModel();
        
        // Try to load pre-trained model
        var modelPath = Path.Combine(FileSystem.AppDataDirectory, "recommendationModel.zip");
        if (File.Exists(modelPath))
        {
            recommendationModel.LoadModel(modelPath);
        }
        
        _bookingAgent = new BookingAgent(dbService, recommendationModel);
    }
    
    private async void OnBookClicked(object sender, EventArgs e)
    {
        try
        {
            LoadingIndicator.IsVisible = true;
            BookButton.IsEnabled = false;
            
            var request = new BookingRequest
            {
                Departure = DepartureEntry.Text,
                Destination = DestinationEntry.Text,
                TravelDate = TravelDatePicker.Date,
                Preferences = new UserPreference
                {
                    PreferredClass = ClassPicker.SelectedItem?.ToString()
                }
            };
            
            var response = await _bookingAgent.BookTicket(request);
            
            ResultsCollection.ItemsSource = response.Trains;
            ResultsCollection.IsVisible = true;
            
            OfflineLabel.IsVisible = Connectivity.NetworkAccess != NetworkAccess.Internet;
            
            if (response.Recommendation != null)
            {
                await DisplayAlert("Recommendation", 
                    $"Based on your preferences, we recommend: {response.Recommendation.Suggestion}", "OK");
            }
        }
        catch (Exception ex)
        {
            await DisplayAlert("Error", ex.Message, "OK");
        }
        finally
        {
            LoadingIndicator.IsVisible = false;
            BookButton.IsEnabled = true;
        }
    }
    
    private async void OnBookTrainClicked(object sender, EventArgs e)
    {
        if (sender is Button button && button.CommandParameter is TrainOption selectedTrain)
        {
            await Navigation.PushAsync(new BookingConfirmationPage(selectedTrain));
        }
    }
}

5. Model Training and Continuous Learning

  1. Initial Training: Start with a basic dataset of common travel patterns

  2. Online Learning: When online, use LLM responses to enhance the local model

  3. User Feedback: Implement a rating system for bookings to improve recommendations

csharp
// Add to BookingAgent.cs
public async Task ProvideFeedback(int bookingId, int rating)
{
    var history = await _dbService.GetBookingHistory();
    var booking = history.FirstOrDefault(h => h.Id == bookingId);
    if (booking != null)
    {
        booking.Rating = rating;
        await _dbService.SaveBookingHistory(booking);
        
        // Retrain model with new feedback
        await RetrainModel();
    }
}

6. Deployment Considerations

  1. Model Size: Compress the ONNX model for mobile deployment

  2. Privacy: All user data stays on device unless explicit consent given

  3. Offline First: Design for offline operation with online enhancements

  4. LLM Setup: Bundle a small LLM (like TinyLlama) for basic online functionality

Advanced Features to Add

  1. Natural Language Processing: Allow voice input for booking requests

  2. Predictive Booking: Suggest bookings based on calendar/email scanning (with user permission)

  3. Price Alert System: Monitor prices and notify when they drop

  4. Travel Companion: Integrate with other travel services (hotels, attractions)

This implementation provides a complete offline-capable AI agent that learns from user interactions while being able to leverage online AI models when available. The machine learning component improves its recommendations over time based on user preferences and feedback.

No comments:

Post a Comment

Cricket Streaming MAUI App: Complete Solution

This document outlines a comprehensive solution for building a cricket streaming MAUI app with subscription plans, geo-restrictions, and pre...

Ads2