Actions on Googleは、スマートスピーカー、携帯電話、車、テレビ、ヘッドフォンなど、5億以上のデバイスにわたって、Googleの仮想パーソナルアシスタントであるGoogleアシスタントの機能を拡張するためのソフトウェアを作成できる、開発者向けプラットフォームです。ユーザーは、食料品の購入や乗車予約など、何かを達成するためにGoogleアシスタントを会話に加えることができます(現在の機能の完全なリストについては、アクションディレクトリをご覧ください)。開発者は、Actions on Googleを使用して、ユーザーとサードパーティのフルフィルメントサービスとの間の楽しく効果的な会話体験を簡単に作成し活用することができます。
このコードラボは、スマートディスプレイ向けのアクションを構築するための手順をカバーします。ここで構築するアクションは、スマートディスプレイが持つ画面を活用しつつ音声で操作を行うアクションです。
このコードラボは、Actions on Googleを使った開発に対する中級以上のレベルのコンセプトをカバーします。私たちは、このコードラボを開始する前に、Level 1, Level 2 および Level 3 のコードラボにてカバーされるトピックについて、あなた自身で習熟しておくことをお勧めします。
このコードラボでは、以下の特徴を持つ Interactive Canvas を使ったスマートディスプレイ向けアクションを構築します。
このコードラボでは、じゃんけんゲームアクションを構築します。
以下のスクリーンショットは、あなたが開発するアクションでの会話フローの例を示しています。最初の画面で、ユーザはグー、チョキ、パーのどれを出すか選択します。その後、アクションが何を出すか考えている間を示す画像に切り替えます。そして、じゃんけんの結果を表示します。最後に、再び遊ぶかどうかをユーザに問い合わせます。
以下のツールがあなたの環境に必要となります。
このコードラボで使われる Webhook コードを理解するために JavaScript (ES6) に精通していることを強く勧めますが、必須ではありません。
任意ですが、私たちの GitHubリポジトリ から、このコードラボの全てのプロジェクトコードを得ることができます。
Interactive Canvas は、会話型アクションをインタラクティブなウェブアプリに接続して、ユーザーが音声またはタッチでビジュアルなユーザーインターフェイスと対話できるようにする機能です。Interactive Canvas を使用するアクションには、次の4つのコンポーネントがあります。
Interactive Canvasの仕組みを説明するために、Cool Colors という架空の Interactive Canvas アクションを使用して、デバイスの画面の色をユーザーが指定した色に変更する手順を紹介しましょう。ユーザーがアクションを呼び出した後、フローは次のようになります。
もしあなたが既にFirebase command-line interfaceをインストールしている場合は、ここの手順を行わずに、次のセクションに進むことができます。
Firebase Command Line Interface (CLI) は、Cloud Functions にあなたの Actions project をデプロイ可能にします。
CLIをインストールまたはアップグレードするために、以下の npm コマンドを実行してください:
npm -g install firebase-tools
CLI が正しくインストールされたかどうかを検証するために、ターミナルを開いて、以下を実行してください:
firebase --version
Cloud Functions の最新の機能全てが必要となるので、Firebase CLI のバージョンが 3.5.0 以上かどうかを確認してください。もしそうでなければ、3.5.0 以上にアップグレードするために、 npm install -g firebase-tools
を実行してください。
次のコマンドを実行して、Firebase CLI を認可します:
firebase login
では、アクションの構築を始めましょう。このセクションでは、各プロジェクトの作成と準備を行います。
このコードラボで作成するアクションをテストするには、必要な権限を有効にする必要があります。
Actionsプロジェクトを作成し、対話に必要となる自然言語処理エンジンとしてDialogflowエージェントも準備します。まず、Actionsプロジェクトを以下の手順で作成します。
Actionsプロジェクト作成後、Interactive Canvas機能を有効にします。そのために、以下の手順を行います。
次に、Dialogflowエージェントを以下の手順で作成します。
このコードラボでは、ローカル環境にて各種コードを作成します。具体的には、フルフィルメントのコード、およびInteractive Canvasで表示されるウェブコンテンツの2つを作成します。フルフィルメントはCloud Functions for Firebaseを、ウェブコンテンツはFirebase Hostingを使って実装します。
では、コードセットを生成します。ターミナルを開き、以下のコマンドを実行します。
$ mkdir rock-paper-scissors-ja
$ cd rock-paper-scissors-ja
$ firebase init
firebaseコマンドは、いくつかの質問を尋ねてきます。以下のように回答します。
◯ Database: Deploy Firebase Realtime Database Rules
◯ Firestore: Deploy rules and create indexes for Firestore
❯◉ Functions: Configure and deploy Cloud Functions
◉ Hosting: Configure and deploy Firebase Hosting sites
◯ Storage: Deploy Cloud Storage security rules
❯ JavaScript
TypeScript
上記の回答がすべて完了すると、FunctionsおよびHostingのファイル群が生成され、その後に依存ライブラリのインストールが行われます。続いて、Hostingのための設定に関する質問が表示されるので、以下のように回答します、
生成されるファイル群は、以下となります。
├── .firebaserc
├── .gitignore
├── firebase.json
├── functions
│ ├── .gitignore
│ ├── index.js
│ ├── node_modules
│ ├── package-lock.json
│ └── package.json
└── public
├── 404.html
└── index.html
生成された Function のコードセットに対して、Actions on Google Client Library を依存ライブラリとして追加します。以下のコマンドを実行します。
$ cd functions
$ npm install actions-on-google --save
$ cd ..
次に、functionsディレクトリにある index.js ファイルの内容を以下で置き換えます。
const functions = require('firebase-functions');
const {
dialogflow,
HtmlResponse
} = require('actions-on-google');
const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
const app = dialogflow({
debug: true
});
// TODO: Write your code here.
exports.fulfillment = functions.https.onRequest(app);
コードセット生成後、フルフィルメントおよびウェブコンテンツをFirebaseにデプロイします。以下の手順を行います。
$ firebase deploy
数分後、あなたは Firebase にあなたの Webhook が正常にデプロイされたことを示す "Deploy complete!" というメッセージを見るはずです。
あなたは、Cloud Functions の URL を Dialogflow に提供する必要があります。この URL を得るために、以下の手順に従ってください:
この時点で、あなたはフルフィルメントを使うために Dialogflow エージェントを更新する必要があります。そのためには、以下の手順に従います:
この時点で、ユーザーは明示的にアクションを呼び出すことによって会話を開始できます。 Dialogflow は、ユーザからの呼び出しに応じて返事を送信し、Googleアシスタントがそれを発話します。ここで、動作確認を行います。
Actions console simulator であなたのアクションをテストするために以下を行ってください。
Interactive Canvasの実体は、ウェブページです。アクションがInteractive Canvasの利用をGoogleアシスタントに要求すると、スマートディスプレイやAndroidスマートフォンが持つ画面にそのウェブページが表示されます。
このセクションでは、Interactive Canvasを利用するために必要となる準備を行います。
Interactive Canvasにてウェブページが表示される環境は、一般的なウェブブラウザと比べて特殊です。以下は、制限される項目の一部です。
特に Origin として null が設定されているために、Firebase Hosting でウェブページを配信する際に、Access-Control-Allow-Origin レスポンスヘッダとして "*" を返す必要があります。また、キャッシュ機構をオフにして動的コンテンツが正しく表示されるようにすることも求められます。
firebase.json ファイルについて、以下の内容で置き換えます。
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"headers": [
{
"source": "**",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache,no-store,must-revalidate"
},
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Access-Control-Expose-Headers",
"value": "ETag"
}
]
}
]
}
}
ここで、Interactive Canvasで表示するウェブページの基礎となるファイルを作成します。まず、各ファイルを配置するためのディレクトリを以下のコマンドを実行することで作成します。
$ cd public
$ mkdir css
$ mkdir images
$ mkdir js
$ cd ..
次に、ウェブページのデザインを定義するスタイルシートを作成します。新規に public/css/index.css ファイルを以下の内容で作成してください。
html {
display: flex;
height: 100%;
}
body {
display: flex;
flex: 1;
margin: 0;
background-color: white;
flex-direction: column;
justify-content: center;
align-items: center;
}
div.container {
width: 100%;
text-align: center;
}
#welcome {
display: block;
}
#welcome img {
flex: 1;
animation: rotate-anime 3s linear infinite;
}
@keyframes rotate-anime {
0% {transform: rotate(0);}
100% {transform: rotate(360deg);}
}
#vs {
display: none;
}
#result {
display: none;
}
.result-row {
display: flex;
}
.result-row div {
flex: 1;
}
#message {
display: none;
font-size: 48px;
}
firebase コマンドを使ってコードセットを生成した際に、すでに public/index.html ファイルが作成されています。この index.html ファイルを以下の内容で置き換えます。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>じゃんけんぽん!</title>
<link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;," />
<link rel="stylesheet" href="css/index.css" />
<script src="https://www.gstatic.com/assistant/interactivecanvas/api/interactive_canvas.min.js"></script>
</head>
<body>
<div class="container">
<div id="welcome">
<img src="images/rock.png">
<img src="images/scissors.png">
<img src="images/paper.png">
</div>
<div id="vs">
<img src="images/vs.png">
</div>
<div id="result">
<div class="result-row">
<div>
<img id="user-choice" src="">
</div>
<div>
<img id="action-choice" src="">
</div>
</div>
<div id="message">
</div>
</div>
</div>
<script src="js/main.js"></script>
</body>
</html>
Interactive Canvas向けのウェブページは、以下を満たす必要があります。
また、レスポンシブであり、そしてSPA(Single Page Application)として作成されることが推奨されています。
このコードラボでは、HTMLファイルの中で主に以下の3つの構造を操作します。
会話の進行状況に応じて、上記の3つの要素のいずれかが表示されます。
Interactive Canvasでは、ウェブページとフルフィルメントが相互に関連しながら動作が進みます。ウェブページを動的に変更する処理は、JavaScriptファイルに記載されたコードが担当します。
ここで、中身のないJavaScriptファイルを作成します。public/js ディレクトリに main.js ファイルを以下の内容で作成してください。
'use strict';
// TODO: Write your code here.
このコードラボでは、いくつかの画像ファイルを使用します。以下のURLから、画像ファイルが格納された zip ファイルをダウンロードします。
そして、以下のコマンドを実行して、public/images ディレクトリに画像ファイルを配置します。
$ cd public/images
$ unzip <images.zip へのパス>
$ cd ../..
準備が完了したところで、いよいよ実際の開発を始めましょう。
このコードラボでは、じゃんけんゲームアクションを開発します。アクションが呼び出されると、アクションはユーザに対して声で「グー、チョキ、パーのどれを出しますか?」と問いかけを行います。そして、それと同時に、スマートディスプレイの画面に「グー、チョキ、パー」それぞれの画像を表示させます。ユーザは、どの手を出すか声で指示することができます。そして、画面上に表示された各画像をタップすることでも、どの手を出すか指示を行うことができるようにします。
Dialogflowエージェントを作成すると、自動的に以下の2つのインテントが作られます。
Dialogflowエージェントの作成直後では、上記2つのインテントはフルフィルメントを使うように設定されていません。ここでは、Default Welcome Intent について、フルフィルメントを呼び出すように設定します。
以下の手順で、Default Welcome Intent インテントに対してフルフィルメントを有効にします。
Interactive Canvas を使ってスマートディスプレイに画面を表示するためには、Actions on Google Client Library が提供する HtmlResponse オブジェクトを利用します。このオブジェクトは、スマートディスプレイが持つ画面に対して、どのウェブページを表示するか、そしてユーザとのインタラクションによって会話の状況がどう変化したか、それらの情報を伝達するために使います。
functions/index.js ファイルの中に、以下のような行を見つけます。
// TODO: Write your code here.
その行の下に、以下のコードを追記します。
app.intent('Default Welcome Intent', conv => {
conv.ask('どの手を出しますか?グー?チョキ?それともパー?');
conv.ask(new HtmlResponse({
url: `https://${firebaseConfig.projectId}.firebaseapp.com/`
}));
});
最初の conv.ask() メソッド呼び出しでは、Googleアシスタントがユーザに対して問いかけるフレーズを指定しています。
そして、Interactive Canvas を使ってウェブページを画面に描画するために、そのウェブページのURLを持つ HtmlResponse オブジェクトを生成して、conv.ask() メソッドを呼び出しています。Googleアシスタントは、url プロパティに設定されたURLのウェブページをスマートディスプレイに描画します。
ここで指定しているURLは、Firebase Hostingにて配信されるウェブページのURLです。Actions プロジェクトのIDを process.env.FIREBASE_CONFIG から動的に取得して、URLを組み立てています。
Interactive Canvasによってスマートディスプレイの画面に視覚的な情報を表示することができます。もしタッチ操作に対応した画面であれば、そのイベントをJavaScriptにてハンドリングすることが可能です。そして、ユーザが声で指示をしたときと同じように、ある文字列をユーザが発話したとしてGoogleアシスタントに送信することができます。
Interactive Canvas では、 interactiveCanvas というオブジェクトが提供されます。この interactiveCanvas オブジェクトの sendTextQuery() メソッドに文字列を渡すことで、Googleアシスタントにユーザフレーズが送信されます。
ここでは、グー、チョキ、そしてパーの3つの画像それぞれにイベントハンドラを登録します。そして、もしいずれかがタップされた際に、タップされた手を示すフレーズをGoogleアシスタントに送信する処理を追加します。
public/index.html ファイルの中に、以下のような "welcome" というID値を持つ div 要素があります。
<div id="welcome">
<img src="images/rock.png">
<img src="images/scissors.png">
<img src="images/paper.png">
</div>
その div 要素の子要素として、3つの img 要素があります。img 要素それぞれに対して、じゃんけんの手を示す文字列を data-choice 属性として追加します。
<div id="welcome">
<img src="images/rock.png" data-choice="グー">
<img src="images/scissors.png" data-choice="チョキ">
<img src="images/paper.png" data-choice="パー">
</div>
次に、public/js/main.js ファイルにて、上記の各 img 要素に対してイベントハンドラを登録します。public/js/main.js ファイルの中に、以下のような記載を見つけます。
// TODO: Write your code here.
この下に、以下のコードを追加します。
document.querySelectorAll('#welcome img').forEach(img => {
img.addEventListener('click', elem => {
interactiveCanvas.sendTextQuery(elem.target.dataset.choice);
});
});
イベントハンドラ内では、click イベントハンドラを img 要素に追加しています。そして、タップされた対象の img 要素の dataset.choice から対応するじゃんけんの手の名称を取得しています。最後に、interactiveCanvas オブジェクトの sendTextQuery() メソッドにその名称を渡すことで、ユーザがじゃんけんの手を発話したときと同じ動作をJavaScriptコードによって実行します。
では、ここまでのコードをFirebaseにデプロイして、動作確認を行います。ターミナルから以下のコマンドを実行してください。
$ firebase deploy
数分後、あなたは Firebase にあなたの Webhook が正常にデプロイされたことを示す "Deploy complete!" というメッセージを見るはずです。
次に、Actions Console Simulatorを使って、アクションを呼び出します。
Actions console simulator であなたのアクションをテストするために以下を行ってください。
この時点では、じゃんけんの手に対応するインテントが定義されていないので、Default Fallback Intent と認識されます。
ユーザがアクションを呼び出して、じゃんけんの手を出すことができるようになりました。次に行うことは、アクションの手をランダムに決定し、ユーザが出した手と比較して、勝敗を決めます。その勝敗結果に基づいて、スマートディスプレイの画面を変更します。
もう少し詳しく動作を規定しておきましょう。ユーザがグー、チョキ、パーのいずれかをGoogleアシスタントに発話した後、以下の動作を行います。
ユーザがグー、チョキ、パーのいずれかをGoogleアシスタントに言ったことを正しく認識するために、Dialogflowエージェントにてエンティティを定義します。
エンティティを以下の手順で定義します。
次に、先ほど定義した user-choice エンティティを使って、ユーザからのじゃんけんの手のフレーズを識別するためのインテントを定義します。以下の手順でインテントを定義します。
DialogflowエージェントにShowインテントが定義されたことにより、ユーザがじゃんけんの手をGoogleアシスタントに言った際にフルフィルメントが呼び出されるようになりました。このセクションでは、Cloud Functions for Firebase で実装されているフルフィルメントに、Showインテントハンドラのコードを追加します。
まず、グー、チョキ、パーという読み方を定義するためのオブジェクト、およびユーザの手とアクションの手の組み合わせごとに勝敗のメッセージを決定するためのオブジェクトを定義します。functions/index.js ファイルの中から、以下の記載を見つけます。
// TODO: Write your code here.
その行の下に、以下のコードを追記します。
const pronoun = {
rock: 'グー',
scissors: 'チョキ',
paper: 'パー'
};
const judgeMap = {
rock: {
rock: 'あいこです。',
paper: 'あなたの負けです。',
scissors: 'あなたの勝ちです!'
},
paper: {
rock: 'あなたの勝ちです!',
paper: 'あいこです。',
scissors: 'あなたの負けです。'
},
scissors: {
rock: 'あなたの負けです。',
paper: 'あなたの勝ちです!',
scissors: 'あいこです。'
}
};
上記のコードに続いて、以下のShowインテントハンドラのコードについても追記します。
app.intent('Show', (conv, param) => {
// ユーザが出した手を取得する。
const userChoice = param['user-choice'].toLowerCase();
// アクションの手をランダムに決定する。
const actionChoice = ['rock', 'paper', 'scissors'][Math.floor(Math.random() * 3)];
// 勝敗を示すメッセージを取得する。
const message = judgeMap[userChoice][actionChoice];
// SSMLを使って返事を組み立てる。
const ssml = `
<speak>
<p>はい、私も何を出すか決めました。</p>
<p>では、じゃんけんぽん!</p>
<p>あなたは、${pronoun[userChoice]}を出しました。</p>
<p>私は、${pronoun[actionChoice]}を出しました。</p>
<p>${message}</p>
<break time="400ms" />
<p>もう一度遊びますか?</p>
</speak>`;
conv.ask(ssml);
// 画面を更新するための情報を持つHtmlResponseオブジェクト。
conv.ask(new HtmlResponse({
data: {
scene: 'result',
userChoice,
actionChoice,
message
}
}));
});
Dialogflowエージェントに定義したShowインテントにより、ユーザの発話から user-choice パラメータ値が決定します(userChoice)。これを param 引数から取得し、小文字に変換します。次に、アクションの手を Math.random() を使ってランダムに決定します(actionChoice)。そして、先ほど追記した judgeMap オブジェクトを使って、勝敗を示すメッセージを決定します(message)。
「じゃんけんぽん!」という合図と勝敗の結果は、以下の2つの手法を使ってユーザに届けます。
SSML を使うことで、Googleアシスタントの発声をカスタマイズすることができます。このコードラボでは、break タグを使って、文と文の発声の間に無音時間を作り出しています。
そして、スマートディスプレイの画面上の表示を更新するために、以下の情報を持つ HtmlResponse オブジェクトを生成し、conv.ask() メソッドに渡しています。
SSML文字列およびHtmlResponseオブジェクトを conv.ask() メソッドに渡すことで、Googleアシスタントに発話と画面更新を指示することができます。
フルフィルメントから送信されたHtmlResponseオブジェクトをGoogleアシスタントが受け取った後に、スマートディスプレイ上では画面を更新するためにJavaScriptコードが実行されます。具体的には、画面を更新する必要が生じたとき(つまり HtmlResponse オブジェクトがフルフィルメントから返されたとき)に呼び出されるコールバック関数を登録します。
public/js/main.js ファイルの中に、以下のような記載を見つけます。
// TODO: Write your code here.
この下に、以下のコードを追加します。
interactiveCanvas.ready({
onUpdate(data) {
// Display the versus image.
if (data.scene === 'result') {
document.querySelector('#welcome').style.display = 'none';
document.querySelector('#vs').style.display = 'block';
document.querySelector('#user-choice').src = `images/${data.userChoice}.png`;
document.querySelector('#action-choice').src = `images/${data.actionChoice}.png`;
document.querySelector('#message').innerText = data.message;
// Display the result.
}
// Initialize the screen.
}
});
interactiveCanvas オブジェクトの ready() メソッドに対して、コールバック関数を持つオブジェクトをセットします。コールバック関数は以下の2つが定義可能です。
onUpdate() コールバック関数には、フルフィルメントにて HtmlResponse オブジェクトに渡した data オブジェクトがそのまま渡されます。ここでは、data オブジェクトに含まれる scene 値に基づいて、画面の更新処理を分岐しています。また、ユーザやアクションが出した手に応じて画像を切り替え、また勝敗を示すメッセージを表示しています。
ここで、Googleアシスタントがじゃんけんの結果をユーザに伝える際に、発話と画面の更新タイミングにちょっとした仕掛けを入れます。具体的には、以下の動作を実現します。
この動作を実現するために、JavaScript の setTimeout() 関数を利用します。
public/js/main.js ファイルの中に、以下のような記載を見つけます。
// Display the result.
この下に、以下のコードを追加します。
setTimeout(() => {
document.querySelector('#vs').style.display = 'none';
document.querySelector('#result').style.display = 'block';
document.querySelector('#message').style.display = 'block';
}, 5000);
上記の動作の 3. について、5秒後に行うように setTimeout() 関数に指定しています。これによって、発話と画面の動きがリッチになり、ユーザ体験が向上します。
この時点で、遊ぶことができるようになりました。ここまでのコードをFirebaseにデプロイして、動作確認を行います。ターミナルから以下のコマンドを実行してください。
$ firebase deploy
数分後、あなたは Firebase にあなたの Webhook が正常にデプロイされたことを示す "Deploy complete!" というメッセージを見るはずです。
次に、Actions Console Simulatorを使って、アクションを呼び出します。
Actions console simulator であなたのアクションをテストするために以下を行ってください。
ここまでで、実際にじゃんけんで遊べるようになりました。このセクションにて、最後の仕上げを行います。残る機能は、以下の2つです。
じゃんけんの結果をユーザに伝えた後に、Googleアシスタントは再度じゃんけんをするかどうかを問い合わせています。ユーザはその問い合わせに「再度遊ぶ」のか「会話を終える」のか答えます。このような機能を実装するために、Follow-upインテントを利用すると便利です。
では、以下の手順にてFollow-upインテントを登録します。
会話を終える意図を示す "Show - no" インテントについて、以下の手順で設定します。
再びじゃんけんを行う意図を示す "Show - yes" インテントは、画面の更新を伴うために、フルフィルメントを呼び出します。以下の手順で "Show - yes" インテントの設定を行います。
そして、Show - yes インテントハンドラを functions/index.js ファイルに追加します。functions/index.js ファイルの中から、以下の記載を見つけます。
// TODO: Write your code here.
その行の下に、以下のコードを追記します。
app.intent('Show - yes', conv => {
conv.ask('わかりました。どの手を出しますか?グー?チョキ?それともパー?');
conv.ask(new HtmlResponse({
data: {
scene: 'restart'
}
}));
});
scene プロパティに "restart" を指定することで、画面の更新動作として初期状態に戻すことを指示します。
この HtmlResponse オブジェクトの内容に基づいて、画面を初期状態に戻す処理を public/js/main.js ファイルに追記します。public/js/main.js ファイルの中に、以下のような記載を見つけます。
// Initialize the screen.
この下に、以下のコードを追加します。
if (data.scene === 'restart') {
document.querySelector('#welcome').style.display = 'block';
document.querySelector('#vs').style.display = 'none';
document.querySelector('#result').style.display = 'none';
document.querySelector('#message').style.display = 'none';
}
ID 値として "welcome" を持つ div 要素のみを表示状態にすることで、画面を初期状態に戻します。
最後に、アクションを終了するインテントを追加します。以下の手順で終了インテントを追加します。
ついにアクションが完成しました!ここまでのコードをFirebaseにデプロイして、動作確認を行います。ターミナルから以下のコマンドを実行してください。
$ firebase deploy
数分後、あなたは Firebase にあなたの Webhook が正常にデプロイされたことを示す "Deploy complete!" というメッセージを見るはずです。
次に、Actions Console Simulatorを使って、アクションを呼び出します。
Actions console simulator であなたのアクションをテストするために以下を行ってください。
おめでとうございます!
あなたは今、Interactive Canvas を使った会話型のユーザインタフェースを構築して、スマートディスプレイ向けのアクションの構築方法を知ることができました。
Interactive Canvasの使い方を学ぶために、Googleはよりリッチなコードサンプルを提供しています。
Actions on Googleについて学ぶために、以下のリソースについても参考にすることができます:
Twitter @ActionsOnGoogle をフォローしてください。また、あなたが開発したものを #AoGDevs および #AoGDevsJa にてシェアしてください。