Even with zero knowledge, we will bring you to the point where you can create Slack Bot and Line Bot using Watson.
[Updated on June 6, 2019] This post was written during the Watson Conversation era, the predecessor of Watson Assistant, and although screen captures are still old, Watson Assitant's basic ideas and operations Does not change, so please read it as the latest environment. </ font>
■ I would like to touch on the following.
(1) Introduction to thinking, creating an account ② Design method of dialogue flow using "Hotel reservation" as an example ③ Utilization of context, practical usage of various functions not mentioned in Tutorial ** ④ How to link with Java logic ** ← This article ⑤ Create a chatbot with Watson + Java + Slack
This time, in Part 4, I will explain ** ④ How to link with Java logic **.
Click here for the last time → ③ Utilization of context, practical usage of various functions not mentioned in Tutorial.
This time, we will link Java logic and Watson Conversation.
First, create a simple workspace to check the operation of the program.
You can download the ** workspace file (JSON) ** for operation check from the following. https://riversun.github.io/wcs/org.riversun.WcsContextTestJa.zip
If you import this, you will have a workspace with only two nodes as shown below.
{
"context": {
"myParam01": "banana",
"myParam02": "apple",
"myParam03": 7777,
"myParam04": true,
"myParam05": {
"subParam01": "orange",
"subParam02": "lemon"
}
},
...
}
The response of this node is
** You said " Input.text?>". The value of the context variable myRemoteParam is " Context ['myRemoteParam']?>". ** **
Returns the user's input text and the Context variable ** myRemoteParam **.
In order to execute the Watson Conversation dialogue flow from a Java program, you need the workspace's ** WorkspaceId **, ** Username **, and ** Password **.
To see these, click the ** deploy icon ** on the left side of the dialog editor as shown below to open the ** deploy ** pane and select the ** Credentials ** tab. Make a note of ** WorkspaceId **, ** Username **, and ** Password **, respectively. You will use these later in your Java program.
The sample code introduced below can be obtained from the following public repositories. https://github.com/riversun/watson-conversation-java-examples-ja
Below is the code that sends a text message to Watson. Also, set the Context variable from Java to Watson Conversation.
WcsExample01.java
package org.example.wcs;
import org.riversun.wcs.WcsClient;
import com.ibm.watson.developer_cloud.conversation.v1.model.MessageResponse;
public class WcsExample01 {
private static final String WCS_USERNAME = "EDIT_ME_USERNAME_HERE";
private static final String WCS_PASSWORD = "EDIT_ME_PASSWORD_HERE";
private static final String WCS_WORKSPACE_ID = "EDIT_ME_WORKSPACE_ID_HERE";
public static void main(String[] args)
{
String wcsClientId = "dummy_user_id";
WcsClient watson = new WcsClient(WCS_USERNAME, WCS_PASSWORD, WCS_WORKSPACE_ID);
MessageResponse wcsWelcomeRes = watson.startConversation(wcsClientId);
System.out.println("FROM WATSON:" + wcsWelcomeRes.getTextConcatenated(""));
final String ctxKey = "myRemoteParam";
final String ctxValue = "I need you!";
watson.put(wcsClientId, ctxKey, ctxValue);
final String myMessage01 = "Hi! Watson";
MessageResponse wcsRes01 = watson.sendMessage(wcsClientId, myMessage01);
System.out.println("FROM WATSON:" + wcsRes01.getTextConcatenated(""));
}
}
private static final String WCS_USERNAME = "EDIT_ME_USERNAME_HERE";//username
private static final String WCS_PASSWORD = "EDIT_ME_PASSWORD_HERE";//password
private static final String WCS_WORKSPACE_ID = "EDIT_ME_WORKSPACE_ID_HERE";//workspaceId
First, set the credentials to access Watson. Edit the following part of the code and replace it with the ** workspaceId, username, password ** you saw earlier in the ** deploy ** pane.
String wcsClientId = "dummy_user_id";
When working with Watson Conversation using the helper library Give a unique user ID to identify the user (here, the variable name is wcsClientId. Note that it is completely different from the workspace username).
User ID is not a concept unique to Watson Conversation, but when multiple users interact with the bot at the same time with a chatbot as shown below, the interaction state between the user and the chatbot is maintained for each user (as a matter of course). Must be.
Therefore, as in this sample, even one user can access Watson by assigning a user ID for convenience. (By the way, in Watson's internal processing, an ID called conversationId is assigned for each conversation session rather than for each user.)
WcsClient watson = new WcsClient(username,password,workspaceId);
New the WcsClient class. This class communicates with watson conversation
MessageResponse wcsWelcomeRes = watson.startConversation(wcsClientId);
System.out.println("FROM WATSON:" + wcsWelcomeRes.getTextConcatenated(""));
The first call uses the ** # startConversation ** method. I think that the node of Watson Conversation is often a ** welcome node **, but ** #startConversation ** executes this ** welcome node **. Receive the response from Watson as ** MessageResponse **.
** # getTextConcatenated ** is for getting the response from Watson as a String.
(Since it may go through multiple nodes before returning the response, the actual response from Watson is JSON String array type (= List
final String ctxKey = "myRemoteParam";
final String ctxValue = "I need you!";
watson.put(wcsClientId, ctxKey, ctxValue);
Using ** watson.put (user ID, context variable name, context variable value) **, the ** Context variable ** of Watson Conversation is set from the Java logic side.
Here, I set the value "** I need you! **" to the Context variable named ** myRemoteParam **.
At this stage, the Context is held only in the Java logic, and it will be reflected on the Watson side the next time Watson is accessed from the Java logic.
final String myMessage01 = "Hi! Watson";
MessageResponse wcsRes01 = watson.sendMessage(wcsClientId, myMessage01);
System.out.println("FROM WATSON:" + wcsRes01.getTextConcatenated(""));
Send a text message to Watson with ** #sendMessage (user ID, message) **. At this timing, the Context variable set earlier is reflected on the Watson side.
Here, the message "** Hi! Watson **" is sent from Java logic.
By the way, the response of the ** Show_Context_node ** node on the Watson Conversation side is You said "**" Input.text?> ". The value of the context variable myRemoteParam is " Context ['myRemoteParam']?>". ** ”
Therefore, the response from Watson received on the Java logic side is as follows.
response
「Hi!You said "Watson". The value of the context variable myRemoteParam is "I need you!"is.
final String myMessage02 = "Hello! Watson";
String wcsResText = watson.sendMessageForText(wcsClientId, myMessage02);
System.out.println("FROM WATSON:" + wcsResText);
** # sendMessageForText ** sends a text message to Watson like ** # sendMessage **, but since it also receives the response from Watson as text, this method is the simplest for sending and receiving text.
Also, if you use ** # sendMessageForText **, you can omit ** # startConversation ** (that is, execute *** startConversation ** in the first ** # sendMessageForText **). ), If you want to create a chatbot that just sends and receives text, the ** # sendMessageForText ** method is all you need.
When you run WcsExample01.java, it will be output to the console as below
Execution result
FROM WATSON:Hello, I'm Watson
FROM WATSON:「Hi!You said "Watson". The value of the context variable myRemoteParam is "I need you!"is.
FROM WATSON:「Hello!You said "Watson". The value of the context variable myRemoteParam is "I need you!"is.
To execute the above code, set the dependent libraries as follows
maven
<dependency>
<groupId>org.riversun</groupId>
<artifactId>wcs</artifactId>
<version>1.0.2</version>
</dependency>
gradle
compile 'org.riversun:wcs:1.0.2'
We have prepared watson-conversation-service-for-java as a helper library for executing Watson Conversation from Java. This library is a thin wrapping of the well-made Basic Library (https://github.com/watson-developer-cloud/java-sdk) and extended for multi-user chatbots.
The following is an example of getting the Context variable set on the Watson Conversation side.
WcsExample02.java
package org.example.wcs;
import java.util.Map;
import org.riversun.wcs.WcsClient;
public class WcsExample02 {
private static final String WCS_USERNAME = "EDIT_ME_USERNAME_HERE";
private static final String WCS_PASSWORD = "EDIT_ME_PASSWORD_HERE";
private static final String WCS_WORKSPACE_ID = "EDIT_ME_WORKSPACE_ID_HERE";
public static void main(String[] args)
{
String wcsClientId = "dummy_user_id";
WcsClient watson = new WcsClient(WCS_USERNAME, WCS_PASSWORD, WCS_WORKSPACE_ID);
watson.startConversation(wcsClientId);
String myParam01 = watson.getAsString(wcsClientId, "myParam01");
System.out.println("myParam01=" + myParam01);
String myParam02 = watson.getAsString(wcsClientId, "myParam02");
System.out.println("myParam02=" + myParam02);
Integer myParam03 = watson.getAsInteger(wcsClientId, "myParam03");
System.out.println("myParam03=" + myParam03);
Boolean myParam04 = watson.getAsBoolean(wcsClientId, "myParam04");
System.out.println("myParam04=" + myParam04);
Map<String, Object> myParam05 = watson.getAsMap(wcsClientId, "myParam05");
String subParam01 = (String) myParam05.get("subParam01");
System.out.println("myParam05.subParam01=" + subParam01);
String subParam02 = (String) myParam05.get("subParam02");
System.out.println("myParam05.subParam02=" + subParam02);
}
}
As seen above, the Context variables set in the ** Welcome_node ** node on the Watson Conversation side are as follows.
{
"context": {
"myParam01": "banana",
"myParam02": "apple",
"myParam03": 7777,
"myParam04": true,
"myParam05": {
"subParam01": "orange",
"subParam02": "lemon"
}
}
}
For example, to get the above Context variable "** myParam01 **" on the Java side, do as follows.
String myParam01 = watson.getAsString(User ID, "myParam01");
Also, in the case of a (nested) composite object such as the Context variable "** myParam05 **", get it as ** Map ** as shown below. Data is stored nested under ** Map **.
**Map<String, Object> myParam05 = watson.getAsMap(wcsClientId, "myParam05");**
The table below also shows how to access other types.
** Method to get Watson Conversation Context variable from Java logic **
Context variable type | Method definition | Return type |
---|---|---|
String type | #getAsString(User ID,Context variable name) | String |
Numeric type(Integer type) | #getAsInteger(User ID,Context variable name) | Integer |
Numeric type(Double type) | #getAsDouble(User ID,Context variable name) | Double |
Boolean type | #getAsBoolean(User ID,Context variable name) | Boolean |
Complex type(If the JSON is nested) | #getAsMap(User ID,Context variable name) | Map |
In the above two examples, we mainly looked at sending and receiving text (String) and exchanging Context variables, but you can actually get various information between Watson and Java logic.
** Information that can be obtained **
information | Description |
---|---|
Input | User input information |
Output | Output information from Watson. Information other than text can be flexibly included in Output |
Context | Context variable information |
Intents | Intent determined by Watson based on user input text(Multiple) |
Entities | Entity extracted by Watson based on user input text(Multiple) |
The following is an example of actually accessing various information included in the response from Watson from Java logic.
public class WcsExample03 {
private static final String WCS_USERNAME = "EDIT_ME_USERNAME_HERE";
private static final String WCS_PASSWORD = "EDIT_ME_PASSWORD_HERE";
private static final String WCS_WORKSPACE_ID = "EDIT_ME_WORKSPACE_ID_HERE";
public static void main(String[] args)
{
String wcsClientId = "dummy_user_id";
WcsClient watson = new WcsClient(WCS_USERNAME, WCS_PASSWORD, WCS_WORKSPACE_ID);
MessageResponse res = watson.startConversation(wcsClientId);
Map<String, Object> context = res.getContext();
Map<String, Object> input = res.getInput();
String inputText = res.getInputText();
List<Intent> intents = res.getIntents();
List<Entity> entities = res.getEntities();
Map<String, Object> output = res.getOutput();
List<String> text = res.getText();
String textConcatenated = res.getTextConcatenated("");
System.out.println("Response JSON from Watson=\n" + res.toString());
}
}
Since Java has a helper library, there aren't many scenes where JSON is parsed directly by Java logic, but the actual response from Watson Conversation is as follows.
Response from Watson Conversation
{
"context": {
"system": {
"dialog_stack": [
{
"dialog_node": "root"
}
],
"dialog_turn_counter": 2.0,
"dialog_request_counter": 2.0,
"_node_output_map": {
"Welcome_Node": [
0.0
],
"Show_Context_Node": [
0.0
]
},
"branch_exited": true,
"branch_exited_reason": "completed"
},
"conversation_id": "xxxx-xxxxxx-xxxxx-xxxx",
"myParam05": {
"subParam01": "orange",
"subParam02": "lemon"
},
"myRemoteParam": "I need you!",
"myParam03": 7777.0,
"myParam04": true,
"myParam01": "banana",
"myParam02": "apple"
},
"entities": [],
"intents": [],
"output": {
"text": [
"「Hi!You said "Watson". The value of the context variable myRemoteParam is "I need you!"is."
],
"nodes_visited": [
"Show_Context_Node"
],
"log_messages": []
},
"input": {
"text": "Hi!Watson"
}
}
This section is a little extra.
It's a little difficult to try Watson Conversation interactions with just a Java console app, so I created a simple GUI.
・ All source code is in the following repository https://github.com/riversun/watson-examples-java-chatbot
・ The dialogue flow is the ** hotel reservation chatbot (enhanced version) ** introduced previous. You can download the workspace file (JSON) from the following. https://riversun.github.io/wcs/org.riversun.HotelReservationV2.zip
@SuppressWarnings("serial")
public class WatsonChat extends JFrame {
private static final String WATSON_CONVERSATION_USERNAME = "EDIT HERE";
private static final String WATSON_CONVERSATION_PASSWORD = "EDIT HERE";
private static final String WATCON_CONVERSATION_WORKSPACE_ID = "EDIT HERE";
private static final String WCS_CLIENT_ID = "dummy_user_id";
private static final int WIDTH_PX = 640;
private static final int HEIGHT_PX = 480;
private final WcsClient mWatson = new WcsClient(WATSON_CONVERSATION_USERNAME, WATSON_CONVERSATION_PASSWORD, WATCON_CONVERSATION_WORKSPACE_ID);
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final StringBuilder mSb = new StringBuilder();
private final JTextArea mTextArea = new JTextArea();
private final JTextField mTextBox = new JTextField("");
public static void main(String args[]) {
EDTHandler.post(new Runnable() {
public void run() {
System.setProperty("jsse.enableSNIExtension", "false");
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException |
IllegalAccessException | UnsupportedLookAndFeelException e) {
}
final WatsonChat frame = new WatsonChat();
frame.setVisible(true);
}
});
}
public WatsonChat() {
EDTHandler.post(new Runnable() {
@Override
public void run() {
buildGUI();
sendMessageOnFirst();
}
});
}
boolean onUserInputText(String userInputText) {
addTextIntoHistory("YOU:" + userInputText + "\n");
sendMessageToWatson(WCS_CLIENT_ID, userInputText);
return true;
}
void onClearButtonPressed() {
mSb.setLength(0);
EDTHandler.post(new Runnable() {
@Override
public void run() {
mTextArea.setText(mSb.toString());
}
});
mWatson.clearConversation(WCS_CLIENT_ID);
sendMessageOnFirst();
}
void sendMessageOnFirst() {
mExecutor.submit(new Runnable() {
@Override
public void run() {
MessageResponse welcomeRes = mWatson.startConversation(WCS_CLIENT_ID);
addTextIntoHistory("WATSON:" + welcomeRes.getTextConcatenated("") + "\n");
unlockTextBox();
}
});
}
void sendMessageToWatson(String wcsClientId, String userInputText) {
mExecutor.submit(new Runnable() {
@Override
public void run() {
final String watsonOutputText = mWatson.sendMessageForText(wcsClientId, userInputText);
addTextIntoHistory("WATSON:" + watsonOutputText);
unlockTextBox();
}
});
}
void addTextIntoHistory(String text) {
mSb.append(text);
EDTHandler.post(new Runnable() {
@Override
public void run() {
mTextArea.setText(mSb.toString());
}
});
}
void unlockTextBox() {
EDTHandler.post(new Runnable() {
@Override
public void run() {
mTextBox.setEditable(true);
mTextBox.requestFocus();
mTextBox.getCaret().setVisible(true);
mTextBox.setCaretPosition(0);
}
});
}
void buildGUI() {
setTitle("Chat with Watson");
setSize(WIDTH_PX, HEIGHT_PX);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLinearLayout layoutParent = new JLinearLayout().setChildOrientation(Orientation.VERTICAL).setPadding(5);
final JLinearLayout layoutHeader = new JLinearLayout().setChildOrientation(Orientation.HORIZONTAL).setPadding(5, 0, 5, 0);
final JLinearLayout layoutCenter = new JLinearLayout().setChildOrientation(Orientation.VERTICAL).setPadding(5, 0, 5, 0);
final JLinearLayout layoutFooter = new JLinearLayout().setChildOrientation(Orientation.VERTICAL).setPadding(5);
JLabel lbChatHistory = new JLabel("Chat History:");
JButton btClear = new JButton("Clear");
btClear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
onClearButtonPressed();
}
});
mTextArea.setLineWrap(true);
mTextArea.setMargin(new Insets(4, 4, 4, 4));
mTextArea.setEditable(false);
JScrollPane textAreaScroll = new JScrollPane(mTextArea);
textAreaScroll.setBorder(new LineBorder(Color.black, 1, true));
JLabel lbInputText = new JLabel("Input Text: (press ENTER-KEY to send)");
mTextBox.setBorder(new LineBorder(Color.black, 1, true));
mTextBox.setEditable(false);
Font font = mTextBox.getFont().deriveFont(Font.PLAIN, 20f);
mTextBox.setFont(font);
mTextBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final String userInputText = mTextBox.getText();
if ("".equals(userInputText)) {
return;
}
mTextBox.setEditable(false);
final boolean consumed = onUserInputText(userInputText);
if (consumed) {
mTextBox.setText("");
}
}
});
addWindowListener(new WindowAdapter() {
public void windowOpened(WindowEvent e) {
mTextBox.requestFocus();
}
});
layoutHeader.addView(lbChatHistory, 1.0d);
layoutHeader.addView(btClear, 0d);
layoutCenter.addView(textAreaScroll);
layoutFooter.addView(lbInputText);
layoutFooter.addView(mTextBox);
layoutParent.addView(layoutHeader, 0.0d);
layoutParent.addView(layoutCenter, 1.0d);
layoutParent.addView(layoutFooter, 0.0d);
JPanel mainPanel = layoutParent.getAsPanel();
Container contentPane = getContentPane();
contentPane.add(mainPanel, BorderLayout.CENTER);
}
}
void sendMessageToWatson(String wcsClientId, String userInputText) {
mExecutor.submit(new Runnable() {
@Override
public void run() {
final String watsonOutputText = mWatson.sendMessageForText(wcsClientId, userInputText);
addTextIntoHistory("WATSON:" + watsonOutputText);
unlockTextBox();
}
});
}
The key to this code is mostly summarized here.
The text entered from the text field (bottom) is sent to Watson with ** # sendMessageForText **. The result is received as text and displayed in the text area (top) of the history requirement.
The ** unlockTextBox ** part unlocks the text field. If you don't lock the text field while communicating with Watson, the next text will be sent before the result is returned.
The rest is a part that is often found in GUI processing, but the ** mExecutor.submit ** part uses Thread Executor to communicate with Watson in a separate thread. If you synchronize here, the GUI will be locked while communicating with Watson.
This time, I introduced how to link Java logic and Watson Conversation and execute the dialogue flow of Watson Conversation from Java.
--Sample code repository https://github.com/riversun/watson-conversation-java-examples-ja
--Helper library repository https://github.com/riversun/watson-conversation-service-for-java
Next time wants to connect with frontends such as Slack and LINE to create chatbot apps such as Slack Bot and LINE Bot.