はじめに
本記事では、Amazon Connect + Amazon Kendra + Amazon Lexを組み合わせて、電話での問い合わせ内容を種別判定し、最適な担当者に振り分ける方法と精度をまとめました。
ユーザーのお問い合わせを種別判定し、種別ごとに担当者に振り分けるコンタクトセンターでの一次対応を想定し、Kendraが種別判定機能を担います。
お問い合わせの種別判定は、一般的な生成AIモデルでも可能ですが、生成AIが利用できない企業様もいらっしゃいますので、今回はKendraで試してみます。
以前、以下の記事で、LexとKendraで検証しましたが、今回はインターフェースが電話(Amazon Connect)の場合について構築方法と精度をまとめます。
構成
構成としては、下記の通りです。
以下は、ユーザーがお問い合わせした内容を種別判定し、内容によって担当者に振り分けするまでのフローです。
- 事前に複数のFAQファイルをS3に保存し、それらをKendraにインポートします。
- 電話でユーザーがお問い合わせ内容を伝えます。
- ユーザーからお問い合わせ内容をLexで受け取ります。インテントは、
FallbackIntent
が呼ばれます。 - 受け取った内容をKendraにクエリ実行します。返ってきた最も高い関連度のドキュメントからファイル名を特定します。
- そのファイル名から種別を判定します。
- お問い合わせの種別によって、適切な担当者に振り分けを行います。
構築
以下のリソースを構築します。
- S3
- Kendra
- Lex
- Lambda
- Connect
S3
S3バケットを作成し、以下の4つのファイルをアップロードします。
- account_opening.txt
- 口座開設に関するFAQ
- application.txt
- アプリケーションに関するFAQ
- card_loss.txt
- カードの紛失に関するFAQ
- customer_info_change.txt
- お客様情報の変更に関するFAQ
これらのファイルをKendraにインポートし、Lambdaでクエリ実行すると、お問い合わせ内容に対して最も関連度が高いドキュメントが返されます。
ドキュメントを返す際、参照されたファイル名も返されますので、それによって種別判定が可能です。
各ファイルの内容は以下の通りです。生成AIを利用して作成しました。
account_opening.txt
口座開設に関するFAQ
Q1. 口座開設に必要な本人確認書類を教えてください。
A1. 口座開設時には、運転免許証、パスポート、健康保険証などの本人確認書類が必要となります。詳しくは最寄りの支店までお問い合わせください。
Q2. 未成年者が口座を開設する際の条件を教えてください。
A2. 未成年者が口座を開設する場合は、親権者の同意が必要です。親権者の方の本人確認書類と印鑑も必要となります。
Q3. 非居住者でも口座を開設できますか?
A3. はい、非居住者の方でも当行で口座を開設することが可能です。ただし、在留カードなど追加の書類が必要となる場合があります。
Q4. 口座開設時に印鑑は必要ですか?
A4. 口座開設時には、届出印として印鑑が必要となります。認印がある印鑑をご用意ください。
application.txt
アプリケーションに関するFAQ
Q1. モバイルバンキングアプリのダウンロード方法を教えてください。
A1. 各モバイル端末の公式ストア(App StoreまたはGoogle Play)から無料でダウンロードできます。ストア内で当行アプリ名を検索し、インストールしてください。
Q2. アプリのセキュリティ対策について教えてください。
A2. 当行アプリでは、生体認証、端末認証、VPN、アプリ起動時の認証コードの入力など、多層的なセキュリティ対策を講じております。お客様の大切な情報を万全に守ります。
Q3. アプリで利用できる主な機能を教えてください。
A3. 残高照会、入出金明細の確認、振込・送金、投資信託の購入・解約、ローン申込など、様々な銀行取引がアプリ上で可能です。ATMや支店に行く手間が省けます。
Q4. アプリのアップデートはどのように行えばよいですか?
A4. アプリのアップデートの際は、公式ストアに新しいバージョンがリリースされますので、そちらから手動でアップデートをお願いします。アップデートには最新のセキュリティ対策が含まれますので、必ずアップデートを行ってください。
card_loss.txt
カードの紛失に関するFAQ
Q1. クレジットカードを紛失した場合の対処方法を教えてください。
A1. クレジットカードを紛失した場合は、速やかにカードロッサービスセンターに連絡し、カードの仮停止手続きを行ってください。警察への紛失届も必要となります。
Q2. カードを置き引きされた場合、どうすればよいですか?
A2. カードを置き引きされた場合も、すぐにカードロッサービスセンターに連絡し、カードの仮停止と再発行の手続きを行ってください。警察への被害届も忘れずに提出してください。
Q3. 再発行の手続き方法と手数料を教えてください。
A3. 再発行は最寄りの支店で手続きできます。手数料は1,100円(税込)かかります。インターネットバンキングからの手続きも可能です。
Q4. 暗証番号を忘れた場合、どうすればよいですか?
A4. 暗証番号を忘れた場合は、最寄りの支店でカードの再発行手続きが必要です。その際、本人確認書類をご提示ください。
customer_info_change.txt
お客様情報の変更に関するFAQ
Q1. 住所変更の手続き方法を教えてください。
A1. 住所変更の手続きは、最寄りの支店に「住所変更届」と本人確認書類(運転免許証など)をご持参ください。また、インターネットバンキングからもお手続きが可能です。
Q2. 氏名変更に必要な書類を教えてください。
A2. 氏名変更の際は、「氏名変更届」と新しい本人確認書類(運転免許証、パスポートなど)が必要になります。支店またはインターネットバンキングからお手続きください。
Q3. 電話番号の変更方法を教えてください。
A3. 電話番号の変更は、最寄りの支店にお越しいただくか、インターネットバンキングからお手続きください。口座名義人ご本人の本人確認書類が必要となります。
Q4. メールアドレスの変更はどのようにすればよいですか?
A4. メールアドレスの変更は、インターネットバンキングからお手続きが可能です。ログイン後、「登録情報変更」からメールアドレスの変更手続きを行ってください。
Kendra では、構造化テキストと非構造化テキストを検索対象として利用できます。構造化テキストは、特定の形式に則ったテキストデータのことで、非構造化テキストは、特定の形式をもたないテキストデータです。
今回は非構造化テキストはtxtファイルを利用します。
KendraとLex
KendraとLexの構築は、以下の記事と同じですので、ご参照ください。
Lambda
Lambdaは以下の設定です。
- ランタイム:Python 3.12
- タイムアウト:10秒
- IAMの管理ポリシーを適用
- AmazonKendraFullAccess
コードは以下の通りです。
import json
import boto3
def elicit_intent():
return {
'messages': [{'contentType': 'PlainText', 'content': ','}],
'sessionState': {
'dialogAction': {
'type': 'ElicitIntent',
},
'intent': {
'name': 'FallbackIntent',
'state': 'Fulfilled'
}
},
}
def close(sessionAttributes):
return {
'messages': [{'contentType': 'PlainText', 'content': ','}],
"sessionState": {
"sessionAttributes": {
"inquiry_type": sessionAttributes
},
'dialogAction': {
'type': 'Close',
},
'intent': {
'name': 'FallbackIntent',
'slots': {},
'state': "Fulfilled"
}
}
}
def get_retrieval_result(query_text, index_id):
kendra_client = boto3.client('kendra')
response = kendra_client.query(
QueryText=query_text,
IndexId=index_id,
AttributeFilter={
'EqualsTo': {
'Key': '_language_code',
'Value': {'StringValue': 'ja'},
},
},
)
results = [result for result in response['ResultItems'] if result['DocumentTitle']]
print('Received results:' + json.dumps(results, ensure_ascii=False))
if results:
return results[0]['DocumentTitle']['Text']
# FAQに該当しないお問い合わせ
else:
return "others"
def convert_inquiry_type(inquiry_type):
INQUIRY_TYPE_MAPPING = {
'account_opening.txt': 'account_opening',
'application.txt': 'application',
'card_loss.txt': 'card_loss',
'customer_info_change.txt': 'customer_info_change',
'others': 'others'
}
return INQUIRY_TYPE_MAPPING.get(inquiry_type, inquiry_type)
def lambda_handler(event, context):
print('Received event:' + json.dumps(event, ensure_ascii=False))
if not event['inputTranscript']:
return elicit_intent()
input_text = event['inputTranscript']
print('Received input_text:' + json.dumps(input_text, ensure_ascii=False))
# Kendra index_id
index_id = 'index id'
retrieval_result = get_retrieval_result(input_text, index_id)
inquiry_type = convert_inquiry_type(retrieval_result)
print('Received inquiry_type:' + json.dumps(inquiry_type, ensure_ascii=False))
return close(inquiry_type)
- 様々なお問い合わせに対応させるため、FallbackIntentが呼ばれるのが前提です。
- お問い合わせ内容をそのままKendraにクエリ実行します。
- KendraのインデックスIDは各自変更ください。
INQUIRY_TYPE_MAPPING
は、本来DynamoDBなどのDBに保存すべき場合もありますが、今回はコードに記載します。- お問い合わせ内容に対して、Kendraが関連性のあるドキュメントを返さない場合、種別は「その他」と判定します。
close
関数では、お問い合わせ種別をLexのセッション属性としてConnectフローに返します。
ログ
クエリ結果はログとして保存されます。以下が例です。
[{
"Id": "af2b09e5-e8c7-48ff-bbb3-638a77eede6f-8c06b110-8ec8-4813-b400-d55b16f203ff",
"Type": "DOCUMENT",
"Format": "TEXT",
"AdditionalAttributes": [],
"DocumentId": "s3://xxxxx/application.txt",
"DocumentTitle": {
"Text": "application.txt",
"Highlights": []
},
"DocumentExcerpt": {
"Text": "Q1. 対応OSを教えてください。 A1. iOS 11以降、Android 8.0以降に対応しております。\n\nQ2. 有料会員になるメリットは? A2. 有料会員になると広告非表示、プレミアムコンテンツ視聴が可能になります。\n\nQ3. ログインできない場合はどうすればいいですか? A3. パスワードを再設定する機能がありますので、そちらをお試しください。\n\nQ4. プッシュ通知を無効化する方法は? A4. アプリ内の設定から、プッシュ通知のオン/オフを切り替えられます。",
"Highlights": [
{
"BeginOffset": 4,
"EndOffset": 6,
"TopAnswer": false,
"Type": "STANDARD"
},
{
"BeginOffset": 6,
"EndOffset": 8,
"TopAnswer": false,
"Type": "STANDARD"
},
{
"BeginOffset": 45,
"EndOffset": 47,
"TopAnswer": false,
"Type": "STANDARD"
}
]
},
"DocumentURI": "https://xxxxx.s3.ap-northeast-1.amazonaws.com/application.txt",
"DocumentAttributes": [
{
"Key": "_source_uri",
"Value": {
"StringValue": "https://xxxxx.s3.ap-northeast-1.amazonaws.com/application.txt"
}
},
{
"Key": "s3_document_id",
"Value": {
"StringValue": "application.txt"
}
}
],
"ScoreAttributes": {
"ScoreConfidence": "MEDIUM"
},
"FeedbackToken": "x"
}]
ScoreConfidence
は、信頼度スコアで、Very High、High、Medium、Lowがあります。
信頼度スコアにより、検索結果に関する推定の精度を知ることができます。
Connectフロー
Connectフローは以下のとおりです。
Connectフロー (クリックすると展開します)
{
"Version": "2019-10-30",
"StartAction": "017a4c36-9944-41fc-81dc-b7418ef4fdb4",
"Metadata": {
"entryPointPosition": { "x": 38.4, "y": 44 },
"ActionMetadata": {
"017a4c36-9944-41fc-81dc-b7418ef4fdb4": {
"position": { "x": 143.2, "y": 44.8 }
},
"7fb43c40-fee6-4e3d-87f1-d956529b9032": {
"position": { "x": 353.6, "y": 44.8 },
"children": ["e4458090-1bab-411e-8788-1eb9cd0d608a"],
"overrideConsoleVoice": true,
"fragments": {
"SetContactData": "e4458090-1bab-411e-8788-1eb9cd0d608a"
},
"overrideLanguageAttribute": true
},
"e4458090-1bab-411e-8788-1eb9cd0d608a": {
"position": { "x": 353.6, "y": 44.8 },
"dynamicParams": []
},
"dcb79f59-cee4-48af-9832-ee97b9e4b178": {
"position": { "x": 596.8, "y": 520.8 }
},
"ac1178f8-a26a-4a95-b40b-22f9b6d0c662": {
"position": { "x": 359.2, "y": 254.4 },
"parameters": {
"Attributes": { "inquiry_type": { "useDynamic": true } }
},
"dynamicParams": ["inquiry_type"]
},
"d1a2eed0-afb1-4340-a72d-08cfc78065c7": {
"position": { "x": 145.6, "y": 255.2 },
"parameters": {
"LexV2Bot": {
"AliasArn": {
"displayName": "TestBotAlias",
"useLexBotDropdown": true,
"lexV2BotName": "cm-hirai-kendra"
}
}
},
"useLexBotDropdown": true,
"lexV2BotName": "cm-hirai-kendra",
"lexV2BotAliasName": "TestBotAlias",
"conditionMetadata": []
},
"685d59a0-7b1b-41cc-9d91-bf903933f9b2": {
"position": { "x": 857.6, "y": 44 }
},
"2f96365a-82b1-4027-ae28-524aff2d5f47": {
"position": { "x": 859.2, "y": 236.8 }
},
"52c238f2-44db-47de-bb64-cff7d433ff8d": {
"position": { "x": 1142.4, "y": 596 }
},
"cc473769-73e7-481f-a746-494300bc1ec9": {
"position": { "x": 600, "y": 160 },
"conditionMetadata": [
{
"id": "c12da0da-742c-46bd-b3f6-4867b2805dbd",
"operator": {
"name": "Equals",
"value": "Equals",
"shortDisplay": "="
},
"value": "account_opening"
},
{
"id": "38efa354-c895-47b1-bcac-d92bae4f2cc4",
"operator": {
"name": "Equals",
"value": "Equals",
"shortDisplay": "="
},
"value": "application"
},
{
"id": "d2f34468-f9fd-432f-89cc-17e2a53f2f52",
"operator": {
"name": "Equals",
"value": "Equals",
"shortDisplay": "="
},
"value": "card_loss"
},
{
"id": "e9660a6d-47a9-48c9-b452-442eb8c3fcd5",
"operator": {
"name": "Equals",
"value": "Equals",
"shortDisplay": "="
},
"value": "customer_info_change"
}
]
},
"844fc6d3-6f7e-443c-aeaf-1502c340ca50": {
"position": { "x": 859.2, "y": 436.8 }
}
},
"Annotations": [],
"name": "cm-hirai-lex-kendra-inquiry",
"description": "",
"type": "contactFlow",
"status": "published",
"hash": {}
},
"Actions": [
{
"Parameters": { "FlowLoggingBehavior": "Enabled" },
"Identifier": "017a4c36-9944-41fc-81dc-b7418ef4fdb4",
"Type": "UpdateFlowLoggingBehavior",
"Transitions": { "NextAction": "7fb43c40-fee6-4e3d-87f1-d956529b9032" }
},
{
"Parameters": {
"TextToSpeechVoice": "Takumi",
"TextToSpeechEngine": "Neural",
"TextToSpeechStyle": "None"
},
"Identifier": "7fb43c40-fee6-4e3d-87f1-d956529b9032",
"Type": "UpdateContactTextToSpeechVoice",
"Transitions": { "NextAction": "e4458090-1bab-411e-8788-1eb9cd0d608a" }
},
{
"Parameters": { "LanguageCode": "ja-JP" },
"Identifier": "e4458090-1bab-411e-8788-1eb9cd0d608a",
"Type": "UpdateContactData",
"Transitions": {
"NextAction": "d1a2eed0-afb1-4340-a72d-08cfc78065c7",
"Errors": [
{
"NextAction": "d1a2eed0-afb1-4340-a72d-08cfc78065c7",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": { "Text": "エラーになりました。電話を切ります。" },
"Identifier": "dcb79f59-cee4-48af-9832-ee97b9e4b178",
"Type": "MessageParticipant",
"Transitions": {
"NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"Errors": [
{
"NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"Attributes": {
"inquiry_type": "$.Lex.SessionAttributes.inquiry_type"
},
"TargetContact": "Current"
},
"Identifier": "ac1178f8-a26a-4a95-b40b-22f9b6d0c662",
"Type": "UpdateContactAttributes",
"Transitions": {
"NextAction": "cc473769-73e7-481f-a746-494300bc1ec9",
"Errors": [
{
"NextAction": "dcb79f59-cee4-48af-9832-ee97b9e4b178",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"Text": "お問い合わせ内容を、お伝え下さい。",
"LexV2Bot": {
"AliasArn": "arn:aws:lex:ap-northeast-1:xxxxxxxxxx:bot-alias/XXXXXXX/XXXXXXX"
}
},
"Identifier": "d1a2eed0-afb1-4340-a72d-08cfc78065c7",
"Type": "ConnectParticipantWithLexBot",
"Transitions": {
"NextAction": "dcb79f59-cee4-48af-9832-ee97b9e4b178",
"Errors": [
{
"NextAction": "ac1178f8-a26a-4a95-b40b-22f9b6d0c662",
"ErrorType": "NoMatchingCondition"
},
{
"NextAction": "dcb79f59-cee4-48af-9832-ee97b9e4b178",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"Text": "口座開設、アプリケーションの担当者に繋ぎます。"
},
"Identifier": "685d59a0-7b1b-41cc-9d91-bf903933f9b2",
"Type": "MessageParticipant",
"Transitions": {
"NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"Errors": [
{
"NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"Text": "カード紛失、お客様情報の変更、の担当者に繋ぎます。"
},
"Identifier": "2f96365a-82b1-4027-ae28-524aff2d5f47",
"Type": "MessageParticipant",
"Transitions": {
"NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"Errors": [
{
"NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {},
"Identifier": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"Type": "DisconnectParticipant",
"Transitions": {}
},
{
"Parameters": { "ComparisonValue": "$.Attributes.inquiry_type" },
"Identifier": "cc473769-73e7-481f-a746-494300bc1ec9",
"Type": "Compare",
"Transitions": {
"NextAction": "844fc6d3-6f7e-443c-aeaf-1502c340ca50",
"Conditions": [
{
"NextAction": "685d59a0-7b1b-41cc-9d91-bf903933f9b2",
"Condition": {
"Operator": "Equals",
"Operands": ["account_opening"]
}
},
{
"NextAction": "685d59a0-7b1b-41cc-9d91-bf903933f9b2",
"Condition": { "Operator": "Equals", "Operands": ["application"] }
},
{
"NextAction": "2f96365a-82b1-4027-ae28-524aff2d5f47",
"Condition": { "Operator": "Equals", "Operands": ["card_loss"] }
},
{
"NextAction": "2f96365a-82b1-4027-ae28-524aff2d5f47",
"Condition": {
"Operator": "Equals",
"Operands": ["customer_info_change"]
}
}
],
"Errors": [
{
"NextAction": "844fc6d3-6f7e-443c-aeaf-1502c340ca50",
"ErrorType": "NoMatchingCondition"
}
]
}
},
{
"Parameters": { "Text": "その他の担当者に繋ぎます。" },
"Identifier": "844fc6d3-6f7e-443c-aeaf-1502c340ca50",
"Type": "MessageParticipant",
"Transitions": {
"NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"Errors": [
{
"NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
"ErrorType": "NoMatchingError"
}
]
}
}
]
}
今回は、内容振り分け精度を確認したかったので、実際には人に繋げません。種別判定によって、各「プロンプトの再生」ブロックに分岐させています。
コンタクト属性の設定
Lexブロック内で呼び出したLambdaのclose
関数で、お問い合わせ種別をLexのセッション属性(inquiry_type
)としてConnectフローに返します。
次の「コンタクト属性の設定」ブロックで、返したお問い合わせ種別inquiry_type
を定義付けします。
コンタクト属性を確認する
「コンタクト属性の設定」ブロックで定義付けしたinquiry_type
(お問い合わせ種別)を「コンタクト属性を確認する」ブロックで種別によって振り分けします。
オペレーターにつなぐ
オペレーターに繋げる場合、以下のフローと方法で実現できます。「作業キューの設定」と「キューへ転送」をブロックに追加します。
オペレーターに繋ぐ以外のケース
お問い合わせの種別によって、人に繋げるだけでなく、回答になるURLをSMSで送信したり、回答を自動応答するなど色々とカスタマイズ可能です。
事前にお問い合わせの種別によって回答の該当URLを設定しておき、回答になるURLをSMSやEメールで送信できます。
回答に必要な情報をヒアリングし、自動応答も可能です。
検証結果
判定結果は以下の通りです。
質問文は生成AIを使用して作成しました。
質問文 | Kendraでの種別判定 | OK/NG |
---|---|---|
カードを紛失してしまい、不安です。どのように対応すべきでしょうか? | カードの紛失 | OK |
残念ながらカードを盗まれてしまいました。早急に何か手続きが必要でしょうか? | カードの紛失 | OK |
カードを無くしてしまい、新しいカードが必要になりました。再発行にはどのような手続きと費用がかかりますか? | カードの紛失 | OK |
カードの暗証番号を完全に忘れてしまいました。暗証番号を再設定するにはどうすればよいでしょうか? | カードの紛失 | OK |
新しく口座を作りたいのですが、必要な書類を教えてください。 | 口座開設 | OK |
16歳の子供が口座を持ちたがっています。親の同意は必要でしょうか? | 口座開設 | OK |
留学生ですが、日本で口座を開けますか?その際の条件を教えてください。 | 口座開設 | OK |
実印は持っていませんが、口座開設時に認印は必須でしょうか? | 口座開設 | OK |
夫婦で口座を共有したいのですが、両方の本人確認書類が必要でしょうか? | 口座開設 | OK |
親の口座から子供名義の口座を開設することは可能でしょうか? | 口座開設 | OK |
銀行にお金を預けたい | アプリケーション | NG (正:口座開設) |
アプリのプッシュ通知を無効化する方法を教えてください。 | アプリケーション | OK |
モバイルバンキングアプリはiPhoneとAndroidの両方で利用できますか? | アプリケーション | OK |
アプリにログインする際の認証方式を教えてください。 | アプリケーション | OK |
アプリ上で外貨預金の開設や解約はできますか? | アプリケーション | OK |
バージョンアップでどのような新機能が追加されましたか? | アプリケーション | OK |
結婚して姓が変わりました。何をすればよいか教えてください。 | お客様情報の変更 | OK |
転勤で住所が変わります。住所変更手続きの際、本人確認書類以外に何か必要でしょうか? | お客様情報の変更 | OK |
最近携帯電話番号を変更しました。番号変更手続きはオンラインでできますか? | お客様情報の変更 | OK |
メールアドレスを変更したいのですが、インターネットバンキングでの手順を詳しく教えてください。 | お客様情報の変更 | OK |
同居している両親の住所情報も一緒に変更できますか? | お客様情報の変更 | OK |
離婚により旧姓に戻す場合も、氏名変更手続きが必要でしょうか? | お客様情報の変更 | OK |
こんにちは | その他 | OK |
預けたお金を引き出すことができません。 | その他 | OK |
お金を借りる方法を教えて下さい | アプリケーション | (正:その他) |
種別判定は、25問中23問が正しく種別判定されました。
比較的精度がよい印象です。FAQのテキストファイルの中身を充実させることで、より精度は上げられると推測します。
最後に
今回は、AIチャットボットを用いて電話の問い合わせ内容に応じて担当者に振り分ける方法と精度をまとめました。
Kendraが種別判定機能を担いましたが、比較的精度はよい印象でしたが、あくまでも今回の検証の場合です。 種別が増えていくと精度は落ちていく可能性が高いため、導入前にしっかりと検証すべきです。