はじめに

皆さん、電話自動応答(IVR)を利用したことがありますでしょうか。
私はこの前、レンタルカーに忘れ物をした時に利用したことがあります。

電話自動応答(IVR)の入力は音声ではなく、ダイヤル入力の方が一般的だと考えます。
ダイヤル入力方式のIVRはAmazon Connectを利用すると実装できます。その上、Amazon Lexと連携することで音声入力も可能です。

AWS上でお客様向けの音声入力が可能なIVRを構築する必要があり、以下の2点の実装を検証しました。

 

Amazon Connectとは

Amazon Web Services (AWS) の提供するクラウドベースのコンタクトセンターサービスです。このサービスを利用することで、独自のコンタクトセンターを柔軟かつ迅速に構築できます。

また、Amazon LexやAWS Lambdaと連携ができるのでただのコールセンターの機能ではなく、ボットとしても利用できるのがポイントです。

 

Amazon Lexとは

Amazon Lexは自動音声認識と自然言語理解の技術を利用して、音声言語理解システムが作成できます。このサービスを使うと、ユーザーからのサンプル発話に基づいて、その背景にある意図を理解します。

適切な応答を呼び出し、ユーザーとコンピューターが自然な会話をすることができます。Amazon Lexを利用するとチャットボットや音声アシスタントなど、さまざまなアプリケーションに活用されています。

 

検証項目

1.話し言葉とダイヤル入力の併存
2.複数のAmazon Lexのインテントを順序通りに進める

Amazon Connectの全体的なフローとAmazon Lexインテントの詳細は省略します。

 

1.話し言葉とダイヤル入力の併存

発話とプッシュの両方を受け取れる注文ボットがあると仮定します。
以下のようなプロンプトがユーザーに提示されます。

ピザを注文する場合は音声で『ピザ』、あるいは番号『1』を入力してください。

コーラを注文する場合は音声で『コーラ』、あるいは番号『3』を入力してください。

ユーザーが「3」を押した場合でも、「コーラ」と認識するためには、発話入力とダイヤル入力を区別し、それぞれの入力方法に対応する処理が必要です。

 

確認を行う前にAmazon Lexが「ダイヤルによる入力」をサポートしているかどうかを確認する必要があります。
2022年11月18日に投稿されたAWSのニュースに関連する情報が含まれていました。

各スロットの音声と DTMF の設定を最初のプロンプトとリトライプロンプトで別々に制御できます。

参考 AWS whats-new <https://aws.amazon.com/jp/about-aws/whats-new/2022/11/dtmf-slot-settings-amazon-lex/>

 

公式ドキュメントによれば、イベントオブジェクトの中に「inputMode」という値が含まれており、これを参照することでユーザーが「発話」または「プッシュ」で入力したかを識別できるようです。

inputModeの値

参考 AWS 公式ドキュメント <https://docs.aws.amazon.com/ja_jp/lexv2/latest/dg/lambda-input-format.html>

「inputMode」の値を使って分岐処理を行えば、簡単に「3」を「コーラ」に変換できると考えます。
それでは、実際に確認してみましょう!

 

1) スロット作成

FreeForminputタイプのFoodスロットを作成します。
プロンプトには、「食べ物の名前または番号を入力してください。」と記入し、ユーザーに音声または番号で入力するように誘導します。

Foodスロット

 

音声とプッシュの両方で回答するには、「詳細オプション」→「その他のプロンプトオプション」で「Audio and DTMF」を選択する必要があります。

プロンプトオプション

 

また、後述しますが、AWS Lambda関数で「numberToText」属性を設定します。
例えば、「3」番を押した場合、Confirmationの確認プロンプトで”「コーラ」で間違いないでしょうか。”と出力させることで、関連付けを確認できると考えます。

Confirmationやフルフィルメントの設定

 

2) AWS Lambda関数を作成

下のコードはAmazon Lexで使用するAWS Lambdaのソースコードの一部です。
たとえば、「3」という数字が入力された場合、それに対応する「コーラ」に変換し、「numberToText」という属性に設定します。
この属性の値は、Amazon Lex側で利用できますし、Amazon Connectフロー側でも利用することができます。

Amazon LexAWS Lambdaを使用する場合、適切なレスポンスを返さないとエラーが発生する可能性があるため、関連ドキュメントを参照してください。
参考 AWS 公式ドキュメント <https://docs.aws.amazon.com/ja_jp/lexv2/latest/dg/lambda-response-format.html>

Lambdaのソースコードの一部
…
#スロット
slots = event["sessionState"]["intent"]["slots"]
#スロットの値
value = slots["Food"]["value"]["interpretedValue"]
#属性
sessionAttribute = event["sessionState"]["sessionAttributes"]
#音声の場合 「Speech」プッシュの場合「Text」 
inputMode = event["inputMode"]
# メニューリスト 
menuList = ["ピザ", "ハンバーグ", "コーラ"]
…
# プッシュで入力された場合
if inputMode == "Text":
  num = int(value) - 1
  numberToText = menuList[num] 
  #1.2.のConfirmationのところで利用される。
  sessionAttribute["numberToText"] = numberToText
  return fnDelegate(slots,sessionAttribute)
…
  
# 音声で入力された場合
elif inputMode == "Speech":
  if value in menuList:
    sessionAttribute['numberToText'] = value
    return fnDelegate(slots,sessionAttribute)
…

def fnDelegate(slots,sessionAttributes):
  return { 
    'sessionState': {
    "sessionAttributes" : sessionAttributes,
    "dialogAction": {
    "type": "Delegate"
    },
    "intent": {
    "name": "OrderFood",
    "slots": slots 
    }
   }
  }

 

3) Amazon ConnectからAmazon Lexを呼び出す

Amazon ConnectのフローでAmazon Lexを呼び出します。

「顧客の入力を取得する」ブロック

 

4) 確認

電話をかけ、メニューを注文する際に「3」を押しました。
Amazon LexのAWS Lambdaのログを確認すると、inputModeの値が「Text」となっていることが確認できます。

AWS Lambda関数のイベントオブジェクトの一部

 

音声で入力した場合はinputModeの値が「Speech」となっていました。

AWS Lambda関数のイベントオブジェクトの一部

 

Confirmationのプロンプトで、”numberToText”にも「コーラ」が出力されていることを確認しました。

Amazon Lexのログ一部

 

2.複数のAmazon Lexのインテントを順序通りに進める

一つのAmazon Lexボットの中には複数のインテントを作成することができます。一般的な使い方だとボットは関連があるインテントで構成されていると考えます。

しかし、特定のインテントのみ実行させたい、またはBインテントはAインテントが実行済みであれば実行させたいなどの条件を付けたい時があると思います。
関連内容が公式ドキュメントに載っていました。

Amazon Lex はコンテキストに基づいてインテントをトリガーすることができます。コンテキストは、ボットを定義するときにインテントに関連付けることができる状態変数です。

コンソールを使用して、または CreateIntent オペレーションを使用してインテントを作成するときに、インテント用のコンテキストを設定します。コンテキストは、英語 (US) (en-US) ロケールでのみ使用することができます。

参考 AWS 公式ドキュメント <https://docs.aws.amazon.com/ja_jp/lexv2/latest/dg/interrupt-bot.html>

コンテキストを利用する方法がありますが、英語のインテントのみ適用されますね。

Amazon ConnectからAmazon Lexを呼び出す際、インテント名を指定する必要があります。インテントA、B、C、Dを順番に実行させたい場合は、Amazon Connectフローのブロックに各インテントのみを指定すれば日本語でも実装できるのではないかと考えました。

一つのインテントのみ設定

 

1) Amazon Connect側で制御

検証のため、予約インテントであるBookHotel、オペレーターのインテントであるAgentSupportを作成しておきました。

AgentSupportインテントのキック条件はBookHotelインテントが実行済みであることだと仮定しましょう。
(=BookHotelが実行済みではないとAgentSupportはキックされない)

二つのインテント作成

 

Amazon ConnectのフローではBookHotelインテントのみ設定しておきました。

BookHotelインテントのみ設定

 

分岐のそれぞれに対応するプロンプトを設定しました。(デフォルトの場合、「デフォルトのプロンプト」という案内が流れます。)

BookHotelインテントのみ設定

 

電話をかけ、ブロック上には設定していないAgentSupportのインテント呼び出してみました。
すると、Amazon LexはAgentSupportに接続されました。その後、
Amazon Connectではデフォルトの分岐に入ってしまいました。Amazon Connect側では無理でしたね。

Amazon Connectの設定で該当するインテントを指定していなくても、AgentSupportを呼び出す発話を行うとキックされることが分かりました。
また、キックされた後はAmazon Connect上でデフォルトの分岐に進むことがわかりました。

Amazon Connect側で制御を行うには、複数のAmazon Lexボットを使用し、各ボット一つだけのインテントを作成して、進行したい順番にAmazon Lexボットを配置する必要があると考えます。

Amazon Lex側では制御できないのか。
公式ドキュメントによると、AWS LambdaのレスポンスにElicitIntentを返すことで、インテントの順番を制御できるようです。

ElicitIntentの詳細

参考 AWS 公式ドキュメント <https://docs.aws.amazon.com/ja_jp/lexv2/latest/dg/lambda-response-format.html>

 

2) Amazon Lex側で制御

インテントのフローチャートを作成しました。インテントは 1)のBookHotel、AgentSupportを利用します。
AgentSupportインテントは初期応対の段階でBookHotelインテントがもう実行済みであるか確認します。
実行済みだとそのまま進ませて、実行済みではない場合は先にBookHotelインテントをキックさせる必要があります。

フローチャート

 

BookHotelインテントのフルフィルメントの段階で、AWS Lambda関数を呼び出しています。
属性に「flag: 1」という値を設定して、BookHotelが実行されたことを示す目印としています。

def lambda_handler(event, context):
…
  sessionAttribute = event['sessionState']['sessionAttributes'] 
  sessionAttribute['flag'] = 1
…

 

AgentSupportの場合、初期応答のAWS Lambda関数で「flag」の値の有無を確認し、
その値とインテント名を確認します。もし「flag」に何も入っていない場合、最初に戻り他のインテントを実行するようにします。

def lambda_handler(event, context):
…
sessionAttribute = event['sessionState']['sessionAttributes']
flag = sessionAttribute.get('flag')
if flag == None:
  flag = 0

if intent != 'BookHotel' and flag == 0: 
  return fnElicitIntent(sessionAttribute)
…
def fnElicitIntent(sessionAttributes):
  return {
    "sessionState": {
      "sessionAttributes" : sessionAttributes,
      "dialogAction": {
          "type": "ElicitIntent"
       }
    },
    "messages": [
      {
        "contentType": "PlainText",
        "content": "ホテルを予約してからお願いいたします。"
      }
     ]
  }

 

Amazon Lexボットの実行が終了するとセッションが終了してしまいますので、flagの値を維持したい場合は、Amazon Connect側でその値を保存しておく必要があります。

Lexの属性設定

 

他のAmazon Lexボットを呼び出す時に保存したflag属性を利用することも可能です。

flag属性

 

Amazon LexのAWS Lambdaを使用することで、分岐処理を行うことができ、インテントの順番を制御できました。
インテントが増えると管理が複雑になる可能性がありますが、Amazon Connect側で制御するよりも効率的だと考えられます。

まとめ

1.話し言葉とダイヤル入力の併存
イベントオブジェクトの「inputMode」で区別が可能です。話し言葉の場合は「Speech」、ダイヤルの場合は「Text」となっております。
ダイヤルの場合値として「DTMF」を予想していたが、「Text」となっているのは意外でした。

 

2.複数のAmazon Lexのインテントを順序通りに進める
英語でインテントを作成する場合は、コンテキストを利用して簡単に実現できると、
他の言語の場合は、AWS Lambdaを利用して実現する必要があると考えます。