電話での問い合わせを、製品名に応じて適切な担当者へ振り分けるAIチャットボットの精度検証[Amazon Lex カスタムスロットタイプ(値を展開)]

電話での問い合わせを、製品名に応じて適切な担当者へ振り分けるAIチャットボットの精度検証[Amazon Lex カスタムスロットタイプ(値を展開)]

2024.08.02

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

本記事では、Amazon Connect と Amazon Lex のカスタムスロットタイプ(値を展開)を組み合わせて、電話での問い合わせを製品カテゴリーに応じて適切な担当者へ振り分けるチャットボットを作成し、その精度結果をまとめました。

振り分けは、Amazon Lex のカスタムスロットタイプ(値を展開)に複数の製品カテゴリー名を登録し、発話内容から特定の製品カテゴリーを判定させることで行います。

製品ごとにお問い合わせ対応担当者が異なる場合、AWS Lambda や Amazon Bedrock などは不要で、Lex のみで振り分け対応できる点がメリットです。

今回検証に使用した製品カテゴリーは以下の25種類です

  • Wifi
  • HDMI
  • LED
  • スマートホームデバイス
  • スマートプラグ
  • スマートウォッチ
  • スマートスピーカー
  • スマートドアベル
  • スマートガーデニングセット
  • スマートミラー
  • スマート体重計
  • ポータブルソーラーパネル
  • ポータブルバッテリー
  • ポータブルエアコン
  • ポータブルプロジェクター
  • 電動自転車
  • 電動ロボット掃除機
  • 電動歯ブラシ
  • 電動キックスクーター
  • ノイズキャンセリング付きヘッドセット
  • ヘッドセット
  • ワイヤレスヘッドセット
  • ワイヤレスキーボード
  • エアフライヤー
  • ウォーターサーバー

Amazon Lex のカスタムスロットタイプ(スロット値に制限)を利用した場合の精度検証は、以下の記事をご覧ください。

https://dev.classmethod.jp/articles/amazon-lex-custom-slot-type-top-resolution-product-category/

Lexの作成

Lex のチャットボットは CloudFormation で作成します。今回は25種類の製品カテゴリーを聞き分けるため、手動でカスタムスロットタイプのスロット値を作成するのは手間がかかります。そのため、CloudFormation を使用して効率的に作成します。

CloudFormationテンプレート

テンプレートの主要な構成要素は以下の通りです。

  1. パラメータ
    • LexBotName: ボットの名前
    • SlotName: スロットの名前
    • CustomSlotTypeName: カスタムスロットタイプの名前
    • AliasName: ボットのエイリアス名
  2. リソース
    • MyLexBot: Lexボットの定義
    • MyLexBotAlias: ボットのエイリアス
    • LexBotRole: Lexボットが使用するIAMロール
    • CloudWatchLogGroup: ログ用のCloudWatchロググループ
    • S3Bucket: 音声ログ保存用のS3バケット
  3. ボットの設定
    • 日本語(ja_JP)ロケールの設定
    • インテント(ProductName)の定義
    • カスタムスロットタイプ(productname)の定義と25種類の製品カテゴリー名の登録
AWSTemplateFormatVersion: 2010-09-09
Description: Amazon Lex Bot with Intent creation (Japanese)

Parameters:
  LexBotName:
    Type: String
    Default: cm-hirai-product-name
    Description: The name of the Lex bot
  SlotName:
    Type: String
    Default: name
    Description: The name of the slot
  CustomSlotTypeName:
    Type: String
    Default: productname
    Description: The name of the custom slot type
  AliasName:
    Type: String
    AllowedValues:
      - dev
      - prd
    Default: dev
    Description: The alias name for the Lex bot

Resources:
  MyLexBot:
    Type: AWS::Lex::Bot
    Properties:
      Name: !Ref LexBotName
      Description: !Ref LexBotName
      DataPrivacy: 
        ChildDirected: false
      IdleSessionTTLInSeconds: 300
      RoleArn: !GetAtt LexBotRole.Arn
      BotLocales:
        - LocaleId: ja_JP  
          NluConfidenceThreshold: 0.40
          VoiceSettings:
            VoiceId: Kazuha
          Intents:
            - Name: FallbackIntent
              ParentIntentSignature: AMAZON.FallbackIntent
            - Name: ProductName
              SampleUtterances:
                - Utterance: '{name}'
                - Utterance: 製品カテゴリー名は、{name}です。
                - Utterance: '{name}です。'
                - Utterance: 聞きたいのは、{name}についてです。
              Slots:
                - Name: !Ref SlotName
                  SlotTypeName: !Ref CustomSlotTypeName
                  ValueElicitationSetting:
                    SlotConstraint: Required
                    PromptSpecification:
                      MaxRetries: 3
                      MessageGroupsList:
                        - Message: 
                            PlainTextMessage: 
                              Value: 製品カテゴリー名をお伝え下さい。
              IntentConfirmationSetting: 
                PromptSpecification:
                  MaxRetries: 3
                  MessageGroupsList:
                    - Message:
                        PlainTextMessage:
                          Value: 製品カテゴリー名は、{name}、ですね。よろしければ、はい、と、異なる場合、いいえ、とお伝え下さい。
                ## 確認プロンプトに NOと伝えた場合のボットのプロンプト
                # DeclinationResponse:
                #   MessageGroupsList:
                #     - Message:
                #         PlainTextMessage:
                #           Value: 正しく製品カテゴリー名を聞き取れず、申し訳ございません。
              ## 応答を閉じる
              # IntentClosingSetting:
              #   ClosingResponse:
              #     MessageGroupsList:
              #       - Message:
              #           PlainTextMessage:
              #             Value: 担当者にお繋ぎします
              SlotPriorities:
                - Priority: 1
                  SlotName: !Ref SlotName
          SlotTypes:
            - Name: !Ref CustomSlotTypeName
              ValueSelectionSetting:
                ResolutionStrategy: ORIGINAL_VALUE
              SlotTypeValues:
                - SampleValue: 
                    Value: Wifi
                - SampleValue: 
                    Value: HDMI
                - SampleValue: 
                    Value: LED
                - SampleValue: 
                    Value: スマートホームデバイス
                - SampleValue: 
                    Value: スマートプラグ
                - SampleValue: 
                    Value: スマートウォッチ
                - SampleValue: 
                    Value: スマートスピーカー
                - SampleValue: 
                    Value: スマートドアベル
                - SampleValue: 
                    Value: スマートガーデニングセット
                - SampleValue: 
                    Value: スマートミラー
                - SampleValue: 
                    Value: スマート体重計
                - SampleValue: 
                    Value: ポータブルソーラーパネル
                - SampleValue: 
                    Value: ポータブルバッテリー
                - SampleValue: 
                    Value: ポータブルエアコン
                - SampleValue: 
                    Value: ポータブルプロジェクター
                - SampleValue: 
                    Value: 電動自転車
                - SampleValue: 
                    Value: 電動ロボット掃除機
                - SampleValue: 
                    Value: 電動歯ブラシ
                - SampleValue: 
                    Value: 電動キックスクーター
                - SampleValue: 
                    Value: ノイズキャンセリング付きヘッドセット
                - SampleValue: 
                    Value: ヘッドセット
                - SampleValue: 
                    Value: ワイヤレスヘッドセット
                - SampleValue: 
                    Value: ワイヤレスキーボード
                - SampleValue: 
                    Value: エアフライヤー
                - SampleValue: 
                    Value: ウォーターサーバー
      TestBotAliasSettings:
        BotAliasLocaleSettings:
          - LocaleId: ja_JP
            BotAliasLocaleSetting:
              Enabled: true
        ConversationLogSettings:
          TextLogSettings:
            - Enabled: true
              Destination: 
                CloudWatch: 
                  CloudWatchLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${CloudWatchLogGroup}"
                  LogPrefix: TestBotAlias/
          AudioLogSettings:
            - Enabled: true
              Destination:
                S3Bucket:
                  S3BucketArn: !GetAtt S3Bucket.Arn
                  LogPrefix: TestBotAlias/
  MyLexBotAlias:
    Type: AWS::Lex::BotAlias
    Properties:
      BotAliasName: dev
      BotId: !Ref MyLexBot
      ConversationLogSettings:
        TextLogSettings:
          - Enabled: true
            Destination: 
              CloudWatch: 
                CloudWatchLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${CloudWatchLogGroup}"
                LogPrefix: dev/
        AudioLogSettings:
          - Enabled: true
            Destination:
              S3Bucket:
                S3BucketArn: !GetAtt S3Bucket.Arn
                LogPrefix: dev/
  LexBotRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lexv2.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: !Sub ${LexBotName}-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - polly:SynthesizeSpeech
                Resource: '*'
              - Effect: Allow
                Action: 
                  - s3:PutObject
                Resource: 
                  - !Sub "arn:aws:s3:::${S3Bucket}/*"
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: 
                  - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${CloudWatchLogGroup}:*"
  CloudWatchLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lex/${LexBotName}"
      RetentionInDays: 365
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${LexBotName}-${AWS::AccountId}

このテンプレートでスタックを作成する際、パラメータはデフォルトのまま作成します。

cm-hirai-screenshot 2024-07-30 13.29.44

作成したLexボットでは、1つのカスタムスロットタイプ(productname)に25種類の製品カテゴリー名がスロットタイプ値として設定されています。

cm-hirai-screenshot 2024-07-30 13.30.22

インテント(ProductName)では、サンプル発話に、カスタムスロットタイプ(productname)を含めています。
cm-hirai-screenshot 2024-07-31 14.09.16

スロット(name)には、カスタムスロットタイプ(productname)を設定しています。そのため、発話時に製品カテゴリー名を伝えると、インテントが誘発されると同時にスロットにも製品カテゴリー名が入り、確認プロンプトが引き出されます。
cm-hirai-screenshot 2024-07-31 14.09.28
cm-hirai-screenshot 2024-07-31 14.09.33

Connectフロー

Connect フローは以下の通りです。

cm-hirai-screenshot 2024-07-30 13.42.50

Connectフロー json
{
  "Version": "2019-10-30",
  "StartAction": "50e274c4-cb71-435e-87c7-721dc2e375da",
  "Metadata": {
    "entryPointPosition": {
      "x": -108,
      "y": 93.6
    },
    "ActionMetadata": {
      "50e274c4-cb71-435e-87c7-721dc2e375da": {
        "position": {
          "x": 6.4,
          "y": 44
        }
      },
      "b408fe22-753f-4414-84c7-1a6f413fe461": {
        "position": {
          "x": 948.8,
          "y": 16
        },
        "conditions": [],
        "conditionMetadata": [
          {
            "id": "d8e854c6-6beb-4266-9d18-1f0d1841404e",
            "operator": {
              "name": "Starts with",
              "value": "StartsWith",
              "shortDisplay": "starts with"
            },
            "value": "スマート"
          },
          {
            "id": "436c2534-f1c0-49ea-84ac-f116c99f3799",
            "operator": {
              "name": "Starts with",
              "value": "StartsWith",
              "shortDisplay": "starts with"
            },
            "value": "ポータブル"
          },
          {
            "id": "455eaa98-d706-4725-8b9d-7a0e71f6a6a9",
            "operator": {
              "name": "Starts with",
              "value": "StartsWith",
              "shortDisplay": "starts with"
            },
            "value": "電動"
          }
        ]
      },
      "25fb5cce-3b55-4437-ac92-762fc10075eb": {
        "position": {
          "x": 1184,
          "y": -273.6
        }
      },
      "59634578-ccd2-4611-b712-157387e57421": {
        "position": {
          "x": 1192,
          "y": 267.2
        }
      },
      "63dc1a56-814e-4fcd-99a5-4d996d53b969": {
        "position": {
          "x": 1188,
          "y": -100
        }
      },
      "c2f5a2bc-70b8-46c4-8e1c-f83c5f0c9859": {
        "position": {
          "x": 1188.8,
          "y": 78.4
        }
      },
      "c4b8c59e-b442-412f-a61d-b814228b9d35": {
        "position": {
          "x": 1634.4,
          "y": 545.6
        }
      },
      "1bfc1f5d-3d25-4df5-aebf-a07fa038e6c7": {
        "position": {
          "x": 217.6,
          "y": 40
        },
        "children": [
          "09aaa689-c70e-47df-a3d7-03c491c5671a"
        ],
        "overrideConsoleVoice": true,
        "fragments": {
          "SetContactData": "09aaa689-c70e-47df-a3d7-03c491c5671a"
        },
        "overrideLanguageAttribute": true
      },
      "09aaa689-c70e-47df-a3d7-03c491c5671a": {
        "position": {
          "x": 217.6,
          "y": 40
        },
        "dynamicParams": []
      },
      "d91c47e9-e290-41bd-ac9c-6cec01fa2d53": {
        "position": {
          "x": 703.2,
          "y": 17.6
        },
        "parameters": {
          "Attributes": {
            " ProductName": {
              "useDynamic": true
            }
          }
        },
        "dynamicParams": [
          " ProductName"
        ]
      },
      "68fde90b-0556-4346-a7af-0b04c793511e": {
        "position": {
          "x": 714.4,
          "y": 396
        }
      },
      "aa614095-6077-495c-a43e-6a804ff15e17": {
        "position": {
          "x": 712,
          "y": 585.6
        }
      },
      "c5c8cd2b-ce74-48a2-a386-52079e55cfb0": {
        "position": {
          "x": 1419.2,
          "y": 477.6
        }
      },
      "1834c1dc-7366-4de8-91ba-e9abcf6d90a4": {
        "position": {
          "x": 436,
          "y": 23.2
        },
        "parameters": {
          "LexV2Bot": {
            "AliasArn": {
              "displayName": "TestBotAlias",
              "useLexBotDropdown": true,
              "lexV2BotName": "cm-hirai-product-name"
            }
          }
        },
        "useLexBotDropdown": true,
        "lexV2BotName": "cm-hirai-product-name",
        "lexV2BotAliasName": "TestBotAlias",
        "conditionMetadata": [
          {
            "id": "f1c8bcf8-19dc-4829-a922-6372c07ee504",
            "operator": {
              "name": "Equals",
              "value": "Equals",
              "shortDisplay": "="
            },
            "value": " ProductName"
          }
        ]
      }
    },
    "Annotations": [],
    "name": "cm-hirai-product-name-by-lex",
    "description": "",
    "type": "contactFlow",
    "status": "PUBLISHED",
    "hash": {}
  },
  "Actions": [
    {
      "Parameters": {
        "FlowLoggingBehavior": "Enabled"
      },
      "Identifier": "50e274c4-cb71-435e-87c7-721dc2e375da",
      "Type": "UpdateFlowLoggingBehavior",
      "Transitions": {
        "NextAction": "1bfc1f5d-3d25-4df5-aebf-a07fa038e6c7"
      }
    },
    {
      "Parameters": {
        "ComparisonValue": "$.Attributes. ProductName"
      },
      "Identifier": "b408fe22-753f-4414-84c7-1a6f413fe461",
      "Type": "Compare",
      "Transitions": {
        "NextAction": "59634578-ccd2-4611-b712-157387e57421",
        "Conditions": [
          {
            "NextAction": "25fb5cce-3b55-4437-ac92-762fc10075eb",
            "Condition": {
              "Operator": "TextStartsWith",
              "Operands": [
                "スマート"
              ]
            }
          },
          {
            "NextAction": "63dc1a56-814e-4fcd-99a5-4d996d53b969",
            "Condition": {
              "Operator": "TextStartsWith",
              "Operands": [
                "ポータブル"
              ]
            }
          },
          {
            "NextAction": "c2f5a2bc-70b8-46c4-8e1c-f83c5f0c9859",
            "Condition": {
              "Operator": "TextStartsWith",
              "Operands": [
                "電動"
              ]
            }
          }
        ],
        "Errors": [
          {
            "NextAction": "59634578-ccd2-4611-b712-157387e57421",
            "ErrorType": "NoMatchingCondition"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "スマート関連担当者"
      },
      "Identifier": "25fb5cce-3b55-4437-ac92-762fc10075eb",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
        "Errors": [
          {
            "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "その他担当者"
      },
      "Identifier": "59634578-ccd2-4611-b712-157387e57421",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
        "Errors": [
          {
            "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "ポータブル関連担当者"
      },
      "Identifier": "63dc1a56-814e-4fcd-99a5-4d996d53b969",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
        "Errors": [
          {
            "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "電動関連担当者"
      },
      "Identifier": "c2f5a2bc-70b8-46c4-8e1c-f83c5f0c9859",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
        "Errors": [
          {
            "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {},
      "Identifier": "c4b8c59e-b442-412f-a61d-b814228b9d35",
      "Type": "DisconnectParticipant",
      "Transitions": {}
    },
    {
      "Parameters": {
        "TextToSpeechVoice": "Takumi",
        "TextToSpeechEngine": "Neural",
        "TextToSpeechStyle": "None"
      },
      "Identifier": "1bfc1f5d-3d25-4df5-aebf-a07fa038e6c7",
      "Type": "UpdateContactTextToSpeechVoice",
      "Transitions": {
        "NextAction": "09aaa689-c70e-47df-a3d7-03c491c5671a"
      }
    },
    {
      "Parameters": {
        "LanguageCode": "ja-JP"
      },
      "Identifier": "09aaa689-c70e-47df-a3d7-03c491c5671a",
      "Type": "UpdateContactData",
      "Transitions": {
        "NextAction": "1834c1dc-7366-4de8-91ba-e9abcf6d90a4",
        "Errors": [
          {
            "NextAction": "1834c1dc-7366-4de8-91ba-e9abcf6d90a4",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Attributes": {
          " ProductName": "$.Lex.Slots.name"
        },
        "TargetContact": "Current"
      },
      "Identifier": "d91c47e9-e290-41bd-ac9c-6cec01fa2d53",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "b408fe22-753f-4414-84c7-1a6f413fe461",
        "Errors": [
          {
            "NextAction": "b408fe22-753f-4414-84c7-1a6f413fe461",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "デフォルト"
      },
      "Identifier": "68fde90b-0556-4346-a7af-0b04c793511e",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
        "Errors": [
          {
            "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "エラー"
      },
      "Identifier": "aa614095-6077-495c-a43e-6a804ff15e17",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
        "Errors": [
          {
            "NextAction": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "LoopCount": "10"
      },
      "Identifier": "c5c8cd2b-ce74-48a2-a386-52079e55cfb0",
      "Type": "Loop",
      "Transitions": {
        "NextAction": "c4b8c59e-b442-412f-a61d-b814228b9d35",
        "Conditions": [
          {
            "NextAction": "1834c1dc-7366-4de8-91ba-e9abcf6d90a4",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "ContinueLooping"
              ]
            }
          },
          {
            "NextAction": "c4b8c59e-b442-412f-a61d-b814228b9d35",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "DoneLooping"
              ]
            }
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "製品カテゴリーをお伝え下さい。",
        "LexV2Bot": {
          "AliasArn": "arn:aws:lex:ap-northeast-1:123456789012:bot-alias/CUDVWVOCYQ/TSTALIASID"
        }
      },
      "Identifier": "1834c1dc-7366-4de8-91ba-e9abcf6d90a4",
      "Type": "ConnectParticipantWithLexBot",
      "Transitions": {
        "NextAction": "aa614095-6077-495c-a43e-6a804ff15e17",
        "Conditions": [
          {
            "NextAction": "d91c47e9-e290-41bd-ac9c-6cec01fa2d53",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                " ProductName"
              ]
            }
          }
        ],
        "Errors": [
          {
            "NextAction": "68fde90b-0556-4346-a7af-0b04c793511e",
            "ErrorType": "NoMatchingCondition"
          },
          {
            "NextAction": "aa614095-6077-495c-a43e-6a804ff15e17",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    }
  ]
}

このフローでは、発話から Lex による製品カテゴリー名の判定に基づいて、振り分け先(スマート関連、ポータブル関連、電動関連、その他)が決定されます。

今回の検証では、振り分け先をプロンプトの再生ブロックとしていますが、実際の運用では転送先となるキューを指定することで、適切な担当者に着信を振り分けることができます。

また、検証のため、フローの最後にループを設定しています。

Lex を呼び出すブロックの設定は以下の通りです

cm-hirai-screenshot 2024-07-30 13.49.36

コンタクト属性の設定ブロックは以下の通りです。ここでは、Lex のスロットの値をコンタクト属性として定義しています。

cm-hirai-screenshot 2024-07-30 13.51.08

検証結果1

発話内容が製品カテゴリー名のみの場合の判定結果は以下の通りです。
発話内容とスロット値に入る製品カテゴリーが同じであれば、判定を◯としています。

発話内容(製品カテゴリー) 文字起こし内容
(inputTranscript)
スロット値 判定
Wifi wifi wifi ◯
LED l e d - ✕
FallbackIntentが呼ばれ、デフォルトフローに遷移
HDMI h d m i - ✕
FallbackIntentが呼ばれ、デフォルトフローに遷移
スマートホームデバイス スマート ホーム デバイス スマート ホーム デバイス ◯
スマートプラグ スマート プラグ スマート プラグ ◯
スマートウォッチ スマート ウォッチ スマート ウォッチ ◯
スマートスピーカー スマート スピーカー スマート スピーカー ◯
スマートドアベル スマート ドア ベル スマート ドア ベル ◯
スマートガーデニングセット スマート ガーデニング セット スマート ガーデニング セット ◯
スマートミラー スマート ミラー スマート ミラー ◯
スマート体重計 スマート 体重 計 スマート 体重 計 ◯
ポータブルソーラーパネル ポータブル ソーラー パネル ポータブル ソーラー パネル ◯
ポータブルバッテリー ポータブル バッテリー ポータブル バッテリー ◯
ポータブルエアコン ポータブル エアコン ポータブル エアコン ◯
ポータブルプロジェクター ポータブル プロジェクター ポータブル プロジェクター ◯
電動自転車 電動 自転 車 電動 自転 車 ◯
電動ロボット掃除機 電動 ロボット 掃除 機 電動 ロボット 掃除 機 ◯
電動歯ブラシ 電動 歯 ブラシ 電動 歯 ブラシ ◯
電動キックスクーター 電動 キック スクーター 電動 キック スクーター ◯
ノイズキャンセリング付きヘッドセット ノイズ キャン セリング 付き ヘッドセット ノイズ キャン セリング 付き ヘッドセット ◯
ヘッドセット ヘッドセット ヘッドセット ◯
ワイヤレスヘッドセット ワイヤレス ヘッドセット ワイヤレス ヘッドセット ◯
ワイヤレスキーボード ワイヤレス キーボード ワイヤレス キーボード ◯
エアフライヤー エア フライヤー エア フライヤー ◯
ウォーターサーバー ウォーター サーバー ウォーター サーバー ◯

この結果から、以下のことが分かりました。

  • 25問中23問が適切に判定されました。
  • 「HDMI」と「LED」は FallbackIntent が呼び出され、正しく判定されませんでした。
    • インテントFallbackIntentが誘発されると、Connectフローは、デフォルトに遷移します。
    • 「FallbackIntentが誘発される」 = 「その他」と判定される。
  • 「ワイファイ」と発話すると、文字起こしでは「wifi」と英語表記になります
  • 文字起こし内容がそのままスロット値になりました。
    • また、文字起こしされた製品カテゴリー名とスロット値には、一部スペースが存在しました。
      • 今回は、スペースの有無は判定に影響させていません。

会話ログ

会話ログ例も載せます。

会話ログ
{
    "timestamp": "2024-07-22T08:01:44.828Z",
    "messages": [
        {
            "contentType": "PlainText",
            "content": "商材名は、スマート プラグ、ですね。よろしければ、はい、と、異なる場合、いいえ、とお伝え下さい。"
        }
    ],
    "messageVersion": "2.0",
    "requestAttributes": {
        "x-amz-lex:accept-content-types": "PlainText,SSML",
        "x-amz-lex:channels:platform": "Connect"
    },
    "sessionId": "c9f3518a-5844-403f-ad61-71a4d168df89",
    "dialogEventLogs": [
        {
            "dialogStepLabel": "Intent/ProductName/StartIntent",
            "nextStep": {
                "dialogAction": {
                    "type": "InvokeDialogCodeHook"
                }
            }
        },
        {
            "dialogStepLabel": "Intent/ProductName/StartIntent/CodeHook/Success",
            "nextStep": {
                "dialogAction": {
                    "type": "ElicitSlot",
                    "slotToElicit": "name"
                }
            }
        },
        {
            "dialogStepLabel": "Intent/ProductName/Slot/name/Success",
            "nextStep": {
                "dialogAction": {
                    "type": "ConfirmIntent"
                }
            }
        }
    ],
    "requestId": "e43ddc62-4cfa-4e0f-8e65-153adb6935ee-ut-0",
    "isTestWorkbenchTraffic": false,
    "inputMode": "Speech",
    "bargeIn": "false",
    "operationName": "StartConversation",
    "interpretations": [
        {
            "nluConfidence": "1.00",
            "intent": {
                "name": "ProductName",
                "state": "InProgress",
                "confirmationState": "None",
                "slots": {
                    "name": {
                        "value": {
                            "originalValue": "スマート プラグ",
                            "interpretedValue": "スマート プラグ",
                            "resolvedValues": [
                                "スマートプラグ"
                            ]
                        },
                        "shape": "Scalar"
                    }
                }
            },
            "interpretationSource": "Lex"
        },
        {
            "intent": {
                "name": "FallbackIntent",
                "slots": {}
            },
            "interpretationSource": "Lex"
        }
    ],
    "developerOverride": false,
    "bot": {
        "name": "cm-hirai-product-name",
        "version": "DRAFT",
        "id": "CUDVWVOCYQ",
        "aliasName": "TestBotAlias",
        "aliasId": "TSTALIASID",
        "localeId": "ja_JP"
    },
    "sessionState": {
        "sessionAttributes": {},
        "intent": {
            "name": "ProductName",
            "state": "InProgress",
            "confirmationState": "None",
            "slots": {
                "name": {
                    "value": {
                        "originalValue": "スマート プラグ",
                        "interpretedValue": "スマート プラグ",
                        "resolvedValues": [
                            "スマートプラグ"
                        ]
                    },
                    "shape": "Scalar"
                }
            }
        },
        "dialogAction": {
            "type": "ConfirmIntent"
        },
        "originatingRequestId": "e43ddc62-4cfa-4e0f-8e65-153adb6935ee"
    },
    "inputTranscript": "スマート プラグ",
    "missedUtterance": false,
    "audioProperties": {
        "contentType": "audio/lpcm; sample-rate=8000; sample-size-bits=16; channel-count=1; is-big-endian=false",
        "duration": {
            "total": 1528,
            "silence": 164,
            "voice": 1364
        },
        "s3Path": "cm-hirai-product-name/TestBotAlias/ja_JP/c9f3518a-5844-403f-ad61-71a4d168df89/e43ddc62-4cfa-4e0f-8e65-153adb6935ee-ut-0.wav"
    },
    "utteranceContext": {}
}

https://docs.aws.amazon.com/ja_jp/lexv2/latest/dg/conversation-logs-cw.html
一部抜粋

    "interpretations": [
        {
            "nluConfidence": "1.00",
            "intent": {
                "name": "ProductName",
                "state": "InProgress",
                "confirmationState": "None",
                "slots": {
                    "name": {
                        "value": {
                            "originalValue": "スマート プラグ",
                            "interpretedValue": "スマート プラグ",
                            "resolvedValues": [
                                "スマートプラグ"
                            ]
                        },

    ##~中略~

    "sessionState": {
        "sessionAttributes": {},
        "intent": {
            "name": "ProductName",
            "state": "InProgress",
            "confirmationState": "None",
            "slots": {
                "name": {
                    "value": {
                        "originalValue": "スマート プラグ",
                        "interpretedValue": "スマート プラグ",
                        "resolvedValues": [
                            "スマートプラグ"
                        ]
                    },
                    "shape": "Scalar"
                }
            }
        },

    ##~中略~

    "inputTranscript": "スマート プラグ",

inputTranscriptは、発話の文字起こし内容です。

slotsのvalue内の3つの値は、AWSドキュメントでは以下の通り説明されています。

  • originalValue(元の値)
    • ユーザー入力のうち、Lexがスロット値に対応していると判断した値です。
  • interpretedValue(解釈された値)
    • Lexがユーザー入力に基づいてスロットに対して決定する値。実際の値は、ボットの値選択戦略の設定によって異なります。ユーザーが入力した値(originalValue)を使用するか、resolvedValuesリストの最初の値を使用します。
    • interpretedValueがスロットに入る
  • resolvedValues(解決された値)
    • Lex がユーザー入力の可能な解決策であると判断した値のリスト。

https://docs.aws.amazon.com/ja_jp/lexv2/latest/APIReference/API_runtime_Value.html

また、2つのカスタムスロットタイプ違いは以下の通りです。

  • 値を展開:ORIGINAL_VALUE
    • ユーザーが入力した値が、スロットの値に類似している場合、ユーザーが入力した値(originalValue)を返します。
      • つまり、originalValueの値がそのままinterpretedValueの値に入ります。
  • スロット値に制限:TOP_RESOLUTION
    • スロットに解決リストがある場合、解決リストの最初の値をスロットタイプの値として返します。解決リストがない場合は、nullが返されます。
      • つまり、resolvedValuesの値がそのままinterpretedValueの値に入ります。resolvedValuesの値が複数であれば、最初の値がinterpretedValueの値となります。

https://docs.aws.amazon.com/lexv2/latest/APIReference/API_CreateSlotType.html#API_CreateSlotType_RequestBody

いくつか会話ログをみてみます。

スマートスピーカーのログ

スマートプラグと同様、originalValueとinterpretedValueには、inputTranscriptと同じ様に、「スマート」と「スピーカー」の間にスペースが入っています。

                "slots": {
                    "name": {
                        "value": {
                            "originalValue": "スマート スピーカー",
                            "interpretedValue": "スマート スピーカー",
                            "resolvedValues": [
                                "スマートスピーカー"
                            ]
                        },
    "inputTranscript": "スマート スピーカー",

スマートウォッチのログ

resolvedValuesが空になっています。
Lexがユーザーの入力をカスタムスロットタイプ(productname) のいずれかの値としても解釈できなかったため、resolvedValues のリストが空になったのだと考えられます。
resolvedValues が空の場合でも、カスタムスロットタイプ(productname) に近い値が存在していれば、ユーザーの入力(originalValue)が、interpretedValue として解釈されます。

                "slots": {
                    "name": {
                        "value": {
                            "originalValue": "スマート ウォッチ",
                            "interpretedValue": "スマート ウォッチ",
                            "resolvedValues": []
                        },
    "inputTranscript": "スマート ウォッチ",

ヘッドセットのログ

製品カテゴリーには、ヘッドセット、ワイヤレスヘッドセット、ノイズキャンセリング付きヘッドセットの3つが存在しているので、リストアップされています。
そのなかで、ユーザーの入力(originalValue)がinterpretedValueの値になります。

                        "value": {
                            "originalValue": "ヘッドセット",
                            "interpretedValue": "ヘッドセット",
                            "resolvedValues": [
                                "ヘッドセット",
                                "ワイヤレスヘッドセット",
                                "ノイズキャンセリング付きヘッドセット"
                            ]
                        },
    "inputTranscript": "ヘッドセット",

検証結果2

発話内容に製品カテゴリー名だけでなく、付随情報も含めた場合の判定結果を以下の表にまとめました。この検証では、より実際の問い合わせに近い状況を想定しています。

発話内容 文字起こし
(inputTranscript)
スロット値 判定
製品カテゴリー名は、スマート体重計です。 製品 カテゴリー 名 は 、 スマート 体重 計 です 。 スマート 体重 計 ◯
ポータブルバッテリーです。 ポータブル バッテリー です 。 ポータブル バッテリー ◯
聞きたいのは、ワイヤレスヘッドセットについてです。 聞き たい の は 、 ワイヤレス ヘッドセット に つい て です 。 ワイヤレス ヘッドセット ◯
スマートホームデバイスに関してです スマート ホーム デバイス に 関し て です スマート ホーム デバイス ◯
スマートプラグについて詳しく知りたいのですが。 スマート プラグ に つい て 詳しく 知り たい の です か スマート プラグ ◯
スマートウォッチの件です スマート ウォッチ の 件 です スマート ✕
スロット値:スマート
スマートスピーカーについてです。 スマート スピーカー に つい て です スマート スピーカー ◯
スマートドアベルの機能について聞きたいです。 スマート ドア ベル の 機能 に つい て 聞き たい です - ✕
インテントFallbackIntentが誘発される
スマートガーデニングセットついて聞きたいです。 スマート ガーデニング セット に つい て 聞き たい です スマート ガーデニング セット ◯
スマートミラーについて話したいのですが マート ミラー に つい て 話し たい の です けど ミラー ✕
スロット値:ミラー
スマート体重計のことについてお話しします スマート 体重 計 の こと に つい て お 話し ます スマート 体重 計 ◯
ポータブルソーラーパネルに関する内容です。 ポータブル ソーラー パネル に 関する 内容 です - ✕
インテントFallbackIntentが誘発される
電動自転車について教えてください。 電動 自転 車 に つい て 教え て ください 電動 自転 車 ◯
電動ロボット掃除機についてご相談があります エンドウ ロボット 掃除 機 に つい て ご 相談 が あり ます ロボット 掃除 機 ✕
スロット値:ロボット 掃除 機
電動歯ブラシの効果について知りたいです。 電動 歯 ブラシ の 効果 に つい て 知り たい です 電動 歯 ✕
スロット値:電動 歯
電動キックスクーターについて述べたいです 電動 キック スクーター に つい て 述べ たい です 電動 キック スクーター ◯
ノイズキャンセリング付きヘッドセットについて教えてください。 ノイズ キャン セリング 付き で と セット に つい て 教え て ください ノイズ セット ✕
スロット値:ノイズ セット
エアフライヤーついてお願いします。 エアー フライヤー に つい て お 願い し ます フラ ✕
スロット値:フラ

この結果から、以下のことが分かりました

  • 18問中10問が正しく判定されました。
  • 検証結果1と比較し、不要な発話があると精度が下がることが分かりました。
  • サンプル発話の種類を増やすことで、正答数が上がる可能性が高いです。
    • サンプル発話に設定していた以下の3つのパターンは、全て正しく判定されました。
      • 製品カテゴリー名は、{name}です。:製品カテゴリー名は、スマート体重計です。
      • {name}です。:ポータブルバッテリーです。
      • 聞きたいのは、{name}についてです。:聞きたいのは、ワイヤレスヘッドセットについてです。

インテントFallbackIntentが誘発された内容を除き、判定が✕の各会話ログを確認してみます。

スマートウォッチのログ

resolvedValuesに、スマートウォッチがはいっていないですね。resolvedValuesには存在しない「スマート」がユーザーの入力(originalValue)として評価され、interpretedValueの値になります。

    "interpretations": [
        {
            "nluConfidence": "0.75",
            "interpretationSource": "Lex",
            "intent": {
                "name": "ProductName",
                "state": "InProgress",
                "confirmationState": "None",
                "slots": {
                    "name": {
                        "value": {
                            "originalValue": "スマート",
                            "interpretedValue": "スマート",
                            "resolvedValues": [
                                "スマートミラー",
                                "スマートプラグ",
                                "スマート体重計",
                                "スマートスピーカー",
                                "スマートホームデバイス"
                            ]
                        },
                        "shape": "Scalar"
                    }
                }
            }
        },
    "inputTranscript": "スマート ウォッチ の 件 です",

スマートミラーのログ

スマートミラーのスが文字起こしされていないため、正しい値が選択されていません。

            "slots": {
                "name": {
                    "value": {
                        "originalValue": "ミラー",
                        "interpretedValue": "ミラー",
                        "resolvedValues": [
                            "スマートミラー"
                        ]
                    },
                    "shape": "Scalar"
                }
            }
        }
    },
    "inputTranscript": "マート ミラー に つい て 話し たい の です けど",

2回目を試すと、正しく認識されました。

            "slots": {
                "name": {
                    "value": {
                        "originalValue": "スマート ミラー",
                        "interpretedValue": "スマート ミラー",
                        "resolvedValues": [
                            "スマートミラー"
                        ]
                    },
                    "shape": "Scalar"
                }
            }
        },
    },
    "inputTranscript": "スマート ミラー に つい て 話し たい の です が",

電動ロボット掃除機のログ

電動がエンドウと文字起こしされているため、正しい値が選ばれませんでした。

            "slots": {
                "name": {
                    "value": {
                        "originalValue": "ロボット 掃除 機",
                        "resolvedValues": [
                            "電動ロボット掃除機"
                        ],
                        "interpretedValue": "ロボット 掃除 機"
                    },
                    "shape": "Scalar"
                }
            }
        },
        "originatingRequestId": "8565eb72-fd49-49c2-90de-1c507f879356"
    },
    "inputTranscript": "エンドウ ロボット 掃除 機 に つい て ご 相談 が あり ます",

2回目を試すと、正しく認識しました。

            "slots": {
                "name": {
                    "value": {
                        "originalValue": "電動 ロボット 掃除 機",
                        "interpretedValue": "電動 ロボット 掃除 機",
                        "resolvedValues": [
                            "電動ロボット掃除機"
                        ]
                    },
                    "shape": "Scalar"
                }
            },
        }
    },
    "inputTranscript": "電動 ロボット 掃除 機 に つい て ご 相談 が あり ます",

電動歯ブラシのログ

resolvedValuesが空になっており、ユーザーの入力を(originalValue)電動 歯と評価され、interpretedValueの値になっています。

            "slots": {
                "name": {
                    "value": {
                        "originalValue": "電動 歯",
                        "interpretedValue": "電動 歯",
                        "resolvedValues": []
                    },
                    "shape": "Scalar"
                }
            }
        },
    },
    "inputTranscript": "電動 歯 ブラシ の 効果 に つい て 知り たい です",

2回試しましたが、同じ結果でした。

ノイズキャンセリング付きヘッドセットのログ

resolvedValuesでは、正しい値が入っていますが、不適切な文字起こしによって、ノイズ セットがinterpretedValueとして解釈されています。

            "slots": {
                "name": {
                    "value": {
                        "originalValue": "ノイズ セット",
                        "interpretedValue": "ノイズ セット",
                        "resolvedValues": [
                            "ノイズキャンセリング付きヘッドセット"
                        ]
                    },
                    "shape": "Scalar"
                }
            }
        },
    },
    "inputTranscript": "ノイズ キャン セリング 付き で と セット に つい て 教え て ください",

2回目も正しい値が入りませんでした。resolvedValuesも空です。

            "slots": {
                "name": {
                    "value": {
                        "originalValue": "ノイズ キャン セ 付き ヘッド セット",
                        "resolvedValues": [],
                        "interpretedValue": "ノイズ キャン セ 付き ヘッド セット"
                    },
                    "shape": "Scalar"
                }
            }
        },
        "originatingRequestId": "ed3dd3cf-b153-456d-97a9-6e606be848a7"
    },
    "inputTranscript": "ノイズ キャン セリング 付き ヘッド セット に つい て 教え て ください",

エアフライヤーのログ

resolvedValuesが空になっており、ユーザー入力の値(originalValue)はフラと評価されinterpretedValueとして解釈されています。

            "slots": {
                "name": {
                    "value": {
                        "originalValue": "フラ",
                        "interpretedValue": "フラ",
                        "resolvedValues": []
                    },
                    "shape": "Scalar"
                }
            }
        }
    },
    "inputTranscript": "エアー フライヤー に つい て お 願い し ます",

2回試しましたが、同じ結果でした。

最後に

本検証を通じて、以下のことが明らかになりました。

  1. 製品カテゴリー名のみの発話であれば、高い精度で判定できます。
  2. サンプル発話の種類を増やすことで、発話に他の情報が含まれていた場合も精度が向上する可能性があります。
  3. カスタムスロットタイプに設定した値に対して、実際にスロットに入る値にはスペースが入ることが多いです。スペースが許容されない場合、Connect フロー上で AWS Lambda などを使用してスペースを削除するなどの対策が必要になります。

今回は Amazon Lex のカスタムスロットタイプ(値を展開)を利用しましたが、次回はカスタムスロットタイプ(スロット値に制限)でも精度検証を行う予定です。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事