AWSでAIサービスを使ってみる〜第10回lex編その2〜

##前回ファイルの実行と今回
前回のlex_create_bot.pyファイルを実行していませんでした。
今回を前回ファイルを実行しbotを作成。botと文字で会話するプログラムを解説し、botを動かして行きます。

前回ファイル

```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'])

```

実行しbotを作成します

```
python lex_create_bot.py
```

実行結果
![スクリーンショット 2021-09-14 9.38.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1658175/c80bbf29-358a-675d-a473-11469408c413.png)

botが作成されました。筆者は先にロールやスロットを作成し、同じロールやスロットを何度も作成し怒られて時間がかかりました。(ちなみに先にロールやスロットを作してしまった際には作成した部分をコメントアウトしてスキップして下さい)

botが作成されたのでbotと文字で会話するプログラムを作成します。

##文字でbotと会話する

以下コードになります。

```lex_chat_text.py
import boto3
import uuid

#①lex-runtimeサービスクライアント作成
lex_runtime = boto3.client('lex-runtime', 'us-east-1')
#②ユーザーIDの作成
user = str(uuid.uuid1())
#③インテントが完了するまで続ける
state = ''
while state != 'Fulfilled':
  result = lex_runtime.post_text(
    botName='MyBot', botAlias='MyBotAlias',
    userId=user, inputText=input('You: '))
  #ボットの応答の表示
  print('Bot:', result['message'])
  #会話の状態を取得
  state = result['dialogState']
#④取得したスロットの値の表示
print()
print('Flavor    :', result['slots']['Flavor'])
print('Container :', result['slots']['Container'])
```
①lex_runtimeのサービスクライアントを作成します。bot作成ではlexのサービスクライアントを作成しますが、botの会話にはlexRuntimeサービスクライアントを使います。

②ユーザーIDの作成
uuidを用いてランダムなユーザーIDを作成する。

③会話の状態がFulfilledになるまで会話を続ける。
post_textメソッドを用いてbotに文字列を送信する。post_textメソッドの戻り値はresultに代入されます。result['message']でbotの応答を指定したりresult['dialogState']で会話の状態を取得します。

④スロットの値を表示
post_textメソッドの戻り値であるresult内の['Flavor']や['Container']を取得して画面に表示します。


##botとの文字での会話の実行
![スクリーンショット 2021-09-16 0.09.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1658175/c9120940-8027-838d-fcac-3675b5b6601b.png)
会話ファイルを実行しチョコレートアイスを注文、するとbotがcornかcupか聞いてcornと答えるとbotから最終の注文を答えてくれました。
![スクリーンショット 2021-09-16 0.14.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1658175/316db13b-df97-2e13-7e45-222d1d94a881.png)
補足ですがFlavorのみ答えても無視されます(笑)
iceというワードが入ると認識してくれましたね、どうやらちゃんとワードを与えないとシカトされる可能性もあるのでお忘れなく。

##まとめ
今回は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)