Quickstart: Create a knowledge base in QnA Maker using Node.js

This quickstart walks you through programmatically creating and publishing a sample QnA Maker knowledge base. QnA Maker automatically extracts questions and answers from semi-structured content, like FAQs, from data sources. The model for the knowledge base is defined in the JSON sent in the body of the API request.

This quickstart calls QnA Maker APIs:

Prerequisites

Note

The complete solution file(s) are available from the Azure-Samples/cognitive-services-qnamaker-nodejs GitHub repository.

Create a knowledge base Node.js file

Create a file named create-new-knowledge-base.js.

Add the required dependencies

At the top of create-new-knowledge-base.js, add the following lines to add necessary dependencies to the project:

'use strict';

let fs = require('fs');
let https = require('https');

Add the required constants

After the preceding required dependencies, add the required constants to access QnA Maker. Replace the value of the subscriptionKeyvariable with your own QnA Maker key.

// Components used to create HTTP request URIs for QnA Maker operations.
let host = 'westus.api.cognitive.microsoft.com';
let service = '/qnamaker/v4.0';
let method = '/knowledgebases/create';

// Build your path URL.
let path = service + method;

// Replace this with a valid subscription key.
let subscriptionKey = '<qna-maker-subscription-key>';

Add the KB model definition

After the constants, add the following KB model definition. The model is converting into a string after the definition.

// Dictionary that holds the knowledge base.
// The data source includes a QnA pair with metadata, the URL for the
// QnA Maker FAQ article, and the URL for the Azure Bot Service FAQ article.
let kb_model = {
    "name": "QnA Maker FAQ",
    "qnaList": [
        {
            "id": 0,
            "answer": "You can use our REST APIs to manage your Knowledge Base. See here for details: https://westus.dev.cognitive.microsoft.com/docs/services/58994a073d9e04097c7ba6fe/operations/58994a073d9e041ad42d9baa",
            "source": "Custom Editorial",
            "questions": [
                "How do I programmatically update my Knowledge Base?"
            ],
            "metadata": [
                {
                    "name": "category",
                    "value": "api"
                }
            ]
        }
    ],
    "urls": [
        "https://docs.microsoft.com/en-in/azure/cognitive-services/qnamaker/faqs",
        "https://docs.microsoft.com/en-us/bot-framework/resources-bot-framework-faq"
    ],
    "files": []
};


// Convert the JSON object to a string.
let content = JSON.stringify(kb_model);

Add supporting functions

Next, add the following supporting functions.

  1. Add the following function to print out JSON in a readable format:

    // Formats and indents JSON for display.
    let pretty_print = function (s) {
        return JSON.stringify(JSON.parse(s), null, 4);
    };
    
  2. Add the following functions to manage the HTTP response:

    // The 'callback' is called from the entire response.
    let response_handler = function (callback, response) {
        let body = '';
        response.on('data', function (d) {
            body += d;
        });
        response.on('end', function () {
            // Call the 'callback' with the status code, headers, and body of the response.
            callback({ status: response.statusCode, headers: response.headers, body: body });
        });
        response.on('error', function (e) {
            console.log('Error: ' + e.message);
        });
    };
    
    // Get an HTTP response handler that calls 'callback' from the entire response.
    let get_response_handler = function (callback) {
        // Return a function that takes an HTTP response and is closed over the specified callback.
        // This function signature is required by https.request, hence the need for the closure.
        return function (response) {
            response_handler(callback, response);
        };
    };
    

Add functions to create KB

Add the following functions to make an HTTP POST request to create the knowledge base. The Ocp-Apim-Subscription-Key is the QnA Maker service key, used for authentication.

// Call 'callback' when we have the entire response from the POST request.
let post = function (path, content, callback) {
    let request_params = {
        method: 'POST',
        hostname: host,
        path: path,
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': content.length,
            'Ocp-Apim-Subscription-Key': subscriptionKey
        }
    };

    // Pass the 'callback' function to the response handler.
    let req = https.request(request_params, get_response_handler(callback));
    req.write(content);
    req.end();
};

// Call 'callback' when we have the response from the /knowledgebases/create POST method.
let create_kb = function (path, req, callback) {
    console.log('Calling ' + host + path + '.');
    // Send the POST request.
    post(path, req, function (response) {
        // Extract the data we want from the POST response and pass it to the callback function.
        callback({ operation: response.headers.location, response: response.body });
    });
};

This API call returns a JSON response that includes the operation ID. Use the operation ID to determine if the KB is successfully created.

{
  "operationState": "NotStarted",
  "createdTimestamp": "2018-09-26T05:19:01Z",
  "lastActionTimestamp": "2018-09-26T05:19:01Z",
  "userId": "XXX9549466094e1cb4fd063b646e1ad6",
  "operationId": "8dfb6a82-ae58-4bcb-95b7-d1239ae25681"
}

Add functions to determine creation status

Add the following function to make an HTTP GET request to check the operation status. The Ocp-Apim-Subscription-Key is the QnA Maker service key, used for authentication.

let get = function (path, callback) {
    let request_params = {
        method: 'GET',
        hostname: host,
        path: path,
        headers: {
            'Ocp-Apim-Subscription-Key': subscriptionKey
        }
    };

    // Pass the callback function to the response handler.
    let req = https.request(request_params, get_response_handler(callback));
    req.end();
};

// Call 'callback' when we have the response from the GET request to check the status.
let check_status = function (path, callback) {
    console.log('Calling ' + host + path + '.');
    // Send the GET request.
    get(path, function (response) {
        // Extract the data we want from the GET response and pass it to the callback function.
        callback({ wait: response.headers['retry-after'], response: response.body });
    });
};

Repeat the call until success or failure:

{
  "operationState": "Succeeded",
  "createdTimestamp": "2018-09-26T05:22:53Z",
  "lastActionTimestamp": "2018-09-26T05:23:08Z",
  "resourceLocation": "/knowledgebases/XXX7892b-10cf-47e2-a3ae-e40683adb714",
  "userId": "XXX9549466094e1cb4fd063b646e1ad6",
  "operationId": "177e12ff-5d04-4b73-b594-8575f9787963"
}

Add create-kb function

The following function is the main function and creates the KB and repeats checks on the status. The create Operation ID is returned in the POST response header field Location, then used as part of the route in the GET request. Because the KB creation may take some time, you need to repeat calls to check the status until the status is either successful or fails.

create_kb(path, content, function (result) {
    // Formats and indents the JSON response from the /knowledgebases/create method for display.

    console.log(pretty_print(result.response));

    // Loop until the operation is complete.
    let loop = function () {

        // add operation ID to the path
        path = service + result.operation;

        // Check the status of the operation.
        check_status(path, function (status) {

            // Formats and indents the JSON for display.
            console.log(pretty_print(status.response));

            // Convert the status into an object and get the value of the 'operationState'.
            var state = (JSON.parse(status.response)).operationState;

            // If the operation isn't complete, wait and query again.
            if (state === 'Running' || state === 'NotStarted') {

                console.log('Waiting ' + status.wait + ' seconds...');
                setTimeout(loop, status.wait * 1000);
            }
        });
    };
    // Begin the loop.
    loop();
});

Run the program

Enter the following command at a command-line to run the program. It will send the request to the QnA Maker API to create the KB, then it will poll for the results every 30 seconds. Each response is printed to the console window.

node create-new-knowledge-base.js

Once your knowledge base is created, you can view it in your QnA Maker Portal, My knowledge bases page.

Clean up resources

When you are done with the quickstart, remove all the files created in this quickstart. Sign on to QnA Maker, and delete the KB.

Next steps