Quickstart: Create a knowledge base in QnA Maker using Java

This quickstart walks you through programmatically creating 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.

Note

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

Create a knowledge base file

Create a file named CreateKB.java

Add the required dependencies

At the top of CreateKB.java, add the following lines to add necessary dependencies to the project:

import java.io.*;
import java.lang.reflect.Type;
import java.net.*;
import java.util.*;
import javax.net.ssl.HttpsURLConnection;

Add the required constants

After the preceding required dependencies, add the required constants to the CreateKB class to access QnA Maker. Replace the value of the subscriptionKeyvariable with your own QnA Maker key. You do not need to add the final curly bracket to end the class; it is in the final code snippet at the end of this quickstart.

public class CreateKB {

    // Replace this with a valid subscription key.
    static String subscriptionKey = "<your-qna-maker-subscription-key>";

    // Components used to create HTTP request URIs for QnA Maker operations.
    static String host = "https://westus.api.cognitive.microsoft.com";
    static String service = "/qnamaker/v4.0";
    static String method = "/knowledgebases/create";

Add the KB model definition classes

After the constants, add the following classes and functions inside the CreateKB class to serialize the model definition object into JSON.

public static class KB {
    String name;
    Question[] qnaList;
    String[] urls;
    File[] files;
}

public static class Question {
    Integer id;
    String answer;
    String source;
    String[] questions;
    Metadata[] metadata;
}

public static class Metadata {
    String name;
    String value;
}

public static class File {
    String fileName;
    String fileUri;
}

public static KB GetKB () {
    KB kb = new KB ();
    kb.name = "Example Knowledge Base";

    Question q = new Question();
    q.id = 0;
    q.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";
    q.source = "Custom Editorial";
    q.questions = new String[]{"How do I programmatically update my Knowledge Base?"};

    Metadata md = new Metadata();
    md.name = "category";
    md.value = "api";
    q.metadata = new Metadata[]{md};

    kb.qnaList = new Question[]{q};
    kb.urls = new String[]{"https://docs.microsoft.com/en-in/azure/cognitive-services/qnamaker/faqs",     "https://docs.microsoft.com/en-us/bot-framework/resources-bot-framework-faq"};

    return kb;
}

Add supporting functions

Next, add the following supporting functions inside the CreateKB class.

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

    public static String PrettyPrint (String json_text) {
        JsonParser parser = new JsonParser();
        JsonElement json = parser.parse(json_text);
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        return gson.toJson(json);
    }
    
  2. Add the following class to manage the HTTP response:

    public static class Response {
        Map<String, List<String>> Headers;
        String Response;
    
        public Response(Map<String, List<String>> headers, String response) {
            this.Headers = headers;
            this.Response = response;
        }
    }
    
  3. Add the following method to make a POST request to the QnA Maker APIs. The Ocp-Apim-Subscription-Key is the QnA Maker service key, used for authentication.

    public static Response Post (URL url, String content) throws Exception{
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setRequestProperty("Content-Length", content.length() + "");
        connection.setRequestProperty("Ocp-Apim-Subscription-Key", subscriptionKey);
        connection.setDoOutput(true);
    
        DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
        byte[] encoded_content = content.getBytes("UTF-8");
        wr.write(encoded_content, 0, encoded_content.length);
        wr.flush();
        wr.close();
    
        StringBuilder response = new StringBuilder ();
        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
        String line;
        while ((line = in.readLine()) != null) {
            response.append(line);
        }
        in.close();
        return new Response (connection.getHeaderFields(), response.toString());
    }
    
  4. Add the following method to make a GET request to the QnA Maker APIs.

    public static Response Get (URL url) throws Exception{
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Ocp-Apim-Subscription-Key", subscriptionKey);
            connection.setDoOutput(true);
        StringBuilder response = new StringBuilder ();
        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
    
        String line;
        while ((line = in.readLine()) != null) {
            response.append(line);
        }
        in.close();
        return new Response (connection.getHeaderFields(), response.toString());
    }
    

Add a method to create the KB

Add the following method to create the KB by calling into the Post method.

public static Response CreateKB (KB kb) throws Exception {
    URL url = new URL (host + service + method);
    System.out.println ("Calling " + url.toString() + ".");
    String content = new Gson().toJson(kb);
    return Post(url, content);
}

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 a method to get status

Add the following method to check the creation status.

public static Response GetStatus (String operation) throws Exception {
    URL url = new URL (host + service + operation);
    System.out.println ("Calling " + url.toString() + ".");
    return Get(url);
}

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 a main method

The main method creates the KB, then polls for the status. The operation ID is returned in the POST response header field Location, then used as part of the route in the GET request. The while loop retries the status if it is not completed.

    public static void main(String[] args) {
        try {
            // Send the request to create the knowledge base.
            Response response = CreateKB (GetKB ());

            // Get operation ID
            String operation = response.Headers.get("Location").get(0);

            System.out.println (PrettyPrint (response.Response));

            // Loop until the request is completed.
            Boolean done = false;
            while (!done) {
                // Check on the status of the request.
                response = GetStatus (operation);
                System.out.println (PrettyPrint (response.Response));
                
                Type type = new TypeToken<Map<String, String>>(){}.getType();

                Map<String, String> fields = new Gson().fromJson(response.Response, type);

                String state = fields.get ("operationState");

                // If the request is still running, the server tells us how
                // long to wait before checking the status again.
                if (state.equals("Running") || state.equals("NotStarted")) {
                    String wait = response.Headers.get ("Retry-After").get(0);
                    System.out.println ("Waiting " + wait + " seconds...");
                    Thread.sleep (Integer.parseInt(wait) * 1000);
                }
                else {
                    done = true;
                }
            }
        } catch (Exception e) {
            System.out.println (e);
        }
    }

}

Compile and run the program

  1. Make sure the gson library is in the ./libs directory. At the command-line, compile the file CreateKB.java:

    javac -cp ".;libs/*" CreateKB.java
    
  2. 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.

    java -cp ",;libs/*" CreateKB
    

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