AWS Lambda with ASK

AWS Lambda with ASKの話題をご紹介します。

AWS Lambda with ASKの概要

AWS Lambda with ASKとは

AWS Lambdaとは、Amazonクラウドが提供するクラウド上のソフトウェア実行環境です。

マネージドサービスとして提供されるため、利用者はサーバのスケーラビリティや運用管理などを気にする必要がなく、何らかのトリガーに基づく処理をクラウドで容易に実行するシステムを構築できます。

ASK(Alexa Skill Kit)とは

Alexaとは

Alexaは、音声によるユーザーインターフェースを使って、クラウドベースでユーザーにさまざまな体験を届けるサービスです。

Amazon Echoをはじめとする様々なデバイスで利用することができ、ユーザーごとの異なる設定や振る舞いを理解することで、パーソナライズされた音声体験を提供します。

Amazon Echo

Amazon EchoはAlexaに対応する、代表的なデバイスです。

音声で操作するハンズフリーのスピーカーで、ユーザーは好きな音楽を再生したり、電気をつけたりと、Alexaの持つ様々な機能を利用できます。

また、ニュースやスポーツの結果、天気などの情報を音声を通して知ることもできます。

Echo以外にも、外部スピーカーを接続できるEcho Dotなどの製品もあります。

AVSとASK

Alexaの大きな特徴として、サードパーティや個人の開発者による独自拡張があります。

拡張の仕組みは大きくわけて2種類があり、それぞれ以下の名称で公開しています。

  • Alexaで使える独自機能の追加 : Alexaスキル
  • 独自デバイスによるAlexa連携 : Alexa Voice Service (AVS)

スキル開発をより簡単に、早くできるようにするのがAlexa Skills Kit(ASK)です。

ASKにはセルフサービスAPI、ツール、ドキュメント、コードサンプルなどがあります。

本稿では、Alexaスキルについて主に扱うため、Alexa Voice Serviceについては詳しく触れることはありませんが、AVS Device SDKを利用することで、 家電や自動車などへAlexaの音声体験をプラスするためのプロトタイプないし製品の開発を行うことができます。

Alexaスキル

Alexaに開発者によって追加された機能をスキルと呼びます。既に世界では、25,000を超えるスキルが開発者により開発されています。

Alexaスキルには、主に以下のような種類があります。

  • カスタムスキル : 汎用のスキル
  • スマートホームスキル : 家電製品などを制御するスキル
  • フラッシュブリーフィングスキル : ニュースなどを読み上げるスキル

カスタムスキルは発声内容に応じた返答を返す汎用のスキルで、他のスキルは専用のAPIが用意された用途に特化したスキルです。

Alexaスキルで実現したいことに近いスキルの種類を選択し、スキルの開発を進めましょう。本稿では、主にカスタムスキルについて解説します。

開発したカスタムスキルは、AmazonのAlexaスキルストアで公開することができます。

スキル実行の仕組み

Alexaカスタムスキルは、以下の流れで実行します。


スキル実行の仕組み

  1. ユーザーがAlexa対応デバイスに発話
  2. デバイスはマイクで集音した音声データを、インターネット経由でAlexaサービスに送信
  3. Alexaサービスは音声データをAIで解析し、AWS Lambdaを呼び出して解析結果を伝える
  4. Lambdaはスキルの処理を実行し、その結果をAlexaサービスにレスポンスとして返す
  5. Alexaサービスはレスポンスに応じた音声データを生成、デバイスに返信
  6. デバイスは音声データを再生し、ユーザーに伝える

Alexaサービスのリクエスト先は、AWS Lambdaのほかに任意のWebサービスを設定することもできますが、今回は以後Lambdaを前提として解説します。

ASK(Alexa Skill Kit)の概要

ASK(Alexa Skill Kit)の種類

ASKには現在3つの種類があります。これをSkill Typeと言います。

  • Custom Skills
  • Smart Home Skills
  • Flash Briefing Skills

Custom Skills

その名の通り全て自由にイチから手作りで作っていくSkillのことです。 自由なので書き方次第では何でもできます。 Google Calendarと連携して「◯時にXXの会議室押さえといて」で予約を入れたり、「ペットの様子を映して」で家にある見守りカメラを起動させたり、、、 要はAPIさえあればそれを声で操作する感覚でできるわけです(ちなみに私は自分の車のスターターを動かせるように仕組みを考え中です)。

ちなみにCustom SkillはAWSの審査を通った後は基本パブリックなAlexaサービス群に配置されます。 他の人に見せたくない時はDevelopment[開発中]としてPublishするとプライベート空間にSkillを置くことができます。 ですがあくまで「開発中」という定義のものなので自アカウントでしか見れません。

Smart Home Skills

これはSkillの中でもスマートホーム機能に特化したSkillです。 このSmart Home SkillsのAPIには予めスイッチのON/OFF、度合いを設定するパーセンテージ、温度調節等のインターフェースと各家電への認証を制御するAuthenticationの仕組みが組み込まれています。

これらのAPIを使ってSkillを組み立てていく事でスマートホームシステムを組むことができます。

Flash Briefing Skills

これはFlash Briefing API、つまりニュースの配信サービス用のSkillになります。これはテキストや音声コンテンツを取得して配信する仕組みで、現在既に1000近くのニュースが配信されています。ニュースソースはHTTPSやRSSから、または直接JSONを指定したりも出来ます。

これらのうち今回は最も使うであろうCustom Skillを実装することを想定して中身を解説したいと思います。

カスタムスキルの作り方

カスタムスキルの例

カスタムスキルの例を以下に示します。


カスタムスキルの例

最初の「Alexa」はWake Wordと呼ばれ、Echoのマイクがこの言葉を拾うとクラウドにあるAlexaと通信を開始し、以降の言葉がストリーミングとしてクラウドに送られます。

ちなみにEchoのマイクはスタンバイ時は無指向性で「Alexa」と呼ぶと、呼びかけた方角が水色に、それ以外の方向は紺色に光ります。

以降は水色の方角の集音が高くなり、より音声コマンドを拾いやすくなる、という仕組みです。

次の「ask」はLaunchフレーズ(起動フレーズ)と言い、この次にくる単語がSkill名であることをAlexaに伝えます。

Launchフレーズはask以外にも「tell」や「launch」「load」「begin」「open」「start」等があり、これらの言葉をSkill名に使うことは出来ません。

この他に接続詞を用いてSkill名を示す方法もあります。

例えば上で言えば「Alexa, can you give me a fact from Anime Facts」と呼びかけた場合も「from」をAlexaが聞き取って、 その後ろにある「Anime Facts」がSkill名だな、と判断します。

最後の「for a fact」はUtteranceと言われます。Utteranceは「発話」「発言」と訳されます。

Alexa Skill Kitでは「Sample Utterances」という項目があり、そこにUtterancesと実際に実行されるSkillが結びつく形で設定されます。

つまりここで「for a fact」と言うことによって、「for a fact」というUtteranceがどのIntent(後述)と結びつくのかをSample UtteranceからAlexaが判断することになります。

ここの文章解釈にもDeep Learningの機能が使われており、多少の言葉のブレがあってもAlexaが聞き取ってくれます。

上で言うと「ask Anime Facts for a fact」、つまりアニメの豆知識的なものを教えてほしい、という意味なので 「fact」が「trivia」とかでも恐らくAlexaは同じ意味だと解釈して「for a fact」というUtteranceが設定されているところと同じIntentが起動するでしょう。

Custom Skillの設定

それでは次にCustom Skillを設定する際に必要となる用語について解説します。用語はそれぞれ

  • Utterance
  • Intent
  • Intent Schema
  • Slot

となり、関係性はこんな感じです。


カスタムスキルの関係性

UtteranceとIntent Schema

ではこちらの設定を例に解説していきます。


UtteranceとIntent Schema

Utterance

Utteranceは実際に話された言葉がどのIntentと結びつくかを表したものです。

またこの中に中カッコでSlot名を指定することにより変数的な設定もできます。

Slot

SlotはIntent Schemaにて指定された型に基づいて変数的に処理される部分です。例えば上の例であればSlot「Answer」は「AMAZON.NUMBER」という型が指定されています。

ここに「One」や「Two」等の数字を表す言葉が入ってくると、Alexaはその部分を自動的に数字の「1」「2」に変換しLambdaに引き渡します。

Slot type(型)にはAmazonが予め用意したものと、カスタムに自分で作れるもの(Custom Slot)があります。

Custom Slotは例えば「Flower」というSlot名で「Rose」「Lily」などの単語を設定しておくことでAlexaに投げかけられた言葉から花の名前を変数としてLambdaに引き渡す事ができます。

Intent

IntentはAlexaに発せられた言葉と実際のSkill内の機能をつなぎ合わせる変数のような役割を指します。

上の例でいくと「the answer is ◯◯」や「my answer is ◯◯」とAlexaに話しかけるとAnswerIntentというIntentが反応して、Lambdaがキックされる時に"Intent.name"として渡されます。

受け取ったLambdaはそのIntent.nameを元に処理を分岐していくように実装します。

Intent Schema

Intent SchemaはSlotとその型、Intentの関係をまとめたJSON形式の設定項目です。

Utteranceの情報とこのIntent Schemaを元にLambdaに送るRequestが作成されます。

カスタムスキルのためのAWSラムダ関数の作成

導入

カスタムAlexaのスキルのためのクラウドベースのサービスを構築するための最も簡単な方法は、使用しているAWSラムダ、アマゾンウェブサービス、それが必要だ場合にのみ、あなたのコードを実行し、 自動的にスケールの提供を、そのようにプロビジョニングまたは継続的に実行するサーバーへの必要はありません。

あなたは、ラムダ関数にあなたのAlexaのスキルのコードをアップロードし、ラムダは、Alexaの音声の相互作用に応答して、それを実行すると、自動的にあなたのためのコンピューティングリソースを管理し、 残りの部分はありません。

ラムダ関数とカスタムスキルについて

あなたのサービスのためにラムダ関数を使用すると、設定して、独自のエンドポイントを管理周りの複雑さのいくつかを排除します。

  • あなたは、あなたのサービスのためのコンピューティングリソースのいずれかを管理または管理する必要はありません。
  • あなたは、SSL証明書は必要ありません。
  • あなたは要求がAlexaのサービスから自分を来ていることを確認する必要がありません。あなたの機能を実行するためのアクセスではなく、AWS内の権限によって制御されます。
  • AWSラムダは、あなたがそれを必要とする場合にのみ、あなたのコードを実行し、使用状況に比例するので、サーバを実行プロビジョニングするか、継続的に必要はありません。
  • アレクサは、ラムダがTLSを利用することで、その通信を暗号化します。参照してくださいAWSのセキュリティのベストプラクティスを。
  • ほとんどの開発者のために、ラムダ自由層は Alexaのスキルをサポートする機能に十分です。最初に百万要求毎月は無料です。ラムダ自由層は自動的に失効するが、無期限に利用可能でないことに注意してください。

AWSラムダは、Node.jsの(JavaScriptの)、Java、およびPythonで書かれたコードをサポートしています。あなたはコピーして、AWSラムダコンソールでインラインコードエディタでJavaScriptやPythonのコードを編集したり、zipファイルにそれをアップロードすることができます。基本的なテストのために、あなたはそれをラムダコンソールでJSONのリクエストを送信することにより、手動で関数を呼び出すことができます。

AWSラムダの概要については、以下を参照してくださいAWSラムダは何ですか?

ラムダ関数は、同じサービス・インターフェースに付着し、アレクサによって送信されたリクエストの3種類を処理する必要があります。Alexaの要求を処理する方法の詳細については、を参照してくださいアレクサによって送信された要求を処理します。JSONインタフェースの詳細については、を参照してくださいカスタムスキルのためのJSONインターフェースリファレンスを。

注: Alexaのスキルのラムダ関数は、でホストされている必要があり、米国東部(N.バージニア)リージョン。現在、これはAlexaのスキルキットがサポートする唯一のラムダ領域です。

この文書はのための新しいラムダ関数の作成 カバーカスタムスキルを。あなたはスマートホームスキルAPIを使用している場合は、作成するためにラムダを使用するスキルアダプタを。参照してくださいアレクサスマートホームのスキルを作成する手順を。

カスタムスキルのためのAWSラムダ関数の例

「星占い」:セッション機能の利用例

"use strict";
const Alexa = require('alexa-sdk');
const fortunes = [
  { 'score' : 'good',   'description' : '星みっつで良いでしょう' },
  { 'score' : 'normal', 'description' : '星ふたつで普通でしょう' },
  { 'score' : 'bad',    'description' : '星ひとつでイマイチでしょう' }
];
// ステートの定義
const states = {
  SYNASTRYMODE: '_SYNASTRYMODE'
};
// 相性占い結果の定義
const synastries = [
  { 'score' : 'good',   'description' : '良いです。仲良くなれるかもしれません' },
  { 'score' : 'normal', 'description' : '普通です。仲良くなるために頑張りましょう' },
  { 'score' : 'bad',    'description' : '悪いです。仲良くなるためには努力が必要かもしれません' }
];
exports.handler = function(event, context, callback) {
  var alexa = Alexa.handler(event, context);
  // alexa.appId = process.env.APP_ID;
  alexa.registerHandlers(handlers, synastriesHandlers); // 既存のハンドラに加えてステートハンドラ(後半で定義)も登録
  alexa.execute();
};
var handlers = {
  'LaunchRequest': function () {
    this.emit('AMAZON.HelpIntent');
  },
  'AMAZON.HelpIntent': function () {
    this.emit(':ask', '今日の運勢を占います。' +
                  'たとえば、うらないでふたご座の運勢を教えてと聞いてください');
  },
  'HoroscopeIntent': function () {
    var sign = this.event.request.intent.slots.StarSign.value;
    var fortune = fortunes[Math.floor(Math.random()*3)];
    this.handler.state = states.SYNASTRYMODE; // ステートをセット
    this.attributes['sign'] = sign; // 星座をセッションアトリビュートにセット
    var message = '今日の' + sign + 'の運勢は' + fortune.description + '。' +
                 '相性を占いますので、お相手の星座を教えてください';
    var reprompt = 'お相手の星座を教えてください';
    this.emit(':ask', message, reprompt); // 相手の星座を聞くためにaskアクションに変更
    console.log(message);
  }
};
// ステートハンドラの定義
var synastriesHandlers = Alexa.CreateStateHandler(states.SYNASTRYMODE, {
  'HoroscopeIntent': function() {
    // ハンドラの実行後、スキルの初期状態に戻すためステートをリセット
    this.handler.state = '';
    this.attributes['STATE'] = undefined;
    var yourSign = this.attributes['sign']; // セッションアトリビュートsignを参照
    var hisSign = this.event.request.intent.slots.StarSign.value; // スロットから相手の星座を参照
    var synastry = synastries[Math.floor(Math.random()*3)]; // ランダムに相性を決める
    var message = yourSign + 'と' + hisSign + 'の相性は' + synastry.description;
    this.emit(':tell', message);
    console.log(message);
  },
  'Unhandled': function() {
    var reprompt = 'お相手の星座を教えてください';
    this.emit(':ask', reprompt, reprompt);
  }
});

「星占い」:DynamoDB機能の利用例

Alexa SDKのAmazon DynamoDB連携

Alexa SDKには、データ保存先としてAWSのデータベースサービスであるAmazon DynamoDBと連携する機能があります。

使い方は非常にシンプルで、Lambda関数のプログラム内でalexa.dynamoDBTableNameメンバにDynamoDBのテーブル名を指定すると、 Lambda関数の初回実行時にDynamoDBのテーブルが自動で作成され、セッション終了時に以下の形式でセッションデータのコピーをDynamoDBに保存します

列名 データ型 説明
userId
(プライマリキー)
文字列 ユーザーID。AlexaアプリのAmazonアカウントごとに一意だが、スキルを無効化すると変化する
mapAttr マップ セッションアトリビュートのコピー

保存後は毎回のスキル実行時にDynamoDBのデータがセッションアトリビュートへ自動でロードされるため、 Lambda関数のプログラムではセッションアトリビュートを参照することで保存したデータを扱うことができます。


Alexa SDKのAmazon DynamoDB連携

注意 : Amazon開発者コンソールのサービスシミュレーターでは、スキルが終了する場合でも別ページに移動するか[Reset]ボタンをクリックするまでセッションが終了しないため、それまではDynamoDBへのデータ保存が行われないことに注意しましょう。(別途、Alexa SDKの設定でデータ保存を行うこともできます)

また、実行のためには、Lambdaの実行ロールにDynamoDBの権限を付与する必要があります。任意のデータを保存する場合はLambdaのプログラムで他のライブラリを利用することで、DynamoDBをはじめとするAWSの各サービスやインターネットで提供される様々なWeb サービスをデータの保存先として利用することもできます。Web サービスとの連携では、第5回で紹介するアカウントリンク機能も参考になるでしょう。

"use strict";
const Alexa = require('alexa-sdk');
const fortunes = [
  { 'score' : 'good',   'description' : '星みっつで良いでしょう' },
  { 'score' : 'normal', 'description' : '星ふたつで普通でしょう' },
  { 'score' : 'bad',    'description' : '星ひとつでイマイチでしょう' }
];
// ステートの定義
const states = {
  SYNASTRYMODE: '_SYNASTRYMODE'
};
// 相性占い結果の定義
const synastries = [
  { 'score' : 'good',   'description' : '良いです。仲良くなれるかもしれません' },
  { 'score' : 'normal', 'description' : '普通です。仲良くなるために頑張りましょう' },
  { 'score' : 'bad',    'description' : '悪いです。仲良くなるためには努力が必要かもしれません' }
];
exports.handler = function(event, context, callback) {
  var alexa = Alexa.handler(event, context);
  
  // ユーザーの星座を保存するDynamoDBのHoroscopeSkillTableテーブルを定義
  alexa.dynamoDBTableName = 'HoroscopeSkillTable';
  
  // alexa.appId = process.env.APP_ID;
  // 既存のハンドラに加えてステートハンドラ(後半で定義)も登録
  alexa.registerHandlers(handlers, synastriesHandlers);
  alexa.execute();
};
var handlers = {
  'LaunchRequest': function () {
    this.emit('AMAZON.HelpIntent');
  },
  'AMAZON.HelpIntent': function () {
    this.emit(':ask', '今日の運勢を占います。' +
                      'たとえば、うらないでふたご座の運勢を教えてと聞いてください');
  },
  'HoroscopeIntent': function () {
    var sign = this.event.request.intent.slots.StarSign.value;
    var fortune = fortunes[Math.floor(Math.random()*3)];
    this.handler.state = states.SYNASTRYMODE; // ステートをセット
    this.attributes['sign'] = sign; // 星座をセッションアトリビュートにセット
    var message = '今日の' + sign + 'の運勢は' + fortune.description + '。' +
                  '相性を占いますので、お相手の星座を教えてください';
    var reprompt = 'お相手の星座を教えてください';
    this.emit(':ask', message, reprompt); // 相手の星座を聞くためにaskアクションに変更
    console.log(message);
  },
  
  'SynastryIntent': function () {
    if (this.attributes['sign']) {
      this.handler.state = states.SYNASTRYMODE;
      var sign = this.attributes['sign']; // DynamoDBから読み込んだデータを参照
      var message = 'あなたの星座は' + sign + 'ですね。' +
                    '相性を占いますので、お相手の星座を教えてください';
      var reprompt = 'お相手の星座を教えてください';
      this.emit(':ask', message, reprompt);
    } else {
      this.emit('AMAZON.HelpIntent');
    }
  },
  // スキルの中断時にもDynamoDBへのデータ保存を実行する設定
  // https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs#making-skill-state-management-simpler
  'SessionEndedRequest': function () {
    this.emit(':saveState', true);
  }
  
};
// ステートハンドラの定義
var synastriesHandlers = Alexa.CreateStateHandler(states.SYNASTRYMODE, {
  'HoroscopeIntent': function() {
    // ハンドラの実行後、スキルの初期状態に戻すためステートをリセット
    this.handler.state = '';
    this.attributes['STATE'] = undefined;
    var yourSign = this.attributes['sign']; // セッションアトリビュートsignを参照
    var hisSign = this.event.request.intent.slots.StarSign.value; // スロットから相手の星座を参照
    var synastry = synastries[Math.floor(Math.random()*3)]; // ランダムに相性を決める
    var message = yourSign + 'と' + hisSign + 'の相性は' + synastry.description;
    this.emit(':tell', message);
    console.log(message);
  },
  'Unhandled': function() {
    var reprompt = 'お相手の星座を教えてください';
    this.emit(':ask', reprompt, reprompt);
  }
});
AWS Lambdaを使ってAmazon Echoに機能追加してみた


お花畑1