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
.NET MAUI Frontend: Cross-platform mobile application
Local AI/ML Engine: ONNX runtime with pre-trained models
Offline Data Storage: SQLite database for user preferences and historical data
Online Fallback: Integration with open-source LLMs (like Llama.cpp)
Implementation Steps
1. Set up the .NET MAUI Project
dotnet new maui -n TrainBookingAgent cd TrainBookingAgent
2. Add Required NuGet Packages
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:
// 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
// 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
// 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
<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
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
Initial Training: Start with a basic dataset of common travel patterns
Online Learning: When online, use LLM responses to enhance the local model
User Feedback: Implement a rating system for bookings to improve recommendations
// 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
Model Size: Compress the ONNX model for mobile deployment
Privacy: All user data stays on device unless explicit consent given
Offline First: Design for offline operation with online enhancements
LLM Setup: Bundle a small LLM (like TinyLlama) for basic online functionality
Advanced Features to Add
Natural Language Processing: Allow voice input for booking requests
Predictive Booking: Suggest bookings based on calendar/email scanning (with user permission)
Price Alert System: Monitor prices and notify when they drop
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