AWSでAIサービスを使ってみる〜第9回lex編〜

##Lexとは
>AWSのbotのAIサービス。Amazon Lex を使うと、すべてのデベロッパーが Amazon Alexa に採用されている深層学習技術と同じ技術を利用できるため、自然言語での高度な対話ボット (チャットボット) を短時間で簡単に構築できます。

今回はlexを用いてアイスクリームのフレーバーや容器について答えるボットを作成して行きます。
コードの量があるので順番に解説して行きます。

ボット作成ファイルの全体像

```lex_create_bot.py
import boto3
import time


iam = boto3.client('iam')
iam.create_service_linked_role(AWSServiceName='lex.amazonaws.com')
lex = boto3.client('lex-models', 'us-east-1')

#フレーバーのスロットタイプの作成
flavor_slot_type = lex.put_slot_type(
  name='FlavorSlotType',
  enumerationValues=[
    {'value': 'vanilla'},
    {'value': 'chocolate', 'synonyms': ['choc']},
    {'value': 'strawberry', 'synonyms': ['berry']}
  ],
  valueSelectionStrategy='TOP_RESOLUTION',
  createVersion=True)
print('slot type:', flavor_slot_type['name'])

#容器のスロットタイプを作成
container_slot_type = lex.put_slot_type(
  name='ContainerSlotType',
  enumerationValues=[
    {'value': 'corn'},
    {'value': 'cup'}
  ],
  valueSelectionStrategy='TOP_RESOLUTION',
  createVersion=True)
print('slot type:', container_slot_type['name'])


#インテントの作成
intent = lex.put_intent(
  name='OrderIntent',
  #インテント内のスロット
  slots=[
    {
      'name': 'Flavor',
      'slotConstraint':'Required',
      'slotType':'FlavorSlotType',
      'slotTypeVersion': '1',
      'valueElicitationPrompt': {
          'messages': [{
              'contentType': 'PlainText',
              'content': 'Vanilla, chocolate or strawberry?'
          }],
          'maxAttempts': 3
      }
    },
    {
      'name': 'Container',
      'slotConstraint': 'Required',
      'slotType': 'ContainerSlotType',
      'slotTypeVersion': '1',
      'valueElicitationPrompt': {
          'messages': [{
              'contentType': 'PlainText',
              'content': 'Corn or cup?'
          }],
          'maxAttempts': 3
      } 
    }
  ],
  #発話例
  sampleUtterances=[
    'I want {Flavor} ice cream in {Container}',
    '{Flavor} ice cream {Container}',
    'ice create'
  ],
  #完了時のセリフ
  conclusionStatement={
      'messages': [{
          'contentType': 'PlainText',
          'content': 'OK, {Flavor} ice cream in {Container}'
      }],
  },
  #完了の動作
  fulfillmentActivity={'type': 'ReturnIntent'},
  createVersion=True)
print('intent:',intent['name'])


#ボットの作成
bot = lex.put_bot(
  name ='MyBot', locale='en-US', childDirected=False,
  #インテント
  intents=[
    {
      'intentName': 'OrderIntent',
      'intentVersion': '1'
    }
  ],
  #中止時のセリフ
  abortStatement={
    'messages':[
      {
        'contentType': 'PlainText',
        'content': 'Please try again.'
      }
    ]
  },
  voiceId='Joanna',
  createVersion=True)
print('bot:', bot['name'])
#ボット作成の進捗表示
start = time.time()
status = ''
while status not in ['READY', 'FAILED']:
  #ボットを取得
  status = lex.get_bot(name='MyBot', versionOrAlias='1')['status']
  time.sleep(10)
  print('{:7.2f} {}'.format(time.time()-start, status))
#作成に失敗した場合は理由を表示
if status == 'FAILED':
  print(lex.get_bot(
    name='Mybot', versionOrAlias='1')['failureReason'])
#ボットエイリアスを作成
bot_alias = lex.put_bot_alias(
  name='MyBotAlias', botName='MyBot', botVersion='1')
print('bot alias:', bot_alias['name'])
```

##①importとサービスクライアント作成

```lex_create_bot.py
import boto3
import time

iam = boto3.client('iam')
iam.create_service_linked_role(AWSServiceName='lex.amazonaws.com')
lex = boto3.client('lex-models', 'us-east-1')
```
boto3とtimeのインポートとiamのロールとlexのサービスクライアントを作成

##②スロットタイプ作成

スロットとはユーザーから聞き出す必要のあるパラメーターです。
今回はフレーバーと容器をスロットに設定しています。

```lex_create_bot.py
#フレーバーのスロットタイプ
flavor_slot_type = lex.put_slot_type(
  name='FlavorSlotType',
  enumerationValues=[
    {'value': 'vanilla'},
    {'value': 'chocolate', 'synonyms': ['choc']},
    {'value': 'strawberry', 'synonyms': ['berry']}
  ],
  valueSelectionStrategy='TOP_RESOLUTION',
  createVersion=True)
print('slot type:', flavor_slot_type['name'])

#容器のスロットタイプ
container_slot_type = lex.put_slot_type(
  name='ContainerSlotType',
  enumerationValues=[
    {'value': 'corn'},
    {'value': 'cup'}
  ],
  valueSelectionStrategy='TOP_RESOLUTION',
  createVersion=True)
print('slot type:', container_slot_type['name'])
```

put_slot_typeメソッドでスロットを作成します。
enumerationValuesでスロットが取りうる値を辞書型で指定
valueSelectionStrategyの'TOP_RESOLUTION'はスロットタイプの中からユーザーの言葉に近いものを返します。今回でいえばchocoと入力すると本来の値のchocolateが返ってくる。



##③インテントの作成
intentとは意思や意図という意味がありますが、lexの中ではユーザーと会話する台本のイメージになります。
今回をアイスクリーム注文するコマンドを受け取るインテントを作成します。パラメーターとしてアイスクリームのフレーバーと容器を受け取リます。

put_intentメソッドでインテントを作成します。
####引数slotsについて
```lex_create_bot.py
#インテントの作成
intent = lex.put_intent(
  name='OrderIntent',
  #インテント内のスロット
  slots=[
    {
      'name': 'Flavor',
      'slotConstraint':'Required',
      'slotType':'FlavorSlotType',
      'slotTypeVersion': '1',
      'valueElicitationPrompt': {
          'messages': [{
              'contentType': 'PlainText',
              'content': 'Vanilla, chocolate or strawberry?'
          }],
          'maxAttempts': 3
      }
    },
    {
      'name': 'Container',
      'slotConstraint': 'Required',
      'slotType': 'ContainerSlotType',
      'slotTypeVersion': '1',
      'valueElicitationPrompt': {
          'messages': [{
              'contentType': 'PlainText',
              'content': 'Corn or cup?'
          }],
          'maxAttempts': 3
      } 
    }
  ],
 
  
```

引数slotsはインテント内にスロットを作成するために使います。
・slotConstraint:スロットの制約です。'Required'または'Optinal'で選択。
・slotTypeVersion:スロットのバージョン。最初はバージョン'1'
・valueElicitationPrompt:スロットの値をユーザーから促すためのセリフ

→valueElicitationPromptに指定する辞書の項目
・message:セリフを表す辞書リスト。その中にcontentTypeとcontentが含まれる
・maxAttempts:聞き返す回数の上限

→→messageに指定する辞書の項目
・contentType:セリフのタイプ。'PlainText','SSML','CustomPayload'から指定
・content:セリフの内容

####引数sampleUtterances
```lex_create_bot.py
 #発話例
  sampleUtterances=[
    'I want {Flavor} ice cream in {Container}',
    '{Flavor} ice cream {Container}',
    'ice create'
  ],
```
引数sampleUtterancesはインテントに対するユーザーの発話例です。
{スロット名}でスロットを表します。{Flavor}の部分にはフレーバー(chocolate,strawberry,vanilla)が入り、{Container}には(cupなど)が入ります。
今回ユーザーの発話を見ることはなさそうですね。


####引数conclusionStatement
```lex_create_bot.py
 #完了時のセリフ
  conclusionStatement={
      'messages': [{
          'contentType': 'PlainText',
          'content': 'OK, {Flavor} ice cream in {Container}'
      }],
  },
```
引数conclusionStatementはボットがユーザーから必要なスロットの情報を聞き出すことができて、インテントが完了したときのセリフです。辞書型の項目として引数slotsのvalueElicitationPromptに指定する辞書の項目と同じです。


####引数fulfillmentActivity
```lex_create_bot.py
  #完了の動作
  fulfillmentActivity={'type': 'ReturnIntent'},
  createVersion=True)
print('intent:',intent['name'])
```
引数fulfillmentActivityはインテントが完了したときの動作です。
引数にはキー'type'を含む辞書を指定します。

typeの種類
ReruenIntent：呼び出し元のプログラムにインテントの情報を返します。
CodeHook：Lambda呼び出してLambdaに登録されたプログラムを実行します。

##④botの作成

最後にボットとボットエイリアスを作成します。Lexでbotを公開するにはボットエイリアスを作成する必要があります。

```lex_create_bot.py
#ボットの作成
bot = lex.put_bot(
  name ='MyBot', locale='en-US', childDirected=False,
  #インテント
  intents=[
    {
      'intentName': 'OrderIntent',
      'intentVersion': '1'
    }
  ],
  #中止時のセリフ
  abortStatement={
    'messages':[
      {
        'contentType': 'PlainText',
        'content': 'Please try again.'
      }
    ]
  },
  voiceId='Joanna',
  createVersion=True)
print('bot:', bot['name'])
```
put_botメソッドを呼び出してボットを作成します。
引数のnameは名前、localeは今回'en-US'、childDirected(子供向け)は子供向けの場合にはTrue、子供向けではない場合はFalseに指定。
引数intentはnameとintentVersionを辞書型で渡します。
引数abortStatementには中止時のセリフを辞書型のリストにして指定します。
voiceIdは'en-US'の中から'Joanna'に指定します。


###作成時の進捗時間表示
```lex_create_bot.py
#ボット作成の進捗表示
start = time.time()
status = ''
while status not in ['READY', 'FAILED']:
  #ボットを取得
  status = lex.get_bot(name='MyBot', versionOrAlias='1')['status']
  time.sleep(10)
  print('{:7.2f} {}'.format(time.time()-start, status))
#作成に失敗した場合は理由を表示
if status == 'FAILED':
  print(lex.get_bot(
    name='Mybot', versionOrAlias='1')['failureReason'])
#ボットエイリアスを作成
bot_alias = lex.put_bot_alias(
  name='MyBotAlias', botName='MyBot', botVersion='1')
print('bot alias:', bot_alias['name'])

```
get_botメソッドでボットの情報を取得しstatusへ、10秒間時間を待ち開始からに時間と取得したボットの情報をstatusに代入して表示します。
作成に失敗した場合は、失敗した理由を取得し表示。
put_bot_aliasメソッドでボットエイリアスを作成し、画面に表示します。


##まとめ
ここまで長くなりましたが、lexでのbot作成のコードと解説でした。
次回はbotにテキストを与えて動かしてみます。

##参考文献
この記事は以下の情報を参考にして執筆しました
[AWSでつくるAIプログラミング入門](https://www.amazon.co.jp/AWS%E3%81%A7%E3%81%A4%E3%81%8F%E3%82%8B-AI%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E5%85%A5%E9%96%80-%E6%9D%BE%E6%B5%A6-%E5%81%A5%E4%B8%80%E9%83%8E/dp/4798059056)