1. はじめに
テレビを見ているけれどリモコンが見当たらない、テレビのチャンネルを見ずにテレビに何かいいものがあるかチェックしたくないという方もいるでしょう。Google アシスタントに、テレビに何が表示されているか尋ねてみましょう。このラボでは、Dialogflow を使用してシンプルなアクションを作成し、Google アシスタントと統合する方法を学びます。
ここでは、一般的なクラウド開発プロセスの手順に沿って以下の順序で演習を進めます。
- Dialogflow v2 エージェントを作成する
- カスタム エンティティを作成する
- インテントを作成する
- Firebase Functions を使用して Webhook を設定する
- chatbot をテストする
- Google アシスタントの統合を有効にする
作業内容
Google アシスタント用のインタラクティブ テレビガイド chatbot エージェントを作成します。特定のチャンネルで現在放送されている内容をテレビガイドに尋ねることができます。次に例を示します。「MTV では何を視聴していますか?」テレビガイドのアクションでは、現在再生中のものと次のコンテンツがわかります。 |
|
学習内容
- Dialogflow v2 を使用して chatbot を作成する方法
- Dialogflow でカスタム エンティティを作成する方法
- Dialogflow で直線的な会話を作成する方法
- Dialogflow と Firebase Functions を使用して Webhook フルフィルメントを設定する方法
- Actions on Google を使用してアプリケーションを Google アシスタントに取り込む方法
前提条件
- Dialogflow エージェントを作成するには、Google ID または Gmail アドレスが必要です。
- JavaScript の基本的な知識は必要ありませんが、Webhook フルフィルメント コードを変更する場合に役立ちます。
2. 設定方法
ブラウザでウェブ アクティビティを有効にする
- ウェブと[アプリのアクティビティ] が [有効] になっている場合:

Dialogflow エージェントを作成する
- 左側のバーのロゴの下にある [Create New Agent] を選択します。既存のエージェントがいる場合は、まずプルダウンをクリックします。

- エージェント名を指定:
your-name-tvguide(独自の名前を使用)

- デフォルトの言語として [English - en] を選択します。
- デフォルトのタイムゾーンとして、最も近いタイムゾーンを選択します。
- [作成] をクリックします。
Dialogflow を構成する
- 左側のメニューで、プロジェクト名の横にある歯車アイコンをクリックします。

- エージェントの説明として「My TV Guide」を入力します。

- [Log Settings] まで下にスクロールし、両方のスイッチを切り替えて、[Dialogflow のインタラクションをログに記録] と [Google Cloud Stackdriver のすべてのインタラクションをログに記録] をオンにします。これは、後でアクションをデバッグする場合に必要になります。

- [保存] をクリックします。
Actions on Google を設定する
- 右側のパネルの [Google アシスタントでの動作を確認] にある [Google アシスタント] のリンクをクリックします。

http://console.actions.google.com を開きます。
Actions on Google を初めて使用する場合は、まずこちらのフォームに入力する必要があります。

- プロジェクト名をクリックして、シミュレーターでアクションを開いてみてください。**
- メニューバーの [テスト] を選択します。

- シミュレータが [English] に設定されていることを確認して、[Talk to my test-app] をクリックします。
このアクションにより、Dialogflow の基本的なデフォルト インテントが表示されます。つまり、Action on Google との統合の設定がうまくいったということです。
3. カスタム エンティティ
エンティティとは、アプリやデバイスがアクションを実行するオブジェクトです。パラメータ / 変数と考えることができます。TV ガイドに「MTV で放送中の番組は?」と尋ねています。MTV はエンティティと変数です。「ナショナル ジオグラフィック」など、他のチャンネルもリクエストできます。または「Comedy Central」を選択します収集されたエンティティは、TV Guide API ウェブサービスへのリクエストでパラメータとして使用されます。
Dialogflow エンティティの詳細については、こちらをご覧ください。
チャネル エンティティの作成
- Dialogflow コンソールのメニュー項目 [Entities] をクリックします。
- [エンティティを作成] をクリックします。
- エンティティ名:
channel(すべて小文字にしてください) - チャンネル名を渡します。(チャンネルによっては、Google アシスタントが別の情報を理解できるように同義語が必要になります)。入力中は Tab キーと Enter キーを使用できます。チャンネル番号を参照値として入力します。類義語としてのチャンネル名:
1 - 1, Net 1, Net Station 1

5**.**青色の保存ボタンの横にあるメニューボタンをクリックして、[**Raw Edit**] モードに切り替えます。

- コピーして他のエンティティを 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"

- [保存] をクリックします。
4. インテント
Dialogflow は、インテントを使用してユーザーの意図を分類します。インテントにはトレーニング フレーズが含まれます。トレーニング フレーズとは、ユーザーがエージェントに話しかける可能性のあるフレーズのサンプルです。たとえば、テレビで流れているものを知りたいユーザーは、「今日のテレビでは何?」と聞くかもしれません。「現在再生中の曲は何?」と話しかけるか、「tvguide」と言います。
ユーザーが何かを書いたり話すことを「ユーザー表現」と呼び、Dialogflow はユーザー表現をエージェント内の最適なインテントにマッチングします。インテントのマッチングは「インテント分類」とも呼ばれます。
Dialogflow インテントの詳細については、こちらをご覧ください。
Default Welcome Intent の変更
新しい Dialogflow エージェントを作成すると、2 つのデフォルト インテントが自動的に作成されます。Default Welcome Intent(デフォルトのウェルカム インテント)は、エージェントとの会話を開始したときに最初に表示されるフローです。Default Fallback Intent(デフォルトのフォールバック インテント)は、エージェントがユーザーを理解できない場合や、話した内容とインテントをマッチングできない場合に発生するフローです。
- [Default Welcome Intent] をクリックします。
Google アシスタントの場合は、Default Welcome Intent で自動的に起動します。これは、Dialogflow が Welcome イベントをリッスンしているためです。ただし、入力したトレーニング フレーズのいずれかを言って、インテントを呼び出すこともできます。

以下は、Default Welcome Intent のウェルカム メッセージです。
ユーザー | エージェント |
「OK Google, your-name-tvguide にアクセスして」 | 「ようこそ。テレビガイドのエージェントです。テレビ チャンネルで現在再生されているものをお伝えできます。たとえば、「MTV で放送中の番組は?」と聞くことができます。 |
- [Responses] までスクロールします。
- すべての返信テキストをクリアします。
- 次の挨拶を含む新しいテキスト レスポンスを 1 つ作成します。
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?

- [保存] をクリックします。
一時的なテスト インテントを作成する
後で Webhook をテストできるように、テスト目的で一時的なテスト インテントを作成します。
- [Intents] メニュー項目をもう一度クリックします。
- [インテントを作成] をクリックします。
- [Intent Name] に次のように入力します。
Test Intent(必ず大文字の T と大文字の I を使用してください。- インテントの表記が違うと、バックエンド サービスは機能しなくなります)。

- [トレーニング フレーズを追加] をクリックします。
Test my agentTest intent

- [Fulfillment] >フルフィルメントを有効にする

今回はレスポンスをハードコードしません。レスポンスは Cloud Functions の関数から返されます。
- [Enable Webhook call for this intent] スイッチをオンにします。

- [保存] をクリックします。
チャンネルのインテントを作成する
チャンネル インテントには、会話の以下の部分が含まれます。
ユーザー | エージェント |
「Comedy Central の番組は?」 | ""ただいま午後 6 時から Comedy Central で「ザ・シンプソンズ」が放送されています。その後の午後 7 時に「ファミリー ガイ」が始まります。」 |
- [Intents] メニュー項目をもう一度クリックします。
- [インテントを作成] をクリックします。
- [Intent Name] に「
Channel Intent」と入力します(ただし、T は大文字、I は大文字にしてください)。- インテントの表記が違うと、バックエンド サービスは機能しなくなります)。 - [トレーニング フレーズを追加] をクリックして、以下の内容を追加します。
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

- [Action and parameters](アクションとパラメータ)まで下にスクロールします。

[@channel] とDialogflow で認識されている @sys.time エンティティ。Webhook の後半で、パラメータ名とパラメータ値がウェブサービスに送信されます。例:
channel=8
time=2020-01-29T19:00:00+01:00
- チャネルを必須としてマーク
TV Guide エージェントと会話する際は、必ずスロット パラメータ名 channel を入力する必要があります。会話の冒頭でチャンネル名が記載されていなかった場合、Dialogflow はすべてのパラメータ スロットが入力されるまでさらに質問します。
プロンプトとして次のように入力します。
For which TV channel do you want to hear the tv guide information?In which TV channel are you interested?

- 時間パラメータを必須として設定しないでください。
時間は省略可能です。時刻が指定されていない場合、ウェブサービスは現在の時刻を返します。
- [Fulfillment] をクリックします。
今回はレスポンスをハードコードしません。Cloud Functions の関数からレスポンスが返されます。したがって、[Enable Webhook call for this intent] スイッチをオンにします。
- [保存] をクリックします。
5. Webhook フルフィルメント
エージェントに静的インテント レスポンス以上のものが必要な場合は、フルフィルメントを使用してウェブサービスをエージェントに接続する必要があります。ウェブサービスを接続すると、ユーザーの式に基づいてアクションを実行し、動的なレスポンスをユーザーに返すことができます。たとえば、ユーザーが MTV の TV スケジュールの受信を希望する場合、ウェブサービスはデータベースをチェックし、MTV のスケジュールであるユーザーに応答できます。
- メインメニューで [Fulfillment] をクリックします。
- [Inline Editor] スイッチを有効にします。

シンプルな Webhook のテストと実装には、インライン エディタを使用できます。サーバーレスの Cloud Functions for Firebase を利用しています。
- エディタで [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);
});

- エディタの [package.json] タブをクリックし、次の JSON コードをコピーして貼り付けてください。すべての Node.js パッケージ マネージャー(NPM)ライブラリがインポートされます。
{
"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"
}
}

- [Deploy] ボタンをクリックします。サーバーレス関数をデプロイしているため、少し時間がかかります。画面下部に、ステータスを示すポップアップが表示されます。
- Webhook をテストして、コードが機能するかどうかを確認します。右側のシミュレータに次のように入力します。
Test my agent.
問題がなければ、「これはテスト メッセージです」と表示されます。
- チャネル インテントをテストしてみましょう。次の質問をしてみましょう。
What's on MTV?
すべて問題がなければ、次のように表示されます。
「MTV の午後 4 時 30 分から、MTV Unplugged が放映されます。その後、午後 5 時 30 分にルポール ドラァグ レースが始まります。」
省略可能な手順 - Firebase
別のチャンネルでこれをテストすると、テレビの結果が同じであることがわかります。これは、Cloud Functions の関数がまだ実際のウェブサーバーから取得されていないためです。
これを行うには、アウトバウンド ネットワーク接続を作成する必要があります。
ウェブサービスを使用してこのアプリケーションをテストする場合は、Firebase プランを Blaze にアップグレードしてください。注: この手順は省略可能です。このラボの次のステップに進んで、Actions on Google でアプリケーションのテストを続けることもできます。
- Firebase コンソール(https://console.firebase.google.com)に移動します。
- 画面下部の [アップグレード] ボタンを押します。

ポップアップで Blaze プランを選択します。
- Webhook が機能することを確認したら、続行して
index.jsのコードを以下のコードに置き換えます。これにより、ウェブサービスからテレビガイド情報をリクエストできるようになります。
'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 アクションを呼び出す必要があります。
アクションが開き、ボイスが変わります。「native」はそのままにします。Google アシスタントのスコープ。つまり、この時点からエージェントに依頼する内容はすべて、自分で作成する必要があります。使いたい場合に、Google アシスタントに突然 Google の天気情報を尋ねることはできなくなりました。まず、アクションの範囲(アプリ)から離れる(閉じる)必要があります。
Google アシスタント シミュレータでアクションをテストする
次の会話をテストしてみましょう。
ユーザー | Google アシスタント |
「OK Google, your-name-tv-guide に話しかけて」 | 「承知しました。your-name-tv-guide を手配します。」 |
ユーザー | Your-Name-TV-Guide エージェント |
- | 「ようこそ、私はテレビガイドです...」 |
エージェントをテストする | 「これはテスト メッセージです。これが表示された場合は、Webhook フルフィルメントが機能していることを意味します。」 |
MTV のコンテンツは? | MTV の午後 4 時 30 分から MTV Unplugged が放映されます。この後、午後 5 時 30 分にルポール ドラァグ レースが始まります。 |
- Google アシスタント シミュレータに戻す
https://console.actions.google.com を開きます。
- マイクのアイコンをクリックして、以下の質問をします。

Talk to my test agentTest my agent
Google アシスタントは次のように応答します。

- ここで、次の質問をしてみましょう。
What's on Comedy Central?
次のような出力が返されます
現在、Comedy Central で午後 6 時から「ザ・シンプソンズ」が放送されています。この後、午後 7 時に「ファミリー ガイ」が始まります。
7. 完了
Dialogflow を使用した最初の Google アシスタント アクションの作成が完了しました。
お気づきのように、アクションは Google アカウントに関連付けられたテストモードで実行されていました。iOS または Android スマートフォンの Google Nest デバイスまたは Google アシスタント アプリに、同じアカウントでログインする場合。アクションをテストすることもできます。
次はワークショップのデモですただし、実際に Google アシスタントのアプリケーションを構築する場合は、アクションを送信して承認を受けることもできます。詳しくは、こちらのガイドをご覧ください。
学習した内容
- Dialogflow v2 を使用して chatbot を作成する方法
- Dialogflow でカスタム エンティティを作成する方法
- Dialogflow で直線的な会話を作成する方法
- Dialogflow と Firebase Functions を使用して Webhook フルフィルメントを設定する方法
- Actions on Google を使用してアプリケーションを Google アシスタントに取り込む方法
次のステップ
この Codelab はいかがでしたか?優れたラボをご確認ください。
このコードラボを Google Chat に統合して続行します。
