Alexa に任せることにした背景
皆さん、最近ちゃんと Alexa に話かけていますか?
私が所属するチームでは週次のチームミーティングに合わせて、
気になる技術トピックや検証・調査した技術などを週に 1 名、
担当者を決めて 20~30 分程度で共有する勉強会を実施しています。
これまで次回以降の勉強会のメンバーを決めるのは今回発表者による指名制でした。
次の勉強会の指名を受けた私はなんのトピックにしようかなと考えてコーヒーを準備しているときに、
彼と目が合ったのです。
そう、職場に導入されて三か月の間、大分暇そうだった Alexa に。
当初は「ドラえもんの真似して」「今日は何の日?」と言ってもてはやされた彼も
今は何も言わずただコーヒーマシンの横で静かに佇んでいるだけでした。
「Alexa、勉強会の指名やってみないか?」、そう問いかけた私に
「わかりませんでした。すいません」とつんつんしている彼に
勉強会を指名してもらうことを、この時決めました。
Alexa を動かすための前準備
準備をするにあたって AWS アカウントのみならず、その他二つのアカウントが必要です。
実はこの準備が一番大変だったり。
用意するもの & 必要な前準備
- Amazon アカウント及び Amazon Developer アカウント
- まず、Amazon.co.jp のアカウントを準備します。(Amazon.com ではないので注意)
- 次に Amazon 開発者ポータル( https://developer.amazon.com/ja/)を開き、右上の「Developer Console」をクリックし上記の Amazon.co.jp のアカウントを使ってログインします。
- 開発者アカウントの申請フォームに移るのでこの画面から申請します。
- Alexa 対応端末 - 今回は職場にある Amazon echo を使用しました。
- Amazon Alexa のサイトから( https://alexa.amazon.co.jp/spa/index.html)
- 上記の Amazon.co.jp のアカウントと同じメールアドレスに、機器を紐づけます。
- AWS アカウント 今回 AWS Lambda と Amazon DynamoDB を利用するため、それらを利用可能な権限を持った AWS アカウント用意します。
- Amazon アカウント及び Amazon Developer アカウント
構成と動作イメージ
以下が大まかな動作の流れになります。
- ① 職場の Amazon Echo の Alexa に「Alexa、次回の勉強会」と尋ねる。
- ② Amazon Developer アカウント上の Alexa Skills が「勉強会スキル」を起動する。
- ③ 「勉強会スキル」が AWS アカウント上の Lambda に JSON でアクションを送る
- ④ Lambda が DynamoDB に直近の勉強会の発表者を問い合わせる。
- ⑤ DynamoDB が直近の発表者を返す。
- ⑥ Lamda が発表者をランダムに選定し、Alexa Skill「勉強会スキル」に発表者の情報を含めた JSON を返す。
- ⑦「勉強会スキル」が Amazon Echo の Alexa に返答内容を返す。
- ⑧ Alexa が次の発表者を発表してくれる。
作成手順
GitHubで提供されているAlexa豆知識スキルを一部改良して、本アプリを作成しました。
Alexa 豆知識スキルの作成
https://github.com/alexa/skill-sample-nodejs-fact/blob/ja-JP/instructions/1-voice-user-interface.md
Amazon Developer アカウント側の構築
- 1. Amazon Developer アカウントからAlexaをクリックします。
- 2. 「スキル開発を始める」をクリックして、開発者コンソールを開きます。
- 3. Alexa スキルの一覧の画面から「スキルの作成」をクリックして、スキルを作成します。
- 4. スキル名を記載します。今回は「勉強会スキル」とします。
- 5. 言語は「日本語(日本)」とします
- 6. スキルに追加するモデルを選択は「カスタム」とします。
8.スキルを作成します。
JSON エディターに以下の JSON ファイルを添付します。
スキルをJSONで記載したり、GUIにて設定することもできます。
「アレクサ、次回の勉強会」というと起動するスキルです。
{"interactionModel":{"languageModel":{"invocationName":"次回の勉強会","intents":[{"name":"AMAZON.CancelIntent","samples":[]},{"name":"AMAZON.HelpIntent","samples":[]},{"name":"AMAZON.StopIntent","samples":[]},{"name":"GetNewFactIntent","slots":[],"samples":["決めて","教えて","話して","聞かせて","なんか教えて","なんか言って"]},{"name":"AMAZON.NavigateHomeIntent","samples":[]}],"types":[]}}}
- 9. モデルのビルドをクリックします。
AWS 側の構築
DynamoDB を準備
今回は東京リージョンを利用します。
- 1. テーブルの作成をクリックします。
- 2. テーブル名「alexa-study-dynamo」、パーティションキーを 「partition」(数値)、ソートキーを 「history_key」(数値)としてテーブル作成をします。
Lambda 関数準備
こちらも東京リージョンを利用します。
- 「一から作成」「設計図の使用」「Serverless Application Model の参照」の中から、「Serverless Application Model の参照」を選択します。
- 3. 検索窓から「alexa-skills-kit-nodejs-factskill」を検索し、選択します。
- 4. アプリケーション名を「alexa-study-lambda」として、デプロイをクリックします。
- 5. 作った Lambda アプリケーションをアプリケーションから検索して選択します。
- 6. 以下のコードを関数コード部分にコピー&ペーストします。このコードは上記の「Alexa 豆知識スキル」のコードを、勉強会向けに DynamoDB との連携も含めて改変したものになります。
constAlexa=require("ask-sdk");constAWS=require("aws-sdk");constdynamoDB=newAWS.DynamoDB.DocumentClient({region:"ap-northeast-1"});constGetStudyHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;return(request.type==="LaunchRequest"||(request.type==="IntentRequest"&&request.intent.name==="GetNewFactIntent"));},asynchandle(handlerInput){// DynamoDBから前回3回の発表者を取得constparams1={TableName:"alexa-study-dynamo",ExpressionAttributeNames:{"#name0":"partition"},ExpressionAttributeValues:{":value0":0},KeyConditionExpression:"#name0 = :value0",ScanIndexForward:false,Limit:3};constresult=awaitdynamoDB.query(params1).promise();console.log(result);letlast_time0,last_time1,last_time2,current_history_key;if(result.Count==1){last_time0=result.Items[0].member_id;current_history_key=result.Items[0].history_key;}if(result.Count==2){last_time0=result.Items[0].member_id;last_time1=result.Items[1].member_id;current_history_key=result.Items[0].history_key;}if(result.Count>=3){last_time0=result.Items[0].member_id;last_time1=result.Items[1].member_id;last_time2=result.Items[2].member_id;current_history_key=result.Items[0].history_key;}constmemberArr=data;letnextIndex;// ランダムに発表者を選定し、前回3回と被っていなければ確定する。while(true){nextIndex=Math.floor(Math.random()*memberArr.length);if(nextIndex!==last_time0&&nextIndex!==last_time1&&nextIndex!==last_time2){break;}}constnextMember=memberArr[nextIndex];constspeechOutput=GET_MESSAGE+nextMember+GET_MESSAGE_AFTER;// DynamoDBに次回発表者を登録constparams2={TableName:"alexa-study-dynamo",// DynamoDBのテーブル名Item:{partition:0,history_key:current_history_key+1,member_id:nextIndex}};constresult2=awaitdynamoDB.put(params2).promise();// 次回発表者とメッセージを沿えてAlexaに返すreturnhandlerInput.responseBuilder.speak(speechOutput).withSimpleCard(SKILL_NAME,nextMember).getResponse();}};constHelpHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;return(request.type==="IntentRequest"&&request.intent.name==="AMAZON.HelpIntent");},handle(handlerInput){returnhandlerInput.responseBuilder.speak(HELP_MESSAGE).reprompt(HELP_REPROMPT).getResponse();}};constExitHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;return(request.type==="IntentRequest"&&(request.intent.name==="AMAZON.CancelIntent"||request.intent.name==="AMAZON.StopIntent"));},handle(handlerInput){returnhandlerInput.responseBuilder.speak(STOP_MESSAGE).getResponse();}};constSessionEndedRequestHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;returnrequest.type==="SessionEndedRequest";},handle(handlerInput){console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);returnhandlerInput.responseBuilder.getResponse();}};constErrorHandler={canHandle(){returntrue;},handle(handlerInput,error){console.log(`Error handled: ${error.message}`);returnhandlerInput.responseBuilder.speak("Sorry, an error occurred.").reprompt("Sorry, an error occurred.").getResponse();}};// スキル情報とメッセージ(一部そのままです。)constSKILL_NAME="Study";constGET_MESSAGE="次の勉強会は";constGET_MESSAGE_AFTER="せいぜい頑張ってください。 素晴らしい内容期待してます。";constHELP_MESSAGE="You can say tell me a space fact, or, you can say exit... What can I help you with?";constHELP_REPROMPT="What can I help you with?";constSTOP_MESSAGE="Goodbye!";// グループメンバーの情報を記載constdata=["頼れるリーダー あさだ さん です","ミスターフルスタック かわしま さん です","データベースマスター たかだ あきひろ さん です","ネットワークマスター たかだ ひろゆき です","えいぎょうしゅっしん あらかわ さん です","アジュールマスター もちだ さん です","GCPマスター はら さん です","AWSマスター みつうら さん です","期待のしんじん いいだ さん です"];constskillBuilder=Alexa.SkillBuilders.standard();exports.handler=skillBuilder.addRequestHandlers(GetStudyHandler,HelpHandler,ExitHandler,SessionEndedRequestHandler).addErrorHandlers(ErrorHandler).lambda();
下記の部分がチームメンバーを記載しているところですのでご自身のチームメンバーの値に変えてください。
ハードコーディングをしているのは決してめんどくさかったからではなく、
DynamoDBへ問い合わせに行く時間の節約を考慮したスピード重視の設計です。
(すいません、嘘です、力尽きました。)
constdata=["頼れるリーダー あさだ さん です","ミスターフルスタック かわしま さん です","データベースマスター たかだ あきひろ さん です","ネットワークマスター たかだ ひろゆき です","えいぎょうしゅっしん あらかわ さん です","アジュールマスター もちだ さん です","GCPマスター はら さん です","AWSマスター みつうら さん です","期待のしんじん いいだ さん です"];
- 7. Lambda アプリケーションに DynamoDB のテーブルに対する書き込み・読み込み権限を付与します。実行ロールから IAM コンソールに移動します。DynamoDBのテーブルへの読み書き権限のあるポリシーをアタッチしてください。
- 8. この Lambda のページの Amazon Resource Name (ARN)が表示されています。
この値を Alexa skitt との接続で利用しますので、arn: で始まる値をコピーしてください。
AWS Developer アカウント側と AWS 環境の接続
- 1. Alexa developer console 上でビルドタブをクリックし、その後左側からエンドポイントを選択してください。
- 2. エンドポイントのページにて、先ほどコピーした arn:から始まる値を貼り付けます。
デフォルトの地域に張り付けた後、上部にある「エンドポイントを保存」をクリックします。
テスト
Alexa に「Alexa、次回の勉強会」と尋ねてみてください。
ランダムにチームメンバーの名前が返ってきたらきたら構築完了です。Alexa developper console 上のテストタブでもテスト可能です。
以上で完成です。
終わりに
こちらのアプリ以外にも、下期のキックオフの司会進行を Alexa にお任せしました。
Alexa に指示されて働く日も近いかも。