Blogg | Knowit

Sundt Chef – en chatbot for lunsjen

Skrevet av Varun Sivipalan | Jul 2, 2018 10:00:00 PM

Forrige påske flyttet Knowit Oslo til splitter nye lokaler i Sundtkvartalet, Lakkegata 53. Her deler vi kantina med blant annet IBM, Skanska og Mattilsynet. Sundt Chef er en bot for Slack som gir deg lunsjmenyen på Sundtkvartalet. Boten bruker Googles Dialogflow for å kunne tilby et “conversational interface”, og Google Cloud Functions for “serverless” funksjoner som laster ned HTML, trekker ut lunsjmenyer og lager meldinger formatert for Slack.



Overview
Figuren viser en forenklet modell av flyten/interaksjonen mellom de ulike tjenestene (caching layer og cron jobs i App Engine er utelatt).

  1. Request menu: User requests menu for a specific day
  2. Call webhook: The Slack bot is subscribed to specific events. When the bot is notified of such an event, it will trigger a HTTP POST to a specified webhook, with the user’s message in the request body.
  3. Determine intent and call webhook for fulfillment: If the intent of the user’s message is to get the lunch menu for a specific day, Dialogflow will call the GCF webhook for fulfillment, with the resolved parameter for day in the request body.
  4. Fetch HTML and extract menu: The GCF fetches HTML from Eurest’s webpage and extracts the lunch menu for the requested day.
  5. Create Slack message and send response: The extracted lunch menu is then used to build a rich message response for Slack which will then be included in the response to Dialogflow.
  6. Post response: Dialogflow posts the Slack message provided by the GCF to Slack

Slack App

Slack-appen Chef er tilgjengelig i Knowit Objectnet workspace, og har følgende funksjonalitet:

  • Bot (@chef): En bot du kan prate med og spørre om menyen for en hvilken som helst arbeidsdag i gjeldende uke. Boten kan legges til i kanaler og direktemeldingsamtaler.
  • Slash Command (/lunch): Gir deg dagens lunsjmeny
  • Incoming Webhook: Poster dagens lunsjmeny til en gitt kanal (#lunch) kl. 10:00 alle hverdager.


Dialogflow
Dialogflow (tidligere api.ai) tilbyr et web-grensesnitt for å bygge og teste “conversational agents”. Disse er basert på en natural language processing (NLP) engine som bruker en hybridløsning; en kombinasjon av machine learning og forhåndsdefinerte regler for klassifikasjon av brukerinput.

Slack Integration
Dialogflow har et brukergrensesnitt som gjør det enkelt å sette opp integrasjoner mot mange populære eksterne tjenester (bl. a. Slack). Dokumentasjonen tar deg stegvis gjennom prosessen med å opprette en Slack App og sette opp integrasjonen mot Dialogflow. Det er også mulig å få satt opp integrasjoner fra Dialogflow mot tjenester som bruker talestyring (som f. eks. Google Assistant, Alexa og Cortana).

Intent
En intent i Dialogflow er det som mapper brukerens input til handlingen som skal utføres. En intent bygges opp ved å legge inn noen eksempler på brukerinput, og i de fleste tilfeller må også deler av disse tekstene linkes til en eller flere entities. Entities brukes for å ekstrahere parameterverdier fra brukerens input.

sundt-chef er en relativt enkel agent som, med unntak av smalltalk og default fallback intent, kun trenger én intent med én entity. Jeg lagde en MenuForDay Intent og la inn eksempler på brukerinput som spør om lunsjmenyen for en gitt dag. Det eneste parameteret som trengs å trekkes ut fra denne inputen er dato/dag. I og med at dato er et veldig vanlig parameter har Dialogflow en forhåndsbygd entity, en såkalt System Entity, for datoer som kan brukes her. Denne mapper brukerens input, f. eks. “today” eller “the day after tomorrow”, til en dato i ISO-format.

Fulfillment Webhook
Dialogflow agenten er satt opp til å bruke en webhook for “fulfillment” dersom den detekterer en MenuForDay intent. Denne peker på en Google Cloud Function som tar imot requesten fra Dialogflow, og responderer med lunsjmenyen for forespurt dag.

Google Cloud Functions (+App Engine)

Google Cloud Functions er Googles FaaS-løsning som lar utviklere lage “serverless” JavaScript funksjoner som kjører i et Node.js-miljø.

For sundt-chef er det laget funksjoner som brukes som webhooks i Dialogflow- og Slack-integrasjonene. I tillegg til dette, er det også laget serverless API funksjoner som gjør det mulig for andre å hente ut lunsjmenyene i JSON-format.

Data Extraction
Selve menyen hentes fra Eurest sine nettsider (http://sundtkvartalet.eurest.no/ukens-meny/). Dette er server-side rendret HTML, og i og med at det ikke tilbys noen endepunkter for å hente ut menyen i et mer strukturert format, må siden “scrapes” for å hente ut dataen.

Cheerio implementerer et subset av jQuery, og kan brukes for parsing av HTML og DOM-traversering på server-siden. Cheerio gjorde det enkelt å trekke ut lunsjmenyen:

Eksempel på generert JSON:

Caching
Jeg la merke til at menyen til tider var utilgjengelig på nettsiden til Eurest. Nettsiden brukte også av og til lang tid på å respondere. I kombinasjon med en mulig “cold start” av funksjonen på GCP kunne dette føre til at det enkelte ganger tok mer enn 5 sekunder fra requesten ble sendt (fra Dialogflow) til en respons ble mottatt. Jeg tok derfor i bruk en Memcached Cloud bucket hos Redis Labs som resulterte i redusert responstid, høyere tilgjengelighet, og sannsynligvis også jevnere fordelt trafikk mot Eurest sine nettsider.

Cron Jobs
I motsetning til AWS Lambda, har ikke Google Cloud Functions (som fortsatt er i beta) støtte for scheduled tasks/cron jobs. Jeg var derfor nødt til å legge på en liten App Engine shim for å utføre tidsplanlagte handlinger. Jeg valgte å skrive denne i Python fordi det krevde lite “boilerplate” kode.

Denne applikasjonen har to cron jobs:

  • En som oppdaterer cache (hver time i tidsrommet 06:00–18:00, alle hverdager)
  • En som poster lunsjmenyen til en spesifisert Slack-kanal (kl. 10:00, alle hverdager)

Se kode og dokumentasjon på GitHub:
https://github.com/sivapalan/sundt-chef