A Bot with a Handlebar Mustache

How Articulate uses Handlebars.js to simplify chatbots

Caleb Keller
Smart Platform Group
6 min readApr 24, 2018

--

Can you imagine creating a fully functional intelligent agent without writing a single line of code? As part of Smart Platform Group’s focus on simplicity, we are working to make that a reality.

One of the ways we’re trying to simplify is by allowing the webhooks and responses generated by our agents to be customizable with a templating tool called Handlebars.js.

If you want to read a tutorial on creating a bot that uses Handlebars check this one out:

But in this article, we’re going to explain briefly how Handlebars.js works and how Articulate uses it.

Handlebar Basics

In the above tutorial one of the API’s we were using returned a payload that looked like the below:

{
"slip": {
"advice":"One of the top five regrets people have is that they didn't stay in contact with friends.",
"slip_id": "116"
}
}

But returning that to the user would be a touch on the unfriendly side, so we want it to say something like:

Advice #116: One of the top five regrets people have is that they didn't stay in contact with friends.

To generate this simple statement with handlebars, you would use the below template.

Advice #{{slip.sip_id}}: {{slip.advice}}

If you’ve worked with JSON or javascript objects, you probably recognize the dot notation used inside the curly brackets. The curly brackets tell handlebars that this is some text that needs replacing and the dot notation inside of them is the pointer to what value should replace it. So {{slip.slip_id}} is saying Replace this text with the value found in slip_id inside of the slip object.

For more information on handlebars and what it is capable of it is advises to read through their comprehensive docs here.

Understanding the Articulate Object

In the above example, the values for replacing came from an object or JSON chunk. And that is true with Articulate as well. During a conversation, we maintain an extensive object that we call the Conversation State Object and everything in it is addressable via handlebars.

At the highest level here is what the CSO contains:

agent - Agent result from the export endpoint
domain - The domain recognized by rasa
scenario - Scenario of the current frame
intent - Intent of the current frame
context - Full context history
currentContext - Current context
parse - Full rasa results
rasaResult - Best rasa result
text - The user text
sessionId - session id
timezone - timezone
slots - Values that fill the slots of the scenario

Several of the above items are complex objects/arrays in and of themselves, but the two values you will likely be using most often are the intent and slots items. Let’s see how to use them in a few different examples.

Formatting the Webhook

There are two places in the webhook that you can use handlebars: the URL and the payload.

The URL

For example, you may have structured intents to where they roughly match an API that you are consuming in your webhook. You can use the intent item in the CSO to customize the API URL:

http://my-api:3000/{{intent.name}}

Or pass query string parameters.

http://my-api:3000/{{intent.intentName}}?keyword={{slots.keyword.value}}

But you can also use handlebars if/else logic to change the URL completely. The tutorial linked above demonstrates this particular usage. Here is the URL that we used in that case:

{{#if slots.topic}}
http://api.adviceslip.com/advice/search/{{slots.topic.value}}
{{else}}
http://api.adviceslip.com/advice
{{/if}}

The above changes the URL based on whether or not a particular slot has a value. When the topic slot is present, it also includes that slot value as part of the URL.

The Payload

The payload has the same type of functionalities. Though there is a Handlebar helper installed to make including JSON objects much more manageable. For example, let’s say you want to pass the entire CSO to the webhook:

{{{JSONstringify this}}}

There are a few things to note about this particular snippet:

  • The CSO is huge, definitely not recommended to pass it to your webhook every time.
  • We used triple curly braces {{{ not double. Triple braces tell handlebars not escape characters.

But we can use this in a practical sense too. For example, maybe we want to pass all of the slots as part of the payload, but we want them to be in the keywords field:

{
"keywords": {{{JSONstringify slots}}}
}

Formatting the Response

Now that you’ve seen how to use Handlebars in the webhook URL and payload, nothing here will surprise you. Handlebars, used for responses, allows you to respond in whichever way you want. But you can only specify one URL and one payload, for agent responses you can specify multiple different options.

In the above example it’s important to know the logic around which response we choose:

  • We won’t pick a response that has a missing value. In the above screenshot, there is no slot for delivery that means that response will never be used.
  • We will choose the most complicated responses. By complicated I mean those with the most handlebar variables. In the above screenshot, assuming there is only a topping’s value, the bottom two response will be preferred over the one without any handlebars in it.
  • If multiple responses tie for selection, we will choose a random one. So again, assuming there is only a topping value in the above screenshot, we will select at random which one of the bottom two responses to return.

Wrapping Up

So with that, you have an idea of how to use Handlebars with Articulate. Want to get started with Articulate, jump here to download the latest release. Want a couple more complex examples, well then keep reading. But for those whose curiosity is already sated: feel free to reach out to us with any further interest or questions. Join us on Gitter, create an issue, or open a pull request: we’re always happy to help!

Complex Examples

As part of our development process we frequently challenge ourselves to dogfood and create applications on our own products. Here a are a few Handlebar snippets from agents we have created:

Luis Malavé built an agent for searching flight information. As part of his webhook he had to format a complex URL and agent response:

http://0.0.0.0:3000/browsequotes/{{context.[0].slots.Place.value.[0]}}/{{context.[0].slots.Place.value.[1]}}/{{#if context.[0].slots.sys.duckling_time.from}}{{dateTimeFormat context.[0].slots.sys.duckling_time.from 'YYYY-MM-DD'}}{{#if context.[0].slots.sys.duckling_time.to}}/{{dateTimeFormat context.[0].slots.sys.duckling_time.to 'YYYY-MM-DD'}}{{else}}anytime{{/if}}{{else}}anytime{{/if}}?apikey=prtl493867498566983194

That is a lot of Handlebars. Most of it is conditional statements for when certain slots are filled. There’s also a special handlebars in there for formatting dates {{dateTimeFormat my-date ‘YYYY-MM-DD’}}. And here is an example agent response from his bot as well:

{{#if quotes.length}} We found {{quotes.length}} flights from {{context.[0].slots.Place.original.[0]}} to {{context.[0].slots.Place.original.[1]}} starting from {{currency.[0].symbol}} {{#if minQuote}}{{minQuote.minPrice}}{{/if}} {{else}} No flights where found :( {{/if}}

Given what you read in the article, can you tell how this could’ve been simplified? Articulate wont use responses with empty variables. So the first if statement could’ve been replaced with one agent response that just said No flights were found. which would get used when there was no {{quotes.length}}.

Daniel Calvo Marín created a doctor’s assistant. Here’s a screenshot of his bot using multiple agent responses, each formatted with Handlebars. He is also formatting a database query in the webhook payload. Advanced formatting allows the elimination of middleware or translation services between the agent and the database.

Once you get up to speed on how to use it, let us know the cool things you’re doing with Handlebars in Articulate!

--

--

Caleb Keller
Smart Platform Group

Mechanical Engineer turned Data Scientist turned Machine Learning practitioner. Focused on solving the problems of enterprise data, starting with how we can Do