Amazon Lex と Twilio Flex を連携させてチャットボットを作る

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズの Twilio事業部エバンジェリストの高橋です。

今回は、4月に日本語対応がされた Amazon Lex と Twilio Flex を使って、チャットシステムを作りたいと思います。
[Amazon Lex](https://aws.amazon.com/jp/lex/) は Alexa でも利用されている会話エンジンで、今までは日本語での利用ができなかったのですが、今回日本語にも対応したことで日本での利用が期待されます。
Twilio と Amazon Lex の組み合わせは、チャットだけに限らず、会話ボットへの展開も可能ですが、今回はチャットに絞って以下の機能を実装していきたいと思います。

- ユーザが Web チャットを使ってホテルの予約をする
- 地域や日時などを Lex が対話形式でヒアリングする
- ヒアリングした項目をオペレータに伝え、以後はオペレータが対応する
- オペレータは Twilio Flex を利用する

完成したイメージは以下のようなものです。

![スクリーンショット 2021-04-05 14.00.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/fe329166-c325-7997-b221-4413b03aa01e.png)

右側が Web チャットの画面（ユーザ側）で、左側が Twilio Flex によるオペレータの画面となります。
ヒアリングで収集した情報が Flex 上にも表示されています。

## Twilio Flex とは

[Twilio Flex](https://cloudapi.kddi-web.com/function/flex)は、フルカスタマイズが可能なコンタクトセンターソリューションです。チャットはもちろん、電話やメールなどのコミュニケーションツールを統合し、すぐに使い始めることが可能です。

# おおまかな流れ

今回は色々な技術を組み合わせるので、全体像を理解しておくことが重要です。

![Flex + Lex 相関図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/7fddb311-35c1-7c6e-de30-d6b00dcde790.png)

何やら箱がたくさん出てきましたね。
Twilio Flex は、Twilio が提供する様々なサービスの組み合わせでできているため、全体構成図にするとこのように複雑な関係になります。
ただし、ご安心ください。今回のハンズオンでは、上記の太枠部分だけをカスタマイズ、もしくは新規に作成することで完成します。

では、上記構成図を実際の流れを沿って簡単に説明します。

1. ユーザが Web チャットで問い合わせをするための UI が、**Flex WebChat UI**です。これは Flex をプロジェクトの中に含まれています。ただし、標準は英語インターフェースとなります。日本語化することも可能ですが、本ハンズオンでは触れません。
1. WebChat UI から届いたチャット本文は、まずは **Twilio Proxy** に渡ります。Twilio Proxy は主にメッセージングのセッション管理を受け持ちます。セッション管理とは、同じ人が何回もメッセージのやり取りをする際に、それを識別しながら管理することです。もし、全く新しいユーザが問い合わせをしてきた場合は、**Flex Channel Orchestration** 経由で新しいセッションを作成します。Flex は、Web チャット以外にも、電話や SMS、WhatsApp などのツールを利用することができ、それぞれをうまくハンドリングしてくれるのが、Flex Channel Orchestration です。
1. 今回はユーザが Web チャット経由で問い合わせをしてきたので、Flex Channel Orchestration は、**Twilio Chat** を使ってやり取りをすることを選択します。Twilio Chat は、チャットのやり取りに参加するユーザの管理や、チャット本文の管理などを担います。管理者が明示的に削除しない限り、チャットのデータは残りますので、ユーザを一意に管理するようにすることで、過去の問い合わせ履歴などを確認することもできます。今回はそこまで厳格にユーザ管理はしないため、セッションが終わった時点で過去のデータとの紐付けはなくなります。
1. **Twilio Studio** は、メッセージングフローを操作することができるツールです。Flex をセットアップした直後は、チャット本文を Twilio Task Router に渡す、非常にシンプルなフローが組み込まれます。ただし今回は、Twilio Task Router に渡す前に Amazon Lex とのやり取りが必要なため、フローを組み替えて Twilio Functions 経由で Amazon Lex とやり取りをし、必要な情報（どこに泊まりたいのか、いつから泊まりたいのかなど）を Amazon Lex とやりとりしながら、ユーザに返信するように実装します。
1. **Twilio Functions** は、今説明したとおり、Amazon Lex とのやり取りを担います。具体的には、AWS の SDK を使ってメッセージを交換しあいます。言語は Node.js となります。
1. **Amazon Lex** については、このハンズオンではほとんど解説はしません。標準で用意されている予約用のシナリオをそのまま利用します。
1. **Twilio Task Router** はスキルベースのタスクルーティングを行うためのものです。具体的には、Web チャットに対応できるエージェントを選択したり、エージェントにタスクを割り当てたりします。エージェントが複数いるようなケースでは、このタスクルータをうまく設計することで、効率良いエージェントの配置や割り当てが可能になります。本ハンズオンでは、エージェントは一名しかいない状態ですので、タスクルータを設定する必要はありません。
1. エージェントが実際にチャットのやり取りを行う画面が **Flex UI** です。Flex UI は、React ベースで作成されたコンポーネントによって実装されており、これらは Flex プラグインを作ることでカスタマイズが可能です。本ハンズオンでは、Amazon Lex によって収集した各種情報を、オペレータの操作画面にも表示させるカスタマイズを行います。


# 準備

今回のハンズオンを行うためには、いくつか準備が必要です。

## Twilio アカウント

このハンズオンでは、KDDIウェブコミュニケーションズのアカウントで行いますので、[こちら](https://cloudapi.kddi-web.com/signup)からトライアルアカウントを作成してください。

## Twilio CLI のセットアップ

今回のハンズオンでは、Amazon Lex とのやり取りを Functions を使って行います。また、Flex のプラグインも作成する必要があります。これらはすべて Twilio CLI を使ってセットアップしていきますので、以下の記事を参考に CLI と サーバーレスプラグイン、Flex プラグインの準備を行っておいてください。

[Twilio CLI（セットアップ編）](https://qiita.com/mobilebiz/items/456ce8b455f6aa84cc1e)
[Twilio CLI（サーバーレス開発編）](https://qiita.com/mobilebiz/items/fb4439bf162098e345ae)
[Twilio Flex Plugins CLI](https://qiita.com/mobilebiz/items/eeca1ce20bb5b561b515)

## AWS アカウント

AWS アカウントは、[こちら](https://aws.amazon.com/jp/register-flow/)から行います。

# ハンズオン

ではいよいよハンズオンを開始していきましょう。
大まかな手順は以下の通りです。

1. Amazon Lex を準備する
1. Twilio Flex をセットアップする
1. Twilio Functions で Lex 連携部分を作る
1. Twilio Studio で対話フローを作成する
1. Flex UI をカスタマイズする

## Amazon Lex を準備する

### Amazon Lex にアクセスできるように IAM ユーザをセットアップする

- AWS 管理コンソールにログインします。
- [IAM のホーム](https://console.aws.amazon.com/iam/home)に移動します。
- アクセス権を付与したいユーザーを選択するか、新しいユーザーを作成します。
- アクセス権限タブで、**アクセス権限の追加**ボタンを押します。
![AWS_IAM_1 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/8e8ed0e7-dcc6-1cb8-eb50-009aea239b12.png)

- **既存のポリシーを直接アタッチ**を選択し、必要なポリシーを割り当てます。このユーザーを API 連携専用とする場合は、**AmazonLexRunBotsOnly** でも良いです。ボットのカスタマイズを行う必要がある場合は、 **AmazonLexFullAccess** を選択すると良いでしょう。
![スクリーンショット 2021-04-05 17.16.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/fc1dc8d6-dca4-7584-8896-b6331d02c54d.png)

- **次のステップ：確認**ボタンを押します。
- **アクセス権限の追加**ボタンを押します。
- アクセス権が追加されたことを確認します。

### AccessKey と SecretAccessKey を準備する

- 認証タブに移動し、**アクセスキーの作成**ボタンをおして、新しいアクセスキーを生成します。
- すでにアクセスキーを保有している場合は、そちらをそのまま使います。
- 作成したアクセスキーとシークレットアクセスキーは後で使いますので、メモ帳などに記録しておいてください。

### Bot をセットアップする

- [Amazon Lex のホーム](https://ap-northeast-1.console.aws.amazon.com/lex/home)にアクセスします。
![スクリーンショット 2021-04-05 17.45.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/e174857b-8246-a895-2336-3aa818e9476b.png)

- **Get Started**ボタンを押します。
- ボットの作成画面が表示されます。
![AWS_LEX_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/d09b01ab-28d9-6666-c8a7-30456e3cd463.png)

- **Bot name**は、デフォルトの「BookTrip_jaJP」のままにしておきます。
- **Language**は、「Japanese(JP)」を選択します。
- **Sentiment analysis**は、「No」を選択します。
- **COPPA**には該当しないので、「No」を選択します。
- 以上で設定は終了なので、**Create**ボタンを押します。

### ボットにエイリアスを設定する

- **Setting**タブに移動します。
- **Aliases**欄に、「Prod」と入力します。
- **Bot version**のプルダウンから「Latest」を選択します。
- 右側にある**+**アイコンをクリックして登録します。

### ボットをテストする

以上で最低限の設定は完了ですので、さっそくボットのテストをしてみましょう。

- 右側の Test bot ウィンドウの中にある入力欄に「ホテルの予約をしたい」と入力して、追加の質問に答えていきます。
- このボットは、滞在場所（Location）、チェックイン日（CheckInDate）、泊数（Nights）、部屋のタイプ（RoomType)を聞き出し、スロットに入れていきます。
- 最終的にすべての情報を聞き出すと**Dialog State**が「ReadyForFulfillment」になります。
![AWS_LEX_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/38dbb3e9-b11b-8875-bf16-1e948ae079db.png)

以上で Amazon Lex のセットアップはすべて終了です。

## Twilio Flex をセットアップする

Twilio Flex は新しいプロジェクトとして作成する必要があります。
以下の手順に従ってセットアップを行います。

- [Twilio 管理コンソール](https://jp.twilio.com/login/kddi-web)にログインします。
- 左側のサービスボタンから、[Flex メニュー](https://jp.twilio.com/console/flex/overview)に移動します。
![FlexSetup_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/01ef3a91-a33d-6cf3-ba7c-1d3ae75e99ce.png)

- **Create My Flex Project**ボタンを押します。
![FlexSetup_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/f74b5ca1-156e-5ae2-36c8-a6706d3f27e6.png)

- **アカウント名**に「AmazonLex」と入力します（名前はなんでも良いです）。
- **確認**ボタンを押します。
![FlexSetup_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/6440d02e-d6d6-dbc6-6c79-4c0f9ec711e6.png)

- **番号**の国が日本になっていることを確認します（なっていなければ日本を選択してください）。
- **Phone Number**欄に、ご自分のスマホ番号を入力します（先頭の0を除いて入力します）。
![FlexSetup_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/68487848-ef27-a110-e447-1b7ce88f4dc1.png)

- **確認**ボタンを押します。
- すぐに入力したスマホに SMS が届きます。
- SMS に記載されていた6桁の数字を入力して、**Submit**ボタンを押します。
![スクリーンショット 2021-04-06 9.00.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/2260092e-684d-d671-0981-8f3e09762dd9.png)

- セットアップが自動的に始まります。
- セットアップが完了すると、以下のようなログイン画面が表示されます。
![スクリーンショット 2021-04-06 9.04.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/517eccc7-7c17-a24c-e216-0300c5d7b48f.png)

<font color='red'>**<注意>この画面からはログインできません。**</font>
このログイン画面は本国のアカウント用なので、KDDIウェブコミュニケーションズのアカウントを使っている場合は、URLを少し変更して頂く必要があります。

`https://www.twilio.com/login?g=%2Fconsole%2Fflex%2Fservice-login%2F〜`
の`login?`の部分を以下のように変更します（ ? の前に kddi-web を挿入してください）。
`https://www.twilio.com/login/kddi-web?g=%2Fconsole%2Fflex%2Fservice-login%2F〜`

![スクリーンショット 2021-04-06 9.09.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/7c81df1d-0e39-fb98-298c-22cf62b60d82.png)

こちらが正しいログインページになりますので、改めてご自分のメールアドレスとパスワードを使ってログインしてください。二要素認証が聞かれることがありますので、その場合は設定している二要素認証デバイスを使って対応してください。
ログインが成功すると、以下のような Twilio Flex 初期画面が表示されます。
![FlexSetup_5 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/cd4162d8-befd-5842-3bcf-db246778da6e.png)

左側に、タスクが5つ自動的に作られていると思いますので、それぞれをクリックしてタスクをすべて完了していきます。
それぞれのタスクリストをクリックして、右上にある **COMPLETE** ボタンを押していくとタスクが完了していきます。
すべてのタスクが完了すると、次のような画面が表示されます。

![スクリーンショット 2021-04-06 9.25.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/88e4b7dc-7a7b-4145-4286-484ca0befa2c.png)

画面中央のプルダウンリストが「Available」になっている場合、エージェントが待受中であることを示しています。席を離れる場合などは、状態を変えておきます。今は Available のままにしておいてください。

### WebChat をテストしてみる。

ではまず、セットアップした初期状態で WebChat をやってみましょう。

- Flex の左側にあるアイコンから Admin アイコンをクリックします。
![FlexWebChat_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/43b44a61-6a9a-b16a-eadd-677803c6602c.png)

- 右側にある WebChat の **LAUNCH** ボタンを押します。
- ブラウザに新しいタブが開いて、以下のページが表示されます。
![スクリーンショット 2021-04-06 9.33.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/ce94b2dc-2ab3-4da0-7681-3b3cb1e02e1f.png)

- **Type message** と書かれている欄に何か文字を入力して、送信（紙飛行機）アイコンを押してみましょう。
- Flex のページに戻ってみてください。Admin アイコンの下のアイコンに赤いマークが表示されています。
![スクリーンショット 2021-04-06 9.35.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/8f05ac6d-e006-e337-8b06-1d851bbcd016.png)

- 赤いマークがある Admin アイコンの下のアイコンを選択します。
- Customer から着信があることがわかります。
![スクリーンショット 2021-04-06 9.36.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/ec86edd6-9aef-3bff-dc55-240b49d6e724.png)

- 緑色の応答アイコンを押します。
- チャット画面が開いて、ユーザの入力した内容が表示されていることがわかります。
![スクリーンショット 2021-04-06 9.38.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/024026a8-30c7-5dea-461b-acd6620c1fde.png)

- エージェント側も何か入力をすると、ユーザ側のチャット画面にも表示されます。
- チャットを終了するには、エージェント側の **END CHAT** ボタンを押します。
- 後処理状態に遷移します。後処理とは、今のやり取りを記録するための時間で、この状態の時は新しい着信はうけません。
- **COMPLETE** ボタンを押すことで、再度待受状態に戻れます。

以上で、Twilio Flex の初期セットアップが終了です。

## Twilio Functions で Lex 連携部分を作る

ではここから Amazon Lex 連携部分を作っていきます。
Amazon Lex は AWS SDK 経由でのアクセスが可能なので、今回は Twilio Functions から SDK 経由でやり取りをすることにします。

### Twilio Functions とは

Twilio のクラウドサービス内でコードを実行することができる FaaS （Function as a Service） です。AWS のユーザであれば、Lambda だと思っていただければわかりやすいです（実際に裏側は Lambda が動きます）。
Twilio Functions には、Twilio 管理コンソール上に直接コードを記述していく方式と、ローカルの開発環境でコードを書いて、CLI 経由でデプロイしていく方法の２種類が用意されていますが、エンジニアには後者の方がわかりやすいと思いますので、今回は後者の方法で進めます。

### CLI プロファイルの作成

Twilio Functions を CLI ベースで開発していくためには、Twilio CLI のインストールと、サーバーレスプラグインのインストールが必要です。すでに[準備](#twilio-cli-%E3%81%AE%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)のところでやっていただいているはずですが、まだの方はここでインストールをしておいてください。

CLI の準備ができたら、まずは今回の Twilio Flex 用のプロファイルを作成したいと思います。そのために、Flex プロジェクトの AccoutSid と AuthToken を調べる必要があります。

- [Twilio 管理コンソール](https://jp.twilio.com/login/kddi-web)にログインします。
![FlexDashboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/4685d220-651a-a3dc-3669-56a1bccea3a0.png)

- 左上のプロジェクト名が先ほど作成した Flex プロジェクトの名前になっていることを確認します。
- **Project Info**セクションにある、**アカウントSID** と **AUTH TOKEN** をメモ帳にコピーします（右側にあるコピーボタンを押すとクリップボードにコピーできます）。

では、Flex プロジェクト用に新しい CLI プロファイルを作成します。

- ターミナル（コマンドプロンプト）を開き、作業ディレクトリに移動します。
- 以下のコマンドでプロファイルを作成していきます。

```sh
twilio profiles:create アカウントSID（ACから始まる文字列）
```

```sh
You can find your Account SID and Auth Token at https://www.twilio.com/console
 » Your Auth Token will be used once to create an API Key for future CLI access to your Twilio Account or Subaccount, and then forgotten.
? Your Twilio Auth Token for your Twilio Account or Subaccount: [hidden]
```

- 先程控えておいた AUTH TOKEN を入力します（入力は非表示です）。

```sh
? Shorthand identifier for your profile: AmazonLex
```

- プロファイルの名前を入れます。今回は Flex プロジェクトと同じ「AmazonLex」にしておきます。

```sh 
Created API Key SKxxxxxxxxxxxxxxxxxxxxx and stored the secret in your keychain. See: https://www.twilio.com/console/runtime/api-keys/SKxxxxxxxxxxxxxxxxxxxxx
twilio-cli configuration saved to "/Users/katsumi/.twilio-cli/config.json"
Saved AmazonLex.
```

上記のように表示されればプロファイルの作成は終了です。
では早速、作成したプロファイルを選択しましょう。

```sh
twilio profiles:use AmazonLex
set "AmazonLex" as active profile
twilio-cli configuration saved to "/Users/katsumi/.twilio-cli/config.json"
```

### Functions のセットアップ

今回使用する Functions はすでに GitHub 上に準備されているので、そちらを使っていきましょう。

- 以下のコマンドを使ってソースコードを取得します。

```sh
git clone https://github.com/twilioforkwc/amazon-lex.git
Cloning into 'amazon-lex'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 9 (delta 0), reused 9 (delta 0), pack-reused 0
Unpacking objects: 100% (9/9), done.
```

- 取得したコードを修正していきます。

```sh
cd amazon-lex
cp .env.sample .env
```

- コピーした `.env` をエディタで開き、各項目を編集してください。

項目名|内容
:--|:--
ACCOUNT_SID|Flex プロジェクトのAccountSid（ACから始まる文字列）
AUTH_TOKEN|同じくAuthToken
AWS_ACCESS_KEY|先程控えておいた AWS IAM のアクセスキー
AWS_SECRET_ACCESS_KEY|同じくシークレットアクセスキー
AWS_REGION|東京以外のリージョンを利用する場合は適宜変更
BOT_ALIAS|Lex のエイリアスで指定した名前（ハンズオン通りであれば `Prod` ）
BOT_NAME|Lex のボット名（ハンズオン通りであれば `BookTrip_jaJP` ）

実際に Amazon Lex とやり取りするコードも見ていきましょう。

- functions/talk.js をエディタで開きます。

```javascript:talk.js
/**
 * Conversation to Amazon lex
 */
const AWS = require("aws-sdk");

exports.handler = async function (context, event, callback) {
  try {
    // Check variables
    const user = event.user || "TEST_USER";
    const message = event.message || "";

    // Initialize AWS configure
    AWS.config.update({
      region: context.AWS_REGION,
      credentials: new AWS.Credentials(
        context.AWS_ACCESS_KEY,
        context.AWS_SECRET_ACCESS_KEY
      ),
    });

    // Send message to lex
    const lexRuntime = new AWS.LexRuntime();
    const params = {
      botAlias: context.BOT_ALIAS,
      botName: context.BOT_NAME,
      inputText: message,
      userId: user,
      sessionAttributes: {},
    };
    lexRuntime.postText(params, (err, data) => {
      if (err) {
        throw err;
      }
      console.log(data);
      console.dir(data.slots);
      callback(null, {
        message: data.message,
        dialogState: data.dialogState,
        slots: data.slots,
      });
    });
  } catch (err) {
    // Error handle
    console.log(err);
    callback(err);
  }
};
```

AWS SDK を使っているため、コード自体は非常にシンプルになっています。
Amazon Lex を呼び出す部分は、以下のコードです。

```javascript
    // Send message to lex
    const lexRuntime = new AWS.LexRuntime();
    const params = {
      botAlias: context.BOT_ALIAS,
      botName: context.BOT_NAME,
      inputText: message,
      userId: user,
      sessionAttributes: {},
    };
    lexRuntime.postText(params, (err, data) => {
      if (err) {
        throw err;
      }
      console.log(data);
      console.dir(data.slots);
      callback(null, {
        message: data.message,
        dialogState: data.dialogState,
        slots: data.slots,
      });
    });
```

呼び出し時に必要なパラメータとして、以下の５つが必要です。

- botAlias: ボットのエイリアス名
- botName: ボットの名前

この２つでどのボットのどのバージョンを利用するかを指定しています。

- inputText: ボットに渡すテキスト
- userId: ユーザを識別するID
- sessionAttributes: セッション属性（セッション中に保持したいデータ）

userId を使って会話のセッション管理を行っているため、ステートフルな会話が可能になります。

postText については、[こちら](https://docs.aws.amazon.com/lex/latest/dg/API_runtime_PostText.html)に説明があります。
Amazon Lex から戻ってくる情報は、`data` という名前の JSON オブジェクトに格納されています。
その中で今回利用するのは以下の3つのデータで、これらを Functions の戻り値として指定しています。

- message: Lex が返答したテキスト
- dialogState: 終了を判定するために利用する会話の状態
- slots: ボットが収集した各種データ

dialogStateは、以下のように遷移していきます。

状態|説明
:--|:--
ElicitSlot|スロット情報を収集中
ConfirmIntent|内容の確認中
ReadyForFulfillment|準備完了（すべての情報を収集完了）

よって、この dialogState が `ReadyForFulfillment` になったことを判定することでオペレータに引き渡せば良いことになります。

ちなみに、収集した slots のデータは次のような感じで格納されています。

```json
{ CheckInDate: '2021-04-21', Location: '大阪', Nights: '2', RoomType: 'エコノミー' }
```

### Functions のデプロイ

では早速、作成した Functions をデプロイしてみましょう。

```sh
twilio serverless:deploy -p CLIプロファイル名（ハンズオン通りなら AmazonLex ） --force
```

`twilio profiles:use` でプロファイルを切り替えていれば -p オプションは不要ですが、切り替えを忘れてしまうこともあるので、プロファイル指定はしておいたほうが良いです。
--force オプションも本来は不要ですが、念の為つけておきます。

以下のような感じでデプロイが完了すればセットアップ終了です。

```sh
Deploying functions & assets to the Twilio Runtime

Account         SKdbdb3df618be2a679a2589203e61c938
Token           LNPR****************************
Service Name    flex-amazon-lex
Environment     dev
Root Directory  /Users/katsumi/Documents/workspace/twilioFlex/amazon-lex
Dependencies    aws-sdk
Env Variables   AWS_ACCESS_KEY, AWS_SECRET_ACCESS_KEY, AWS_REGION, BOT_ALIAS, BOT_NAME
Runtime         undefined

✔ Serverless project successfully deployed

Deployment Details
Domain: flex-amazon-lex-XXXX-dev.twil.io
Service:
   flex-amazon-lex (ZSXXXXXXXXXXXXXXXXXXXXXXXXXXX)
Environment:
   dev (ZEXXXXXXXXXXXXXXXXXXXXXXXXXXX)
Build SID:
   ZBXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Runtime:
   node12
View Live Logs:
   https://www.twilio.com/console/assets/api/ZSXXXXXXXXXXXXXXXXXXXXXX/environment/ZEXXXXXXXXXXXXXXXXXXXXXXXXXX
Functions:
   https://flex-amazon-lex-XXXX-dev.twil.io/talk
Assets:

```

### Functions のテスト

作成した Functions が動作するかをテストしたいと思います。
Functions の実行URLは、先程デプロイしたときに表示されています。

`https://flex-amazon-lex-XXXX-dev.twil.io/talk`（XXXXは数字が入ります）

ただし、Functionsには、以下の2つのパラメータを渡す必要があります。

- user: ユーザ識別子
- message: 最初の質問

なので、今回は以下の URL をブラウザに貼り付けて実行テストをします。

`https://flex-amazon-lex-XXXX-dev.twil.io/talk?user=test&message=ホテルの予約をしたい`（XXXXはご自分の環境に合わせて修正してください）

しばらくして、以下のような画面が表示されれば、とりあえず Functions は成功です。

![スクリーンショット 2021-04-07 13.24.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/cdbd1d15-f16e-90c1-fc5c-4ec576dea855.png)

## Twilio Studio で対話フローを作成する

ではいよいよ Flex 側のカスタマイズをしていきます。
ユーザから WebChat が届くと、メッセージは Twilio Studio 経由で TaskRouter に引き渡されます。その前にも処理はありますが、今回のハンズオンではとくに意識する必要はないので割愛します。

では、Twilio Studio のデフォルト設定がどの様になっているかを確認してみましょう。

- [Twilio 管理コンソール](https://jp.twilio.com/login/kddi-web)にログインします。
- 左側のサービスアイコン（ドットが3つあるアイコン）を押して、スライドメニューから Studio を選択します。
![StudioFlex_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/bcbe0f8b-1252-5c4f-fcb4-3891feea90ae.png)

- すでに3つのフローが登録されていると思いますので、その中の**WebChat Flow**を選択します。
![スクリーンショット 2021-04-07 14.19.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/31aa37a0-6adc-cbc7-22bb-50bba3549147.png)

デフォルトの WebChat Flow は非常にシンプルで、着信したメッセージをそのまま TaskRouter に流しているだけです。上の図でいうと、**SendMessageToAgent** という名前がついたウィジェット（ Studio では、フローに利用する部品のことをウィジェットと呼びます）が TaskRouter に引き渡すウィジェットになります。

### フローのテンプレートを読み込む

ここに先程作成した Functions を呼び出すウィジェットなどを追加していくのですが、一つづつやっているととても大変なので、今回はすでに作ってあるフローを読み込んで、必要なところを修正していきましょう。

- **<Back** リンクを押して、一つ前のページに戻ります。
![StudioFlex_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/f97966d6-11fc-7369-e12e-3ae4ba89358f.png)

- 青いプラスアイコンを押します。
![スクリーンショット 2021-04-07 14.49.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/55d9e6f0-efda-28c6-3c68-f605cb6c0820.png)

- **FLOW NAME**欄に「AmazonLexFlow」と入力して、**Next** ボタンを押します。
![スクリーンショット 2021-04-07 14.51.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/112b2701-fa2f-0f25-7af3-d1950aa563e9.png)

- テンプレートの一覧の一番下までスクロールして、**Import from JSON** を選択して、**Next** ボタンを押します。
- 先程 GitHub からクローンしたプログラムの中の studio フォルダ内にある、`AmazonLexFlow.json` の内容をすべてコピーし、**New Flow** の中に貼り付けます。すでに入っている`{}`は削除してから貼り付けてください。
![スクリーンショット 2021-04-07 14.54.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/d865b93f-76b5-99a0-0fc3-83838a742cb7.png)

- **Next** ボタンを押します。
![スクリーンショット 2021-04-07 14.56.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/3d7a242d-72e2-ebe8-01e2-344e9979b6e5.png)

### ウィジェットを修正する

では、修正が必要なウィジェットを編集していきましょう。

- **LexTalk** ウィジェットを選択します。
![StudioFlex_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/ecc4b6c1-e8cf-34f6-97bd-6d1d64d2a9a0.png)

- 右側のプロパティウィンドウに、このウィジェットの設定内容が表示されます。
- **SERVICE** のリストから「flex-amazon-lex」を選択します。
- **ENVIRONMENT** のリストから「dev-environment」を選択します。
- **FUNCTION** のリストから「/talk」を選択します。
- プロパティウィンドの下部にある、**Save** ボタンを押します。

今編集したウィジェットは、先ほど作成した Twilio Function を呼び出すものです。プロパティページの **Function Parameters** で、`message`と`user`の2つのパラメータを設定しています。

では次に、TaskRouter を呼び出すウィジェットを編集していきましょう。

- **SendMessageToAgent** ウィジェットを選択します。
![StudioFlex_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/a4f1c15b-d4f9-05ca-e71d-a1322c36fb37.png)

- **WORKFLOW** のリストから「Assign to Anyone」を選択します。
- **CHANNEL** のリストから「Programmable Chat」を選択します。
- プロパティページの下部にある **Save** ボタンを押します。

### フローをパブリッシュする

以上でウィジェットの修正は完了しましたが、最後にパブリッシュをしないと変更は反映されません。

- 画面上部にある **Publish** ボタンを押します。
![StudioFlex_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/6bbf182f-004a-f4f0-bd4b-21173390265c.png)

- 確認のダイアログが表示されますので、**Publish** ボタンを押します。

以上でフローが完成しました。

### Flex Flow を修正する

では次に、WebChat で届いたメッセージを、今作成したフローにつなぎます。

- Twilio 管理コンソールから、Flex に移動します。
- さらに、**メッセージの送受信**メニューを選択します。
![Flex_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/56719f5a-98c7-de9f-2c13-99f8aaa64654.png)

- **Manage Flex Flows** の中の **web** の行の右側にある編集アイコンを押します。
![Flex_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/3d5e3fec-935c-9aab-a204-0c763b98ea43.png)

- **STUDIO FLOW** のリストから「AmazonLexFlow」を選択します。
- **Submit** ボタンを押します。

### テスト

ではここまでの状態でテストをしましょう。

- Flex の**概要**メニューを選択します。
![Flex_1 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/c7127ba9-ba0a-78e1-3fd0-ec39ccca6a10.png)

- **Launch Flex** ボタンを押します。
![Flex_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/991db2f2-8b87-f02c-ca49-df4f3d5683c0.png)

- Admin メニューの右側にある、**LAUNCH** ボタンを押します。
- WebChat のタブが開きます。
![スクリーンショット 2021-04-07 15.46.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/1d28e7c7-d9b0-5502-5a43-555c711ac19e.png)

- 「ホテルの予約をしたい」と入力して、会話を始めてください。
- 最後にオペレータに変わるとのメッセージが出たら、Flex のオペレータ画面を見てみます。
- オペレータが「Available」になっていれば、着信ができるので着信をします。
![スクリーンショット 2021-04-07 15.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/c9223c6e-7182-4350-5ffd-4fe187fd0943.png)

ちゃんとオペレータに転送されたようです。
ここまではうまくいっていますね。

## Flex UI をカスタマイズする

最後の仕上げとして、Amazon Lex が収集した情報（Slots）をエージェントの画面上にも表示させたいと思います。
表示させる場所は、TaskListItem と呼ばれるユーザーごとのタスクの部分にします（下図参照）。

![Flex_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/ceda62c3-31cd-6b7a-7098-daac41c4297d.png)

このように Flex UI をカスタマイズするには、Flex プラグインを作成する必要があります。
Flex プラグインも Twilio CLI 経由で一から作成することができますが、色々とカスタマイズが必要なので、こちらもすでに作成してある雛形を使っていきましょう。

- 作業ディレクトリに移動します。
- 以下のコマンドを使って、プラグインを Clone します。

```sh
git clone https://github.com/twilioforkwc/plugin-amazon-lex.git
cd plugin-amazon-lex
```

では、今回カスタマイズした部分の解説をしていきます。

### src/AmazonLexPlugin.js

```javascript:AmazonLexPlugin.js
（省略）
  init(flex, manager) {
    this.registerReducers(manager);

    // Console i18n
    manager.strings = { ...manager.strings, ...japanese };

    // Action Framework
    registerActionExtensions();

    flex.TaskListItem.defaultProps.itemSize = "LargeSelected";
    manager.strings.TaskExtraInfo = `
      <h1>予約希望内容</h1>
      宿泊地：{{task.attributes.Location}}<br />
      チェックイン日：{{task.attributes.CheckInDate}}<br />
      泊数：{{task.attributes.Nights}}<br />
      ルームタイプ：{{task.attributes.RoomType}}<br />
    `;
（省略）
```

`init（flex, manager）` がプラグインで最初にアクセスされる部分です。

`manager.strings = { ...manager.strings, ...japanese };`
の部分で、Flex の UI を日本語にしています。日本語のテンプレートが、`src/i18n/JP.js`にあるので、文言を変更したい場合はそちらを修正してください。

`flex.TaskListItem.defaultProps.itemSize = "LargeSelected";` は、タスクリストを選択したときにリストのサイズが最大化される設定です。
そして、`manager.strings.TaskExtraInfo` が TaskListItem に追加で表示される部分になります。
Flex UI はすべて React で作られているため、{{task.attributes.〜}} と記述しておくことで、動的にタスクの属性が表示されるようになります。

### タスクの属性（ task.attributes ）

Studio の部分でも説明しましたが、ユーザが入力したテキストメッセージは TaskRouter に渡ってからエージェントに引き渡されます。先程のフローでいうと以下の部分です。
![StudioFlex_4_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/6e206519-cbae-7490-d8c9-4a978a0a8a71.png)

このように、Studio 内で取得した Amazon Lex の Slots をタスクの属性として引き渡しておくことで、最終的にエージェント画面でその内容を参照することができるのです。

### src/eventListeners/actionFramework

Flex 内では色々なイベントが発生します。そのイベントを取得するのが、`actionFramework`です。actionFramework については、[こちらのドキュメント](https://www.twilio.com/docs/flex/developer/ui/actions)に記載されています。

```javascript:acceptTask.js
  Actions.addListener("beforeAcceptTask", async (payload) => {
    console.log("BEFORE ACCEPT TASK: ", payload);
    let { attributes, workerSid, channelType } = payload.task;

    // Set attributes from Amazon Lex Slots
    const slots = JSON.parse(
      attributes.Slots.replace(/=/g, '":"')
        .replace(/, /g, '","')
        .replace(/{/, '{"')
        .replace(/}/, '"}')
    );
    attributes = { ...attributes, ...slots };
    await payload.task.setAttributes(attributes);
  });
```

今回はこの actionFramework のしくみを使って、エージェントがタスクを許可したとき（着信に応答したとき）のイベントである `beforeAcceptTask` をトリガーにして、task.attributes を操作しています。
本来は、先程の Studio フローの中で、Slots オブジェクト（JSON形式）を分解して、一つづつの項目別に Attributes として設定してもよいのですが、それだと Slots の内容を変更するたびに Studio 側のフローを修正する必要があります。Studio は GUI ツールなので、非エンジニアにとっては便利なのですが、エンジニアにとってはコードとして管理することができない（JSONにして管理する方法はありますが）ため、できればあまり変更したくないところです。
そこで、Studio では Slots オブジェクト全体をタスク属性として渡してしまい、Flex Plugin 側で分解するようにしています。この分解を行っているのが、上記のコードです。
このように記述しておくことで、少なくとも Amazon Lex で取得した Slots についてはすべてタスク属性としてハンドリングできるようになり、表示する部分でどの項目を使うかをコーディングするだけでよくなります。

### ビルドとデプロイ、リリース

では早速プラグインのビルドとデプロイをしていきましょう。
それぞれ、CLI を使って実行することができます。

```sh:build
twilio flex:plugins:build -p AmazonLex
```

`-p AmazonLex` は CLI のプロファイルを指定する部分ですので、`twilio profiles:use` ですでにプロファイルを選択している場合は指定は不要です。

```sh
Using profile AmazonLex (ACxxxxxxxxxxxxxxxxxxxxx)

Compiling a production build...

Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
Plugin plugin-amazon-lex was successfully compiled.

2 files were compiled:
         62.2 KB         build/plugin-amazon-lex.js
         0.1 KB          build/plugin-amazon-lex.js.map
```

このように表示されればビルドは成功です。
ではこれをデプロイしていきましょう。

```sh:deploy
twilio flex:plugins:deploy -p AmazonLex --changelog='Deploy at '+`date +%Y%m%d_%H%M%S`
```

デプロイ時には、`--changelog` オプションが必須なため、現在の時間をコメントとして指定しています。

```sh
Using profile AmazonLex (ACxxxxxxxxxxxxxxxxxxxxx)

✔ Validating deployment of plugin plugin-amazon-lex
⠙ Compiling a production build of plugin-amazon-lexBrowserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
✔ Compiling a production build of plugin-amazon-lex
✔ Uploading plugin-amazon-lex
✔ Registering plugin plugin-amazon-lex with Plugins API
✔ Registering version v0.0.2 with Plugins API

🚀 Plugin (private) plugin-amazon-lex@0.0.2 was successfully deployed using Plugins API

Next Steps:
Run $ twilio flex:plugins:release --plugin plugin-amazon-lex@0.0.2 --name "Autogenerated Release 1617923406772" --description "The description of this Flex Plugin Configuration." to enable this plugin on your Flex application
```

上記のように表示されればデプロイも成功です。
これでプラグイン自体は Twilio 側にデプロイされています。しかし、プラグインは**リリース**という作業をしなくては、エージェント側には反映されません。リリース作業は、Flex の管理画面からもできますが、CLI からもできるので今回は CLI を使いましょう。

先程のデプロイ結果の中に、リリースするためのコマンドが表示されています。

```sh
twilio flex:plugins:release --plugin plugin-amazon-lex@0.0.2 --name "Autogenerated Release 1617923406772" --description "The description of this Flex Plugin Configuration."
```

の部分がそうです。
ではさっそくプラグインをリリースしましょう（途中に出てくる数字などは皆さんの環境によって違うので、必ずデプロイコマンドで表示されたコマンドを利用してください）。

```sh:release
twilio flex:plugins:release --plugin plugin-amazon-lex@0.0.2 --name "Autogenerated Release 1617923406772" --description "The description of this Flex Plugin Configuration."
```

```sh
Using profile AmazonLex (ACxxxxxxxxxxxxxxxxxxxxx)

✔ Creating configuration
✔ Enabling configuration FJc493905eed54f6fb23e3883ae5720cd4

🚀 Configuration FJc493905eed54f6fb23e3883ae5720cd4 was successfully enabled.

Next Steps:
Visit https://flex.twilio.com/admin/plugins to see your plugin(s) live on Flex.
```

このような感じに表示されればリリースも完了です。

# 完成

かなり長い道のりでしたが、ようやくこれですべて完成です。
では最後にテストをしましょう。

- Flex の**概要**メニューを選択します。
![Flex_1 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/c7127ba9-ba0a-78e1-3fd0-ec39ccca6a10.png)

- **Launch Flex** ボタンを押します。
![Flex_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/991db2f2-8b87-f02c-ca49-df4f3d5683c0.png)

- Admin メニューの右側にある、**LAUNCH** ボタンを押します。
- WebChat のタブが開きます。
![スクリーンショット 2021-04-07 15.46.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/1d28e7c7-d9b0-5502-5a43-555c711ac19e.png)

- 「ホテルの予約をしたい」と入力して、会話を始めてください。
- 最後にオペレータに変わるとのメッセージが出たら、Flex のオペレータ画面を見てみます。
- オペレータが「Available」になっていれば、着信ができるので着信をします。
![スクリーンショット 2021-04-09 8.50.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/40382516-7e8c-8919-8597-31093ae9e51d.png)

タスクリストに Amazon Lex で取得した項目が表示されていることを確認します。

# まとめ

かなり色々な機能を紹介したため、すべてを理解するのは難しかったかもしれません。特に Flex UI カスタマイズの部分は、ほんの少ししか紹介をしていなかったので、また改めて Flex については記事を書いていこうと思います。
今回のハンズオンを通じて、マルチチャネル/オムニチャネルコンタクトセンターソリューションである Twilio Flex の可能性が少しでも伝われば幸いです。

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilio は音声通話、メッセージング（SMS /チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウド API サービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。

自己紹介|　
:--|:--
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b51a8b88-abd5-ce5e-195a-de3564ba2ae6.png" height='200' width='150'/>|高橋克己（Katsumi Takahashi） 自称「<font color='red'>赤い芸人</font>」<br />グローバル・インターネット・ジャパン株式会社　代表取締役<br />株式会社KDDIウェブコミュニケーションズ　Twilio事業部エバンジェリスト<br /><br />2001年より大手通信事業者の法人サービスの教育に携わり、企業における電話のしくみや重要性を研究。2016年よりTwilio事業部にジョインし、Twilioを使ったスマートコミュニケーションの普及活動を精力的に行っている。<br />2015 Hall of Doers<br />2019 Twilio Champions