Angenommen ich möchte einen Alexa Skill entwickeln
Wenn ich die Dialoge mit Alexa vorab als ausführbare Akzeptanztests ablegen könnte
Dann wäre ein großer Schritt zu hochwertigen Skills getan
Behavior-Driven Alexa Skill Development mit cucumber.js
Motiviation
Eigentlich hatte ich diesen Blog-Artikel gar nicht geplant. Mich interessierte die Nutzerinteraktion von Anwendern mit Voice-UIs und ich wollte mich diesem Thema mit einem einfachen Alexa Skill nähern. Nun habe ich an vielen Stellen gelesen, wie einfach es doch ist, einen eigenen Skill zu schreiben. Allerdings sind mir die ersten Schritte dann leider doch recht schwer gefallen, also habe ich beschlossen meine Erfahrungen damit in einem Blog-Artikel zusammen zu fassen.
Schritt für Schritt kam dann der Wunsch nach Tests auf. Eigentlich wollte ich ja nur was ausprobieren und auf Tests komplett verzichten. Warum sich das für mich nicht bewährt hat erkläre ich im weiteren Verlauf.
Im ersten Teil dieser Blogserie beschreibe ich das initiale Skill-Setup.
Die initialen Anforderungen
- Meine initialen Anforderungen an die Skill-Entwicklung waren:
Es sollte über ein einfaches “Hello World”-Beispiel hinausgehen. Für mich hält sich der Lerneffekt bei extrem vereinfachten Beispielen doch sehr in Grenzen. Das Thema ist nunmal komplex und diese Komplexität wollte ich erfahren und nicht ausblenden. Also habe ich mir als Thema einen Skill vorgenommen, der das lästige Punktenotieren bei einem Würfelspiel übernimmt. - Also erwartete ich auch mehr als nur ein paar Zeilen Code. Diesen wollte ich in einem Git Repository sichern.
- Auf Programmierebene wollte ich möglichst viel Vertrautes einsetzen, einfach damit ich das Maximale über Skill-Entwicklung lerne. Das hieß für mich: modernen JavaScript-Code schreiben – inkl. Features wie async / await oder einfache imports.
Setup des Skills
Ich habe mir das erstbeste Tutorial geschnappt um einfach mal loszulegen. Diesen Teil werde ich hier recht knapp halten. Dafür gibt es bereits gute Anleitungen im Netz (u.a. auch hier im codecentric Blog ). Da sich das Vorgehen aber leicht geändert hat, gebe ich zumindest einen kurzen Überblick.
Für die Skill-Entwicklung müssen wir mindestens zwei Artefakte erstellen:
- Das erste ist das Voice Interaction Model. In diesem Modell definieren wir, welche Befehle (Intents) ein Anwender in unserem Skill triggern kann und welche Spracheingaben (Utterances) welchen Intent auslösen.
- Das zweite ist der Code der genau diese definierten Intents behandelt. Der also die Anfragen entgegennimmt, verarbeitet und (üblicherweise) mindestens mit einer Sprachausgabe antwortet. Dieser Code kann theoretisch als ein beliebiger Webservice angebunden werden, Amazon macht es einem aber einfacher, wenn man dafür eine Lambda-Funktion in der AWS-Cloud anlegt (so gehen wir hier vor).
Das Voice Interaction Model
Also unter: https://developer.amazon.com/alexa/console/ask einen neuen Skill anlegen, wofür zunächst ein Amazon Developer Account benötigt wird.
Da ich für diesen Blog-Eintrag einen “normalen” Skill erstellen wollte (kein Briefing, Smart Home oder Video Skill) muss als Model “Custom” gewählt werden. Zudem wird es für diesen Blog-Eintrag ein Skill in deutscher Sprache werden.
Ist der Skill angelegt, werden wir von der Alexa Developer Console begrüßt. Dies ist die Seite in der das Sprachinterface (Voice Interaction Model) erstellt wird. Das Voice Interacation Model definiert, wie Anwender mit unserem Skill interagieren können, also welche ausgesprochenen Sätze erkannt und verarbeitet werden.
Auf der rechten Seite ist Amazon so hilfreich uns eine Checkliste anzubieten. Um den Skill lauffähig zu bekommen müssen wir also folgendes tun:
- einen Invocation Name festlegen: der Name, den ein Anwender nennt um unseren Skill zu starten. In unserem Beispiel: Alexa, starte Fünferpasch (also ist der Invocation Name: “Fünferpasch”.
- Intents, Samples and Slots definieren: die einzelnen Befehle die unser Skill entgegennimmt und wie der Anwender sie aufschreibt.
- Das Modell abspeichern und bauen.
- Den Endpoint definieren: der Endpoint ist der Code der aufgerufen wird um die Befehle zu verarbeiten. Im Allgemeinen eine AWS-Lambda-Funktion, obwohl auch ein anderer Webservice möglich ist.
Der Invocation Name ist recht schnell festgelegt. Als nächstes folgt der erste Intent für den Skill. Für das Würfelspiel ist der erste Schritt, dass man ein Spiel mit x Spielern starten kann. Also benötigen wir einen Intent zum Start eines Spieles.
Unter Intents:
wird nun ein neuer Intent “starteSpiel” angelegt. Unter “Sample Utterances” kann man Phrasen eintragen, die der Anwender sagen kann um diesen Intent auszulösen.
Innerhalb einer solchen Utterance kann man sogenannte Slots verwenden, als Platzhalter für variable Benutzereingaben.
Für das Würfelspiel starten wir mit einer einzigen Utterance:
"Starte ein Spiel mit {spieleranzahl} Mitspielern"
Die geschweiften Klammern kennzeichnen den Slot “spieleranzahl” dem im unteren Bereich des Bildschirms der Slot Typ “AMAZON.number” zugeordnet wird. Im Resultat kann ein Anwender nun den Intent mit einer beliebigen Anzahl an Mitspielern aufrufen.
Wir könnten noch weitere Utterances ergänzen, oder Dialoge definieren mit denen Alexa fehlende Slots vom Anwender erfragen kann, aber an dieser Stelle wollen wir nur einen ersten Durchstich bis zur Implementierung der Lambda erzeugen.
Das Modell kann nun gespeichert und gebaut werden.
Setup der Lambda
Um die Intents verwenden zu können, muss nun ein Service angelegt werden, der die Anfragen dazu verarbeitet.
Verknüpft werden der Skill und die Lambda-Funktion unter dem Navigationseintrag “Endpoint”. Hier können wir momentan noch nicht die Lambda eintragen (die müssen wir ja erst anlegen), aber dort erfahren wir bereits die ID unseres Skill. Diese können wir im späteren Verlauf verwenden um den Zugriff auf unsere Lambda nur auf diesen einen Skill zu begrenzen.
Die neue Lambda wird angelegt aus der Lambda Management Console unter:https://eu-west-1.console.aws.amazon.com/lambda/home?region=eu-west-1#/functions
Für einen Alexa Skill in Deutschland muss die AWS Region (oben rechts) auf EU (Ireland) gestellt werden. Hier wählen wir “Funktion erstellen”.
Auf der folgenden Seite können wir auswählen ob wir den Skill von Grund auf neu bauen, eine Vorlage verwenden oder uns im AW Serverless Application Repository bedienen.
Hier habe ich schon den ersten Fehler gemacht und unter „Vorlagen“ eine Vorlage für einen Alexa Skill verwendet. Das ist aber sooo 2018. Damit erstellt man eine Lambda auf Basis des (natürlich völlig veralteten) Alexa-SDK. Wir wollen aber das aktuelle Alexa Skills Kit SDK for Node.js (kur ASK-SDK v2) verwenden. Dazu wählen wir das AWS Serverless Application Repository und verwenden dort die Applikation “alexa-skills-kit-nodejs-factskill” als Vorlage:
Auf dem folgenden Bildschirm vergeben wir noch einen Namen für die neue AWS-Anwendungen und schließen den Vorgang mit einem Klick auf Deploy ab.
Der Vorgang kann einige Zeit dauern. Aber die aktualisierte Vorlage legt automatisch auch eine Rolle mit Berechtigungen für die Ausführung der Lambda-Funktion an und erzeugt diese direkt für die Laufzeitumgebung node 8 (statt node 6). Wenn alles gut geht sieht der Bildschirm anschließend so aus:
Über den Button “Test App” kommen wir in die Anwendungsübersicht und von dort unter Ressourcen direkt zu unserem neuen Skill.
Im Abschnitt Konfiguration > Designer ist bereits ein Auslöser (Trigger) für das Alexa Skills Kit eingetragen. Unsere neue Lambda-Funktion kann also durch einen Alexa Skill ausgelöst werden. Hier könnten wir auch die Skill-Id aus dem oberen Abschnitt hinterlegen um Zugriffe nur von diesem einen Skill zu erlauben.
Wählen wir im Designer den Wurzelknoten für unsere Lambda aus (das ist bereits der Default wenn man die Seite öffnet), erscheint im unteren Bereich der Funktionscode unserer Lambda.
Der Wizard hat uns dort einige Handler angelegt, leider für Intents die wir für unseren Skill gar nicht benötigen. Diesen Code könnten wir nun online in einer rudimentären IDE bearbeiten.
Da wir aber mit git und unserer lokalen IDE arbeiten wollen, werden wir dies nun im nächsten Schritt ändern. Um aber einmal den Skill und die Lambda im Verbund testen zu können ersetzen wir nun den Code für die Index.js mit folgendem Code:
1const Alexa = require('ask-sdk');
2
3const LaunchRequestHandler = {
4 canHandle(handlerInput) {
5 const request = handlerInput.requestEnvelope.request;
6 return request.type === 'LaunchRequest';
7 },
8 handle(handlerInput) {
9 const speech = 'Willkommen bei Fünferpasch';
10 return handlerInput.responseBuilder
11 .speak(speech)
12 .withSimpleCard(SKILL_NAME, speech)
13 .getResponse();
14 },
15 };
16
17const HelpHandler = {
18 canHandle(handlerInput) {
19 const request = handlerInput.requestEnvelope.request;
20 return request.type === 'IntentRequest'
21 && request.intent.name === 'AMAZON.HelpIntent';
22 },
23 handle(handlerInput) {
24 return handlerInput.responseBuilder
25 .speak('Hilfe kommt')
26 .getResponse();
27 },
28};
29
30const ExitHandler = {
31 canHandle(handlerInput) {
32 const request = handlerInput.requestEnvelope.request;
33 return request.type === 'IntentRequest'
34 && (request.intent.name === 'AMAZON.CancelIntent'
35 || request.intent.name === 'AMAZON.StopIntent');
36 },
37 handle(handlerInput) {
38 return handlerInput.responseBuilder
39 .speak('Tschüss')
40 .getResponse();
41 },
42};
43
44const SessionEndedRequestHandler = {
45 canHandle(handlerInput) {
46 const request = handlerInput.requestEnvelope.request;
47 return request.type === 'SessionEndedRequest';
48 },
49 handle(handlerInput) {
50 console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);
51 return handlerInput.responseBuilder.getResponse();
52 },
53};
54
55const ErrorHandler = {
56 canHandle() {
57 return true;
58 },
59 handle(handlerInput, error) {
60 console.log(`Error handled: ${error.message}`);
61 return handlerInput.responseBuilder
62 .speak('Tut mir leid. Es ist ein Fehler aufgetreten')
63 .reprompt('Der Techniker ist informiert')
64 .getResponse();
65 },
66};
67
68const SKILL_NAME = 'Fünferpasch';
69
70const skillBuilder = Alexa.SkillBuilders.standard();
71
72exports.handler = skillBuilder
73 .addRequestHandlers(
74 LaunchRequestHandler,
75 HelpHandler,
76 ExitHandler,
77 SessionEndedRequestHandler
78 )
79 .addErrorHandlers(ErrorHandler)
80 .lambda();
Neben den Standard-Handlern für Error und Hilfe gibt es nun nur noch einen LaunchRequestHandler der ausgeführt wird, wenn unser Skill gestartet wird.
Damit unser Skill dies auch auslöst, müssen wir nun unsere Lambda-Funktion als Endpoint für unseren Skill registrieren. Die ARN dazu finden wir am oberen rechten Bildschirmrand, praktischerweise versehen mit einem Button um diese direkt in die Zwischenablage zu kopieren.
Nun müssen wir wieder zurück zu unserem Skill wechseln und die ARN unter Endpoint in das Feld “Default Region” einfügen.
Nicht vergessen, diese Änderung mit “Save Endpoints” abzuspeichern.
Damit haben wir schon eine erste funktionsfähige Version unseres Skill. Bislang werden wir nur begrüßt, aber immerhin.
Die einfachste Möglichkeit dies zu testen ist es, in der Hauptnavigation in den Tab “Test” zu wechseln. Dort könntet ihr über den Alexa Simulator per Tastatur mit dem Skill interagieren:
Um den Skill aufzurufen, müsst ihr die Tests im Simulator zunächst erlauben. Dies stellt ihr im Dropdown-Feld oben links ein (im Screenshot markiert).
Ausblick
Damit hätten wir den ersten Schritt erreicht. Der nächste Schritt wird sein, die Entwicklung der Lambda aus der Online IDE in eine lokale IDE zu verlegen.
Weitere Beiträge
von Stefan Spittank
Dein Job bei codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog-Autor*in
Stefan Spittank
User Experience Specialist
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.