A Better Human (topic)

Many people use their Dexter bot tell their story via social media chat. By doing so, they change the basic nature of their account’s chat from person-to-person to person-to machine. This is largely a win for the bot’s owner, as the machine can have far longer conversations with far more people far more efficiently than a human (or even a team of humans) ever could.

There are times, of course, when a machine can’t fully substitute for the adaptability and breadth of a conversation between two real human brains. If the people behind your account have the staff and patience to lend your bot an occasional human counterpart, Dexter has always been ready to help you step up and provide that service.

We have 2 tools to help a human step into your conversation. For serious usage, many bot writers choose to take advantage of the Relay tool that comes with our Platform and Enterprise plans. Others engineer a special “Human mode” section of the bot where their users can choose to wait for an agent to step in for a chat.

For human mode bots, there’s a sneaky little UI issue that can creep in when a human doesn’t immediately step into a conversation. Let’s say your bot is Sally’s Pizza, and a user Frank comes to start a detailed discussion on how you should treat the tomatoes in your famous fresh tomato pie. You, however, are busy slinging hundreds of your coal-fired beauties and miss Frank’s question. Frank, understanding that you’re probably busy, closes his browser and promptly forgets he asked the question. The next day, Frank hops back on his chat with your bot to check your hours….but uh oh, your bot doesn’t respond. Radio button silence. Dead macbook air. What happened?

Well, most of the time Human Mode involves a topic that looks like this:

+ *
- <noreply>

That means that, since Frank placed himself in Human mode, he can no longer interact with your bot! Most bot writers understand this possibility and give their users an obvious way out:

+ human-start
- OK, we'll try to get a human to you ASAP. If you want to stop waiting, just say "exit" and we'll take you back to the main menu.
+ (exit|stop|menu)
- Sorry we couldn't get to you! I'll take you back to the main menu now. <send> {topic=default} {@ menu}

This isn’t as useful as it could be for Frank, however. Since he came back to the bot later, he probably doesn’t remember exactly what he was doing last time he was there. Then, either through not paying attention to the instructions or having them somehow disappear from his immediate view, he doesn’t know about the “exit” instruction, and is stuck with an unresponsive agent. This is not, as you might imagine, a great experience.

One fairly simple way around this problem is to remind the user of the instructions occasionally. Our recommended strategy is to keep track of how long the user has been in Human Mode and, after a certain amount of time has passed, use the catch-all to respond with a fresh reminder of the exit instructions. So as to not hold you in suspense, here’s the whole topic that implements this idea:

// Use this to start the human takeover process
// Called from anywhere via {topic=human} {@ human_start}
+ human_start
- OK, I'll step aside so a human can come in and chat with you. <send> If you get tired of waiting, just say "Back to Bot" and I'll take you back to the main menu. <call>setHumanStart</call> ^buttons('Back to Bot')
// Tell the user to call this to get back to the regular bot
+ back to [the] bot
- Welcome back! <set human_start> {topic=default} {@ menu}
// This is what kills the bot's response while a human is talking
// to the user. It mostly means the bot won't respond at all, but
// after a certain amount of time it'll remind the user that
// they're still waiting for a human.
+ *
* <call>isStuckInHumanTopic</call> == true => Hi there! You're still waiting for a human to get back to you. If you get tired of waiting, just say "Back to Bot"! <call>setHumanStart</call> ^buttons('Back to Bot')
- ^bypass()
// Set up a time when the user last heard from the bot.
// We'll use this to see if we need to remind them how to exit.
> object setHumanStart javascript
rs.setUservar(rs.currentUser(), 'human_start', Date.now())
< object
// Check and see if the user is stuck in the topic based on
// how long it's been since the bot last chatted with them.
> object isStuckInHumanTopic javascript
// Get when we started waiting for a human
var started = rs.getUservar(rs.currentUser(), 'human_start', Date.now())
// Let the caller give a specific age they want to use and default to 5 minutes otherwise. Change this to change the wait time.
, waitFor = args[0] || 1000 * 60 * 5
;
// We're only "stuck" in the human topic if we haven't been waiting long
return (Date.now() - waitFor) > started;
< object

That’s it! If you want a breakdown of what’s going on, let’s take it chunk-by-chunk:

+ human_start
- OK, I'll step aside so a human can come in and chat with you. <send> If you get tired of waiting, just say "Back to Bot" and I'll take you back to the main menu. <call>setHumanStart</call> ^buttons('Back to Bot')

This is the standard Human Mode intro, explaining to the user what’s going on and what they should do. There are 2 important features here:

  • <call>setHumanStart</call> We call a macro that remembers when we started waiting for a human
  • ^buttons(‘Back to Bot’) We give the user a clickable way to exit the bot if they want.
+ back to [the] bot
- Welcome back! <set human_start> {topic=default} {@ menu}

Here’s the standard exit, but note that we clear the human_start flag to make sure any future human conversation requests start from a clean slate.

+ *
* <call>isStuckInHumanTopic</call> == true => Hi there! You're still waiting for a human to get back to you. If you get tired of waiting, just say "Back to Bot"! <call>setHumanStart</call> ^buttons('Back to Bot')
- ^bypass()

This is the big change — we’ve tweaked our usual catch-all-and-kill block so that it constantly checks to see how long the user’s been stuck in this topic. If our macro decides we’ve been waiting too long, it’ll respond with the instructions, then reset the clock so that it’ll again wait before the next reminder.

> object setHumanStart javascript
rs.setUservar(rs.currentUser(), 'human_start', Date.now())
< object

A quick-and-dirty macro to capture the current time for later comparison.

> object isStuckInHumanTopic javascript
var started = rs.getUservar(rs.currentUser(), 'human_start', Date.now())
, waitFor = args[0] || 1000 * 60 * 5
;
return (Date.now() - waitFor) > started;
< object

A less quick-and-dirty macro that decides if the user’s ready to see a reminder. We grab the time of our last reminder (started), figure out how long we want to wait before nagging (waitFor), and return true if we’ve been waiting long enough.