Dari nol hingga pahlawan “Tindakan di Google”: kode Anda

gambar


Pada bagian pertama, kami menemukan prinsip-prinsip dasar merancang dan mengembangkan aplikasi untuk Asisten Google. Sekarang saatnya menulis asisten Anda sendiri sehingga pengguna akhirnya dapat memilih film untuk malam itu. Desainer Shipa_o , raenardev, dan ComradeGuest terus berbicara.


Tulis kode Anda

Mari kita coba menulis sesuatu yang lebih rumit.
Katakanlah agen kami merekomendasikan film berdasarkan genre.
Kami memintanya untuk "Tunjukkan horor", dan agen akan mengurai genre, mencari film dalam koleksi berdasarkan genre dan menampilkannya di layar.


Pertama, kami akan menyimpan koleksi film dalam sebuah variabel:
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("") ]); } 

Sekarang jawabannya menjadi lebih informatif.
Kami menampilkan teks, kartu dengan informasi dan tips:


Tampilkan informasi film

Fitur yang baik dari Dialogflow adalah bahwa ia berada di luar kotak yang diadaptasi untuk perangkat yang berbeda.
Jika perangkat memiliki speaker, maka semua frasa yang kami kirim ke metode add akan disuarakan, dan jika tidak ada layar, objek Card dan Suggestion tidak akan ditampilkan.


Kami menghubungkan database

Mari menyulitkan tugas dan menambahkan memperoleh data dari database (DB).
Cara termudah adalah dengan menggunakan basis data realtime firebase.
Misalnya, kami akan menggunakan API Database Admin.


Pertama, Anda perlu membuat database dan mengisinya.
Anda dapat melakukan ini di proyek yang sama yang dibuat untuk Fungsi Cloud :


Database realtime firebase yang terisi

Setelah database penuh, sambungkan ke pemenuhan:
 //   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; }); } 

Mengakses database membutuhkan multithreading. API basis data firebase dirancang untuk menggunakan Janji . Metode .once('value') mengembalikan kita Janji. Lalu kami mendapatkan data kami di blok then() dan mengembalikan Promise dengan mereka sebagai hasil dari eksekusi fungsi.
Penting untuk mengembalikan Janji ini ke metode handleRequest() , jika tidak, agen akan keluar dengan callback kami tanpa menunggu respons dan memproses hasilnya.


Film versi pencarian berdasarkan genre menggunakan database:
 '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); }); } 

Tambahkan ketidakpastian

Keahlian kami akan menghasilkan film dalam satu urutan setiap kali. Jawaban yang sama akan mengganggu pengguna pada awalnya, dan kemudian dia akan berhenti berbicara dengan robot kami.


Perbaiki ini dengan perpustakaan pencampuran array shuffle-array.


Tambahkan dependensi ke file package.json :


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

Tambahkan pencampuran array:
 //   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); }); } 

Sekarang setiap kali film baru akan dikeluarkan.
Dengan cara yang sama, Anda dapat menambahkan output dari frasa yang berbeda: membuat array dengan frasa, mencampurnya dan mengambil yang pertama dari array.


Bekerja dengan konteks

Kami bertanya kepada agen:


Perlihatkan fantasi

Agen itu memperlihatkan kepada kita film "Lord of the Rings."
Lalu kami bertanya:


Apa yang dia bicarakan?

Orang tidak mengatakan: "Apakah film" Lord of the Rings "" itu tidak wajar. Karena itu, kita perlu menyimpan informasi tentang film yang ditampilkan. Ini dapat dilakukan dalam konteks:


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

Maka kita dapat membaca informasi tentang film seperti ini:
 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); }); } 

Dengan cara yang sama, kita dapat memfilter daftar film yang sudah ditampilkan.


Integrasi Telegram


Dokumentasi dan tautan bermanfaat:



Untuk berintegrasi dengan Telegram, hampir tidak ada yang diperlukan, tetapi ada beberapa fitur yang perlu dipertimbangkan.


1) Jika dalam pemenuhan untuk menampilkan Kartu atau Saran, maka di Telegram mereka juga akan berfungsi.
Tetapi ada satu bug : untuk balasan cepat, Anda harus menentukan judul, jika tidak, "Pilih item" akan ditampilkan di Telegram.
Sejauh ini kami belum berhasil memecahkan masalah menunjukkan judul dalam pemenuhan.


2) Jika niat menggunakan Chips Saran untuk asisten google


Contoh

maka fungsi yang sama untuk Telegram dapat diimplementasikan dalam dua cara:


Balasan cepat


Menyiapkan balasan cepat

Muatan kustom
Di sini Anda dapat menerapkan jawaban cepat menggunakan keyboard utama:


Tangkapan layar keyboard

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

dan keyboard bawaan:


Tangkapan layar keyboard bawaan

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

Keyboard utama akan mengirim pesan yang akan disimpan dalam riwayat, sedangkan keyboard bawaan tidak.


Penting untuk diingat bahwa keyboard utama tidak hilang seiring waktu. Ada permintaan khusus untuk ini di API Telegram. Karena itu, Anda perlu memastikan bahwa pengguna selalu memiliki kiat yang relevan.


3) Jika Anda memerlukan logika berbeda untuk Telegram dan Google Assistant, Anda dapat melakukan ini seperti ini:


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

4) Mengirim file audio dapat diimplementasikan sebagai berikut:


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

5) Konteks dalam Dialogflow akan disimpan selama 20 menit. Anda perlu mempertimbangkan ini saat merancang bot Telegram. Jika pengguna terganggu selama 20 menit, maka ia tidak akan dapat melanjutkan dari tempat yang sama.


Contohnya


Kami akan segera mempublikasikan kode sumber keterampilan. Segera setelah dirilis.


PS. Apa yang terjadi di hackathon.


gambar


Itu adalah 2 hari yang sibuk.
Pada awalnya ada kuliah pendidikan, dan pada sore hari kami mulai menerapkan proyek kami.
Hari berikutnya secara aktif merevisi proyek dan menyiapkan presentasi.


Orang-orang dari Google selama ini membantu kami dan menjawab banyak pertanyaan yang pasti muncul dalam pekerjaan. Itu adalah kesempatan bagus untuk belajar banyak dan meninggalkan umpan balik sementara setrika masih panas.


Terima kasih kepada semua peserta, penyelenggara Google dan para pakar yang memberikan ceramah dan membantu kami sepanjang hackathon!


Ngomong-ngomong, kami menempati posisi kedua.


Jika Anda memiliki pertanyaan, Anda dapat menulis:
shipa_o
raenardev
comradeguest


Dan juga ada obrolan Telegram yang didedikasikan untuk diskusi antarmuka suara, buka:
https://t.me/conversational_interfaces_ru

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


All Articles