人型の簡易版Siriと友達になれるか試してみた

# コンシェルジュ型チャットボットの開発

▼完成動画

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">※音声出力推奨<br>バーチャルコンシェルジュを作成して会話してみました。<br>開発手順の詳細はQiitaにアップします。<a href="https://twitter.com/hashtag/AWS?src=hash&amp;ref_src=twsrc%5Etfw">#AWS</a> <a href="https://twitter.com/hashtag/Sumerian?src=hash&amp;ref_src=twsrc%5Etfw">#Sumerian</a> <a href="https://twitter.com/hashtag/Lex?src=hash&amp;ref_src=twsrc%5Etfw">#Lex</a> <a href="https://t.co/AKb1fV3YGm">pic.twitter.com/AKb1fV3YGm</a></p>&mdash; 小池駿平@フルスタックエンジニア (@shunpei42ba_) <a href="https://twitter.com/shunpei42ba_/status/1075707038179708928?ref_src=twsrc%5Etfw">2018年12月20日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 


## 本記事の開発難易度

今回は機能を限定して取り上げるので、トータル所要時間は**1時間**程度になります。

今回ご紹介するSumerianに関しては

機能数：**多**
英語の情報量：**少**
日本語の情報量：**激少**

という状況なので、数ある機能を使いこなすまでには時間がかかります。公式に英語版のTutorialがあるため、本格的に学びたい方はビギナーコースからじっくり動画をみて手を動かしてみることをオススメします。

#### Sumerian Beginner Course

5-30分程度の動画が15個あり、これだけでも大分ボリューミーです(o.o;)
:pencil: https://docs.sumerian.amazonaws.com/courses/beginner-course/

## 登場人物

本題に入る前に、本記事で登場する代表的なAWSサービスをご紹介します。全てを理解する必要はないので、概要だけ理解できればOKです。

### Amazon Sumerian

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206255/fff9eb38-6055-0728-bb1a-6b77f42faa53.png)

AR/VR/3Dのアプリケーションを簡単に作成できるゲームエンジンです。イメージとしてはUnityに近いです。AWSのあらゆるサービスと連動することで、３Dアプリケーションの表現の幅が広がります。

本記事における主人公です。

### Amazon Polly

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206255/99987676-69f0-132e-b68a-f1dce6e140d0.png)

文章をリアルな音声に変換するサービスです。
人型のキャラクターにテキストを読み上げてもらうことで、対話のリアル感を演出します。

キーパーソンの1人です。

### Amazon Lex

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206255/eda7e6ee-d06e-8c9d-011f-b05ed4457eac.png)

音声やテキストを使用して、任意のアプリケーションに対話型インターフェイスを構築するサービスです。
こちらが提供する情報をテキストベースで理解してもらい、より自然なコミュニケーションを目指します。

こちらも本記事における重要人物です。

### Amazon Cognito

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206255/870146af-fae6-a442-d28a-a388ef2f64a1.png)

AWSサービスをSumerianから呼び出せるよう、権限管理を行ってくれます。
今回登場するのは、Cognitoの中でもUser Poolではなく、Identity Poolの方です。

:pencil: [Cognito User Pool vs Identity Pool](https://serverless-stack.com/chapters/cognito-user-pool-vs-identity-pool.html)

### AWS CloudFormationt

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206255/ead32218-185c-9f60-1038-ca0bdbb4aed3.png)

Infrastructure as Codeとして管理されるテンプレートにより、リソースの自動生成を行います。今回はCognitoを作成する時に使用しており、AWSが提供するCloudFormationテンプレートを流すだけで、必要な設定が完了している状態でCognitoが作成されます。

### AWS Lambda

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206255/13eb0284-56f8-6e73-7cfd-7667b785c58a.png)

会話の後に期待する動作を関数として記述します。
本記事ではLambdaについて深くは触れず、ユースケースの紹介に留めます。


# 1. Sumerianを使った人型ロボットの作成

まずはSumerianのページから、シーンを作成していきましょう。
https://console.aws.amazon.com/sumerian/home/start


左上の「create new scene」から３D世界を作成します。
今回は「Siri Sample」という名前にします。

![FireShot Capture 117 - Sumerian Console_ - https___console.aws.amazon.com_sum.png](https://qiita-image-store.s3.amazonaws.com/0/206255/f124d6ef-a7b7-f839-82a0-3925252e935f.png)

※今回紹介するサービスのうち、執筆時点で東京リージョンでは対応していないものも存在するため、本記事ではバージニアリージョン前提でご紹介していきます。

## Cognito Identity Poolの作成

AWSサービスをSumerianから呼び出せるように、CloudFormationでIdentity Poolを作成します。
AWS側で既に用意してくれているため、**以下リンクから作成ボタンを押すだけでOK**です。

:pencil: [AWS CloudFormation stack template](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?templateURL=https:%2F%2Fs3.amazonaws.com%2Fsumerian-cfn-templates%2FLexPollyExampleTemplate.yml&stackName=AmazonSumerianLexPollyTutorialStack)
※「AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」にはチェックを入れておきましょう。

少し時間を置き、Cognitoが意図通り作成されているか確認してみましょう。
「Congito」->「IDプールの管理」->「SumerianTutorialCognitoIdentityPoolAmazonSumerianLexPollyTutorialStack」->「ID プールの編集」をクリックし、「IDプールのID」に表示されているIDを取得します。

*us-east-1:xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx*

Sumerianのコンソール画面から、上記で確認したIDを埋め込みましょう。左メニューに表示された「Siri Sample」を選択すると、右メニューにAWSの設定が表示されるため、Cognito IDプールの空欄に**リージョン名を含めて**コピーしてあげてください。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/05e64a41-c263-4238-1aba-56c4e6a624cf.png">

## ホストの作成

ホストと呼ばれる、人型のオブジェクトを作成していきます。
Sumerian画面の上部にある「アセットのインポート」をクリックして、好みのオブジェクトを選択します。今回は「Grace」と呼ばれるスーツの女性を選択していきます。

![FireShot Capture 119 - Sumerian Console_ - https___console.aws.amazon.com_sum.png](https://qiita-image-store.s3.amazonaws.com/0/206255/a53fdca5-fec2-47ed-78bf-102f33e7a244.png)

対象をダブルクリックすると、右下のアセット欄に先ほど選択した「Grace」が登場します。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/81ef8d37-6ec5-4678-f522-b5516cad19f6.png">

Graceが2つ並んでいますが、**下側の子Graceのみ**を選択して、３Dマップ上にドラッグ＆ドロップします。

少し時間がかかりますが、３Dマップと左メニューにGraceが表示されれば成功です。

![FireShot Capture 121 - Sumerian Console_ - https___console.aws.amazon.com_sum.png](https://qiita-image-store.s3.amazonaws.com/0/206255/c015d7e3-fc23-1fcd-08b4-8093c02050c8.png)

# 2. Pollyでテキストを読み上げる

それでは、このホストにテキストを読み上げてもらいましょう。

## テキストから音声への変換

まずは、ホストを選択した状態で、右メニューの音声に注目します。


<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/7b379711-1472-fbc1-1d88-9128b704cb20.png">

「音声ファイルのドロップ」の右側にある＋ボタンをクリックすると、エディタの別ウィンドウが立ち上がりますので、適当な挨拶文を入力してみましょう。

<img width="547" alt="スクリーンショット 2018-12-19 20.20.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/f0f2d878-d0aa-85ad-2909-3a8d08cd0c1e.png">

*Hello*というテキスト名で保存します。左下のSaveボタンを押してウィンドウを閉じると、*Hello*が追加されていることがわかります。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/ddec7423-4a7c-33a6-3960-b673f89bd5a9.png">

*Hello*の右側にある再生ボタン▶︎を押してみます。
テキストが読み上げられていれば成功です。

## ホストにテキストを読ませる

この状態だとホストが話している感覚にはならないので、実際にホストが話しているようなリアル感を演出していきます。

右メニューの「コンポーネントの追加」から、「ステートマシン」を選択すると、「音声」の下に「ステートマシン」が追加されます。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/d8595828-10b4-ffc8-a7e1-ea2391feeb82.png">

「動作の削除」の右側にある「＋」ボタンをクリックすると、Sumerianの画面が次のように拡張されます。

![FireShot Capture 125 - Sumerian Console_ - https___console.aws.amazon.com_sum.png](https://qiita-image-store.s3.amazonaws.com/0/206255/4031e922-a72d-d6e1-71a1-0a1617dfb438.png)

State1という名の四角形を選択して、右メニューの「アクションの追加」から、検索メニューで「Start Speech」を選択します。

![FireShot Capture 126 - Sumerian Console_ - https___console.aws.amazon.com_sum.png](https://qiita-image-store.s3.amazonaws.com/0/206255/08b46387-6c5f-9ebc-26a6-19119b6c25c3.png)

右メニューに戻り、「Speech」欄を先ほど作成した「Hello」に変更して準備完了です。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/4e57d0cf-beeb-6e7f-b34a-97e4598b7f8a.png">

中央の再生ボタン▶︎から再生してみましょう。
再生後、すぐにテキストが読み出されれば成功です。

## さらにリアル感を追加

このままでも良いのですが、棒読み感が否めないため、もう少しリアル感を出してみます。
言葉に合わせて適切なジェスチャーを表現できるよう、右メニューの「音声」コンポーネントへ戻り、「ジェスチャマップ」に注目してみます。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/37eb6eba-742f-2cba-a1b2-48dd14f5f30d.png">

「gesturemapをドロップ」の右側にある「＋」をクリックするとテキストウィンドウが開きます。

<img width="1014" alt="スクリーンショット 2018-12-19 21.03.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/48cfcb21-376f-da6c-8aef-45d45351afd0.png">

このデフォルトで用意されたJSONファイルでは、各ワードに対するジェスチャーが定義されており、例えば「add」というワードが含まれていた場合には、「big」を表すジェスチャーをするようになっています。

今回は「my」というキーワードがあらわれた時に「heart」ジェスチャーをするように追加してみます。

テキストウィンドウを閉じた後、このジェスチャーに合わせた形にテキストを変換します。ジェスチャマップに先ほど修正したDefaultGestureMapが指定されていることを確認した後、下図の赤枠で囲まれた自動生成マークをクリックします。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/f86b6dd8-34f1-cc69-fb21-e7efe06ce32b.png">

以前に作成したHelloのテキストファイルを開いてみると、ジェスチャーのタグ付けが追加されていることが確認できます。

<img width="966" alt="スクリーンショット 2018-12-19 21.05.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/2fceedeb-39c9-7cb2-d164-a89f33a8fbd4.png">

さらに、視点にも手を加えてみましょう。
右メニューの「ホスト」から、対象となるエンドポイントとして「エンティティを表示」を選択し、ターゲットエンティティとして「Default Camera」を選択します。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/be365ac4-2feb-cccc-3200-adf85b69276c.png">

ここままで、一旦再生ボタン▶︎を流してみましょう。
視点を追いかけながら、ジェスチャーが追加されたGraceをみることができます。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/65b73de8-0e07-2297-d0b9-88cd671322ed.png">


# 3. Lexを使ってコミュニケーション

ここからは、Lexを使ってホストと対話することを目標にします。

## ボットの作成

まずは、Amazon Lexのボットを作成していきます。
Amazon Lexのコンソール画面を開き、ボットの作成を進めます。

「Bots」->「Create」から、今回は「BookTrip」というテンプレートを選択して進めます。下記の通り入力して「Create」ボタンを押します。

![FireShot Capture 132 - Amazon Lex_ - https___console.aws.amazon.com_lex_home.png](https://qiita-image-store.s3.amazonaws.com/0/206255/246adbca-1996-906a-6c75-6915fec627ab.png)

以下のような画面が表示されればOKです。

![FireShot Capture 134 - Amazon Lex_ - https___console.aws.amazon.com_lex_home.png](https://qiita-image-store.s3.amazonaws.com/0/206255/7136316e-637e-e49e-670a-b0e639d6f79b.png)

右側の「Test Chatbot」を開くとチャット画面が表示されるので、「Book a hotel」(ホテルの予約をお願いします)を入力します。
質問に答えていくと、下側に表示されている「Inspect response」のSlotsが埋まっていくのがわかります。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/41c13dd4-76b7-7baa-f39f-0399d344c726.png">

想定通り動作することを確認したら、右上の青い「Publish」ボタンから、エイリアスの作成を行います。今回は「Test」という名前で作成しましょう。

![FireShot Capture 136 - Amazon Lex_ - https___console.aws.amazon.com_lex_home.png](https://qiita-image-store.s3.amazonaws.com/0/206255/9dac820a-cdb4-e13d-093c-59b1aa4ddb68.png)


以上で、サンプルボットの作成が完了です。
今回はデフォルトで用意されているボットでしたので、特に設定をいじる必要はありません。

## SumerianからLexBotの呼び出し

Sumerianコンソールに戻り、今作成したボットを設定に組み込んでみましょう。右メニューにある「コンポーネントの追加」から、「ダイアログ」を選択して名前とエイリアスを下図のように穴埋めします。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/58e34346-8478-0baa-7ad1-f40b414a8661.png">

## 状態とアクションの作成

次にステートマシンの追加です。
右メニューにある「コンポーネントの追加」から、「ステートマシン」を選択します。

ステートマシンでは、対象のオブジェクトにどういった動きを、どの順序でさせたいかを制御することが可能です。例えば、あるA操作が完了したら、B操作を行い、C状態で待ちの後、入力をトリガーにD操作を実行する、といった流れをつくることができます。

ステートマシンでは状態とアクションという概念が重要になります。
上記の例だと、「State1」という名前で挨拶するステートマシンを作成したと思いますが、この「State1」というのが状態名で、この状態に含まれている「Start Speech」がアクションです。1つの状態で複数のアクションを指定することもできます。

ここからは、上記で作成済みの状態に加えて、新たに6つの状態を作成していきます。

#### :one: AWS SDkの初期化

ステートマシン画面左上の「＋状態の追加」から、新たに状態を作成します。状態名は「Start」にしてください。

「アクションの追加」から、「AWS SDK Ready」を選択します。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/f7231314-7614-4ca2-274f-b8f451400302.png">

下記のように追加されていればOKです。

![FireShot Capture 137 - Sumerian Console_ - https___console.aws.amazon.com_sum.png]
(https://qiita-image-store.s3.amazonaws.com/0/206255/65f0d336-adcc-8935-7146-97b26b422c6d.png)

####:two: 待ち状態

ユーザの入力待ち状態を作成します。

上記同様、「状態の追加」->「アクションの追加」から、「Key Down」を選択します。状態名は「Wait for Input」へ変更します。
さらに、「Key」欄にフォーカスを当てた状態で、Spaceキーを入力してください。AからSpeceに変わればOKです。これにより、Spaceキーの押下を待つようになります。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/493ab2a3-8976-3157-a22d-912e18b7a571.png">

####:three: 録音開始

入力音声を認識する開始時間になります。

状態名を「Start Recording」とし、アクションとして2つ、「Start Microphone Recording」と「Key Up」を追加します。「Key」欄には同じくSpeceキーを入力します。これらによって、Spaceキーを押している間を録音時間とするようになりました。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/c5a7b825-104a-db57-1ec2-ea3685b4c5cf.png">

####:four: 録音終了

入力音声の時間を完了します。

状態名は「Stop Recording」とし、アクションとして「Stop Microphone Recording」を追加します。これによって、:three:で作成したSpaceキーがKey Upした直後に録音ストップの操作が行われることを期待します。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/bf1b69b5-0454-cd54-9ee4-cd1831cfa60b.png">

####:five: 音声認識

Lexにて音声認識を行います。

状態名は「Process with Lex」とし、アクションとして「Send Audio Input to Dialogue Bot」を追加します。下図の通り、チェックボックスは空でOKです。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/d5415750-c12f-2184-a06e-bf9e1ee36381.png">

####:six: 音声出力

最後に、レスポンスを音声を使って行います。

状態名は「Play Response」で、アクションとして「Start Speech」を選択します。「Use Lex Response」にチェックを入れて下さい。

<img width="452" alt="スクリーンショット 2018-12-19 22.12.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/206255/6f665367-269e-e8d6-962f-8b9b82de2e39.png">

## シーケンスの作成

ここまでで単体として作成してきた状態を連結させていきます。

流れとしては:one:->:two:->:three:->:four:->:five:->:six:->:one:という矢印を引っ張っていきます。状態のアクションをクリックした状態でスライドすると矢印が伸びるので、次の状態まで引っ張ります。一番最初に作成した「State1」の挨拶は、「Hello」という名前に変更して:one:に繋がるよう修正しました。

最終的に以下のような矢印を作成していけばOKです。

![FireShot Capture 145 - Sumerian Console_ - https___console.aws.amazon.com_sum.png](https://qiita-image-store.s3.amazonaws.com/0/206255/74152941-1d46-6e36-e408-d6a032592b07.png)

## 動作確認

ここまでの成果物を動作確認していきましょう。

再生ボタン▶︎をクリックすると、*Hello*の状態が実行されて入力待ちになります。

Spaceボタンを押しながら「book a hotel」と呼びかけてみましょう。レスポンスが返ってくれば成功です。

以下会話のテンプレートになります。

```
自分   : book a hotel

Grace : What city will you be staying in?

自分   : Tokyo

Grace : What day do you want to check in?

自分   : December 25th

Grace : How many nights will you be staying?

自分   : 7

Grace : What type of room would you like, queen, king or deluxe?

自分   : queen

Grace : Okay, I have you down for a 7 night stay in Tokyo starting 2018-12-25. Shall I book the reservation?

自分   : yes

Grace : Intent BookHotel is ReadyForFulfillment: CheckInDate:2018-12-25 Location:Tokyo Nights:7 RoomType:queen
```

以上で動作確認まで完了になります。
今回は会話することが目的だったため実際の予約は行いませんでしたが、最後のレスポンスの後Lambdaをフックさせることにより、期待する動作を続けることも可能です。

# おわりに

コンシェルジュとインタラクティブな会話をすることができました。
まだまだ距離はありますが、"友達になれそうなくらい"には仲良くなれた気がします。

Sumerianに関する日本語の情報はまだまだ少ないのが現状です。
今後もAR/VR開発やAWS全般にわたる情報を発信していくので、定期的にWatchいただけると幸いです。