Einen Alexa Skill mit BotMan entwickeln

Vor einiger Zeit wurde ich auf die Entwicklung eines Alexa Skills für den Abfallkalender Erftstadt angesprochen. Doch wie entwickelt man einen Alexa Skill und stellt die Verbindung zu einem Webservice her?

Amazon bietet hierzu eine sehr umfangreiche Dokumentation an, mit der man die Schnittstelle zum Webservice erstellen könnte. Aber praktischerweise gibt es auch BotMan.io, ein Open-Source ChatBot Projekt von Marcel Pociot, welches einem viel Arbeit abnimmt. Denn neben diversen anderen Treibern wie für den Facebook Messenger, Telegram u.v.m. gibt es für BotMan.io auch einen Alexa-Treiber, der die Erstellung eines Skills deutlich vereinfacht. Marcel hat vor kurzem auch selbst einen Blogpost zur Entwicklung eines Alexa Skills geschrieben. Bei der Veröffentlichung des Skills müssen jedoch noch einige Anforderungen seitens Amazon berücksichtigt werden. So muss z.B. der eingehende Request validiert werden und der Webservice muss auf durch Amazon vorgegebene intents auf eine bestimmte Art und Weise reagieren.

Anhand des Abfallkalenders zeige ich, wie man mit Botman.io und wenig Aufwand einen Skill entwickelt und die Validierung durch Amazon besteht.

Einbindung von Botman und dem Botman Alexa Treiber

Botman.io gibt es in zwei unterschiedlichen Varianten. Botman.io Studio ist bereits eine vollwertige Umgebung zur Entwicklung von Chatbots. Botman lässt sich aber auch in bereits bestehende Anwendungen einbinden. Da mein Abfallkalender bereits seit mehreren Jahren existiert und auf dem PHP Framework Laravel basiert, habe ich letztere Möglichkeit genutzt.

Mit

composer require botman/botman
composer require botman/driver-amazon-alexa
composer require craigh/alexa-request-validator

sind die benötigten Pakete schnell eingebunden. Neben Botman und dem passenden Alexa-Treiber nutze ich noch ein Paket zur Validierung des eingehenden Requests.

Alexa Skill erstellen

Bevor es in Laravel weitergeht, muss auf der Developer Seite von Amazon (ihr benötigt einen kostenlosen Developer-Account), zunächst ein Alexa Skill angelegt und ein Interaktionsschema definiert werden. Ich habe dazu den angebotenen Schema Builder verwendet.

Der Amazon Alexa Schema Builder

Die Fragen, die der Nutzer dem Skill stellen kann, werden über Intents verwaltet. Neben durch Amazon bereits vorgegebenen Intents, wie z.B. „Stopp“ oder „Abbrechen“ können individuelle Intents hinzugefügt werden. Jeder Intent hat diverse Sätze (Utterances), über welche der jeweilige Intent ausgelöst wird. Diese Sentences können Slots enthalten, deren Inhalt von Amazon an Euren Webservice übergeben werden. Hier ist es wichtig den richtigen Typ des Slots zu wählen. Da jeder Skill individuell ist, sollte man spätestens hier einen Blick in die offizielle Dokumentation werfen.

Für den Abfallkalender habe ich unter anderem ein Intent NaechsteAbfuhr angelegt. Dieser Intent wird z.B. über Utterances wie

„wann wird die nächste Tonne in Lechenich abgeholt“

oder

„wann wird die graue Tonne in Liblar abgeholt“

erklärt.

Die Art der Mülltonne und der Ort sind dabei die Slots, die je nach Anfrage variieren können.

Erstellung der Webservice Schnittstelle

Sind die Intents und Slot Typen definiert geht es in unserer Laravel Anwendung weiter. Damit der Skill mit der Anwendung kommunizieren kann, erstellen wir zunächst einen Controller.

php artisan make:controller AlexaController

Im Konstruktor des Controllers laden wir den Alexa Treiber und erzeugen unseren Bot.

// App\Http\Controllers\AlexaController

protected botman;

public function __construct()
{
	$config = [];
	DriverManager::loadDriver(AmazonAlexaDriver::class);
	$this->botman = BotManFactory::create($config);
}

Die Methode index() des Alexa Controllers sorgt in unserer Anwendung für die Reaktion auf den eingehenden Request von Amazon.

// App\Http\Controllers\AlexaController
public function index()
{
	$this->botman->on('LaunchRequest', function($payload, $bot) {
    $bot->reply('Willkommen beim Abfallkalender Erftstadt. Der Abfallkalender kann Dir die Abfuhrtermine
    der Mülltonnen in Erftstadt nennen. Was möchtest Du wissen?');
	});
	
	...
	
	$this->botman->hears('NaechsteAbfuhr', function ($bot) {
    $tonne = $bot->getMessage()->getExtras('slots')->get('tonne');
    $ort = $bot->getMessage()->getExtras('slots')->get('ort');

    $tonne = array_key_exists('value', $tonne) ? $tonne['value'] : '';
    $ort = array_key_exists('value', $ort) ? $ort['value'] : '';

		$bot->reply($this->abfuhr($ort, $tonne), ['shouldEndSession' => true]);
    });
    
	...
	
	$this->botman->listen();
}

Hier werden nur zwei Beispiele gezeigt. Im oberen Teil reagiert unser Bot auf den LaunchRequest. Dieser wird gesendet, wenn der Abfallkalender mit

„öffne Abfallkalender“

gestartet wird. Der untere Teil reagiert auf den individuell erstellen Intent NaechsteAbfuhr. Das Prinzip ist in beiden Fällen gleich: Der Bot antwortet auf die eingehende Nachricht mit reply('Antworttext', [options]). Beim LaunchRequest bleibt die Session für weitere Anfragen offen, bei einem getroffenen Intent wird die Session abgeschlossen.

Zuletzt müssen wir für einen ersten Test des Alexa Skill noch eine Route definieren.

// routes/api.php
...
Route::post('alexa', 'AlexaController@index');
...

Damit ist der Skill für einen ersten Test bereit.

Testen des Alexa Skills

Amazon bietet im Skill Builder einen Test Bereich an. Für einen Test muss im Menüpunkt XX die URL des Webservices ergänzt werden. Mit Valet und dem Befehl valet share lässt sich bequem eine öffentliche URL für ein lokales Projekt erzeugen.

Der Skill kann nun unter dem Menüpunkt Test mit Fragen getestet werden. Auch auf Eurem Echo und in der Alexa App ist der Skill nun verfügbar und lässt sich testen.

Request Validierung

Bevor der Skill zum Review durch Amazon eingereicht wird, müssen wir noch einige Dinge beachtet werden. Amazon verlangt, dass der Request im Webservice validiert wird. Wir haben dazu bereits ein Package via composer installiert und erstellen jetzt mit

php artisan make:middleware

eine Middleware zur Validierung des eingehenden Requests.

// App\Middleware\VerifyAlexaRequest
public function handle($request, Closure $next)
	{
    $validator = new AlexaRequestValidator(
        'amazon-skill-id',
        $request->getContent(),
        $request->server('HTTP_SIGNATURECERTCHAINURL'),
        $request->server('HTTP_SIGNATURE')
    );

    try {
        $validator->validateRequest();
    } catch (AlexaValidationException $e) {
        abort(400, $e->getMessage());
    }
        return $next($request);
    }

Die erstellte Middleware ergänzen wir in der Kernel.php

// App\Kernel.php
...
protected $routeMiddleware = [
		...
   'alexa' => \App\Http\Middleware\VerifyAlexaRequest::class
    ];
...

und verknüpfen sie mit unserer Route.

// routes/api.php
Route::post('alexa', 'AlexaController@index')->middleware('alexa');

Wenn wir jetzt unsere Alexa-Schnittstelle z.B. über Postman mit einem gültigen JSON ansprechen, wird, da der Request nicht von Amazon kam, ein 400er Fehler zurückgegeben.

Notwendige Events und Intents

Amazon gibt ebenfalls vor, wie auf bestimmte Events und Intents zu reagieren ist. Neben den individuellen Intents muss in jedem Falle auf folgende Intents reagiert werden:

  • LaunchRequest
  • AMAZON.StopIntent
  • AMAZON.CancelIntent
  • AMAZON.HelpIntent

Der LaunchRequest verlangt nach einer kurzen Willkommensnachricht und soll mit einer Frage abschließen. Der HelpIntent beschreibt die Funktionen des Skills ausführlicher und soll den Nutzer über die Verschiedenen Möglichkeiten aufklären. Unter anderem mit Beispielsätzen. Der StopIntent schließt den Skill, der CancelIntent bricht die aktuelle Anfrage ab. Hier solltet ihr in jedem Falle mal einen Blick in die Dokumentation werfen.

Wenn alle Dinge implementiert und getestet sind, ist der Skill nun bereit zur Prüfung und Veröffentlichung. Die Prüfung des Abfallkalender Skills war innerhalb eines Tages erledigt. Wer den Skill ausprobieren möchte findet ihn im Amazon Alexa Skill Store unter dem Suchbegriff "Abfallkalender Erftstadt" oder über den direkten Link.

Share this post!