A little more Action, please!

Mit AssistantJS eine Node.js-Dialogflow-Anwendung entwickeln
Keine Kommentare

Conversational Interfaces sind aktuell ein großes Thema. Das zu Recht – denn sie können digitale Services verbessern und erweitern. In diesem Artikel geht es um Erfahrungen und Erkenntnisse aus der ersten Entwicklungsphase einer Action für den Google Assistant. Insbesondere der Entwicklungsworkflow, Tools sowie AssistantJS – das Node.js-Framework, das als Grundlage dient – steht im Fokus.

Beginnen wir mit dem Zusammenspiel von Dialogflow und der lokalen Node.js-Applikation zur Entwicklungszeit. Dialogflow ist eine Web-App für die Verwaltung von Apps für Google Assistant. Falls noch nicht geschehen, macht euch mit dem Vokabular innerhalb des Kontextes von Dialogflow sowie Conversational Interfaces vertraut. Das ist für das weitere Verständnis dieses Artikels hilfreich.

Entwicklungsworkflow und Tools

Eine Hürde besteht in der Verbindung von Dialogflow zum Fulfillment. In Dialogflow kann eine URL als Fulfillment gesetzt werden, die als Webhook dient. Diese URL wird somit aufgerufen, sobald Dialogflow einen Intent in den Sprachäußerungen deiner Nutzer erkennt. Somit muss die Applikation auch schon zur Entwicklungszeit als Fulfillment öffentlich erreichbar sein, was dein localhost meistens nicht ist. Um diesen Workflow zu vereinfachen, kommt ngrok ins Spiel. ngrok generiert eine Art Proxy zwischen einer generischen Subdomain und deinem localhost. Nach erfolgreichem Set-up einfach den von ngrok bereitgestellten URL für das Fulfillment nutzen. Bevor es mit der Entwicklung der Action losgeht, stellt sicher

  • dass bei jedem Intent in Dialogflow „enable webhook call for this Intent“ aktiviert ist. Sonst wird euer Fulfillment nicht aufgerufen.
  • dass, wenn ihr mit einer anderen Sprache als Englisch arbeitet, diese in Dialogflow aktiviert ist. Aus unerklärlichen Gründen kommt es vor, dass nach erneutem Log-in wieder Englisch aktiviert ist, und dann werden keine Intents mehr erkannt.

Tipp: nodemon ist bei der Entwicklung von Node.js-Anwendungen ein sehr hilfreiches Werkzeug.

Jetzt kann es mit dem zweiten Teil losgehen. Der Entwicklung einer Action auf Basis von AssistantJS.

AssistantJS

Wie erwähnt, ist dieses Projekt für uns die erste Umsetzung eines Conversational Interface, weshalb wir zu Beginn Nachforschungen zu Beispielen und Best Practices betrieben haben.

Hier sind wir auf AssistantJS gestoßen, das durch die Web Computing GmbH entwickelt wird und für das wir uns letztendlich aus folgenden Gründen entschieden haben:

  • plattformagnostischer Ansatz
  • Dependency Management mit InversifyJS
  • State Management
  • Cross-Plattform-Session-Management
  • vielseitig verwendbare Template-Engine
  • i18n-Unterstützung
  • CLI-Assistent
  • TypeScript-Quellcode
  • praktisches Test-Set-up

In die ersten fünf Punkten dieser umfangreichen Liste möchten wir im Folgenden einen näheren Einblick geben. Für detaillierte Informationen sei euch auch das AssistantJS-Wiki ans Herz gelegt. Mit dem CLI-Assistenten habt ihr in wenigen Momenten eine Basis am Laufen und könnt loslegen.

Plattformagnostischer Ansatz

Applikationen, welche mit AssistantJS entwickelt werden, können auf unterschiedliche Plattformen ausgespielt werden – sei es als Google Home Action oder Amazon Alexa Skill. Das alles funktioniert über Konfigurationen im Quellcode. Die Businesslogik eurer Applikation muss aber nur einmal geschrieben werden. Um die Konfigurationen vollständig zu halten, helfen die sehr gut implementierten Typescript Interfaces von AssistantJS.

Dependency Management mit InversifyJS

AssistantJS nutzt InversifyJS für Dependency Injection. Ich habe schon davor mit dieser Bibliothek gearbeitet und sie als sehr ausgereift kennengelernt. AssistantJS nutzt für jedes Modul einen Deskriptor, um Abhängigkeiten an den Dependency-Injection-Container zu binden. Der Quellcode hierfür sieht in etwa so aus:

export const descriptor: ComponentDescriptor = {
  name: 'my-module', bindings: {
    root: (bindService) => {
      bindService.bindGlobalService<MyServiceInterface>('my-service').to(MyService);
    }
  }
};

Auf diese Weise registrierte Abhängigkeiten könnt ihr nun überall in eurer Applikation auf Konstruktorebene einfügen: @inject(‚my-module:my-service‘) protected MyService: MyServiceInterface

State Management

Das State-Management-Konzept von AssistantJS wirkt innovativ und war ein Grund, aus dem wir uns für das Framework entschieden haben. Im AssistantJS-Wiki wird es wie folgt beschrieben: „[…] you implement your states as classes and your intents as methods.

Genauer heißt das, jeder State ist eine eigene Klasse und beinhaltet eigene Intent-Methoden. Jede dieser Intent-Methoden regelt dabei einen gleich lautenden Aufruf von Dialogflow. Die State-Klassen sind als ES6-Klassen umgesetzt. AssistantJS stellt eine BaseState-Klasse bereit, die als Abstract für alle State-Klassen der Applikation dient. Dieses Abstract beinhaltet auch bereits mehrere generische Intent-Methoden, die greifen, wenn ein Dialogflow-Aufruf keiner Intent-Methode des aktuellen States zugeordnet werden kann.

Generell wird das State Management von einer internen StateMachine-Klasse geregelt. Diese kann Wechsel (Transitions) von einem State in den nächsten veranlassen. Durch Promise-Rückgaben sind diese Wechsel bereits auf Asynchronität ausgelegt.

Cross-Plattform-Session-Management

Ein weiteres tolles Feature von AssistantJS ist das Cross-Plattform-Session-Management. Daten für eine Session können in einem Redis Store gespeichert werden. Es folgt eine kurze Anleitung hierfür.
Erst sollte die aktuelle Session injiziert werden:

@inject(injectionNames.current.sessionFactory) private sessionFactory: () => servicesInterfaces.Session

Danach können Daten gespeichert bzw. geladen werden:

await this.sessionFactory().set('key', JSON.stringify(data))  
await this.sessionFactory().get('key')  

Und sobald eine Session beendet wird, zum Beispiel so

this.responseFactory.createVoiceResponse().endSessionWith()

wird der Sessionspeicher von AssistantJS aufgeräumt. Der hier benutzte Redis Store basiert auf npm redis. Abschließend wollen wir noch auf weitere Feature von AssistantJS eingehen.

Vielseitig verwendbare Template-Engine

Die Template-Engine hilft, Antworten variantenreicher zu gestalten und dient auch als Grundlage für Mehrsprachigkeit. Alle Antworten, die die Applikation in Intents geben kann, werden in einer translation.json gespeichert. Diese kann so aussehen:

{
  "ProgramState": {
    "broadcastIntent": "Um {{ time }} Uhr läuft auf {{ channel }} {{ title }}"
  }
}

Dabei sind {{ }} Platzhalter. Um die Arbeit zu erleichtern, bietet AssistantJS Hilfsmittel an. Beispielsweise translateHelper. Mit dieser Hilfsfunktion kann man mit den obenstehenden Daten so etwas machen:

this.translateHelper("ProgramState.broadcastIntent",
  {
    time: "20:15"
    channel: "channel1",
    title: "My favorite movie"
  }
)

Der Ergebnissatz wäre nun: „Um 20:15 Uhr läuft auf channel1 My favorite movie“. So können ganz einfach Dialoge kreiert werden (Abb. 1).

Abb. 1: Ein Dialog entsteht

Abb. 1: Ein Dialog entsteht

Die Template-Engine kann jedoch noch einiges mehr. Zum Beispiel:

{
  ...
  "FavoriteChannelsState": {
    "getFavoritesIntent": "Hier { hörst du | sind } deine  Lieblingssender"
  }
  ...
}

Hier sind { *** | *** } Variationen. Die Ergebnissätze wären nun entweder „Hier hörst du deine Lieblingssender“ oder aber „Hier sind deine Lieblingssender“. Diese Variationen sind noch erweiterbar:

{
  ...
  "FavoriteChannelsState": {
    "addToFavoritesIntent":[
      "Ich habe {{ channel }} zu deinen Lieblingssendern hinzugefügt",
      "{{ channel }} ist nun auf deiner Favoritenliste"
    ]
  }
  ...
}

Hier ist jeder Eintrag im Array eine mögliche Variation. Das Ganze könnte sogar noch mit der davor genannten Syntax kombiniert werden.

AssistantJS ist an erfahrene Entwickler gerichtet, die TypeScript-Kenntnisse vorweisen können. Die Abstraktion von bestehenden Plattformen hilft, jetzt zukunftssichere Applikationen zu entwickeln und auf unterschiedlichen Platformen zu veröffentlichen.

Um die jeweilige Variante aus den Möglichkeiten auszuwählen, erzeugt AssistantJS nach eigenen Angaben ein kartesisches Produkt aus der gegebenen Menge. Diese Möglichkeiten, variantenreichere Antworten zu erzeugen, helfen, die User Experience zu verbessern.

Fazit

Alles in allem habe ich sehr gute Erfahrungen mit AssistantJS gemacht. Es ist an erfahrenere Entwickler gerichtet, die TypeScript-Kenntnisse vorweisen können. Die Abstraktion von bestehenden Plattformen hilft, jetzt zukunftssichere Applikationen zu entwickeln und auf unterschiedlichen Plattformen zu veröffentlichen. Deshalb sage ich: „AssistantJS – los gehts!“

PHP Magazin

Entwickler MagazinDieser Artikel ist im PHP Magazin erschienen. Das PHP Magazin deckt ein breites Spektrum an Themen ab, die für die erfolgreiche Webentwicklung unerlässlich sind.

Natürlich können Sie das PHP Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -