Über unsMediaKontaktImpressum
Werner Eberling 29. Januar 2019

Serverless mit Alexa – Skills mit AWS Lambda entwickeln

Mit der Bereitstellung selbstgeschriebener Skills für digitale Assistenten hat sich in der letzten Zeit ein neues, interessantes Anwendungsfeld für cloud-native Entwicklung ergeben. Dieser Artikel zeigt, wie ein einfacher Alexa Skill sowohl per Web Console, als auch lokal per Development Kits implementiert werden kann. Das Zusammenspiel mit AWS Lambda ermöglicht dabei einen ersten Kontakt und eine einfache Einführung in die Welt der Serverless-Programmierung innerhalb der AWS Cloud. Die Beispiele, aus dem die Code-Beispiele dieses Artikels entnommen sind, sind auf github verfügbar [1,2].

Digitale Assistenten sind aus so manchem Haushalt nicht mehr wegzudenken. Gerade Amazon ist hier, u. a. durch eine entsprechend aggressive Vermarktungspolitik, ganz vorne mit dabei. "Alexa erinnere mich um 14 Uhr an…!", "Alexa, setze Milch auf die Einkaufsliste!", "Alexa wie spät ist es?", selbst die einfachsten Tätigkeiten werden inzwischen per Sprachkommando erledigt. Unabhängig davon, wie man der ständigen Möglichkeit zur Aufnahme und automatischen Verarbeitung von Sprachkommandos, u. a. aus datenschutzrechtlicher Sicht, gegenübersteht, so ist dies doch aus technischer Sicht ein sehr interessantes, neues Anwendungsfeld. Und verspielt, wie Softwareentwickler nun einmal sind, hat sicher auch der Kauf und Einsatz der entsprechenden Hardware seinen Reiz. Natürlich nur um die eigene Implementierung auch im Einsatz testen zu können.

Die Technik

Grundsätzlich zerfällt die Abarbeitung einer Anfrage in zwei inhaltlich unterschiedliche und auch technisch getrennte Aufgaben:

  1. Die Entgegennahme und Erkennung des Sprachkommandos, sowie die sprachliche Antwort auf die Anfrage.
  2. Die Abarbeitung der dem Kommando zugeordneten fachlichen Logik und die Berechnung/Lieferung des entsprechenden Ergebnisses.

Die zuerst genannte Aufgabe wird bei Amazons Alexa von den sogenannten Skills übernommen. Diese Skills sind Anwendungen in der Amazon Cloud, die per Sprachkommando aktiviert werden und denen über vorher trainierte Sätze auch Daten übergeben werden können. Nach der Erkennung eines Kommandos und der Extraktion der darin evtl. enthaltenen Daten wird die eigentliche Verarbeitungslogik angestoßen. Diese ist aus Sicht des Skills lediglich ein externer Service, der entweder als http-Endpunkt oder eben als AWS-Lambda-Funktion bereitgestellt werden kann. Da der Skill selbst bereits in der Cloud zuhause ist, bietet sich die Entwicklung als Serverless-Funktion mit AWS Lambda natürlich an. Hinzu kommt, dass ein Skill nun einmal die meiste Zeit inaktiv ist. Ganz nach der Philosophie "Don’t pay for idle!" ist hier die Implementierung als Lambda in der Cloud die erste Wahl.

Die in diesem Artikel vorgestellte Kombination von Amazons Alexa und AWS Lambda gibt es natürlich auch für die anderen gängigen Sprachassistenten. Ohne besondere Gründe wurde für diesen Artikel der Amazon Stack gewählt. So ist der geneigte Leser eingeladen, einen vergleichbaren Versuch auch einmal mit einem der alternativen Stacks (z. B. von Google oder Microsoft) zu unternehmen.

Die Voraussetzungen

Bevor es losgehen kann, werden erst einmal zwei voneinander unabhängige Zugänge in die Welt der Amazon Cloud benötigt.

Um den eigentlichen Skill zu entwickeln, ist ein Zugang zu den Amazon Developer Services notwendig [3]. Geräte, die mit diesem Account verbunden sind, können direkt zum Test der selbst entwickelten Skills verwendet werden. Aber auch wenn kein solches Gerät vorhanden ist, kann der erstellte Skill in der Amazon Developer Console getestet werden [4]. Der Spaßfaktor, dies direkt per Spracheingabe am eigenen Echo-Endgerät zu tun, ist aber natürlich nicht zu vernachlässigen.

Soll der Endpunkt zur Verarbeitung der Anfrage als AWS Lambda implementiert werden, wird zusätzlich ein AWS Developer Account benötigt [5]. Auch wenn während der Einrichtung Zahlungsdaten in Form einer Kreditkarte abgefragt werden, bleiben die Services für die ersten zwölf Monate – im Rahmen gewisser Nutzungsmengen – kostenfrei. Zum Entstehungszeitpunkt dieses Artikels ist die Nutzung von Serverless-Lambda-Funktionen darüber hinaus für die ersten eine Millionen Aufrufe pro Monat kostenfrei [6].

Entwicklung über die Amazon Console(n)

Für die ersten Gehversuche mit der Entwicklung von Alexa Skills bietet sich die direkte Verwendung der hierfür angebotenen Web Console an [4]. Dadurch ist keinerlei Software-Installation notwendig, wodurch die Einstiegshürde schon einmal kräftig gesenkt wird. Nach dem Login über den Amazon Developer Account wird hier eine Liste der bereits erstellten Alexa Skills angezeigt.

Über den Button Create Skill öffnet sich ein Wizard zur Anlage eines neuen Alexa Skills. Im Rahmen dieses Artikels soll es nun ein sogenanntes "Custom Skill" werden. Diese Option ist bereits vorbelegt, so dass lediglich ein Name und die Sprache gewählt werden muss, für den der Skill verfügbar sein soll. Die Wahl der Sprache ist dabei sowohl für die Spracherkennung, als auch für die Sprachsynthese im Rahmen der Antwort wichtig. Grundsätzlich können Skills für mehrere Sprachen angeboten werden, hier soll das Angebot in Deutsch ausreichen.

Nach der initialen Erzeugung des Skills wird die Übersichtsseite über die einzelnen Bestandteile angezeigt. Um den Skill ansprechen zu können, muss zuerst einmal ein Aufruf ("Invocation") definiert werden. Über diesen Aufruf wird der Skill gestartet und ihm ggf. auch direkt Daten übergeben. Der Name muss dabei nicht zwingend eindeutig über alle Alexa Skills sein, sollte aber zur besseren Erkennung zumindest aus zwei Wörtern bestehen. Reservierte Wörter, wie die Aufrufkommandos für Alexa selbst (z. B. Alexa, Computer oder Echo) oder sogenannte "Launch Phrases" (z. B. sage, frage oder starte) sind dabei nicht möglich. Um den Skill später über das Kommando "Alexa, frage Geo Besserwisser…" zu starten, muss also hier der Aufrufname "geo besserwisser" vergeben werden. Hierbei ist zu beachten, dass Aufrufnamen – auch bei deutschen Skills – in Kleinbuchstaben definiert werden.

Nach der Ansprache eines Skills muss der Aufrufer diesem mitteilen, was er von ihm möchte. Dies geschieht über die Definition sogenannter "Intents" (Absicht, Zweck). Diese Intents werden über vordefinierte Sätze (Amazon nennt diese "Utterances" (Äußerungen)) aktiviert und können Datenvariablen, sogenannte "Slots", enthalten. Für den Geo Besserwisser muss nun ein neuer Intent mit Namen CapitalIntent erzeugt werden. Nach dessen Anlage gilt es, mehrere Beispielsätze zu definieren, mit denen der neue Intent angesprochen werden soll. Dabei werden Datenvariablen in geschweiften Klammern angegeben. Jeder dieser Variablen (Slots) muss ein Typ zugewiesen werden, der sich auf die Spracherkennung und -verarbeitung auswirkt. Für den Slot country bietet sich hier der Typ AMAZON.country an.

  • "wie heißt die Hauptstadt von {country}"
  • "was ist die Hauptstadt von {country}"
  • "nach der Hauptstadt von {country}"

Bei der Definition der Beispielsätze sollte der Gesamtaufruf "Alexa, frage Geo Besserwisser nach der Hauptstadt von Frankreich" im Auge behalten werden.

Für den Aufruf des Skills ist nun alles vorhanden. Es fehlt lediglich noch die Verarbeitungslogik für die Anfrage. Diese wird in Form eines Service Endpoints hinterlegt, der über einen HTTP-POST Request angesprochen wird. Grundsätzlich könnte hier nun auch ein selbst betriebener REST WebService angegeben werden. Im Rahmen dieses Artikels soll aber eine AWS-Lambda-Funktion für die Abarbeitung der Anfrage genutzt werden.

Die Funktion hinter dem Skill

Hier entsteht bei der Entwicklung über die Web Console ein Bruch innerhalb des Toolings, da der Service-Endpunkt aus Sicht des Skills eine externe REST-Ressource darstellt. Daher ist dieser Teil nicht mehr über die Alexa Developer Console abgedeckt. Um die Verarbeitungslogik nun per AWS Lambda zu implementieren, ist der Wechsel in die AWS Console [7] und dort der Login mit den Daten eines AWS Developer Accounts notwendig.

Nach der Auswahl der Lambda Services und der Wahl des bevorzugten Standorts, kann per Wizard eine neue Funktion erstellt werden. Für die Vereinfachung der Implementierung und die initiale Einbindung der notwendigen Module bietet sich die Verwendung einer entsprechenden Vorlage (z. B. alexa-skill-kit-sdk-factskill) an [8].

Bei der Erzeugung der Funktion muss neben dem Namen eine Berechtigungsrolle vergeben werden, unter der die Funktion ausgeführt werden soll. Für die Anbindung an einen Alexa Skill wird hier eine Rolle mit "Einfachen Microservice Berechtigungen" empfohlen, die aus einer entsprechenden Vorlage erzeugt und direkt angewendet werden kann.

Nach der Erstellung der Funktion muss zuerst ein Auslöser definiert werden, über den die Lambda-Funktion aufgerufen werden soll. Für die Bereitstellung als Endpoint für einen Alexa Skill wird hier ein Auslöser von Typ "Alexa Skill Kit" angeboten. Dieser kann und sollte mit einer aktiven "Qualifikations_ID-Verifizierung" konfiguriert werden. Diese Verifizierung erhält die APP-ID des Alexa Skills, der die Funktion nutzen soll. Diese wird vom Skill beim Aufruf mit übergeben, um sicherzustellen, dass nur der für den Aufruf qualifizierte Skill diese Funktion nutzt. Die APP-ID eines Skills kann in der Endpoint-Konfiguration des Skills innerhalb der Alexa Developer Console nachgeschlagen werden.

Nach der Konfiguration des Auslösers, kann die Funktion implementiert werden. Der Code der Funktion wird in Listing 1 dargestellt und kann nach Auswahl der Funktion im Bereich Funktionscode für die Datei index.js eingegeben werden. Zusätzlich muss noch die APP-ID des Skills im Code gesetzt werden.

Listing 1: Einfache Implementierung der JavaScript-Funktion für den Skill-Endpoint


const Alexa = require('alexa-sdk');

const capitals = new Map([
    ['deutschland', 'berlin'], 
    ['england', 'london'], 
    ['frankreich', 'paris']
    ]);

const handlers = {
    // Funktion fuer die Verarbeitung des CapitalIntents
    'CapitalIntent': function () {
        const country = this.event.request.intent.slots.country.value;
        const capital = capitals.get(country);
        const response = capital ?
            'die hauptstadt von '+ country +' ist ' + capitals.get(country) :
            'die hauptstadt von '+ country +' kenne ich leider nicht';
        this.response.speak(response);
        this.emit(':responseReady');
    },
    // Alle anderen Intents landen hier
    'Unhandled': function () {
        this.response.speak('Da fehlt wohl etwas');
        this.emit(':responseReady');
    },
};

exports.handler = function (event, context, callback) {
    const alexa = Alexa.handler(event, context, callback);
    alexa.APP_ID = '<APP-ID hier einfuegen>';  // hier APP-ID einfuegen
    alexa.registerHandlers(handlers);
    alexa.execute();
};

Nach dem Import des Alexa SDKs und der Initialisierung der Datenbasis (aus Übersichtsgründen auf drei Länder begrenzt) werden die Handler für die Skill Intents definiert. Im aktuellen Beispiel wird mit dem CapitalIntent nur ein Intent explizit unterstützt. Alle anderen Anfragen werden mit Hilfe des ‚Unhandled‘ Handlers behandelt.

Wird dieser Aufruf zur Ermittlung einer Hauptstadt gesendet, wird der Inhalt des Slots country ausgelesen, die entsprechende Hauptstadt aus dem Datenbestand ermittelt und eine entsprechende Antwort generiert. Die Ausgabe der Antwort kann direkt über die Methode speak() des Response-Objektes ausgelöst werden. Die Auslösung des Events :responseReady zeigt dem SDK, dass die Antwort vollständig ist und zurückgegeben werden kann.

Wurde die Lambda-Funktion erfolgreich gespeichert, kann zurück in die Alexa Developer Console gewechselt werden. Hier muss in der Endpoint Definition als letzter Schritt noch den Amazon Resource Name (ARN) des Lambdas eingetragen werden. Ist dies geschehen, kann der Skill gebaut werden.

Den Skill testen

Sobald alle Haken in der Skill Build List grün sind, kann in die Testansicht der Konsole gewechselt werden. Hier muss als erstes das interaktive Testing des Skills aktiviert werden. Mit dem Alexa-Simulator kann hierzu eine Anfrage direkt eingegeben werden: "frage geo besserwisser nach der Hauptstadt von Frankreich". Alternativ kann, wenn das Mikrofon für den Browser freigegeben wurde, die Anfrage auch gesprochen werden. Hierzu muss das Mikrofonsymbol gedrückt und während des Sprechvorgangs gehalten werden.

Läuft während des Tests etwas nicht wie erwartet, wird Alex dies mitteilen ("Bei der Antwort des Skills ist ein Fehler aufgetreten"). Da es sich hierbei i. d. R. um einen Fehler der Lambda-Funktion handelt, können Details zu diesem Fehler in den Cloud Watch Logs der AWS Developer Console nachgeschlagen werden.

Ist der Skill erfolgreich im Alexa-Simulator testbar, so kann er auch über jedes im Alexa Developer Account registrierte Endgerät getestet werden. Selbst entwickelte Custom Skills werden automatisch auf alle im Account bekannten Geräte aufgespielt. Hierzu muss allerdings das Aktivierungswort vorangestellt werden ("Alexa, frage geo besserwisser nach der Hauptstadt von Frankreich!").

Lokale Entwicklung mit Alexa Skill Kit CLI

Auch wenn für die ersten Schritte die Entwicklung über die Web Console(n) zu schnellen Erfolgen führt, für ein realistisches Entwicklungsszenario muss dies natürlich über die lokale Umgebung erfolgen. Hierzu stehen mit dem Alexa Skill Kit (ASK) CLI (Command Line Interface) [9] und dem AWS CLI [10] zwei lokale Entwicklungskits zur Verfügung. Die notwendigen Installationen für beide Toolkits sind auf den entsprechenden Webseiten beschrieben [9,10]. Bei der Installation des AWS CLIs ist darauf zu achten, auch die Entwickler Credentials zu generieren und lokal auf dem Entwicklungsrechner zu speichern.

Nach erfolgreicher Installation muss das Alexa Toolkit noch mit den Zugangsdaten des AWS CLI initialisiert werden. Nach dem Aufruf von ask init kann hierzu ein auf dem Rechner konfiguriertes AWS-Konto ausgewählt werden.

Wurde das ASK CLI korrekt eingerichtet, kann mit dem Aufruf ask new -n capitalSkillCLI ein neuer Skill inkl. zugehöriger Lambda-Funktion angelegt werden. Das Ergebnis ist die in Abbildung 8 dargestellte Ordnerstruktur.

Der eigentliche Skill wird über die JSON-Datei skill.json im Wurzelverzeichnis beschrieben. Hier können Skillname und weitere Metainformationen, wie Beispielaufrufe und Beschreibungen, hinterlegt werden. Auch die Ablageorte der verfügbaren Interaktionsmodelle und die als Endpunkt zu verwendende AWS Lambda Funktion müssen hier aufgeführt werden.

Das initiale Interaktionsmodell ist in models/en-US.json abgelegt. Dieses enthält alle Bestandteile, die im vorhergehenden Abschnitt über das Web Interface eingegeben wurden. Die einzige Ausnahme bildet die Definition des Endpunkts auf den der Skill zugreift. Da dieser durch die Lambda-Funktion im Ordner lambda/custom desselben Projektes implementiert wird, wird die Endpunkt-Konfiguration automatisch mit allen notwendigen Konfigurationen beim Deployment erzeugt. Die Verknüpfung von Skill und Lambda ist dabei in der Skillbeschreibung (Skill.json) hinterlegt.

Bevor der Skill deployed werden kann, muss aber erst noch das bestehende Template durch die konkrete Implementierung des Skills ersetzt werden. In diesem Fall soll es ein englischsprachiger Skill werden, auch um beide Skills parallel deployen zu können.

Listing 2: Englischsprachiges Skill-Model im JSON-Format

{
    "interactionModel": {
        "languageModel": {
            "invocationName": "geo know it all",
            "intents": [
                {
                    "name": "AMAZON.FallbackIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.NavigateHomeIntent",
                    "samples": []
                },
                {
                    "name": "CapitalIntent",
                    "slots": [
                        {
                            "name": "country",
                            "type": "AMAZON.Country"
                        }
                    ],
                    "samples": [
                        "what ist he capital of {country} ",
                        "about the capital of {country} ",
                        "what is the name of the capital of {country} "
                    ]
                }
            ],
            "types": []
        }
    }
}

 

Die Abarbeitungslogik der Anfrage wird wieder als AWS Lambda implementiert. Hierzu muss der initial generierte HelloWorld-Code der Datei lambda/custom/index.js durch die Logik des Skills ersetzt werden. Diese entspricht der Variante aus dem ersten Abschnitt. Diesmal wird allerdings die neue Version 2 des Alexa SDKs verwendet [11].

Listing 3: Fachliche Logik des CLI-Skills als Lambda-Endpunkt


const Alexa = require('ask-sdk-core');

const capitals = new Map([
  ['germany', 'berlin'], 
  ['england', 'london'], 
  ['france', 'paris']
  ]);

const CapitalIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'CapitalIntent';
  },
  handle(handlerInput) {
    const country = handlerInput.requestEnvelope.request.intent.slots.country.value;
    const speechText = country ? 
                        'the capital of '+ country +' is ' + 
capitals.get(capitals.get(country.toLowerCase())) :
                        'i don\'t know the capital of '+ country ;
    return handlerInput.responseBuilder
      .speak(speechText)
      .getResponse();
  },
};

const UnhandledHandler = {
  canHandle(handlerInput) {
    return true;
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
    .speak('I don\'t no what to do with this')
    .getResponse();
  },
};

const skillBuilder = Alexa.SkillBuilders.custom();

exports.handler = skillBuilder
  .addRequestHandlers(
    CapitalIntentHandler,
    UnhandledHandler
  )
  .lambda();

Nun ist alles bereit, um sowohl Skill als auch Funktion in die Amazon Cloud zu deployen. Dies geschieht über den Aufruf ask deploy. Dadurch wird der Skill im Rahmen der Amazon Developer Services und die Lambda-Funktion in der Umgebung der AWS Services bereitgestellt. Da für das AWS Deployment keine spezielle Region angegeben wurde, wird die Funktion in der US-Ostküste (N.Virginia oder Ohio) deployed.

War das Deployment erfolgreich, wird dies durch die folgenden Konsolenausgaben quittiert:

-------------------- Create Skill Project --------------------
Profile for the deployment: [default]
Skill Id: amzn1.ask.skill.<SKILL-ID>
Skill deployment finished.
Model deployment finished.
Lambda deployment finished.
Lambda function(s) created:
  [Lambda ARN] arn:aws:lambda:us-east-1:<ID>:function:ask-custom-capitalSkillCLI-default
Your skill is now deployed and enabled in the development stage. Try invoking the skill via the "ask simulate" command.

Sowohl der hochgeladene Skill als auch die Lambda-Funktion sind nun in den bereits bekannten Web-Konsolen verfügbar und könnten über den Alexa-Simulator oder direkt per Endgerät getestet werden. Die Ausgabe des Deploy-Jobs schlägt aber bereits die CLI-Variante des Tests vor. Das Kommando ask simulate ermöglicht den Test des Skills (inkl. des Durchgriffs auf die Lambda-Funktion) über die Kommandozeile. Dabei muss der gesprochene Text und die Lokale, für die der Aufruf erfolgen soll, angegeben werden:

ask simulate -t "ask geo know it all about the capital of germany" -l en-US

Eine erfolgreiche Antwort enthält u. a. die generierte Sprachausgabe in Form eines SSML Strings:

…
"response": {
            "outputSpeech": {
              "type": "SSML",
              "ssml": "<speak>the capital of Germany is berlin</speak>"
            }
          },
…

Auch dieser Skill ist natürlich wieder automatisch auf allen im Amazon Developer Account registrierten Endgeräten verfügbar. Für den Test muss das Gerät aber die entsprechende Lokale unterstützen.

Resümee

Die Entwicklung von Skills für Alexa zeigt sich als sehr einsteigerfreundlich. Gleichzeitig stellt diese Art von Anwendung ein perfektes Szenario für die Serverless-Entwicklung dar. Natürlich ist dabei auch das spielerische Moment nicht zu unterschätzen. Aber gerade durch die gute und einfache Integration der Lambda-Funktionen in die Welt der Alexa Skills werden gerade dem AWS-Neuling viele Hürden aus dem Weg geräumt (Konfiguration des API.Gateways, Access Management, …). Gleichzeitig stehen der Implementierung nun alle Technologien des AWS-Kosmos zur Verfügung, was der Phantasie bei der Skill-Entwicklung viel Freiraum lässt.

Weitere Schritte bei der Entwicklung von Skills wären der Einsatz einer User-Session, die eine weiterführende Interaktion mit dem User zulässt. Auch die Unterstützung von Anzeigen, Audio- oder Video-Streams als Rückgabe sind mögliche nächste Evolutionsstufen des eigenen Skills.

Unabhängig davon, welcher Weg durch die Welt der Skills dem geneigten Leser am meisten zusagt, der Ansatz ist eine schöne Gelegenheit, sich mit cloud-native Entwicklung im Allgemeinen und Serverless-Entwicklung im Speziellen auseinanderzusetzen und die dahinter liegenden Technologien kennenzulernen. Und was beim eigenen Alexa Skill gut funktioniert, mag ja auch eines Tages bei der Entwicklung anderer Anwendungen hilfreiche Dienste leisten.

Daher: Ran an die Tasten und selbst ausprobieren!

Autor
© Werner Eberling

Werner Eberling

Werner Eberling ist als Principal Consultant und Trainer für die MATHEMA Software GmbH tätig. Seit 1999 arbeitet er im Bereich verteilter Systeme auf der Basis von CORBA und der Java EE.
>> Weiterlesen
botMessage_toctoc_comments_9210