Alexa + Java = Fun

#AWS #Alexa

Mit AWS Lambda und einer Prise Java macht es richtig Spaß, Amazons Alexa neue Tricks beizubringen. Das ist kein Hexenwerk, sondern mit einfachem Java- und etwas AWS-Know-how leicht zu bewerkstelligen.

Alexa? Wer ist das denn?

Als Alexa bei uns eingezogen ist, war meine Frau nicht gerade erfreut. Neben Ihr sollte es keine Andere im Haus geben. Und das, obwohl es sich dabei eigentlich nur um einen schwarzen Zylinder handelt. Um etwas präziser zu sein: Alexa ist lediglich der Sprachassistent, der irgendwo in den Weiten des Internets existiert. Das Gerät heißt Amazon Echo. Es handelt sich um eine Lautsprecherbox und sieben Mikrofone, die auf das Netzwerk zugreifen können. Diese sieben Mikrofone lauschen beständig in den Raum hinein, ob jemand das Schlüsselwort Alexa sagt. Falls dem so ist, wird die aufgezeichnete Nachricht an Alexa geschickt, die dann hoffentlich weiterhelfen kann.

Datenschützer graust es hier bereits: Amazon hört mit, wenn auch nur kurz. Ständig. Immerhin muss man ja irgendwie das Schlüsselwort identifizieren. Man kann das Mikrofon ausschalten, doch wir wissen ja, dass die NSA auch ausgeschaltete Handys abhören kann. Das Grauen ist perfekt, wenn man sich Amazon Show ins Haus holt. Dort bekommt Alexa auch noch Bildschirm und Kamera. Offiziell um Modetipps zu geben. Aber was sonst noch so übertragen und wie es genutzt wird, kann man natürlich nie genau sagen.

Glücklicherweise sind wir zu Hause nicht paranoid. Trotzdem steht das Echo-Gerät nicht in besonders privaten Räumen wie dem Kinder- oder Schlafzimmer. Vertrauliche Gespräche im Büro darf Alexa auch nicht mithören.

Wer sich trotz aller Kritik mit Alexa anfreunden kann, der kann sie fragen: “Alexa, wie wird das Wetter heute?” Oder: “Wie viel Uhr ist es?” Das sind die langweiligen Dinge. Interessanter wird es, wenn die verschiedenen Anbieter wie die Deutsche Bahn Auskunft zum Zugfahrplan geben. Das was Alexa kann, nennt man Skills, also Fähigkeiten.

Von einem erfolgreichen, werbefinanzierten oder bezahlten Skill habe ich bisher noch nie gehört. Was nicht ist, kann ja noch kommen. Amazon bietet solche Einstellmöglichkeiten in seiner Entwicklerkonsole bereits an. Aber auch kostenfrei ist es natürlich ziemlich cool, sein Produkt oder Unternehmen via Voice-Support zu unterstützen. Man stelle sich vor: “Alexa, frage Dr. Mustermann ob er morgen noch Termine frei hat” Oder: “Alexa, frage das Café Chapeau ob es morgen geöffnet hat.” Und so weiter. Sprachsteuerung kann nützlich werden, nicht nur im Auto.

Mit Java ist es sehr einfach, ebenfalls einen Skill zu entwickeln. Obwohl JavaScript in AWS Lambda offenbar Einwohner Nummer Eins ist.

„Hallo Alexa“ – Begriffe

Alexa ist der Sprachassistent. Genauso wie Siri Apples Sprachassistent ist. Also nur eine Menge an Software, die Sprache versteht und antwortet. Das Gerät, dass im Folgenden verwendet wird, ist Amazon Echo. Es gibt auch Varianten wie Amazon Echo Dot (etwas kleiner, mit schlechterer Box) oder Amazon Show (mit Bildschirm). Mittlerweile gibt es weitere alexaunterstützte Geräte. Wie zum Beispiel Sonos One. Es handelt sich dabei um eine sprachgesteuerte HiFi-Box.

Den folgenden Code nennt man Skill. Ein Skill ist eine Fähigkeit. Irgendeine Funktionalität, die das Gerät erfüllen kann. Es gibt Standard-Skills, also Fähigkeiten die Alexa von vorneherein mitbringt. Üblicherweise meint man mit dem Wort Skill aber eine Eigenentwicklung, also einen Custom-Skill. Dieser Skill kann eine oder mehrere Intents, also Absichten beinhalten. So könnten Sie beispielsweise sagen: “Alexa, frage Crypto nach den letzten Preisen.” Und Sie würden derzeitige Preise der gängigen Cryptowährungen erhalten. Dann wäre GetCurrencies also möglicherweise Ihr erstes Intent.

Sie könnten zusätzlich fragen: “Alexa, frage Crypto ob es den Euro noch gibt.” Und Sie hätten möglicherweise ein zweites Intent namens CheckEuro. Der Begriff Intent ist ebenfalls in der Android-Entwicklung bekannt, wo er recht ähnlich verwendet wird.

Ablauf eines Alexa Aufrufs

Prinzipiell ist der Ablauf eines Alexa-Requests relativ einfach. Das Gerät lauscht wie gesagt immer ein paar Sekunden mit. Sobald es das Keyword Alexa hört, wird die Aufnahme zur Auswertung an den Alexa-Service gesendet. Das ist die Domain von Amazon. Ihre Anfrage wir dort auch gespeichert, analysiert und offenbar zur Verbesserung von Alexa verwendet.

Der Alexa-Service interpretiert die Anfrage und leitet dann optimalerweise den Text an Lambda weiter. Zumindest meistens. Denn obwohl Lambda normalerweise die beste Wahl ist und auch von Amazon empfohlen wird, könnte man theoretisch auch seinen eigenen Server dahinterklemmen und damit ohne Lambda arbeiten.

Amazon Web Services (AWS) bietet Lambda an, womit man serverlose Funktionen im Netz verfügbar machen kann. Manch einer verbindet den Begriff Microservice damit. Die Funktion wird komplett von Amazon gewartet und ist daher extrem stabil. Es ist also möglich, auch ohne einen AWS-Zugang einen Alexa-Skill zu erstellen. Da es aber nicht die empfohlene Vorgehensweise ist, hält sich diese Einführung ebenfalls an AWS.

Alexa-Flow - von Gerät zu Ihrem Code. (Abb. 1)

Alexa-Flow – von Gerät zu Ihrem Code. (Abb. 1)

Speechlet und Lambda-Code

Um Lambda verwenden zu können, benötigt man einen AWS-Zugang. Für Einsteiger bietet AWS ein kostenfreies Kontingent an. Um schnell einsteigen zu können, könnte man beispielsweise mit Maven ein Standardprojekt generieren.

mvn archetype:generate

Wer die Defaults verwendet, kann sich damit ein sehr einfaches Java-Projekt generieren, dem man nur noch die Abhängigkeit zum Alexa-Skills-Kit konfigurieren muss, wie in (Listing 1) ersichtlich ist.

(Listing 1)
<dependencies>
  <dependency>
    <groupId>com.amazon.alexa</groupId>
    <artifactId>alexa-skills-kit</artifactId>
    <version>1.3.0</version>
  </dependency>
</dependencies>

Mit dieser Abhängigkeit hat man alle notwendigen Klassen zur Verfügung um den Skill zu bauen.

Im Prinzip geht es nun um zwei Dinge: zunächst muss die eigentliche Alexa-Funktionalität gebaut werden. Und zweitens, diese Alexa-Funktionalität muss via Lambda bereitgestellt werden. Der interessantere Code ist in (Listing 2) zu sehen, worin es um die eigentliche Abhandlung der Voice-Anfrage geht.

(Listing 2)
public class CrypoSpeechlet implements Speechlet {

  public void onSessionStarted(
    SessionStartedRequest sessionStartedRequest, Session session) throws SpeechletException {

  }

public SpeechletResponse onLaunch(

    LaunchRequest launchRequest, Session session) throws SpeechletException {

  }

  public SpeechletResponse onIntent(
    IntentRequest intentRequest, Session session) throws SpeechletException {

  }

  public void onSessionEnded(
    SessionEndedRequest sessionEndedRequest, Session session) throws SpeechletException {

  }

Hier ist zu sehen, wie wir die Methoden des Speechlet Interfaces implementieren. Das Speechlet wird von den JavaDocs als Web-Service beschrieben. Dieser Service wird üblicherweise noch gewrapped, z.B. durch das SpeechletServlet oder den SpeechletRequestStreamHandler. Ersteres ermöglicht dem Speechlet in Java-EE-Containern zu existieren, zweiteres bindet das Speechlet an AWS Lambda an.

Die Methoden sind nahezu selbsterklärend. Es wird ganz klar deutlich, dass wir uns nicht darum kümmern müssen, die Bytes aus den MP3s zu ziehen und auf komplexe Art und Weise auszuwerten. Das alles macht Amazon für uns. Und wenn es das getan hat, ruft Amazon unser Speechlet auf.

Die onLaunch-Methode wird aufgerufen, wenn der Skill gestartet wird ohne einen besonderen Befehl abzugeben. Ein Beispiel wäre: “Alexa, öffne Crypto.” Dadurch würde Alexa eben den Skill in Bereitschaftsmodus versetzen. Würde der Crypto-Skill nur eine Sache machen, dann könnte hier der wichtige Teil der Anwendungslogik implementiert werden. Wenn der Skill allerdings mehr Informationen braucht, dann sollte man hier mit einer Antwort aufwarten und weitere Anweisungen geben.

onLaunch öffnet immer auch eine neue Session die im Aufruf von onSessionStarted mündet. Ganz generell wir onSessionStarted bei jeder Interaktion mit dem Gerät aufgerufen, da jede Interaktion als neue Sitzung gilt. Ähnlich dazu wird onSessionEnded verwendet. Dieser Callback wird aufgerufen, wenn der Benutzer nicht weiter mit dem Gerät kommuniziert oder den Skill beendet hat. Besonders interessant ist dann onIntent. Hier lebt der Großteil der Anwendungslogik, bzw. von hier aus werden die verschiedenen Funktionalitäten aufgerufen.

(Listing 3)
public SpeechletResponse onIntent(
       IntentRequest intentRequest, Session session) throws SpeechletException {

String name = intentRequest.getIntent().getName();

   String speechText = „You called “ + name;

// Cards – für Amazon Show

   Card card = new SimpleCard();
   card.setTitle(„Hello Crypto“);
   card.setContent(speechText);

// Sprachausgabe

   PlainTextOutputSpeech speech = new PlainTextOutputSpeech();
   speech.setText(speechText);
   return SpeechletResponse.newTellResponse(speech, card);
}

Eine beispielhafte Implementierung von onIntent findet man in (Listing 3). Mittels dem IntentRequest Objekt können wir auf die Details des Requests zurückgreifen. In diesem Beispiel greifen wir lediglich auf den Namen zu. Es könnte sich hierbei um folgendes handeln: “Alexa, frage Crypto nach den Kursen.” Crypto wäre der Skill, und Kurse ist der Name des Intents.

Weiter könnte man sogenannte Slots definieren. Das sind Platzhalter, die dem Request hinzugefügt werden. Zum Beispiel: “Alexa, frage Crypto nach den Kursen für Bitcoin.” In diesem Fall wäre das Intent nach wie vor Kurse, aber der Begriff Bitcoin könnte auch mit Ether oder Litecoin ausgewechselt werden. Hier handelt es sich um einen Slot. Auch auf diese Slots könnte man im Speechlet mittels des IntentRequest zugreifen.
In dieser Implementierung ist lediglich der Name des Intents notwendig um daraus einen String speechText zu erzeugen. Der wird für zwei Fälle verwendet: Zum ersten, erzeugen wir eine SimpleCard. Falls das Gerät einen Bildschirm besitzt, wird eine visuelle Ausgabe ebenfalls angezeigt. Das ist derzeit mit Amazon Show möglich. Im Anschluss wird die Sprachausgabe mit dem gleichen Text erzeugt. Obwohl es aufgrund der Neuartigkeit der Geräte vermutlich weniger Best-Practices gibt, ist es vermutlich sinnvoll für visuelle und Sprachausgabe den gleichen Text zu verwenden – ähnlich den Untertiteln im Fernsehen.

Zu guter Letzt wird die Antwort mit SpeechletResponse.newTell Response(speech, card); erzeugt. Die Tell-Response ist dafür gedacht eine Antwort zu geben und ermöglicht keinen komplexen Workflow. Dem gegenüber steht die Ask-Response, welche die Möglichkeit bereitstellt Rückfragen an den Benutzer zu richten.

Skill erstellen. (Abb. 2)

Skill erstellen. (Abb. 2)

So weit so gut: Derzeit verfügt der Code bereits über eine Art Hello-World-Logik, doch es fehlt noch die Anbindung an Lambda. (Listing 4) zeigt den notwendigen Code.

(Listing 4)
public class CryptoSpeechletRequestStreamHandler
  extends SpeechletRequestStreamHandler {
  private static final Set<String> appIds =
  new HashSet<String>();

static {

     appIds.add(„<Ihre App ID>“);
  }

  public CryptoSpeechletRequestStreamHandler() {
     super(new CrypoSpeechlet(), appIds);
  }
}

Im Prinzip muss lediglich von SpeechletRequestStream Handler abgeleitet werden, der wiederum das erstellte Speechlet wrapped. Hinzu kommen eine oder mehrere App-IDs, die wir mit Java-Code nicht erzeugen können. Denn an dieser Stelle verlassen wir nun die Java-Welt und müssen mittels Amazon-Developer-Console und AWS-Management-Console die beiden Welten verdrahten und konfigurieren.

Amazon-Developer-Services konfigurieren

Um diesen Dienst nutzen zu können, muss man sich als Entwickler registrieren. Das ist glücklicherweise recht problemlos und auch kostenfrei. Wenn man sich zum Reiter Alexa und Create new Alexa App durch gehangelt hat, folgen zahlreiche Einstellungsmöglichkeiten, die letztlich nur ein Ziel haben: irgendwie eine App-ID bekommen, die dem Code in (Listing 4) hinzugefügt werden kann. Zunächst gibt man den Namen an und auch die Sprachen, in denen man seinen Skill zur Verfügung stellen möchte.

 

Wer weiterhin auf der bequemen Oberfläche arbeiten möchte, der hat seit neuestem die Möglichkeit mit dem Interaction-Model-Builder seine Anwendung angenehm zu konfigurieren. Vorher war dies nur mittels JSON möglich, was natürlich nicht wirklich schön war. Allerdings existiert ja bekanntermaßen nur Code, wenn er auch in Git committed wurde. Insofern sollte man bei professionellen Entwicklungen zumindest das generierte JSON kopieren und in die Versionskontrolle kopieren.

Wie man in (Abb. 3) sehen kann, besteht in dieser Oberfläche die Möglichkeit mehrere Intents anzulegen. Dazu konfiguriert man beispielsweise die Keywords, die das Intent auslösen sollen. Ich habe in meinem Fall einfach eine Menge von Wörtern definiert, die mir behilflich sein könnten. Dazu können auch die Slots, also die Platzhalter angelegt werden.

Im Punkt Configuration muss das ARN (eine Art AWS-ID einer AWS-Resource) der Lambda-Funktion angegeben werden – was wir derzeit noch nicht haben. Es handelt sich um ein Henne-Ei-Problem. Sie müssen sich also darauf vorbereiten, irgendwann die Lambda zu erstellen und dann die ARN hierhin zu kopieren.

Skill-Builder. (Abb. 3)

Skill-Builder. (Abb. 3)

AWS Lambda konfigurieren

In der AWS-Management-Console kann man AWS Lambda bequem über eine Weboberfläche konfigurieren. Leider gibt es keine Anleitung für das Erstellen von Lambda-Funktionen mit Java. Aber das kann man alles eigenständig lösen, wie in (Abb. 2) ersichtlich. Wichtig ist hierbei lediglich nur die korrekte Runtime und die Rolle. Im Prinzip sollten für die meisten Anwendungen grundlegende Rechte (Basic-Permissions) reichen.

AWS-Lambda - Funktion erstellen. (Abb. 4)

AWS-Lambda – Funktion erstellen. (Abb. 4)

Im Anschluss kann der Trigger, also der Auslöser für diese Funktion, definiert werden. Das geschieht bequem über ein Dropdown-Menü. Außerdem wird ein Handler benötigt. Das ist die Klasse, die wir in unserem Paket von SpeechletRequestStreamHandler abgeleitet haben.

In meinem Fall ist das also solutions.grobmeier.crypto.CryptoSpeechletRequestStreamHandler. Dann kann die JAR-Datei bereits hochgeladen werden. Die JAR erstellt man übrigens am einfachsten mit folgendem Maven-Kommando:

(Listing 5)
mvn assembly:assembly -DdescriptorId=jar-with-dependencies package

 AWS-Lambda - Trigger erstellen. (Abb. 5)

 AWS-Lambda – Trigger erstellen. (Abb. 5)

Hiermit wird ein Assembly erstellt das auch alle Abhängigkeiten für unser Paket beinhaltet.

Sobald die Lambda erstellt ist, vergessen Sie bitte nicht die ARN der Lambda, ersichtlich rechts oben in der Konsole, in die Amazon-Developer-Console zu kopieren. Damit sollte dann alles bereit sein für unseren ersten Test.

Test

Zurück in der Amazon-Developer-Console steht im Reiter Test der Service-Simulator bereit. Hier kann man sein Keyword eingeben und es so abschicken, als wäre es von Alexa erkannt worden.

Die Antwort erfolgt im JSON-Format und sogar die Card wird gerendert, als wäre eine Show vorhanden. Im Test ist aufgefallen, dass man die Lambda-Funktion nicht mit zu wenig Zeit ausstatten sollte. In den Basic-Settings sollte man eher mit zehn Sekunden rechnen, bis die Lambda durchgelaufen ist. Es ist auch möglich, mit seinem eigenen Gerät zu testen.

Alexa-Testing. (Abb. 6)

Alexa-Testing. (Abb. 6)

Fazit

Allen Privatsphärenbedenken zum Trotz: Alexa macht einfach Spaß. Die Möglichkeit mit unserem Computer zu sprechen ist nicht erst seit Star Trek populär. Um Alexa-Geräte zu entwickeln, benötigt man allerdings auch etwas Geduld: Nicht nur Java und die SDKs der Sprachsteuerung wollen gemeistert werden. Auch AWS Lambda benötigt etwas Zeit und natürlich muss man sich auch mit der Amazon-Developer-Console etwas auskennen. Bis man dann vom Code zu einem fertigen, signierten Paket im Skill-Store kommt, kann es einige Zeit dauern. Die zugrundeliegenden Ideen sind zwar verständlich, aber neu. Trotzdem: An einem Wochenende sollte man auch ohne jegliche AWS-Vorkenntnisse zu einem halbwegs anständigen Skill kommen.

 

Christian Grobmeier arbeitet selbst, ständig und gerne. Am liebsten mag er irgendwas mit Spring oder Solr. Er ist auch kein JavaScript-Kostverächter und treibt sich gerne mit Angular oder React rum. Hin und wieder greift er sogar zur Ruby- und PHP-Keule. Und wenn alles nichts mehr hilft, schreibt er halt ein Buch.

https://grobmeier.solutions
https://github.com/grobmeier
Linkedin: Christian Grobmeier
Twitter: @grobmeier
Email

Carolyn Molski


Leave a Reply