القائمة الرئيسية

الصفحات

Creating a Chatbot with RiveScript in Java

Motivation

"Artificial Intelligence (AI) is considered a major innovation that could disrupt many things. Some people even compare it to the Internet. A large investor firm predicted that some AI startups could become the next Apple, Google or Amazon within five years" 
- Prof. John Vu, Carnegie Mellon University.

Using chatbots to support our daily tasks is super useful and interesting. In fact, "Jenkins CI, Jira Cloud, and Bitbucket" have been becoming must-have apps in Slack of my team these days.

There are some existing approaches for chatbots including pattern matching, algorithms, and neutral networks. RiveScript is a scripting language using "pattern matching" as a simple and powerful approach for building up a Chabot.

Architecture

Actually, it was flexible to choose a programming language for the used Rivescript interpreter like Java, Go, Javascript, Python, and Perl. I went with Java.


Used Technologies and Tools

  • Oracle JDK 1.8.0_151
  • Apache Maven 3.5.2
  • Apache Tomcat 7.0.85
  • RiveScript-Java
  • Jersey sever/client
  • MyFaces

Module ChatBot Backend

I had a backend for chatbot's brain which provided APIs responding to received messages from users via a GUI.

1. Generate a web app project via Maven

mvn archetype:generate \
-DgroupId=vn.nvanhuong \
-DartifactId=chatbot_rivescript_backend \
-DarchetypeArtifactId=maven-archetype-webapp \
-DinteractiveMode=false;

Tips: When importing the project into Eclipse, I encountered an error "The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path". I solved it by "Right click on the project/Properties/Project Facets/Runtimes/Check Apache Tomcat v.7.0"

2. Add dependencies needed in `pom.xml`

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>vn.nvanhuong</groupId>
<artifactId>chatbot_rivescript_backend</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>chatbot_rivescript_backend Maven Webapp</name>
<url>http://maven.apache.org</url>

<dependencies>
<!-- ChatBot Brain -->
<dependency>
<groupId>com.rivescript</groupId>
<artifactId>rivescript-core</artifactId>
<version>0.10.0</version>
</dependency>

<!-- RESTful APIs -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.8</version>
</dependency>

<!-- JSON -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>

<!-- Unit tests -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>chatbot_rivescript_backend</finalName>
</build>
</project>

3. Create chatbot's brain with RiveScript

I created a file "chatbot_brain.rive" under the folder "src/main/resources/rivescript". I copied the content of template file "rs_standard.rive" at https://www.rivescript.com/try
+ hello bot
- Hello human!

4. Create RESTful APIs

package vn.nvanhuong.chatbot.rivescript.backend;

import javax.ws.rs.POST;
import javax.ws.rs.Path;

import com.rivescript.Config;
import com.rivescript.RiveScript;
import com.sun.jersey.spi.resource.Singleton;

@Path("/bot")
@Singleton
public class ChatBot {
private RiveScript bot;

public ChatBot() {
String rivescriptFilePath = ChatBot.class.getClassLoader().getResource("rivescript").getFile();
bot = new RiveScript(Config.utf8());

bot.loadDirectory(rivescriptFilePath);
bot.sortReplies();
}

@POST
public String getMsg(String msg) {
return bot.reply("user", msg);
}

}

5. Configure RESTful at `web.xml`

<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Restful Web Application</display-name>

<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>vn.nvanhuong.chatbot.rivescript.backend</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>

</web-app>

6. Write a test case

package vn.nvanhuong.chatbot.rivescript.backend.test;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

import vn.nvanhuong.chatbot.rivescript.backend.ChatBot;

public class ChatBotTest {

@Test
public void should_say_hello() {
ChatBot bot = new ChatBot();

assertEquals("Hello Human!", bot.getMsg("Hello Bot"));
}
}

7. Test the API with Postman

URL: http://localhost:8080/chatbot_rivescript_backend/rest/bot

Module ChatBot GUI

1. Generate a web app project via Maven

mvn archetype:generate \
-DgroupId=vn.nvanhuong \
-DartifactId=chatbot_rivescript_gui \
-DarchetypeArtifactId=maven-archetype-webapp \
-DinteractiveMode=false

2. Add dependencies needed in pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>vn.nvanhuong</groupId>
<artifactId>chatbot_rivescript_gui</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>chatbot_rivescript_gui Maven Webapp</name>
<url>http://maven.apache.org</url>

<dependencies>
<!-- JAX-RS Client -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.25.1</version>
</dependency>

<!-- JSF Pages -->
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-api</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-impl</artifactId>
<version>2.2.0</version>
</dependency>

<!-- Unit test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>chatbot_rivescript_gui</finalName>
</build>
</project>

3. Configure JSF at web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">

<!-- JSF mapping -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

<!-- welcome page -->
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
</web-app>

4. Create a GUI

Rename index.jsp to index.xthml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
<h:head>
<title>RiveScript</title>
<style>

.container {
display: block;
margin: 50px auto;
width: 90%;
}

.chatbox {
height: 600px;
border: solid 1px #039;
background-image: url(bot_logo.png);
background-repeat: no-repeat;
background-position: center;
background-size: contain;
display: flex;
justify-content: center;
align-items: center;
}

.chatbox .bot-dialog {
width: 90%;
border: dashed 1px purple;
text-align: center;
background-color: orange;
}

.chatbox .bot-dialog > span{
font-size: larger;
}

.message {
display: flex;
justify-content: space-between;

}
.message > input.message-input {
width: 90%;
margin-top: 10px;
line-height: 2.3;
}

.message > input.submit {
width: 9%;
background-color: #039;
color: white;
font-size: 15px;
margin-top: 10px;
}

.message-display > span {
font-style: italic;
}
.message-display > label {
font-weight: bold;
}
.message-display {
margin-top: 5px;
}

</style>
</h:head>
<h:body>
<h:form>
<h:panelGroup layout="block" styleClass="container">
<h:panelGroup layout="block" styleClass="chatbox">
<h:panelGroup layout="block" styleClass="bot-dialog">
<h:outputText id="botMessage" value="#{controller.botMessage}" escape="false"/>
</h:panelGroup>
</h:panelGroup>

<h:panelGroup layout="block" styleClass="message">
<h:inputText id="input" value="#{controller.humanMessage}" styleClass="message-input"
p:placeholder="Send a message to the bot"
p:autofocus="true"
onblur="this.focus()"/>
<h:commandButton id="button" value="Send" actionListener="#{controller.onSend}" styleClass="submit"/>
</h:panelGroup>

<h:panelGroup layout="block" styleClass="message-display" rendered="#{not empty controller.humanMessageDisplay}">
<h:outputLabel for="messageDisplay" value="You just said: "/>
<h:outputText id="messageDisplay" value="#{controller.humanMessageDisplay}"/>
</h:panelGroup>
</h:panelGroup>
</h:form>
</h:body>
</html>

5. Create a Controller to call the RESTful APIs

package vn.vanhuong.chatbot.rivescript.gui;

import javax.faces.bean.ManagedBean;
import javax.faces.event.ActionEvent;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@ManagedBean(name = "controller")
public class Controller {

private String humanMessage;
private String botMessage;
private String humanMessageDisplay;

public void onSend(ActionEvent event) {
Response response = ClientBuilder.newClient().target("http://localhost:8080/chatbot_rivescript_backend/rest/bot")
.request(MediaType.APPLICATION_FORM_URLENCODED)
.post(Entity.entity(humanMessage, MediaType.APPLICATION_FORM_URLENCODED));
this.botMessage = response.readEntity(String.class);
this.humanMessageDisplay = humanMessage;
this.humanMessage = null;
}

public String getHumanMessage() {
return humanMessage;
}

public void setHumanMessage(String humanMessage) {
this.humanMessage = humanMessage;
}

public String getBotMessage() {
return botMessage;
}

public void setBotMessage(String botMessage) {
this.botMessage = botMessage;
}

public String getHumanMessageDisplay() {
return humanMessageDisplay;
}

public void setHumanMessageDisplay(String humanMessageDisplay) {
this.humanMessageDisplay = humanMessageDisplay;
}
}

6. Enjoy playing with your ChatBot

Check out my source code as below

- Backend: https://github.com/vnnvanhuong/chatbot_rivescript_backend.git
- GUI: https://github.com/vnnvanhuong/chatbot_rivescript_gui.git

References:
[1]. http://science-technology.vn/?p=5761
[2]. https://www.rivescript.com/interpreters
[3]. https://github.com/aichaos/rivescript-java
[4]. https://youtu.be/wf8w1BJb9Xc

Comments