まぁ、つまらないものですが

エンジニニャー見習いの気ままな技術ブログ、日々のログを残してゆきます。

ALEXAでスマートスピーカー開発入門

さて本稿は10日ぐらいに出す予定の記事だったのですが、大遅刻かまして今日出しました。
けして、ガールズアンドパンツァー劇場版一話を観に行っていたとか、FGOの英霊剣豪七番勝負を大急ぎで消化してたとか、アーケード版電車でGo!!をやりこんでたとかそういう訳では無いです。

実際のところは書物が多くて手がついてないだけでした…

スマートスピーカーAlexaの特徴

AlexaはAmazonが開発を行っているボイスアシスタントです。現在amazon echo, amazon echo dot, amazon echo plusの3機種やamazonバイスなどに搭載され前述の3つはスマートスピーカーとしてamazonで販売されています。
2017-12-15現在リクエスト式の販売になっており購入リクエストを送ると数日後から数週間後に購入可能の連絡がくると購入出来るようになっています。

他社スマートスピーカーとの差異はあまりありませんが、

  • Amazon独自の音声処理技術(文脈などから同音異義語は判断するなど)
  • amazonサービス(amazon music, audibleなど)との連携
  • 初音ミクと会話ができる(alexaスキル「Hey MIKU!」)
  • 他のと違い人名に近いため呼びやすい などが大きいところでしょうか。 実際の使用感や使える機能などは他のスマートスピーカーあまり変わらなそうです。

Alexa開発の流れ

Alexaでは機能やアプリケーションを「skil」と呼びます。skilの開発にはAlexa Skils Kit(以下ASK)を用いて開発することになります。

まずAmazon Developerのアカウントを取得しましょう。費用は特にかかりません。 Amazon Developer Services and Technologies

取得出来たら早速上のTabからAlexaを選択してAlexa Skills Kitを押してみましょう。

すると右上に新しい「新しいスキルを追加する」というボタンがあるので押してみましょう。

押すと以下のような画面になりどのようなSkilを作成したいか求められます。 今回はカスタム対話モデルの日本語の設定で「ハローワールド」という名前で作成してみようと思います。 ただ、せっかくなので画面を少し見てみましょう。

スキルの種類

スキルの種類はそのままどのような機能をAlexaに実装するかということです。

カスタム対話モデル(カスタムスキル)

一番オーソドックなものです。ユーザーと対話を行い入力された情報から求められた情報を返すスキルです。
Amazonのドキュメント内ではカスタム対話モデルやカスタムスキルなど呼ばれています。
例えば「Alexa、 音泉アプリで佐倉としたい大西をながして」 (音泉さん作ってくれないかなぁ)などです。

スマートホームスキルAPI(スマートホームスキル)

照明やテレビなどスマートホーム端末を制御するスキルを作るのに特化したものです。音声の処理を作らなくて良いため用意に実装出来ます。ただし、ユーザー体験のデザインの自由度は下がります。

フラッシュブリーフィングスキルAPI(フラッシュブリーフィングスキル)

フラッシュブリーフィングskilはalexaに「Alexa,フラッシュニュースを聞かせて」や「Alexa、ニュースを教えて」と聞く起動されるスキルです。Alexaはそのリクエストを受けると有効になっているAlexaフラッシュブリーフィングニュースすべてを実行します。朝や寝る前などの仕事に行く準備している時に聞いたりすることを想定しています。 こちらはRSSフィードJSONを指定して開発を行います。

ビデオスキルAPI

こちらはまだ日本語対応しておらず、日本語ドキュメントにもありませんか英語ドキュメントを見てみるとビデオデバイスやストリーミングサービスの制御に使うものとなっているようです。

言語

Alexaで主に使用する言語を指定します。
2017-12-15現在カスタム対話モデルでは

  • 英語(アメリカ)
  • 英語(英国)
  • 英語(カナダ)
  • 英語(オーストラリア)
  • 英語(インド)
  • ドイツ語
  • 日本語 となっています。

スキル名

ユーザ側に表示される名前になります。わかりやすいものにしておきましょう

呼び出し名

Alexaでアプリを呼び出す時にユーザーが発言する名前です。呼びやすいものにしておきましょう。

Alexa特有の概念

Echoから来た言葉はAlexaへ送られます。そこで事前に作った対話モデルを使い文章を解析します。その後解析された結果は登録されているプログラムへJSONに整形され送られます。その後処理を行い返答をJSONで行いAlexaに介されEchoで発音されると、言う流れです。
プログラムに関しては公式ではAWSのLambdaを使うことが推奨されています。 本稿もLambdaを使用します。

さて早速対話モデルを組んでいきましょう。今回は現在BETAとして公開されているBuilderを使ってみます。
Builderに飛ぶとまた違うダッシュボードが出てきます。 またここからAlexa特有の概念がここから出てきます。

インテント

インテントはユーザーが求めるアクションのことです。例えば先述したラジオアプリなら。「再生」「一時停止」「飛ばして」などが考えられます。
処理を書く際はインテントごとに関数を分けると良いと思います。

スロット

発言する際に使用する変数名です。予め考えられるものを入れて置くとAlexaが文脈から抜き出し登録されたものからどれが当てはめて適切なのを返してくれます。

対話モデルを作成する

では早速インテントとスロットを登録していきましょう。自動で保存してくれますが、心配であれば上のナビバーのSaveModelを適宜押しておきましょう。

スロットを作成する

まずはスロットを作成していきます。左のメニューにある「Slot Types」の横にあるADDを押すと下のような画面になります。
2つ選択がありオリジナルで作る「Create a new custom slot type」とAmazonのビルドインのものをつかう「Use existing slot type from Alexa's built-in library」町名や日時などはこちらが便利です。
今回はオリジナルで「language」という名前で作成します。
すると今後はどのような物が含まれるかを入力できるページになります。
上のテキストボックスにまず想定される言葉を入力します。すると下に行として追加されます。また「SYNONYMS」という同義語を入力出来る列が現れます。そこに想定できる限りの似たような言葉を入れていきます。 今回は日本語と英語を用意してみます。

インテントを作成する

こちらも左にあるメニューのADDから追加していきますが、スロットと違い最初から3つ登録されています。こちらはそのままでも大丈夫なのでそのまま独自の物を追加していきます 。
こちらもカスタムとビルドインがありますが、今回はカスタムインテントを追加していきます。今回は挨拶という意味合いの命令にしたいので「greeting」という名前で作成します。 するとスロットと似たような画面に飛びます。ここではその命令を呼び出す際に考えられる発現パターンを書いていきます。 また入力する際に{}で言葉を囲うって変数として使用することができます。{}の前後はスペースを入れておきましょう
今回は以下の用に設定してみました。 {}を使うと左のメニューバーに囲われた言葉がサブツリーで出てきます。クリックしてみるとこの変数はどのスロットを使うのかを設定出来ます。今回は先程作成したlanguage スロットを設定してみます。

ここまででできれば対話モデルは完成です。 上のナビバーからBuildModelを推してモデルをビルドしてみましょう。問題がなければ右上に成功のメッセージが飛んできます。

AWS lambdaを用いて開発する

さてここからがAlexa Skilのコアプログラムの処理の話になってい行きます。

Amazon Web Service(AWS)とは

ここからはAmazon Developerから一旦離れAmazon Web Service通称AWSで作業を行います。
AWSAmazonが行っているクラウドサービスです。その内容は多岐にわたりVPS(EC2)やオブジェクトストレージ(S3)などその数は年々(もしくは月々)増えています。今回はそのなかのLambdaというサービスを使用します。 ものとしてはサーバーレス(自前でサーバーを持たない)でコードをクラウド上でホスティングし、設定したトリガーから様々なプログラムを動かすことが出来るサービスです。今回はテンプレートとして存在するサンプル「alexa-skills-kit-color-expert-python」を用いて開発します。
因みに料金は従量課金制で、幾つかサービスごとに無料枠があります。また学生なら学生クーポンがあるので積極的に活用していきましょう。

AWS Lambdaを使う

AWSのアカウントを作成してログインするとAWSのダッシュボードが出てきます。 サービスから探すのは大変ですので検索のテキストボックスでlambdaを検索して選択しましょう。

そうするとLamdaのページに飛びますので早速「関数の作成」を選択して作っていきましょう。

作成ウィザードに飛びます。今回はPythonのサンプルを用いたいと思います。「設計図」を選択して「alexa-skills-kit-color-expert-python」を用います。検索ボックスで検索して選択してください。 選択すると改めて設定画面になるので必要な情報を埋めていきましょう。
今回はAlexaHelloWorldという名前にします。ロールは任意の物を使用してください。無ければ 「テンプレートから新しいロールを作成」を選択してください。入力出来たらページ下部の関数の作成を押しましょう。 作成したら以下のような画面に変わります。ここで各種設定とコードを書いていきます。

自分が読んで分かる範囲でコードを解説していきます。

lambda_handler(event, context)

lambdaが呼ばれた時に一番最初に実行される関数です。
コードを参照してみましょう(一部省略してます)

def lambda_handler(event, context):

    if event['session']['new']:
        on_session_started({'requestId': event['request']['requestId']},event['session'])

    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])

eventにはLambdaに渡された情報が格納されています。contextにはLambdaに関する各種プロパティを参照出来ます。今回はeventだけ参照すれば良いようです。
コードのなかはIF文によって分岐しています。分岐はAlexaから送信されるリクエストタイプから行っています。 リクエストタイプには3種類あって役割は以下のようになっています。

LaunchRequest

スキル名のみで呼び出し、コマンドが含まれていない時。

IntentRequest

ユーザーが対応するコマンドを発言した時。

SessionEndedRequest

ユーザーが以下の理由でスキルの仕様を停止する時。

  • 「終了して」と言われた時
  • ユーザーが応答しないまたは定義しているインテントが該当しない時
  • エラーが起きた時

→詳細 alexaドキュメント - Alexaから送信されたリクエストを処理する

今回は定義したインテント「greeting」を処理したいのでインテントから分岐している「on_intent」関数を折っていきたいと思います。

on_intent(intent_request, session)

ここではなんのインデントを呼ばれたかを判定して各関数に分岐する関数です。
コードを見てみましょう(一部省略しています)

def on_intent(intent_request, session):
 
    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']

    # ここで各関数に分岐
    if intent_name == "MyColorIsIntent":
        return set_color_in_session(intent, session)
    elif intent_name == "WhatsMyColorIntent":
        return get_color_from_session(intent, session)
    elif intent_name == "AMAZON.HelpIntent":
        return get_welcome_response()
    elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
        return handle_session_end_request()
    else:
        raise ValueError("Invalid intent")

今回のコードの以下の部分はサンプルの色を聞かれた時に分岐するところです。自分のオリジナルスキルを書く際は省いておくと良いでしょう。

if intent_name == "MyColorIsIntent":
        return set_color_in_session(intent, session)
    elif intent_name == "WhatsMyColorIntent":
        return get_color_from_session(intent, session

build_speechlet_response(title, output, reprompt_text, should_end_session):

インテントを処理し終わった後にAlexaに戻すために使う関数です。
中身としては各パラメタをJSONに当てはめています。
ここでは返答の処理の部分のJSONのみを当てはめています。

def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': "SessionSpeechlet - " + title,
            'content': "SessionSpeechlet - " + output
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }

outputspeechはAlexaで流す音声の情報についてを書きます詳しくはalexaドキュメント - OutputSpeechオブジェクトで確認できます。

cardはAlexaアプリなどに表示される部分について書くことができます。詳しくはalexaドキュメント - Cardオブジェクト

shouldEndSessionは発音後にアプリを閉じるかどうかを決めることができます。終わる場合はTrueにすると終了できます。

build_response(session_attributes, speechlet_response)

こちらはbuild_speechlet_responseで作ったJSONを更に返答用のJSONに格納して返しているものです。最後にここで吐き出されたものをAlexaに返します。

コードを追加する

コードの中身を一通り覗いてみたので早速同時の関数を追加していきます。今回はインテント「greeting」に合わせて言語ごとに挨拶する関数を作ってみます。

実際に追加しのは以下のコードです。

def set_greeting_text(intent, session):
    """ Sets the color in the session and prepares the speech to reply to the
    user.
    """

    card_title = intent['name']
    session_attributes = {}
    should_end_session = False

    # スロットの中にlanguage変数があるか確認する
    if 'language' in intent['slots']:
        language = intent['slots']['language']['name']
        if language == '日本語':
            speech_output = 'こんにちはAlexaです。今日本語で挨拶しています'
        elif language == '英語':
            speech_output = 'Hello, I am alexa. speak English now.'
    else:
        speech_output = "にゃーん"
       
    reprompt_text = speech_output
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))

on_intenのIF分のところも以下の用に修正しました。

    if intent_name == "greeting":
        return set_greeting_text(intent, session)
    elif intent_name == "AMAZON.HelpIntent":
        return get_welcome_response()
    elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
        return handle_session_end_request()
    else:
        raise ValueError("Invalid intent")

テストツールで試してみる

さてここまで来ればあと一歩です。まずはAWSと作ったスキルを連携させましょう。 一旦Amazon Developerに戻って設定を開きましょう。 今回はエンドポイントのタイプはAWSLambdaを選択しましょう。 するとデフォルトが表示されます。ここのに入れるのはAWS Lambdaの以下の赤枠の部分を入れてください。

おめでとうございます!ここまで来ればテストが出来るようになります! 早速テストのページを開いてみましょう!

ボイスシュミレーターでは実際のAlexaの合成音声が試せます。もう一つのサービスシュミレーターでは実際の応答を試すことが出来ます。
さっそくしてみましょう。

出来ました!右下のPlayボタンで再生もできます。

デバッグの仕方

もしうまくいかない場合はデバッグをする必要があります。Lambdaでログを追ってく際に毎回AmazonDeveloperの方に戻るのも手間なのでLambda側で行いましょう。
サービスシュミレータのサービスリクエストのJSONをまずコピーします。

そのごAWS Lambdaに戻りテストボタンから新規テストの作成を押しましょう。
そうするとテストイベントの設定が出てきますのでそこに先程コピーしたJSONを貼り付けて保存してください。

設定できたらテストボタン横のプルダウンで追加したものを選択してください。その後はテストボタンを押せば都度実行することが出来ます。

参考資料

CRYPTON - 「初音ミク」と音声会話ができる!Amazon Alexaのスキル『Hey MIKU!』提供開始! response - Alexa日本語対応とEcho発売---アマゾン勝利の方程式
音泉 - 佐倉としたい大西
DeveloperIO - [Alexa] 日本語でフラッシュブリーフィングスキルを作成してみる
Amazon Alexaドキュメント

相互リンク

  • 技術ブログ:ヤモト.tvp
  • 友人氏の技術ブログ 数学ガチ勢がエンジニアになっていく奮闘記