Chatbots in JS

Stacks and implementation

Yann LEFLOUR

Deputy CTO @ BAM

@yleflour

yleflour

slides.com/yleflour

A wee bit about chatbots first

What chatbots are not

  • Artificial Intelligence
  • A must have solution
  • A replacement for apps

What chatbots are

  • A new platform to engage with users
  • A way to engage more personaly with users
  • A marketing tool
  • A frontend in disguise

A wee more about chabot services

What we won't be talking about

Why stick with JS?

Answer: It's code

Chatbot

Architecture

The 3 components

  • Recognizer
  • Connector
  • Bot Builder

Universal Chatbot Architecture

Bot Builder

Bot Builder

  • Handles the dialog flow
  • Persists user context
  • Akin to a Controler in a MVC architecture

Some bot builder librairies (JS only)

Botkit

Microsoft

Botbuilder

Our choice:

  • Open Source (MIT)
  • Fully typed and heavily documented
  • Includes I18N
  • Unit testable
  • Easy to implement custom logic

Microsoft Bot Builder

Connector

Connector service (input)

  • Listens to messages through a webhook
  • Takes a channel specific message format
  • Converts it from to a standardized format

Connector service (ouput)

  • Takes a standardized message format
  • Converts it to the specified channel message format
  • Sends the message to the platform

Some connector services

Our choice:

export default class MessengerConnector {

Pure JS (for the win)

  /** onInvoke, onEvent, startConversation, update, delete, isInvoke */
}

  listenMessengerEvent() {
    // Express listener
    return async (req, res) => {
      // Respond immediately to messenger to to prevent it from getting stuck
      res.send();
      const events = await mapMessengerPostToEvents(req.body);
      this.onEventHandler(events);
    };
  }
  async send(messages, done) {
    const messengerMessages = mapBotBuilderToMessengerMessages(messages);
    await sendMessengerMessages(messengerMessages);
    done(null, []);
  }

Recognizer

Recognizer

  • Map user sentences to actionable inputs
  • Uses machine learning (NLU)
  • Needs to be trained
  • Ouputs intents and entities

Example: "I want to book a restaurant in Verona"

  • Intent: "restaurant_booking"
  • Entities: [{type: "city", value: "verona"}]

Some NLU Services

Our choice

  • Plug & Play SaaS API
  • They were very available
  • Handles multiple languages well (EN, FR, ...)
export default class RecastRecognizer {
  async recognize(context, callback) {
    try {
      const recastResults = await recast.analyseText(context.message.text);
      const output = mapRecastToBotBuilderIntent(recastResults);
      callback(null, output);
    } catch(err) {
      callback(err);
    }
  }
}

Recast.ai + JS mapper

A typical conversation track with Bot Builder

Example

  • User says: "I would like to book a restaurant"
  • Structure:

Conversation

A root dialog which resets the bot to its initial state at the end

export const bookRestaurantConversation = [
];

  (session, results) => {
    session.dialogData.restaurantId = results.response.restaurantId;
    session.beginDialog('/getDateTimeDialog', session);
  },
  (session, results, next) => {
    session.dialogData.dateTime = results.response.dateTime;
    next();
  },
  async (session) => {
    const { restaurantId, dateTime } = session.dialogData;
    await bookingAPI.bookRestaurant(restaurantId, dateTime);
    session.endConversation();
  }}
  (session, args) => {
    session.beginDialog('/getRestaurantDialog', session);
  },

Dialog

A branch in your conversation tree

export const getRestaurantDialog = [
];

  (session, args, next) => {
    session.beginDialog(
      session'/locationPrompt',
      { prompt: "Where do you want to book?" }
    );
  },
  (session, results, next) => {
    const location = results.response;
    session.beginDialog(
      session'/locationPrompt',
      { prompt: "Which restaurant?", location }
    );
  },

  (session, results) => {
    const restaurantId = results.response;
    const output = { response: restaurantId };
    session.endDialogWithResults(output);
  },

Prompt

A single step dialog which handles user input

export const restaurantPrompt = new Prompt()
  

  // Used to display the prompt message
  .onFormatMessage(async (session, text, _speak, callback) => {
    const location = session.dialogData.options.location;
    const restaurants = await getRestaurantsFromAPI(location)
    const message = formatRestaurantCarouselMessage(text, restaurants);
    return callback(null, message);
  })
    
  // Used to handle the user input
  .onRecognize((context, callback) => {
    const entities = context.intent.entities;
    const restaurantId = EntityRecognizer.findEntity(entities, 'restaurant_id');
    
    if (!restaurantId) return callback(null, 0);
    return callback(null, 1, { restaurantId });
  })

Server

Setup

const connector = new MessengerConnector();
const bot = new UniversalBot(connector).recognizer(new RecastRecognizer());

// Root Dialog
bot.dialog('/', /** ... */)
  .beginDialogAction(
    'bookRestaurantFromIntentAction',
    '/bookRestaurantConversation',
    { matches: 'book_restaurant' }
  );

// Setup Conversations
bot.dialog('/bookRestaurantConversation', bookRestaurantConversation);

// Setup Dialogs
bot.dialog('/getRestaurantDialog', getRestaurantDialog);
bot.dialog('/getDateTimeDialog', getDateTimeDialog);

// Setup Prompts
bot.dialog('/locationPrompt', LocationPrompt);
bot.dialog('/restaurantPrompt', restaurantPrompt);
bot.dialog('/dateTimePrompt', dateTimePrompt);

Bot setup

const app = express();

const server = http.createServer(app);
const port = process.env.PORT || 80;

/**
 * Declare the bot and its connector
 */

// Listen to messages
app.use(bodyParser.json());
app.post('/api/messages', connector.listenMessengerEvent());

// start the server
server.listen(port, () => {
  console.log('Listening on %s', port);
});

Express setup

Quick warning

  • Type everything
  • Unit test everything
  • Yes, that includes the NLU
  • Plan out your conversation flow
  • Implementing a bot takes time

Happy bot building!

Chatbots in JS

By Yann Leflour

Chatbots in JS

  • 1,862