4/5/2017 Webmaster
Programmatically Updating A QnA Maker Service Using the QnA Maker API
** Note this is outdated – See new information here: https://docs.microsoft.com/en-us/azure/cognitive-services/QnAMaker/quickstarts/csharp
The QnA Maker website, allows you to quickly create a Chat Bot or Service based on your existing Frequently Asked Questions (and answers) known as a FAQ, and then query that database using natural language queries.
You can also programmatically update and train a QnA Maker service using the QnA Maker API.
We will start with the application created in the article: Using QnA Maker To Easily Create and Consume A Chat Bot.
In that article, we created a FAQ database.
We then have the ability to View/Edit and Deploy the FAQ database using the online web application at QnaMaker.ai.
This creates a QnA Maker Service.
Finally we created a ASP.NET Core web application to consume the database.
In this article (you can download the complete code on the downloads page of this site), we demonstrate how you can programmatically retrieve the FAQ database as a .csv file.
You can programmatically add to the FAQ database by entering a question and an answer and clicking the Add Entry button.
However, after doing so, while the item will show up when you log into the interface on the QnAMaker.ai website…
… it will not show up when you look at the export after clicking the Get FAQ button.
The reason is that you must first click the Train And Publish button to retrain the Model and Publish any changes to the FAQ database.
This is the equivalent to clicking the Save and retrain and Publish buttons in the web interface on the QnAMaker.ai website.
After clicking the Train And Publish button the additions show up in the FAQ database.
The same process is used to remove items from the FAQ database.
The exact question and answer to be removed are entered, and the Delete Entry button is clicked.
However, until you then click the Train And Publish button, the FAQ database will not be updated.
Programmatically Get The FAQ Database
To programmatically retrieve the FAQ database, we add a button to our View:
<input name="GetFAQ" type="submit" id="GetFAQ" value="Get FAQ" />
We then add code to the Controller to respond to the click, to call the GetFAQ method, and to return the resulting .csv file:
if (Request.Form["GetFAQ"].Count() > 0) { // The GetFAQ button was pressed // Call the QnA Service and get the FAQ var strFAQ = await GetFAQ(); // Return the contents as a .csv file return File( new System.Text.UTF8Encoding().GetBytes(strFAQ), "text/csv", "KnowledgeBase.csv"); }
This is the code for the GetFAQ method that retrieves the FAQ database:
private static async Task<string> GetFAQ() { string strFAQUrl = ""; string strLine; StringBuilder sb = new StringBuilder(); // Get the URL to the FAQ (in .tsv form) using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient()) { string RequestURI = String.Format("{0}{1}{2}{3}{4}", @"https://westus.api.cognitive.microsoft.com/", @"qnamaker/v2.0/", @"knowledgebases/", _KnowledgeBase, @"? "); client.DefaultRequestHeaders.Add( "Ocp-Apim-Subscription-Key", _OcpApimSubscriptionKey); System.Net.Http.HttpResponseMessage msg = await client.GetAsync(RequestURI); if (msg.IsSuccessStatusCode) { var JsonDataResponse = await msg.Content.ReadAsStringAsync(); strFAQUrl = JsonConvert.DeserializeObject<string>(JsonDataResponse); } } // Make a web call to get the contents of the // .tsv file that contains the database var req = WebRequest.Create(strFAQUrl); var r = await req.GetResponseAsync().ConfigureAwait(false); // Read the response using (var responseReader = new StreamReader(r.GetResponseStream())) { // Read through each line of the response while ((strLine = responseReader.ReadLine()) != null) { // Write the contents to the StringBuilder object string[] strCurrentLine = strLine.Split('\t'); sb.Append((String.Format("{0},{1},{2}\n", CleanContent(strCurrentLine[0]), CleanContent(strCurrentLine[1]), CleanContent(strCurrentLine[2]) ))); } } // Return the contents of the StringBuilder object return sb.ToString(); }
Note that this method does two things.
First it makes a web call to retrieve the web URL that indicates where the FAQ database is, then it makes another web call to actually get the database as a .tsv (tab delimited) file.
Finally it converts the tab delimited file into the comma delimited (.csv) format.
Programmatically Add and Delete FAQ Items
In the View we add Add Entry and Delete Entry buttons:
<input name="AddEntry" type="submit" id="AddEntry" value="Add Entry" /> <input name="DeleteEntry" type="submit" id="DeleteEntry" value="Delete Entry" />
The following code, in the Controller, determines what Mode (Add or Delete) and calls the UpdateQueryQnABot method:
else if (Request.Form["AddEntry"].Count() > 0) { // The AddEntry button was pressed if ((NewQuestion != null) && (NewAnswer != null)) { // Call the QnA KnowledgeBase Service objQnAResult.Message = await UpdateQueryQnABot(NewQuestion, NewAnswer, Mode.Add); } } else if (Request.Form["DeleteEntry"].Count() > 0) { // The DeleteEntry button was pressed if ((NewQuestion != null) && (NewAnswer != null)) { // Call the QnA KnowledgeBase Service objQnAResult.Message = await UpdateQueryQnABot(NewQuestion, NewAnswer, Mode.Delete); } }
The UpdateQueryQnABot method is as follows:
private static async Task<string> UpdateQueryQnABot( string newQuestion, string newAnswer, Mode paramMode) { string strResponse = ""; // Create the QnAKnowledgeBase that contains the new entry QnAKnowledgeBase objQnAKnowledgeBase = new QnAKnowledgeBase(); QnaPair objQnaPair = new QnaPair(); objQnaPair.question = newQuestion; objQnaPair.answer = newAnswer; if (paramMode == Mode.Add) { Add objAdd = new Add(); objAdd.qnaPairs = new List<QnaPair>(); objAdd.urls = new List<string>(); objAdd.qnaPairs.Add(objQnaPair); objQnAKnowledgeBase.add = objAdd; } if (paramMode == Mode.Delete) { Delete objDelete = new Delete(); objDelete.qnaPairs = new List<QnaPair>(); objDelete.urls = new List<string>(); objDelete.qnaPairs.Add(objQnaPair); objQnAKnowledgeBase.delete = objDelete; } using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient()) { string RequestURI = String.Format("{0}{1}{2}{3}? ", @"https://westus.api.cognitive.microsoft.com/", @"qnamaker/v2.0/", @"knowledgebases/", _KnowledgeBase); using (HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("PATCH"), RequestURI)) { request.Content = new StringContent( JsonConvert.SerializeObject(objQnAKnowledgeBase), System.Text.Encoding.UTF8, "application/json"); request.Content.Headers.Add( "Ocp-Apim-Subscription-Key", _OcpApimSubscriptionKey); HttpResponseMessage response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { strResponse = $"Operation {paramMode} completed."; } else { string responseContent = await response.Content.ReadAsStringAsync(); strResponse = responseContent; } } } return strResponse; }
Train and Publish
The Train and Publish button calls the following method:
private static async Task<string> TrainAndPublish() { string strResponse = ""; using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient()) { string RequestURI = String.Format("{0}{1}{2}{3}", @"https://westus.api.cognitive.microsoft.com/", @"qnamaker/v2.0/", @"knowledgebases/", _KnowledgeBase); var httpContent = new StringContent("", Encoding.UTF8, "application/json"); httpContent.Headers.Add( "Ocp-Apim-Subscription-Key", _OcpApimSubscriptionKey); System.Net.Http.HttpResponseMessage response = await client.PutAsync(RequestURI, httpContent); if (response.IsSuccessStatusCode) { strResponse = $"Operation Train and Publish completed."; } else { string responseContent = await response.Content.ReadAsStringAsync(); strResponse = responseContent; } } return strResponse; }
Ai Help Website Links
Using QnA Maker To Easily Create and Consume A Chat Bot
Links
Create your first QnA bot using botframework’s QnA Maker
QnA Maker Dialog for Bot Framework – Gary Pretty
Adding rich attachments to your QnAMaker bot responses – Gary Pretty
Download
You can download the code from the Download page