2
3

More than 1 year has passed since last update.

Lex 組み込みスロットタイプのAMAZON.TIMEのあいまいな時間の対処法

Last updated at Posted at 2022-02-28

はじめに

Lex 組み込みスロットタイプのAMAZON.TIMEを使用した際、
21時と伝えると、聞き取ってくれますが、9時だと、9:00 もしくは 21:00の2通りあるため、Lexがどっちか分からず、エラーになります。

下記のドキュメントに記載されていました。

ただし、時間の発話の範囲が、9:00〜19:00の場合9時と答えると、21時ではないため、9時と認識してほしいものです。
その方法をまとめます。

事前構築

下記の記事を参考にしました。

Lexは、下記の通りにしました。

スクリーンショット 2022-02-28 17.29.32.png

実際の発話

21時と答えた場合

スクリーンショット 2022-02-28 17.32.00.png

LambdaのCloudWatchLogsからログを確認してみると、
interpretedValue(解釈した値)は、21:00となっていました。

{
    "what_time": {
        "shape": "Scalar",
        "value": {
            "originalValue": "21",
            "resolvedValues": [
                "21:00"
            ],
            "interpretedValue": "21:00"
        }
    }
}
  • interpretedValue

    Amazon Lex V2 がスロットについて決定する値。実際の値は、ボットの値を選択する設定によって異なります。
    ユーザーが入力した値を使用できるか、Amazon Lex V2 に resolvedValues のリスト中の値を選ばせるかが行えます。

  • originalValue

    スロットに対して入力されたユーザーからの発話のテキスト。

  • resolvedValues

    スロットで認識された追加の値のリスト。

9時と答えた場合

9時と答えるとエラーになりました。

スクリーンショット 2022-02-28 17.33.19.png

LambdaのCloudWatchLogsからログを確認してみると、
resolvedValues内に、"09:00","21:00"の2つがあることがわかります。

{
    "what_time": {
        "shape": "Scalar",
        "value": {
            "originalValue": "9",
            "resolvedValues": [
                "09:00",
                "21:00"
            ]
        }
    }
}

interpretedValue(解釈した値)は、ないことも分かりました。

対処方法

LexのLambdaは、以下のようにしました

Lambda
import json
from decimal import Decimal

# json形式でログを出力するため、Decimalがある場合取り除く
def decimal_to_int(obj):
    if isinstance(obj, Decimal):
        return int(obj)

def elicit_slot(slot_to_elicit, intent_name, slots):
    return {
        'sessionState':{
            'dialogAction': {
                'type': 'ElicitSlot',
                'slotToElicit': slot_to_elicit,
            },
            'intent':{
                'name': intent_name,
                'slots': slots,
                'state': 'InProgress'
            }
        }
    }

def validation_slot(slot_to_elicit, message_content, intent_name, slots):
    return {
        'messages': [{'contentType': 'PlainText', 'content': message_content}],
        'sessionState':{
            'dialogAction': {
                'type': 'ElicitSlot',
                'slotToElicit': slot_to_elicit,
            },
            'intent':{
                'name': intent_name,
                'slots': slots,
            }
        }
    }

def confirm_intent(message_content, intent_name, slots):
    return {
        'messages': [{'contentType': 'PlainText', 'content': message_content}],
        'sessionState':{
            'dialogAction': {
                'type': 'ConfirmIntent',
            },
            'intent':{
                'name': intent_name,
                'slots': slots,
                'state': 'Fulfilled'
            }
        }
    }
def input_slots_value(value):
    return {
        "shape": "Scalar",
        "value": {
            "originalValue": value,
            "resolvedValues": [value],
            "interpretedValue": value
        }
    }

def close(fulfillment_state, message_content, intent_name, slots):
    return {
        'messages': [{'contentType': 'PlainText', 'content': message_content}],
        "sessionState": {
            'dialogAction': {
                'type': 'Close',
            },
            'intent':{
                'name': intent_name,
                'slots': slots,
                'state': fulfillment_state
            }
        }
    }

# "time"インテント
def time_reserve(intent_request):
    print("Received event:" + json.dumps(intent_request, default=decimal_to_int, ensure_ascii=False))

    intent_name = intent_request['sessionState']['intent']['name']
    slots = intent_request['sessionState']['intent']['slots']

    print('Received intent_name:' +  json.dumps(intent_name, default=decimal_to_int, ensure_ascii=False))
    print('Received slots:' +  json.dumps(slots, default=decimal_to_int, ensure_ascii=False))

    if slots['what_time'] is None:
        return elicit_slot('what_time',intent_name,slots)

    what_time_resolved = slots['what_time']['value']['resolvedValues']

    counts = len(what_time_resolved)

    # あいまいな時間を言った場合
    for i in range(counts):
        if "09:00" <= what_time_resolved[i] <= "19:00":
            what_time = what_time_resolved[i]
            slots['what_time']['value']['interpretedValue'] = what_time
            break

    try:
        if "09:00" <= slots['what_time']['value']['interpretedValue'] <= "19:00":
            what_time = slots['what_time']['value']['interpretedValue']
        # 20時の場合
        else:
            return validation_slot('what_time',f"{what_time}ですか?時間は、9:00から19:00時の間でお伝え下さい。",intent_name,slots)

    # 8時の場合
    except KeyError:
        return validation_slot('what_time',f"{what_time_resolved[0]}ですか?時間は、9:00から19:00時の間でお伝え下さい。",intent_name,slots)

    confirmation_status = intent_request['sessionState']['intent']['confirmationState']
    print(confirmation_status)

    if confirmation_status == "Confirmed":

        return close( "Fulfilled", '承知しました。', intent_name, slots)

    elif confirmation_status == "Denied":

        return close( "Failed", 'キャンセルしました。', intent_name, slots)

    elif confirmation_status == "None":

        return confirm_intent( 
            f"{what_time}ですね?よろしければ、はいと、そうでない場合、いいえとお伝え下さい。",
            intent_name, slots)

# インテントのルーティング
def dispatch(intent_request):
    # 他にインテントを使用する場合
    intent_name = intent_request['sessionState']['intent']['name']

    # "book"インテント
    if intent_name == 'book':
        return book_reserve(intent_request)

    # "time"インテント
    if intent_name == 'time':

        return time_reserve(intent_request)

def lambda_handler(event, context):

    return dispatch(event)

注意点ですが、時間を判定する際、時間は、"9:00"ではなく、"09:00"と0が必要です。

if what_time < "9:00" :  # ✕
if what_time < "09:00" :  # ◯

テスト

9時と答えた場合

9時と答えた場合、21:00ではなく、9:00と判断されました。

スクリーンショット 2022-03-14 21.33.29.png

8時と答えた場合

9:00〜19:00を答えた場合、拒否されます。

スクリーンショット 2022-03-14 21.34.47.png

別の方法

今回時間範囲が9:00〜19:00でしたが、時間範囲がない場合の対処法は、時間の前に午前・午後をつけると聞き取ってくれます。

スクリーンショット 2022-02-28 18.27.16.png

AMAZON.TIMEの発話リスト

他にも以下の伝え方だと、聞き取ってくれます。

発話 判定
3時 ✕(聞き取れない)
15時 15:00
15時半 15:30
午後3時 15:00
午前12時 00:00
午後0時 00:00
午後12時 12:00
午後0時 12:00
現在の時刻
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3