Opdracht van E-sites

Design+Bouw voor een E-sites klant een Conversational Interface op Google Home of Amazon Echo Dot

Twintig jaar geleden, in ‘97, was er de opkomst van de browser (Click); tien jaar geleden, in‘07, de opkomst van de smartphone (Touch) en nu, in ‘17, de overgang naar deConversation. Waar we sinds de opkomst van de computer een command line of schermals interface gebruiken, gebruiken we steeds vaker conversatie als interface. Waar bedrijvendecennia geleden begonnen met het bouwen van websites, starten ze nu dus met hetbouwen van bots.

De technieken voor AI zullen de komende jaren steeds beter worden en op den duur gaat AIdaarmee steeds cognitiever worden, oftewel realistischer, waardoor gesprekken metapplicaties steeds menselijker worden. Om dit te kunnen bereiken, moet het vermogen vancomputers om op een natuurlijke manier te communiceren bevorderd worden. Deverwachting is dat binnen drie tot vijf jaar de conversaties via AI veel verfijnder zijn. We gaanhiermee dus echt een verandering in informatica zien.

Kies een klant van E-sites in de focusgebieden (sport, health, retail), design eenconversational interface op basis van een Google Home of Amazon Echo en bouw eenwerkend prototype.

Bekijk volledige briefing

Concept

Catherina Ziekenhuis

Als concept hebben wij Sophie bedacht. Sophie staat bij jou thuis nadat je bent opgenomen in het ziekenhuis en is er om jou te helpen. Sophie heeft drie hoofd taken; voeding, planning en instructies (bij bijvoorbeeld oefeningen). Daarnaast kan Sophie natuurlijk ook al jouw vragen beantwoorden. Sophie heeft een eigen persoonlijkheid waardoor ze vermaak kan bieden of een luisterend oor zijn.

Sophie is heel toegankelijk omdat je haar gewoon kan aanspreken. Ze heeft alle informatie die ze nodig heeft om jouw medische dossier in te kijken. Zo kan ze dus ook een terugkoppeling geven over jouw situatie bij de dokter waardoor hij sneller kan reageren of hulp kan bieden zonder dat je zelf naar het ziekenhuis hoeft.

Design & Development

Techniek

Bij deze opdracht was ik meteen enthousiast over het technische gedeelte, dus dit heb ik vrijwel meteen opgepakt. Na de gezamenlijke concepting sessies ben ik gaan onderzoeken wat de mogelijkheden zijn van het apparaatje.

Onderzoek Google Home

Deskresearch (Bieb)

Wij genoten van het voordeel dat er bij Greenhouse Group een aantal afstudeerders zaten die bezig zijn met een Google Home project. Daarvoor heb ik een gesprek ingepland om te peilen hoe de ervaringen met de Google Home tijdens het project tot nu toe zijn geweest. Hier heb ik vooral een korte uitleg gekregen over hoe het API.AI platform werkte en wat je globaal gezien met het apparaat kon doen. Ook heb ik uitleg gekregen over het project waar ze mee bezig waren en hoe ze daarbij het conversational interface gebruiken.

Interview (Veld)

Het deskresearch hield in dat ik de mogelijkheden van het Google Home apparaat moest gaan onderzoeken. Helaas bleek dat het apparaatje nog beperkt is in zijn kunnen. Zo zijn er nog heel veel functies die in Nederland nog niet uitgerold zijn. Deze zijn alleen beschikbaar in Amerika en Engeland. Ook is het apparaat nog Engelstalig. Het apparaatje werkt nauw samen met het ontwikkelplatform API.AI van Google. Omdat wij deze moesten gaan gebruiken in ons project, heb ik hier iets uitgebreider onderzoek naar gedaan en heb me in dit platform verdiept.

API.AI

API.AI is een platform van Google waarmee conversational interfaces gebouwd kunnen worden voor chatbots, applicaties en apparaten.

Zoals te zien is in bovenstaand diagram, zorgt de gebruiker voor input. Deze input geeft de gebruiker via een app, chatbot of apparaat. Dit kan middels het toetsenbord of via spraak. Vervolgens wordt er een query verstuurd naar het API.AI platform. Deze wordt door het platform verwerkt; het platform zoekt naar het meest passende antwoord . Dit kan direct weer terug naar de gebruiker als output, maar de input van de gebruiker kan ook nog opgeslagen worden of gebruikt worden voor een API of een webservice.

Dit is video die ik gebruikt heb als instructie voor API.AI. In deze video wordt een conversational interface gemaakt met het platform. Hierbij laten ze zien welke elementen er te gebruiken zijn binnen het platform en hoe je deze dan ook correct toe kan passen op een conversational interface.

Technisch vraagstuk

Het was technisch nog niet mogelijk om (in Nederland) aan de hand van een conversatie iets te laten verschijnen op bijvoorbeeld een TV. In ons concept kunnen we de patiënt ook visueel helpen, dus hier moest een technische oplossing voor bedacht worden. Hieronder leg ik uit hoe ik dat gedaan heb.

Middelen

Ik heb voor het oplossen van het technisch vraagstuk gebruik gemaakt van een aantal middelen. Hieronder is te lezen wat deze middelen inhouden (korte beschrijving) en uiteindelijk hoe ik met deze middelen een oplossing heb bereikt.

Git is een code versioning systeem. Dit houd in dat je het gebruikt om je programmeer projecten op te slaan en de veranderingen bewaard blijven. Zo kan je per opslag moment (een ‘commit’) veranderingen zien. Voor projecten in groepsvorm of als het grotere projecten zijn ervaar ik het als prettig om GitHub te gebruiken zodat andere groepsleden ook mee kunnen kijken om eventueel te helpen en mijn voortgang te kunnen zien.



Zoals boven in het onderzoek is beschreven is API.AI een platform van google waarmee wij onze conversational mee konden bouwen. We hebben API.AI in dit project ingezet om de conversaties te ontwerpen en er de bijbehorende acties aan te hangen.

Om de berichten met bijbehorende data van API.AI te ontvangen heb ik Heroku gebruikt. Heroku is een service die een applicatie kan draaien op virtuele servers. In de API.AI voorbeelden van Google maken ze zelf ook gebruik van Heroku. Het werkt samen met Git dus dat kwam in dit project goed uit. Heroku support Ruby, Node.js, Python, Java, Go, PHP, en Scala. Je bent dus best wel vrij in wat je het liefste gebruikt. Hieronder zal je lezen dat wij gebruik maken van node.js.

Node.js is een platform dat gebaseerd is op de V8 JavaScript engine van Google. Wat de V8 engine doet is eigenlijk machine-code genereren uit JavaScript code waardoor je dus applicaties kan bouwen met deze engine. Node.js is eigenlijk een back-end variant van JavaScript. Dit platform zorgt voor snelle I/O events zoals web calls, netwerk communicatie, files, etc. Ik heb node.js ingezet voor dit project om de backend te regelen omdat het dus gebaseerd is op javascript dus het was niet een grote moeite om node.js te leren begrijpen.

Om de node.js weer te vertalen naar front-end heb ik gebruik gemaakt van socket.io. Dit is een javascript library voor realtime web applicaties. Dit regelt de communicatie tussen de web clients en de servers. Het heeft dus twee onderdelen; een client-side library die in je browser draait en een server-side library voor node.js. Zo laat je dus de back-end communiceren met de front-end. In dit geval met een html pagina met javascript.

Oplossing

Bovenstaande middelen zijn dus gebruikt om het technische vraagstuk op te lossen. Om te beginnen is er een input nodig die gegeven wordt d.m.v. spraak. Dit vangt de Google Home device op en gaat er d.m.v. API.AI iets mee doen. Door het tijdsgebrek hebben we geen “slimme” conversational interface kunnen bouwen. We hebben aan de hand van bepaalde input een antwoord (output op Google Home in de vorm van spraak) en een actie meegegeven. Hieronder zie je hoe dit in het er in het API.AI platform uit ziet.



Met die actie gaan we verder. Elke keer als API.AI input krijgt, wordt deze ook opgevangen door Heroku. Heroku vangt o.a. een json-bestand op die we weer kunnen gebruiken in de volgende stappen. We gebruiken in ons geval alleen de “action” data, maar er kan natuurlijk veel meer data worden doorgegeven als dit nodig zou zijn.

											
					/******************
					 * SETUP
					 *
					 * Code for the setup of the Katrien Server
					 * Handles custom actions for a Google Home bot, through webhooks to API.ai
					 ******************/
					//test

					// Enabling ES6 support and defining global variables
					(function(globals){
					    "use strict";
					    globals.currentAction = {
					        action: ""
					    };
					}( (1, eval)('this') ));

					// Import Express for routing, etc.
					const express = require('express');
					// Import filesystem 
					const fs = require("fs");
					// Import Path
					const path = require('path');
					// Import BodyParser for parsing data
					const bodyParser = require('body-parser');
					// Import Request for HTTP requests
					const request = require('request');

					const app = express();

					// Import HTTP server
					const http = require('http').Server(app);

					// Import socket.io
					const io = require('socket.io')(http);

					const WatchJS = require("melanke-watchjs")
					var watch = WatchJS.watch;
					var unwatch = WatchJS.unwatch;
					var callWatchers = WatchJS.callWatchers;

					app.use(bodyParser.json());

					app.post('/hook', function (req, res) {

					    console.log('Request from API.ai received');

					    try {   
					        if (req.body && req.body.result) {
					            var body = req.body;

					            if (body.result.fulfillment) {
					                console.log(body.result.fulfillment.speech);
					            }

					            if (body.result.action && currentAction.action != body.result.action) {
					                console.log("Updating action to: " + body.result.action);
					                currentAction.action = body.result.action;
					                res.sendStatus(200);
					            } else {
					                return res.status(400).json({
					                    status: {
					                        code: 400,
					                        failedAction: body.result.action
					                    }
					                });
					            }
					        }


					    } catch (err) {
					        console.error("Can't process request", err);

					        return res.status(400).json({
					            status: {
					                code: 400,
					                errorType: err.message
					            }
					        });
					    }
					});

					io.on('connection', function(socket){
					      console.log('a user connected');

					    //defining a 'watcher' for an attribute
					    watch(currentAction, "action", function(){

					        if(!!currentAction.action) {

					            switch (currentAction.action) {
					                case "intro":
					                    console.log("intro action triggered");
					                    socket.emit('intro', { description: currentAction.action});
					                    break;
					                case "research":
					                    console.log("research action trigggerd!");
					                    socket.emit('research', { description: currentAction.action});
					                    break;  
					                case "result":
					                    console.log("result action trigggerd!");
					                    socket.emit('result', { description: currentAction.action});
					                    break;  
					                case "sassy":
					                    console.log("sassy action trigggerd!");
					                    socket.emit('sassy', { description: currentAction.action});
					                    break;  
					                case "joke":
					                    console.log("joke action trigggerd!");
					                    socket.emit('joke', { description: currentAction.action});
					                    break; 
					                case "benefits":
					                    console.log("benefits action trigggerd!");
					                    socket.emit('benefits', { description: currentAction.action});
					                    break; 
					                case "home":
					                    console.log("home action trigggerd!");
					                    socket.emit('home', { description: currentAction.action});
					                    break; 
					                case "agenda":
					                    console.log("agenda action trigggerd!");
					                    socket.emit('agenda', { description: currentAction.action});
					                    break; 
					                case "file":
					                    console.log("file action trigggerd!");
					                    socket.emit('file', { description: currentAction.action});
					                    break; 
					                case "fysio":
					                    console.log("fysio action trigggerd!");
					                    socket.emit('fysio', { description: currentAction.action});
					                    break;
					                case "recepten":
					                    console.log("recepten action trigggerd!");
					                    socket.emit('recepten', { description: currentAction.action});
					                    break;  
					                case "spongebob":
					                    console.log("spongebob action trigggerd!");
					                    socket.emit('spongebob', { description: currentAction.action});
					                    break;  
					                case "conclusion":
					                    console.log("conclusion action trigggerd!");
					                    socket.emit('conclusion', { description: currentAction.action});
					                    break;  


					                case "cookie":
					                    console.log("cookie action trigggerd!");
					                    socket.emit('cookie', { description: currentAction.action});
					                    break;
					                case "LookAtMenu":
					                    console.log("look at menu action triggered");
					                    socket.emit('menu', { description: currentAction.action});
					                    break;
					                
					                
					                default: 
					                    console.log(currentAction.action);
					            }
					        }
					    });

					    });

					// Defining a route handler / that gets called when we hit our website home.
					app.get('/', function(req, res){
					    res.sendFile(__dirname + '/frontend/index.html');
					});



					http.listen((process.env.PORT || 5000), function () {
					    console.log("Server started");
					});


							

									

Via Heroku krijg ook node.js de data door en kan hier iets mee gaan doen. Via node.js kijk ik welke actie er doorkomt en deze stuur ik met socket.io naar mijn front-end. Hierboven zie je de code die ik daarvoor geschreven heb. De data komt, tegelijk met het spraakbericht die je op het Google Home apparaat hoort, als output in een browser. De output die op het scherm te zien is, is een simpel div element waar de content steeds in veranderd met javascript. Hieronder is een globaal schema te zien hoe de communicatie verloopt.

Het eindresultaat is te zien op http://katrien.herokuapp.com/. Er zijn dus echter wel calls nodig vanuit het Google Home apparaat om de applicatie ook daadwerkelijk in werking te zien. In het filmpje van de presentatie een stukje later op deze pagina zie je mijn oplossing in werking.



Presentatie

Presentatie

De presentatie van Sophie was een kleine uitdaging. We kregen opdracht van E-sites om ons concept met maximaal 3 slides te presenteren. Ik was toen al bezig met het aansturen van beeld d.m.v. commando’s van de Google Home. We hebben hiervoor dus een creatieve oplossing bedacht en besloten de presentatie GEEN slides te geven en alles via een browser te presenteren. Dit was wel tricky aangezien alles wel af zou hangen van de internetverbinding. We hebben met Sophie de presentatie geoefend zodat ze zelf kon vertellen wat ze allemaal kon doen en wat de voordelen hiervan waren. Hieronder is de presentatie te zien.

De presentaties liepen niet helemaal zoals gepland, omdat de apparaten niet helemaal mee wilde werken maar uiteindelijk is de presentatie toch goed gelopen. Zo goed zelfs dat we deze opdracht hebben gewonnen!