This article will describe how to integrate the Microsoft Bot Framework with a weather API that enables the development of smart, responsive bots that can deliver real-time weather updates. This guide will take you through the process of setting up your bot using conversational language understanding (CLU), integrating it with a weather API, and deploying it effectively.
Prerequisites
Before we begin, ensure you have the following:
Azure Subscription: An active Azure subscription is necessary to create and manage resources. https://azure.microsoft.com/en-us/free/ai-services/
Visual Studio: Download and install Visual Studio, preferably with .NET Core CLI. https://visualstudio.microsoft.com/vs/community/
Bot Framework SDK: Ensure you have the Bot Framework SDK installed. https://github.com/microsoft/botframework-sdk
BotFramework-Emulator: a desktop application that allows bot developers to test and debug bots built using the Bot Framework SDK. You can use the Bot Framework Emulator to test bots locally on your machine or connect to bots running remotely through a tunnel.
Weather API Key: If you don’t have an API key yet, head over to Visual Crossing Weather Data Services https://www.visualcrossing.com/weather/weather-data-services to sign up for your free account – you will be able to make up to 1000 requests for free every day.
What is conversational language understanding?
Conversational language understanding is one of the custom features offered by Azure AI-Language. It is a cloud-based API service that applies machine-learning intelligence to enable you to build a natural language understanding component to be used in an end-to-end conversational application.
What is the weather API
The Visual Crossing Weather API provides developers with weather data for any programming language or script. The Weather API provides instant and scalable access to historical weather reports and forecast data in an easy-to-use JSON or text format. See https://www.visualcrossing.com/weather-api for more information and additional documentation, sample code, and use cases.
Get start
Step 1: Setting Up the Bot Framework
- Create a New Bot Project:
- Open Visual Studio and create a new project.
- Select “Core Bot (Bot Framework v4 – .NET Core 3.1” as Framework template.
- Select “Core Bot (Bot Framework v4 – .NET Core 3.1” project.
- Install Necessary Packages:
- Install the Bot Framework SDK and other necessary packages using NuGet Package Manager. There are some packages:
- Microsoft.Bot.Builder.Dialogs
- Microsoft.Bot.Builder.AI.Integration.AspNet.Core
- Azure.AI.Language.Conversations
- Microsoft.Recognizers.Text.DataTypes.TimexExpression
- Microsoft.AspNetCore.Mvc.NewtonsoftJson
- Configure the Bot:
- In the appsettings.json file, add your bot’s configuration settings.
{ "MicrosoftAppType": "", "MicrosoftAppId": "", "MicrosoftAppPassword": "", "MicrosoftAppTenantId": "", "CluProjectName": "YOUR_CLU_PROJECT_NAME", "CluDeploymentName": "YOUR_CLU_DEPLOYMENT_NAME ", "CluAPIKey": "YOUR_CLU_API_KEY", "CluAPIHostName": "YOUR_CLU_HOST_NAME", }
Step 2: Setting Up Conversational Language Understanding (CLU)
- Create a Language resource:
- Navigate to Azure Language Studio and create a new Language resource.
- Import a project in conversational language understanding
- Create a JSON file that defines intents, entities, and utterances.
- Sign into the Language Studio and import the JSON file. This will automatically import the CLU project with all the intents, entities, and utterances.
- Training job, provide the model name.
- Deploy the model once training is complete.
Step 3: Integrating the Weather API
- Add Weather API Integration:
- In your bot’s main dialog (e.g., WeatherDialog.cs), create a method to call the weather API.
- Integrate CLU
- Modify the JSON file that defines intents, entities, and utterances of the weather.
- Implement the Weather API Call:
- Use RestService library to send a request to the weather API and parse the response.
- Handle Weather Response:
- Create a modal class WeatherData.cs to parse the resulting API into the object.
Step 4: Testing and Deployment
- Test Locally:
- Run your bot locally using Visual Studio.
- Use the Bot Framework Emulator to interact with your bot and test weather-related queries.
- Finally, enjoy and chat with your bot.
- Deploy to Azure:
- Publish your bot to Azure.
- Set up the necessary configurations in the Azure portal, including the Web App Bot and App Service
- Monitor and Scale:
- Use Azure Monitoring tools to keep track of your bot’s performance.
- Scale your resources based on usage patterns to ensure optimal performance.
Detailed Example: Building the FightBooking Bot integrates with the weather API.
It follows the tutorial: Integrate conversational language understanding with Bot Framework (https://learn.microsoft.com/en-us/azure/ai-services/language-service/conversational-language-understanding/tutorials/bot-framework). The project name is FlightBook. It has already designed the intent BookFlight, but the intent GetWeather hasn’t been implemented yet in this sample. So, we can continue to improve it by linking it with the weather API.
switch (cluResult.TopIntent().intent) { case FlightBooking.Intent.BookFlight: // Initialize BookingDetails with any entities we may have found in the response. var bookingDetails = new BookingDetails() { Destination = cluResult.Entities.toCity, Origin = cluResult.Entities.fromCity, TravelDate = cluResult.Entities.flightDate, }; // Run the BookingDialog giving it whatever details we have from the CLU call, it will fill out the remainder. return await stepContext.BeginDialogAsync(nameof(BookingDialog), bookingDetails, cancellationToken); case FlightBooking.Intent.GetWeather: // We haven't implemented the GetWeatherDialog so we just display a TODO message. var getWeatherMessageText = "TODO: get weather flow here"; var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput); await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken); break; default: // Catch all for unhandled intents var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {cluResult.TopIntent().intent})"; var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput); await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken); break; }
Creating the Bot Project
- Create the Bot:
- Use Visual Studio.
- Download the CoreBotWithCLU sample from GitHub to use as a base.
https://github.com/Azure-Samples/cognitive-service-language-samples/tree/main/CoreBotWithCLU
- Configure App Settings:
- In the appsettings.json file, add your Azure CLU and weather API details.
{ "MicrosoftAppType": "", "MicrosoftAppId": "", "MicrosoftAppPassword": "", "MicrosoftAppTenantId": "", "CluProjectName": "YOUR_CLU_PROJECT_NAME", "CluDeploymentName": "YOUR_CLU_DEPLOYMENT_NAME ", "CluAPIKey": "YOUR_CLU_API_KEY", "CluAPIHostName": "YOUR_CLU_HOST_NAME", "WeatherAPIKey": "YOUR_WEATHER_API_KEY", }
- Define the Bot Logic:
- Open the project CoreBotWithCLU.
- Define the class such as WeatherDialog.cs which is inherited from class CancelAndHelpDialog.
- The dialog will prompt for the location and datetime from the user request before calling the weather API.
public class WeatherDialog : CancelAndHelpDialog { public WeatherDialog(IConfiguration configuration) : base(nameof(WeatherDialog)) { var waterfallSteps = new WaterfallStep[] { PromptForLocationStepAsync, PromptDateTimeStepAsync, FetchWeatherStepAsync }; AddDialog(new TextPrompt(nameof(TextPrompt))); AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps)); InitialDialogId = nameof(WaterfallDialog); } }
private async Task<DialogTurnResult> PromptForLocationStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var bookingDetails = (BookingDetails)stepContext.Options; if (bookingDetails.Destination == null) { var promptMessage = MessageFactory.Text(DestinationStepMsgText, DestinationStepMsgText, InputHints.ExpectingInput); return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken); } return await stepContext.NextAsync(bookingDetails.Destination, cancellationToken); }
private async Task<DialogTurnResult> FetchWeatherStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { if (!string.IsNullOrEmpty((string)stepContext.Result)) { var bookingDetails = (BookingDetails)stepContext.Options; var dateTime = stepContext.Result; var weather = GetWeather(bookingDetails.Destination, dateTime.ToString()); await stepContext.Context.SendActivityAsync(MessageFactory.Text($"The weather in {bookingDetails.Destination} is: {weather}"), cancellationToken); } return await stepContext.EndDialogAsync(null, cancellationToken); }
Integrate CLU
Modify the FlightBooking.json project with weather intents, entities, and utterances. Then import this file into Language Studio. The example content:
{ "api-version": "2022-03-01-preview", "stringIndexType": "Utf16CodeUnit", "metadata": { "projectKind": "conversation", "settings": { "confidenceThreshold": 0 }, "projectName": "FlightBooking", "multilingual": false, "description": "CLU Model for CoreBot", "language": "en-us" }, "assets": { "intents": [ { "category": "Cancel" }, { "category": "GetWeather" }, { "category": "None" } ], "entities": [ { "category": "toCity", "compositionSetting": "returnUnion" }, { "category": "weatherDate", "compositionSetting": "returnUnion", "prebuilts": [ { "category": "DateTime" } ] } ], "utterances": [ { "text": "what's the forecast for this friday?", "intent": "GetWeather", "entities": [] }, { "text": "what's the weather like for tomorrow", "intent": "GetWeather", "entities": [] }, { "text": "what's the weather like in new york", "intent": "GetWeather", "entities": [ { "category": "toCity", "offset": 27, "length": 8 } ] }, { "text": "what's the weather like?", "intent": "GetWeather", "entities": [] }, { "text": "winter is coming", "intent": "None", "entities": [] } ] } }
- Implement Weather API Call
public const string UrlTimeLine = https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline"; public static string WeatherApiKey = ""; private string GetWeather(string location, string dateTime) { if (string.IsNullOrEmpty(location) || string.IsNullOrEmpty(dateTime)) { return "Sorry, I didn't get that. Please try asking in a different way"; } var restService = new RestService(); var url = $"{UrlTimeLine}/{location}/{dateTime:yyyy-MM-ddTHH:mm:ss}?key={WeatherApiKey}"; var results = restService.GetWeatherData(url); if (results != null) { var description = results.Days[0].Description; var temperature = results.Days[0].Temp; return $"{description}. The temperature of {temperature}°F"; } return "Sorry, I didn't get that. Please try asking in a different way"; } }
- Create a class WeatherData.cs to parse the result into model:
using System.Collections.Generic; using Newtonsoft.Json; namespace WeatherBotCLU.Model { public class WeatherData { [JsonProperty("address")] public string Address { get; set; } [JsonProperty("description")] public string Description { get; set; } [JsonProperty("days")] public List<Days> Days { get; set; } public string Period { get; set; } public string BingMapApiKey { get; set; } public double Longitude { get; set; } public double Latitude { get; set; } } public class Days { [JsonProperty("datetime")] public string DateTime { get; set; } [JsonProperty("windgust")] public string Windgust { get; set; } [JsonProperty("windspeed")] public string WindSpeed { get; set; } [JsonProperty("winddir")] public string WindDir { get; set; } [JsonProperty("pr")] public string Pr { get; set; } [JsonProperty("tempmin")] public string TempMin { get; set; } [JsonProperty("temp")] public string Temp { get; set; } [JsonProperty("tempmax")] public string TempMax { get; set; } [JsonProperty("feelslike")] public string FeelsLike { get; set; } [JsonProperty("feelslikemin")] public string FeelsLikeMin { get; set; } [JsonProperty("feelslikemax")] public string FeelsLikeMax { get; set; } [JsonProperty("description")] public string Description { get; set; } [JsonProperty("pressure")] public string Pressure { get; set; } [JsonProperty("humidity")] public string Humidity { get; set; } [JsonProperty("dew")] public string Dew { get; set; } [JsonProperty("precip")] public string PreCip { get; set; } [JsonProperty("precipprob")] public string PreCipProb { get; set; } [JsonProperty("precipcover")] public string PreCipCover { get; set; } [JsonProperty("preciptype")] public object PreCipType { get; set; } [JsonProperty("snow")] public string Snow { get; set; } [JsonProperty("snowdepth")] public string SnowDepth { get; set; } [JsonProperty("hours")] public List<Hours> Hours { get; set; } } public class Hours { [JsonProperty("datetime")] public string DateTime { get; set; } [JsonProperty("windgust")] public string Windgust { get; set; } [JsonProperty("windspeed")] public string WindSpeed { get; set; } [JsonProperty("winddir")] public string WindDir { get; set; } [JsonProperty("temp")] public string Temp { get; set; } [JsonProperty("feelslike")] public string FeelsLike { get; set; } [JsonProperty("description")] public string Description { get; set; } [JsonProperty("pressure")] public string Pressure { get; set; } [JsonProperty("humidity")] public string Humidity { get; set; } [JsonProperty("visibility")] public string Visibility { get; set; } [JsonProperty("dew")] public string Dew { get; set; } [JsonProperty("precip")] public string PreCip { get; set; } [JsonProperty("precipprob")] public string PreCipProb { get; set; } [JsonProperty("preciptype")] public object PreCipType { get; set; } [JsonProperty("snow")] public string Snow { get; set; } [JsonProperty("snowdepth")] public string SnowDepth { get; set; } [JsonProperty("cloudcover")] public string CloudCover { get; set; } [JsonProperty("solarradiation")] public string SolarRadiation { get; set; } [JsonProperty("solarenergy")] public string SolarEnergy { get; set; } [JsonProperty("uvindex")] public string UvIndex { get; set; } [JsonProperty("severerisk")] public string SevereRisk { get; set; } [JsonProperty("conditions")] public string Conditions { get; set; } [JsonProperty("icon")] public string Icon { get; set; } [JsonProperty("stations")] public object Stations { get; set; } [JsonProperty("source")] public string Source { get; set; } [JsonProperty("name")] public string Name { get; set; } } }
- Handle the weather Intent
I reused the BookingDetail class with 2 properties: Destination and TravelDate.
case FlightBooking.Intent.GetWeather: var bookingDetails = new BookingDetails() { Destination = cluResult.Entities.GetToCity(), TravelDate = cluResult.Entities.GetWeatherDate(), }; return await stepContext.BeginDialogAsync(nameof(WeatherDialog), bookingDetails, cancellationToken);
- Return the weather information for the user. For example:
The weather in London is: Partly cloudy throughout the day with afternoon rain… The temperature of 58.9°F
Testing the Bot
- Run Locally:
- Press F5 in Visual Studio to run the bot locally.
- Use the Bot Framework Emulator (v4) to interact with your bot.
- Test Weather Queries:
- Ask the bot questions like “What’s the weather today?” to see if it responds with accurate weather information.
Deploying to Azure
- Publish the Bot:
- Right-click the project in Visual Studio and select “Publish”.
- Choose “Azure” and follow the prompts to publish your bot.
- Configure in Azure:
- Navigate to the Azure portal.
- Set up the Web App Bot and configure the necessary settings, including the bot’s endpoint.
- Monitor Performance:
- Use Azure’s monitoring tools to keep track of your bot’s performance and user interactions.
What’s next:
The bot currently retrieves weather information based only on the provided location and date. To enhance the bot’s capabilities, you could train it to understand more complex queries. For instance, you could add features to forecast weather for the next three days, retrieve historical weather data for a specific location, and offer user-specific advice based on weather conditions.
- Improved Features for the Weather Bot
1. Forecasting Weather for Multiple Days:
Allow users to ask for weather forecasts for the upcoming days.
Example query: “What’s the weather forecast for the next three days in New York?”
2. Retrieving Weather History:
Enable the bot to provide historical weather information.
Example query: “What was the weather like in London last week?”
3. Providing Weather-Based Advice:
Offer suggestions based on current or forecasted weather.
Example query: “Should I bring an umbrella to San Francisco tomorrow?”
Conclusion
By following the steps outlined in this guide, you can create a demonstrating bot capable of understanding user queries and providing accurate weather updates. This integration not only enhances the functionality of your bot but also provides a valuable service to users, demonstrating the power and flexibility of combining Azure services with external APIs.