Von Null bis zum Helden "Aktionen bei Google": Ihr Code

Bild


Im ersten Teil haben wir die Grundprinzipien für das Entwerfen und Entwickeln von Anwendungen für den Google-Assistenten herausgefunden. Jetzt ist es Zeit, Ihren eigenen Assistenten zu schreiben, damit Benutzer endlich einen Film für den Abend auswählen können. Shipa_o , raenardev und ComradeGuest Designer sprechen weiter.


Schreiben Sie Ihren Code

Versuchen wir etwas komplizierteres zu schreiben.
Angenommen, unser Agent empfiehlt Filme nach Genre.
Wir bitten ihn, "den Horror zu zeigen", und der Agent analysiert das Genre, sucht nach einem Film in der Sammlung nach Genre und zeigt ihn auf dem Bildschirm an.


Zunächst speichern wir die Filmsammlung in einer Variablen:
var json = { "filmsList": [ { "id": "1", "title": " :  ", "description": "   ", "genres": ["", "", ""], "imageUrl": "http://t3.gstatic.com/images?q=tbn:ANd9GcQEA5a7K9k9ajHIu4Z5AqZr7Y8P7Fgvd4txmQpDrlQY2047coRk", "trailer": "https://www.youtube.com/watch?v=RNksw9VU2BQ" }, { "id": "2", "title": " :  2 –  ", "description": "   ", "genres": ["", "", "", ""], "imageUrl": "http://t3.gstatic.com/images?q=tbn:ANd9GcTPPAiysdP0Sra8XcIhska4MOq86IaDS_MnEmm6H7vQCaSRwahQ", "trailer": "https://www.youtube.com/watch?v=vX_2QRHEl34" }, { "id": "3", "title": "", "description": "  ", "genres": ["", "", ""], "imageUrl": "https://www.kinopoisk.ru/images/film_big/386.jpg", "trailer": "https://www.youtube.com/watch?v=xIe98nyo3xI" } ] }; 

 exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); let result = request.body.queryResult; let parameters = result.parameters; let outputContexts = result.outputContexts; let intentMap = new Map(); //    agent  parameters,       intentMap.set('search-by-genre', searchByGenre.bind(this, agent, parameters)); agent.handleRequest(intentMap); }); function searchByGenre(agent, parameters) { let filmsList = json.filmsList; //     let filteredFilms = filmsList.filter((film) => { // ,             return film.genres.some((genre) => genre == parameters.genre); }); //       let firstFlim = filteredFilms[0]; //    agent.add(firstFlim.title); //       agent.add(new Card({ title: firstFlim.title, imageUrl: firstFlim.imageUrl, text: firstFlim.description, buttonText: ' ', buttonUrl: firstFlim.trailer })); //      agent.add([ "    ?", new Suggestion("  ?"), new Suggestion(""), new Suggestion("") ]); } 

Jetzt ist die Antwort informativer geworden.
Wir zeigen Text, eine Karte mit Informationen und Tipps:


Filminformationen anzeigen

Ein gutes Merkmal von Dialogflow ist, dass es sofort für verschiedene Geräte geeignet ist.
Wenn das Gerät über Lautsprecher verfügt, werden alle Phrasen, die wir an die Add-Methode senden, gesprochen. Wenn kein Bildschirm vorhanden ist, werden die Card und Suggestion einfach nicht angezeigt.


Wir verbinden die Datenbank

Lassen Sie uns die Aufgabe komplizieren und das Abrufen von Daten aus der Datenbank (DB) hinzufügen.
Am einfachsten ist es, die Firebase-Echtzeitdatenbank zu verwenden.
Zum Beispiel werden wir die Admin-Datenbank-API verwenden.


Zuerst müssen Sie eine Datenbank erstellen und füllen.
Sie können dies in demselben Projekt tun, das für Cloud-Funktionen erstellt wurde :


Gefüllte Firebase-Echtzeitdatenbank

Nachdem die Datenbank voll ist, verbinden Sie sie mit Fulfillment:
 //   firebase-admin const firebaseAdmin = require('firebase-admin'); //  firebaseAdmin firebaseAdmin.initializeApp({ credential: firebaseAdmin.credential.applicationDefault(), databaseURL: 'https://<ID->.firebaseio.com' }); //  ,      function getFilmsList() { return firebaseAdmin .database() .ref() .child('filmsList') .once('value') .then(snapshot => { const filmsList = snapshot.val(); console.log('filmsList: ' + JSON.stringify(filmsList)); return filmsList; }) .catch(error => { console.log('getFilmsList error: ' + error); return error; }); } 

Der Zugriff auf die Datenbank erfordert Multithreading. Die Firebase-Datenbank-API wurde für die Verwendung von Promise entwickelt . Die .once('value') Methode .once('value') gibt uns ein Versprechen zurück. Dann holen wir unsere Daten in den then() -Block und geben Promise als Ergebnis der Funktionsausführung mit ihnen zurück.
Es ist wichtig, dieses Versprechen an die handleRequest() -Methode zurückzugeben. Andernfalls wird der Agent mit unserem Rückruf beendet, ohne auf eine Antwort zu warten und das Ergebnis zu verarbeiten.


Versionssuchfilm nach Genre mithilfe der Datenbank:
 'use strict'; const functions = require('firebase-functions'); const firebaseAdmin = require('firebase-admin'); const { WebhookClient } = require('dialogflow-fulfillment'); const { Card, Suggestion } = require('dialogflow-fulfillment'); firebaseAdmin.initializeApp({ credential: firebaseAdmin.credential.applicationDefault(), databaseURL: 'https://<ID->.firebaseio.com' }); exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); let result = request.body.queryResult; let parameters = result.parameters; let outputContexts = result.outputContexts; let intentMap = new Map(); intentMap.set('search-by-genre', searchByGenre.bind(this, agent, parameters)); agent.handleRequest(intentMap); }); function getFilmsList() { return firebaseAdmin .database() .ref() .child('filmsList') .once('value') .then(snapshot => { const filmsList = snapshot.val(); console.log('filmsList: ' + JSON.stringify(filmsList)); return filmsList; }) .catch(error => { console.log('getFilmsList error: ' + error); return error; }); } function searchByGenre(agent, parameters) { return getFilmsList() .then(filmsList => { let filteredFilms = filmsList.filter((film) => { return film.genres.some((genre) => genre == parameters.genre); }); let firstFlim = filteredFilms[0]; agent.add(firstFlim.title); agent.add(new Card({ title: firstFlim.title, imageUrl: firstFlim.imageUrl, text: firstFlim.description, buttonText: ' ', buttonUrl: firstFlim.trailer })); agent.add([ "    ?", new Suggestion("  ?"), new Suggestion(""), new Suggestion("") ]); }) .catch(error => { console.log('getFilmsList error' + error); }); } 

Unvorhersehbarkeit hinzufügen

Unsere Fähigkeiten werden jedes Mal Filme in einer Sequenz produzieren. Die gleichen Antworten werden den Benutzer zuerst ärgern, und dann hört er auf, mit unserem Roboter zu sprechen.


Beheben Sie dies mit der Shuffle-Array-Array- Mischbibliothek .


Fügen Sie die Abhängigkeit zur Datei package.json :


 "dependencies": { // ... "shuffle-array": "^1.0.1" // ... } 

Fügen Sie die Mischung des Arrays hinzu:
 //   const shuffle = require('shuffle-array'); function searchByGenre(agent, parameters) { return getFilmsList() .then(filmsList => { let filteredFilms = filmsList.filter((film) => { return film.genres.some((genre) => genre == parameters.genre); }); //    shuffle(filteredFilms); let firstFlim = filteredFilms[0]; agent.add(firstFlim.title); agent.add(new Card({ title: firstFlim.title, imageUrl: firstFlim.imageUrl, text: firstFlim.description, buttonText: ' ', buttonUrl: firstFlim.trailer })); agent.add([ "    ?", new Suggestion("  ?"), new Suggestion(""), new Suggestion("") ]); }) .catch(error => { console.log('getFilmsList error' + error); }); } 

Jetzt wird jedes Mal ein neuer Film veröffentlicht.
Auf die gleiche Weise können Sie die Ausgabe verschiedener Phrasen hinzufügen: Erstellen Sie ein Array mit Phrasen, mischen Sie es und nehmen Sie die erste des Arrays.


Mit dem Kontext arbeiten

Wir fragen den Agenten:


Zeigen Sie Fantasie

Der Agent zeigt uns den Film "Herr der Ringe".
Dann fragen wir:


Worüber redet er?

Die Leute sagen nicht: "Was ist der Film" Herr der Ringe "" ist unnatürlich. Daher müssen wir Informationen über den angezeigten Film speichern. Dies kann im Zusammenhang erfolgen mit:


 //    ,       agent.setContext({ name: 'current-film', lifespan: 5, parameters: { id: firstFlim.id } }); 

Dann können wir die Informationen über den Film folgendermaßen lesen:
 function genreSearchDescription(agent) { //   current-film const context = agent.getContext('current-film'); console.log('context current-film: ' + JSON.stringify(context)); //  id    const currentFilmId = context.parameters.id; //    return getFilmsList() .then(filmsList => { //    id const currentFilm = filmsList.filter(film => film.id === currentFilmId); agent.add(currentFilm[0].description); agent.add([ ' ?', new Suggestion(''), new Suggestion(' ') ]); }) .catch(error => { console.log('getFilmsList error:' + error); }); } 

Auf die gleiche Weise können wir die Liste der bereits gezeigten Filme filtern.


Telegrammintegration


Dokumentation und nützliche Links:



Für die Integration in Telegram ist fast nichts erforderlich, es müssen jedoch mehrere Funktionen berücksichtigt werden.


1) Wenn die Karte oder der Vorschlag vollständig angezeigt wird, funktionieren sie auch im Telegramm.
Es gibt jedoch einen Fehler : Für schnelle Antworten müssen Sie einen Titel angeben, andernfalls wird im Telegramm "Artikel auswählen" angezeigt.
Bisher ist es uns nicht gelungen, das Problem der vollständigen Angabe des Titels zu lösen.


2) Wenn Intent Suggestion Chips für Google Assistant verwendet


Beispiel

Dann kann dieselbe Funktionalität für Telegramm auf zwei Arten implementiert werden:


Schnelle Antworten


Schnelle Antworten einrichten

Benutzerdefinierte Nutzlast
Hier können Sie schnelle Antworten über die Haupttastatur implementieren:


Tastatur-Screenshot

  { "telegram": { "text": "      :", "reply_markup": { "keyboard": [ [ "", "", "", "", "" ] ], "one_time_keyboard": true, "resize_keyboard": true } } } 

und eingebaute Tastatur:


Eingebauter Tastatur-Screenshot

  { "telegram": { "text": "      :", "reply_markup": { "inline_keyboard": [ [{ "text": "", "callback_data": "" }], [{ "text": "", "callback_data": "" }], [{ "text": "", "callback_data": "" }], [{ "text": "", "callback_data": "" }], [{ "text": "", "callback_data": "" }] ] } } } 

Die Haupttastatur sendet eine Nachricht, die im Verlauf gespeichert wird, die integrierte Tastatur jedoch nicht.


Es ist wichtig zu beachten, dass die Haupttastatur nicht mit der Zeit verschwindet. Hierfür gibt es eine spezielle Anfrage in der Telegramm-API. Daher müssen Sie sicherstellen, dass der Benutzer immer relevante Tipps hat.


3) Wenn Sie für Telegram und Google Assistant eine andere Logik benötigen, können Sie dies folgendermaßen tun:


 let intentRequest = request.body.originalDetectIntentRequest; if(intentRequest.source == 'google'){ let conv = agent.conv(); conv.ask('    ?'); agent.add(conv); } else { agent.add('    ?'); } 

4) Das Senden einer Audiodatei kann wie folgt implementiert werden:


 { "telegram": { "text": "https://s0.vocaroo.com/media/download_temp/Vocaroo_s0bXjLT1pSXK.mp3" } } 

5) Der Kontext in Dialogflow wird 20 Minuten lang gespeichert. Sie müssen dies beim Entwerfen eines Telegramm-Bots berücksichtigen. Wenn der Benutzer 20 Minuten lang abgelenkt ist, kann er nicht an derselben Stelle fortfahren.


Beispiele


Wir werden den Skill-Quellcode in Kürze veröffentlichen. Unmittelbar nach seiner Veröffentlichung.


PS. Was ist beim Hackathon passiert?


Bild


Es waren 2 arbeitsreiche Tage.
Zuerst gab es pädagogische Vorträge und am Nachmittag begannen wir mit der Umsetzung unserer Projekte.
Am nächsten Tag wurden Projekte aktiv überarbeitet und Präsentationen vorbereitet.


Die Jungs von Google haben uns die ganze Zeit geholfen und eine Reihe von Fragen beantwortet, die bei der Arbeit unweigerlich auftauchen. Es war eine großartige Gelegenheit, viel zu lernen und Feedback zu hinterlassen, solange das Bügeleisen noch heiß ist.


Vielen Dank an alle Teilnehmer, die Organisatoren von Google und die Experten, die Vorträge gehalten und uns während des gesamten Hackathons geholfen haben!


Wir haben übrigens den zweiten Platz belegt.


Wenn Sie Fragen haben, können Sie schreiben:
shipa_o
raenardev
Kamerad Guest


Außerdem gibt es einen Telegramm-Chat, der sich mit der Diskussion von Sprachschnittstellen befasst.
https://t.me/conversational_interfaces_ru

Source: https://habr.com/ru/post/de420111/


All Articles