실습: Dialogflow 및 Actions on Google을 사용하여 Google 어시스턴트용 TV 가이드 작업 만들기

1. 소개

TV를 시청하고 있는데 리모컨을 찾을 수 없거나, TV 채널을 일일이 방문하여 볼 만한 프로그램이 있는지 확인하고 싶지 않으신가요? Google 어시스턴트에게 TV에서 무엇을 볼 수 있는지 물어보세요. 이 실습에서는 Dialogflow를 사용하여 간단한 작업을 빌드하고 Google 어시스턴트와 통합하는 방법을 알아봅니다.

실습은 일반 클라우드 개발자 환경을 반영하여 다음과 같은 순서로 진행됩니다.

  1. Dialogflow v2 에이전트 만들기
  2. 맞춤 항목 만들기
  3. 인텐트 만들기
  4. Firebase Functions로 웹훅 설정
  5. 챗봇 테스트
  6. Google 어시스턴트 통합 사용 설정

빌드할 항목

Google 어시스턴트용 대화형 TV 가이드 챗봇 에이전트를 빌드합니다. TV 가이드에 특정 채널에서 현재 방송 중인 프로그램을 물어볼 수 있습니다. 예를 들어 'MTV에서 뭘 하고 있어?'라고 물으면 TV 가이드 작업에서 현재 방송 중인 프로그램과 다음 방송 프로그램을 알려줍니다.

학습할 내용

  • Dialogflow v2로 챗봇을 만드는 방법
  • Dialogflow로 맞춤 항목을 만드는 방법
  • Dialogflow로 선형 대화를 만드는 방법
  • Dialogflow 및 Firebase Functions로 웹훅 fulfillment를 설정하는 방법
  • Actions on Google을 사용하여 애플리케이션을 Google 어시스턴트로 가져오는 방법

기본 요건

  • Dialogflow 에이전트를 만들려면 Google ID / Gmail 주소가 필요합니다.
  • JavaScript에 대한 기본 지식은 필수는 아니지만 웹훅 이행 코드를 변경하려는 경우 유용할 수 있습니다.

2. 설정

브라우저에서 웹 활동 사용 설정하기

  1. 클릭: http://myaccount.google.com/activitycontrols

  1. 웹 및 앱 활동이 사용 설정되어 있는지 확인합니다.

bf8d16b828d6f79a.png

Dialogflow 에이전트 만들기

  1. 열기: https://console.dialogflow.com

  1. 왼쪽 막대에서 로고 바로 아래에 있는 '새 에이전트 만들기'를 선택합니다. 기존 에이전트가 있는 경우 먼저 드롭다운을 클릭합니다.

1d7c2b56a1ab95b8.png

  1. 에이전트 이름을 지정합니다. your-name-tvguide (자신의 이름을 사용하세요)

35237b5c5c539ecc.png

  1. 기본 언어로 영어 - en을 선택합니다.
  2. 기본 시간대로 가장 가까운 시간대를 선택합니다.
  3. 만들기를 클릭합니다.

Dialogflow 구성

  1. 왼쪽 메뉴에서 프로젝트 이름 옆에 있는 톱니바퀴 아이콘을 클릭합니다.

1d7c2b56a1ab95b8.png

  1. 내 TV 가이드라는 에이전트 설명을 입력합니다.

26f262d359c49075.png

  1. 로그 설정까지 아래로 스크롤하고 Dialogflow의 상호작용을 로깅하고 Google Cloud Stackdriver의 모든 상호작용을 로깅하도록 스위치를 모두 전환합니다. 나중에 작업을 디버그하려는 경우에 필요합니다.

e80c17acc3cce993.png

  1. 저장을 클릭합니다.

Actions on Google 구성

  1. 오른쪽 패널의 Google 어시스턴트에서 작동 방식 보기에서 Google 어시스턴트 링크를 클릭합니다.

5a4940338fc351e3.png

그러면 http://console.actions.google.com이 열립니다.

Actions on Google을 처음 사용하는 경우 다음 양식을 먼저 작성해야 합니다.

3fd4e594fa169072.png

  1. 프로젝트 이름을 클릭하여 시뮬레이터에서 작업을 열어봅니다.
  2. 메뉴 바에서 테스트를 선택합니다.

6adb83ffb7adeb78.png

  1. 시뮬레이터가 영어로 설정되어 있는지 확인하고 테스트 앱과 대화하기를 클릭합니다.

작업이 기본적인 Dialogflow 기본 인텐트로 인사합니다. 즉, Google 어시스턴트와의 통합 설정이 완료되었습니다.

3. 커스텀 개체

항목은 앱이나 기기에서 작업을 수행하는 객체입니다. 매개변수 / 변수라고 생각하면 됩니다. TV 가이드에서는 "MTV에서 뭘 해?"라고 묻습니다. MTV는 항목과 변수입니다. '내셔널 지오그래픽' 또는 '코미디 센트럴'과 같은 다른 채널을 요청할 수도 있습니다. 수집된 항목은 TV 가이드 API 웹 서비스에 대한 요청에서 매개변수로 사용됩니다.

Dialogflow 항목에 대한 자세한 정보는 여기를 참고하세요.

채널 항목 만들기

  1. Dialogflow 콘솔에서 항목 메뉴 항목을 클릭합니다.
  2. 항목 만들기를 클릭합니다.
  3. 법인 이름: channel (모두 소문자여야 함)
  4. 채널 이름을 전달합니다. (Google 어시스턴트가 다른 것을 이해하는 경우를 대비해 일부 채널에는 동의어가 필요합니다.) 입력하는 동안 Tab 키와 Enter 키를 사용할 수 있습니다. 채널 번호를 참조 값으로 입력합니다. 채널 이름을 다음과 같이 동의어로 사용합니다.
  • 1 - 1, Net 1, Net Station 1

ee4e4955aa77232d.png

5**.** 파란색 저장 버튼 옆에 있는 메뉴 버튼을 클릭하여 **원시 편집** 모드로 전환합니다.

e294b49b123e034f.png

  1. CSV 형식으로 다른 항목을 복사하여 붙여넣습니다.
"2","2","Net 2, Net Station 2"
"3","3","Net 3, Net Station 3"
"4","4","RTL 4"
"5","5","Movie Channel"
"6","6","Sports Channel"
"7","7","Comedy Central"
"8","8","Cartoon Network"
"9","9","National Geographic"
"10","10","MTV"

ed78514afd5badef.png

  1. 저장을 클릭합니다.

4. 인텐트

Dialogflow인텐트를 사용하여 사용자의 의도를 분류합니다. 인텐트에는 학습 문구가 있는데, 이는 사용자가 에이전트에게 할 수 있는 말의 예시입니다. 예를 들어 TV에서 무엇을 하는지 알고 싶은 사용자는 '오늘 TV에서 무엇을 해?'라고 물을 수 있습니다. "지금 재생 중인 거 뭐야?"라고 말하거나 간단히 "tvguide"라고 말합니다.

사용자가 사용자 표현이라고 하는 내용을 쓰거나 말하면 Dialogflow는 사용자 표현을 에이전트에서 가장 유사한 인텐트와 일치시킵니다. 인텐트 일치를 인텐트 분류라고도 합니다.

Dialogflow 인텐트에 대한 자세한 정보는 여기를 참고하세요.

기본 시작 인텐트 수정

새 Dialogflow 에이전트를 만들면 두 개의 기본 인텐트가 자동으로 생성됩니다. 기본 시작 인텐트는 에이전트와 대화를 시작할 때 가장 먼저 표시되는 흐름입니다. 기본 대체 인텐트는 에이전트가 사용자를 이해할 수 없거나 사용자가 방금 말한 내용과 일치하는 인텐트를 찾을 수 없을 때 표시되는 흐름입니다.

  1. 기본 시작 인텐트를 클릭합니다.

Google 어시스턴트의 경우 기본 시작 인텐트로 자동 시작됩니다. 이는 Dialogflow가 시작 이벤트를 수신 대기하고 있기 때문입니다. 하지만 입력한 학습 문구 중 하나를 말하여 인텐트를 호출할 수도 있습니다.

6beee64e8910b85d.png

다음은 기본 시작 인텐트의 환영 메시지입니다.

사용자

Agent

"Hey Google, talk to your-name-tvguide."

"안녕하세요. 저는 TV 가이드 상담사입니다. 현재 TV 채널에서 재생 중인 콘텐츠를 알려드릴 수 있습니다. 예를 들어'MTV에서 뭘 해?'라고 물을 수 있습니다.

  1. 응답까지 아래로 스크롤합니다.
  2. 모든 텍스트 응답을 지웁니다.
  3. 다음 인사말이 포함된 새 텍스트 응답을 하나 만듭니다.

Welcome, I am the TV Guide agent. I can tell you what's currently playing on a TV channel. For example, you can ask me: What's on MTV?

84a1110a7f7edba2.png

  1. 저장을 클릭합니다.

임시 테스트 인텐트 만들기

테스트를 위해 나중에 웹훅을 테스트할 수 있도록 임시 테스트 인텐트를 만듭니다.

  1. 인텐트 메뉴 항목을 다시 클릭합니다.
  2. 인텐트 만들기를 클릭합니다.
  3. 인텐트 이름을 입력합니다. Test Intent 대문자 T와 대문자 I를 사용해야 합니다. - 의도를 다르게 입력하면 백엔드 서비스가 작동하지 않습니다.)

925e02caa4de6b99.png

  1. 학습 문구 추가를 클릭합니다.
  • Test my agent
  • Test intent

2e44ddb2fae3c841.png

  1. Fulfillment > Enable Fulfillment을 클릭합니다.

7eb73ba04d76140e.png

이번에는 응답을 하드코딩하지 않습니다. 대답은 클라우드 함수에서 제공됩니다.

  1. 이 인텐트에 웹훅 호출 사용 설정 스위치를 전환합니다.

748a82d9b4d7d253.png

  1. 저장을 클릭합니다.

채널 인텐트 만들기

채널 의도에는 대화의 다음 부분이 포함됩니다.

사용자

Agent

"Comedy Central에서 뭐 해?"

""현재 Comedy Central에서 오후 6시부터 심슨 가족이 재생되고 있습니다. 그 후 오후 7시에 Family Guy가 시작됩니다.'

  1. 인텐트 메뉴 항목을 다시 클릭합니다.
  2. 인텐트 만들기를 클릭합니다.
  3. 의도 이름(Channel Intent)을 입력합니다. 대문자 T와 대문자 I를 사용해야 합니다. - 의도를 다르게 입력하면 백엔드 서비스가 작동하지 않습니다.)
  4. 학습 문구 추가를 클릭하고 다음을 추가합니다.
  • What's on MTV?
  • What's playing on Comedy Central?
  • What show will start at 8 PM on National Geographic?
  • What is currently on TV?
  • What is airing now.
  • Anything airing on Net Station 1 right now?
  • What can I watch at 7 PM?
  • What's on channel MTV?
  • What's on TV?
  • Please give me the tv guide.
  • Tell me what is on television.
  • What's on Comedy Central from 10 AM?
  • What will be on tv at noon?
  • Anything on National Geographic?
  • TV Guide

6eee02db02831397.png

  1. 작업 및 매개변수까지 아래로 스크롤합니다.

b7e917026760218a.png

Dialogflow에서 인식하는 @channel@sys.time 항목을 확인하세요. 웹훅의 후반부에서 매개변수 이름과 매개변수 값이 웹 서비스로 전송됩니다. 예를 들면 다음과 같습니다.

channel=8

time=2020-01-29T19:00:00+01:00

  1. 채널필수로 표시

TV 가이드 에이전트와 대화할 때는 항상 슬롯 매개변수 이름 channel을 입력해야 합니다. 대화 시작 시 채널 이름이 언급되지 않은 경우 Dialogflow는 모든 매개변수 슬롯이 채워질 때까지 추가 질문을 합니다. 6f36973fd789c182.png

프롬프트에 다음을 입력합니다.

  • For which TV channel do you want to hear the tv guide information?
  • In which TV channel are you interested?

cdb5601ead9423f8.png

  1. 시간 매개변수를 필수 항목으로 설정하지 마세요.

시간은 선택사항입니다. 시간을 지정하지 않으면 웹 서비스에서 현재 시간을 반환합니다.

  1. Fulfillment를 클릭합니다.

이번에는 응답을 하드코딩하지 않습니다. 대답은 클라우드 함수에서 제공됩니다. 따라서 이 인텐트에 웹훅 호출 사용 설정 스위치를 전환합니다.

  1. 저장을 클릭합니다.

5. 웹훅 Fulfillment

에이전트에 정적 인텐트 응답 이상이 필요한 경우 fulfillment를 사용하여 웹 서비스를 에이전트에 연결해야 합니다. 웹 서비스를 연결하면 사용자 표현에 따라 작업을 수행하고 사용자에게 동적 응답을 보낼 수 있습니다. 예를 들어 사용자가 MTV의 TV 일정을 받으려고 하면 웹 서비스가 데이터베이스를 확인하여 사용자에게 MTV의 일정을 응답할 수 있습니다.

  1. 기본 메뉴에서 Fulfillment를 클릭합니다.
  2. 인라인 편집기 스위치 사용 설정

cc84351f0d03ab6f.png

간단한 웹훅 테스트 및 구현의 경우 인라인 편집기를 사용할 수 있습니다. 서버리스 Firebase용 Cloud Functions를 사용합니다.

  1. 편집기에서 index.js 탭을 클릭하고 Node.js 코드용 JavaScript 부분을 복사하여 붙여넣습니다.
'use strict';

process.env.DEBUG = 'dialogflow:debug';

const {
  dialogflow,
  BasicCard,
  Button,
  Image,
  List
 } = require('actions-on-google');

const functions = require('firebase-functions');
const moment = require('moment');
const TVGUIDE_WEBSERVICE = 'https://tvguide-e4s5ds5dsa-ew.a.run.app/channel';
const { WebhookClient } = require('dialogflow-fulfillment');
var spokenText = '';
var results = null;


/* When the Test Intent gets invoked. */
function testHandler(agent) {
    let spokenText = 'This is a test message, when you see this, it means your webhook fulfillment worked!';

    if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
        let conv = agent.conv();
        conv.ask(spokenText);
        conv.ask(new BasicCard({
            title: `Test Message`,
            subTitle: `Dialogflow Test`,
            image: new Image({
                url: 'https://dummyimage.com/600x400/000/fff',
                alt: 'Image alternate text',
            }),
            text: spokenText,
            buttons: new Button({
                title: 'This is a button',
                url: 'https://assistant.google.com/',
            }),
        }));
        // Add Actions on Google library responses to your agent's response
        agent.add(conv);
    } else {
        agent.add(spokenText);
    }
}

/* When the Channel Intent gets invoked. */
function channelHandler(agent) {
    var jsonResponse = `{"ID":10,"Listings":[{"Title":"Catfish Marathon","Date":"2018-07-13","Time":"11:00:00"},{"Title":"Videoclips","Date":"2018-07-13","Time":"12:00:00"},{"Title":"Pimp my ride","Date":"2018-07-13","Time":"12:30:00"},{"Title":"Jersey Shore","Date":"2018-07-13","Time":"13:00:00"},{"Title":"Jersey Shore","Date":"2018-07-13","Time":"13:30:00"},{"Title":"Daria","Date":"2018-07-13","Time":"13:45:00"},{"Title":"The Real World","Date":"2018-07-13","Time":"14:00:00"},{"Title":"The Osbournes","Date":"2018-07-13","Time":"15:00:00"},{"Title":"Teenwolf","Date":"2018-07-13","Time":"16:00:00"},{"Title":"MTV Unplugged","Date":"2018-07-13","Time":"16:30:00"},{"Title":"Rupauls Drag Race","Date":"2018-07-13","Time":"17:30:00"},{"Title":"Ridiculousness","Date":"2018-07-13","Time":"18:00:00"},{"Title":"Punk'd","Date":"2018-07-13","Time":"19:00:00"},{"Title":"Jersey Shore","Date":"2018-07-13","Time":"20:00:00"},{"Title":"MTV Awards","Date":"2018-07-13","Time":"20:30:00"},{"Title":"Beavis & Butthead","Date":"2018-07-13","Time":"22:00:00"}],"Name":"MTV"}`;
    var results = JSON.parse(jsonResponse);
    var listItems = {};
    spokenText = getSpeech(results);

    for (var i = 0; i < results['Listings'].length; i++) {
        listItems[`SELECT_${i}`] = {
            title: `${getSpokenTime(results['Listings'][i]['Time'])} - ${results['Listings'][i]['Title']}`,
            description: `Channel: ${results['Name']}`
        }
    }
    if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
        let conv = agent.conv();
        conv.ask(spokenText);
        conv.ask(new List({
            title: 'TV Guide',
            items: listItems
        }));
        // Add Actions on Google library responses to your agent's response
        agent.add(conv);
    } else {
        agent.add(spokenText);
    }
}

/**
 * Return a text string to be spoken out by the Google Assistant
 * @param {object} JSON tv results
 */
var getSpeech = function(tvresults) {
    let s = "";
    if(tvresults['Listings'][0]) {
        let channelName = tvresults['Name'];
        let currentlyPlayingTime = getSpokenTime(tvresults['Listings'][0]['Time']);
        let laterPlayingTime = getSpokenTime(tvresults['Listings'][1]['Time']);
        s = `On ${channelName} from ${currentlyPlayingTime}, ${tvresults['Listings'][0]['Title']} is playing.
        Afterwards at ${laterPlayingTime}, ${tvresults['Listings'][1]['Title']} will start.`
    }

    return s;
}

/**
 * Return a natural spoken time
 * @param {string} time in 'HH:mm:ss' format
 * @returns {string} spoken time (like 8 30 pm i.s.o. 20:00:00)
 */
var getSpokenTime = function(time){
    let datetime = moment(time, 'HH:mm:ss');
    let min = moment(datetime).format('m');
    let hour = moment(datetime).format('h');
    let partOfTheDay = moment(datetime).format('a');

    if (min == '0') {
        min = '';
    }

    return `${hour} ${min} ${partOfTheDay}`;
};

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
    var agent = new WebhookClient({ request, response });

    console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
    console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
   
    let channelInput = request.body.queryResult.parameters.channel;
    let requestedTime = request.body.queryResult.parameters.time;
    let url = `${TVGUIDE_WEBSERVICE}/${channelInput}`;

    var intentMap = new Map();
    intentMap.set('Test Intent', testHandler);
    intentMap.set('Channel Intent', channelHandler);
    agent.handleRequest(intentMap);
});

cc84351f0d03ab6f.png

  1. 편집기에서 package.json 탭을 클릭하고 모든 Node.js 패키지 관리자 (NPM) 라이브러리를 가져오는 다음 JSON 코드를 복사하여 붙여넣습니다.
{
  "name": "tvGuideFulfillment",
  "description": "Requesting TV Guide information from a web service.",
  "version": "1.0.0",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "8"
  },
  "scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
  "dependencies": {
    "actions-on-google": "^2.2.0",
    "firebase-admin": "^5.13.1",
    "firebase-functions": "^2.0.2",
    "request": "^2.85.0",
    "request-promise": "^4.2.5",
    "moment" : "^2.24.0",
    "dialogflow-fulfillment": "^0.6.1"
  }
}

af01460c2a023e68.png

  1. 배포 버튼을 클릭합니다. 서버리스 함수가 배포되므로 잠시 시간이 걸립니다. 화면 하단에 상태를 알려주는 팝업이 표시됩니다.
  2. 코드가 작동하는지 확인하기 위해 웹훅을 테스트해 보겠습니다. 오른쪽의 시뮬레이터에 다음을 입력합니다.

Test my agent.

모든 것이 올바르면 'This is a test message'가 표시됩니다.

  1. 이제 채널 의도를 테스트해 보겠습니다. 다음 질문을 해 보세요.

What's on MTV?

모든 항목이 올바르면 다음이 표시됩니다.

'오후 4시 30분부터 MTV에서 MTV 언플러그드가 방송됩니다. 그 후 오후 5시 30분에 RuPaul's Drag Race가 시작됩니다.'

선택사항 단계 - Firebase

다른 채널로 테스트하면 TV 결과가 동일한 것을 확인할 수 있습니다. 이는 클라우드 함수가 아직 실제 웹 서버에서 가져오지 않기 때문입니다.

이를 위해 아웃바운드 네트워크 연결을 만들어야 합니다.

웹 서비스로 이 애플리케이션을 테스트하려면 Firebase 요금제를 Blaze로 업그레이드하세요. 참고: 이 단계는 선택사항입니다. 이 실습의 다음 단계로 이동하여 Actions on Google에서 애플리케이션을 계속 테스트할 수도 있습니다.

  1. Firebase Console(https://console.firebase.google.com)로 이동합니다.

  1. 화면 하단에서 업그레이드 버튼을 누릅니다.

ad38bc6d07462abf.png

팝업에서 Blaze 요금제를 선택합니다.

  1. 이제 웹훅이 작동하는 것을 확인했으므로 계속해서 index.js의 코드를 아래 코드로 바꿉니다. 이렇게 하면 웹 서비스에서 TV 가이드 정보를 요청할 수 있습니다.
'use strict';

process.env.DEBUG = 'dialogflow:debug';

const {
  dialogflow,
  BasicCard,
  Button,
  Image,
  List
 } = require('actions-on-google');

const functions = require('firebase-functions');
const moment = require('moment');
const { WebhookClient } = require('dialogflow-fulfillment');
const rp = require('request-promise');

const TVGUIDE_WEBSERVICE = 'https://tvguide-e4s5ds5dsa-ew.a.run.app/channel';
var spokenText = '';
var results = null;


/* When the Test Intent gets invoked. */
function testHandler(agent) {
    let spokenText = 'This is a test message, when you see this, it means your webhook fulfillment worked!';

    if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
        let conv = agent.conv();
        conv.ask(spokenText);
        conv.ask(new BasicCard({
            title: `Test Message`,
            subTitle: `Dialogflow Test`,
            image: new Image({
                url: 'https://dummyimage.com/600x400/000/fff',
                alt: 'Image alternate text',
            }),
            text: spokenText,
            buttons: new Button({
                title: 'This is a button',
                url: 'https://assistant.google.com/',
            }),
        }));
        // Add Actions on Google library responses to your agent's response
        agent.add(conv);
    } else {
        agent.add(spokenText);
    }
}

/* When the Channel Intent gets invoked. */
function channelHandler(agent) {
    var listItems = {};
    spokenText = getSpeech(results);

    for (var i = 0; i < results['Listings'].length; i++) {
        listItems[`SELECT_${i}`] = {
            title: `${getSpokenTime(results['Listings'][i]['Time'])} - ${results['Listings'][i]['Title']}`,
            description: `Channel: ${results['Name']}`

        }
    }
    if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
        let conv = agent.conv();
        conv.ask(spokenText);
        conv.ask(new List({
            title: 'TV Guide',
            items: listItems
        }));
        // Add Actions on Google library responses to your agent's response
        agent.add(conv);
    } else {
        agent.add(spokenText);
    }
}

/**
 * Return a text string to be spoken out by the Google Assistant
 * @param {object} JSON tv results
 */
var getSpeech = function(tvresults) {
    let s = "";
    if(tvresults && tvresults['Listings'][0]) {
        let channelName = tvresults['Name'];
        let currentlyPlayingTime = getSpokenTime(tvresults['Listings'][0]['Time']);
        let laterPlayingTime = getSpokenTime(tvresults['Listings'][1]['Time']);
        s = `On ${channelName} from ${currentlyPlayingTime}, ${tvresults['Listings'][0]['Title']} is playing.
        Afterwards at ${laterPlayingTime}, ${tvresults['Listings'][1]['Title']} will start.`
    }

    return s;
}

/**
 * Return a natural spoken time
 * @param {string} time in 'HH:mm:ss' format
 * @returns {string} spoken time (like 8 30 pm i.s.o. 20:00:00)
 */
var getSpokenTime = function(time){
    let datetime = moment(time, 'HH:mm:ss');
    let min = moment(datetime).format('m');
    let hour = moment(datetime).format('h');
    let partOfTheDay = moment(datetime).format('a');

    if (min == '0') {
        min = '';
    }

    return `${hour} ${min} ${partOfTheDay}`;
};

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
    var agent = new WebhookClient({ request, response });

    console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
    console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
   
    let channelInput = request.body.queryResult.parameters.channel;
    let requestedTime = request.body.queryResult.parameters.time;
    let url = `${TVGUIDE_WEBSERVICE}/${channelInput}`;

    if (requestedTime) {
        console.log(requestedTime);
        let offsetMin = moment().utcOffset(requestedTime)._offset;
        console.log(offsetMin);
        let time = moment(requestedTime).utc().add(offsetMin,'m').format('HH:mm:ss');
        url = `${TVGUIDE_WEBSERVICE}/${channelInput}/${time}`;
      }
    
      console.log(url);
  
      var options = {
          uri: encodeURI(url),
          json: true
      };
       
      // request promise calls an URL and returns the JSON response.
      rp(options)
        .then(function(tvresults) {
            console.log(tvresults);
            // the JSON response, will need to be formatted in 'spoken' text strings.
            spokenText = getSpeech(tvresults);
            results = tvresults;
        })
        .catch(function (err) {
            console.error(err);
        })
        .finally(function(){
            // kick start the Dialogflow app
            // based on an intent match, execute
            var intentMap = new Map();
            intentMap.set('Test Intent', testHandler);
            intentMap.set('Channel Intent', channelHandler);
            agent.handleRequest(intentMap);
        });
});

6. Actions on Google

Actions on Google은 Google 어시스턴트용 개발 플랫폼입니다. 이를 통해 확장된 기능을 제공하는 Google 어시스턴트용 애플릿인 '작업'을 서드 파티에서 개발할 수 있습니다.

Google에 앱을 열거나 앱과 대화해 달라고 요청하여 Google 작업을 호출해야 합니다.

이렇게 하면 작업이 열리고, 음성이 변경되며, '기본' Google 어시스턴트 범위에서 벗어납니다. 즉, 이 시점부터 에이전트에게 요청하는 모든 항목은 사용자가 만들어야 합니다. Google 날씨 정보를 원하는 경우 갑자기 Google 어시스턴트에게 요청할 수 없습니다. 먼저 작업 범위(앱)를 종료해야 합니다.

Google 어시스턴트 시뮬레이터에서 작업 테스트하기

다음 대화를 테스트해 보겠습니다.

사용자

Google 어시스턴트

"Hey Google, your-name-tv-guide와 대화해 줘."

'네. your-name-tv-guide를 보여 줘."

사용자

Your-Name-TV-Guide Agent

-

"안녕하세요. 저는 TV 가이드입니다."

에이전트 테스트

'테스트 메시지입니다. 이 메시지가 표시되면 웹훅 이행이 작동한 것입니다.'

MTV에서 무엇을 볼 수 있나요?

오후 4시 30분부터 MTV에서 MTV Unplugged가 재생됩니다. 그 후 오후 5시 30분에 RuPaul's Drag Race가 시작됩니다.

  1. Google 어시스턴트 시뮬레이터로 다시 전환

열기: https://console.actions.google.com

  1. 마이크 아이콘을 클릭하고 다음과 같이 질문합니다.

c3b200803c7ba95e.png

  • Talk to my test agent
  • Test my agent

Google 어시스턴트는 다음과 같이 대답해야 합니다.

5d93c6d037c8c8eb.png

  1. 이제 다음과 같이 질문합니다.
  • What's on Comedy Central?

이는 다음을 반환해야 합니다.

현재 Comedy Central에서 오후 6시부터 심슨 가족이 재생되고 있습니다. 이후 오후 7시에는 Family Guy가 시작됩니다.

7. 축하합니다

Dialogflow로 첫 번째 Google 어시스턴트 작업을 만들었습니다. 잘하셨습니다.

테스트 모드에서 작업이 실행되었으며 이 모드는 Google 계정과 연결되어 있습니다. Nest 기기 또는 iOS 또는 Android 휴대전화의 Google 어시스턴트 앱에 동일한 계정으로 로그인하는 경우 작업을 테스트할 수도 있습니다.

이것은 워크숍 데모입니다. 하지만 Google 어시스턴트용 애플리케이션을 실제로 빌드하는 경우 승인을 위해 작업을 제출할 수 있습니다. 자세한 내용은 이 가이드를 참고하세요.

학습한 내용

  • Dialogflow v2로 챗봇을 만드는 방법
  • Dialogflow로 맞춤 항목을 만드는 방법
  • Dialogflow로 선형 대화를 만드는 방법
  • Dialogflow 및 Firebase Functions로 웹훅 fulfillment를 설정하는 방법
  • Actions on Google을 사용하여 애플리케이션을 Google 어시스턴트로 가져오는 방법

다음 단계

이 Codelab이 재미있었나요? 이 멋진 실습을 살펴보세요.

Google Chat에 통합하여 이 Codelab을 계속 진행하세요.

G Suite 및 Dialogflow로 TV 가이드 Google Chat 만들기