Amazon Connectで自動ヒアリングした内容をBedrockで要約し、メール通知する(Kinesis Data Stream + Step Functions)

Amazon Connectで自動ヒアリングした内容をBedrockで要約し、メール通知する(Kinesis Data Stream + Step Functions)

Clock Icon2024.12.23

はじめに

本記事では、Amazon ConnectとAmazon Lexを活用し、自動ヒアリングした内容をAmazon Bedrockで要約してメール通知する方法を、Amazon Kinesis Data Streams(以下、KDS)とAWS Step Functionsを用いて実装する手順を解説します。

この実装によるユーザー体験の流れは、以下の通りです。

ステップ 実行者 アクション
1 ユーザー 電話をかける
2 Connect 「申し訳ございません。ただいま電話に出ることができません。メッセージを録音してよければ、はい、とお伝えください。」
3 ユーザー 「はい」と応答
4 Connect(Lex) 「ご要件、お名前、住所の3点をお伝えください。無音の状態が一定時間ある場合、録音が切れますので、ご了承ください。」
5 ユーザー 必要事項を伝える
6 Step Functions 録音内容をBedrockで要約し、メール通知する
7 Connect 「以上で録音を終了します。電話を切ります。」

以下に構成図を示します。

cm-hirai-screenshot 2024-12-23 8.51.07
処理の内容は以下のとおりです。

  1. Lexボットでお問い合わせ内容をヒアリングする
  2. ヒアリング内容をコンタクト属性として保存する
    • キー名:recording、値はヒアリング内容のテキスト
  3. 切断直後、問い合わせレコードがKDSにストリーミングされ、EventBridge Pipes経由でStep Functionsステートマシンを起動する
    • 問い合わせレコードには、コンタクト属性(recording)、コンタクトID、発信元電話番号、日時などが保存されている
  4. ステートマシンでは以下の処理を行う
    1. ステートマシンに渡された問い合わせレコードから、ヒアリング内容であるコンタクト属性(recording)を取得する
    2. Bedrockを使用して、お問い合わせ内容の文章を整形する
    3. 整形した文章とコンタクトID、発信元電話番号等をAmazon SNSでメール送信する

以前、AWS Lambdaを利用する方法や、EventBridgeとAWS Step Functionsステートマシンを利用する方法をご紹介しました。
cm-hirai-screenshot 2024-12-05 15.23.58
Lambdaを利用する方法

https://dev.classmethod.jp/articles/amazon-connect-lex-bedrock-summary-email/

cm-hirai-screenshot 2024-12-09 16.25.39
EventBridgeとAWS Step Functionsステートマシンを利用する方法

https://dev.classmethod.jp/articles/amazon-connect-bedrock-eventbridge-step-functions/

AWS Lambdaを利用する方法では、Lambdaのコード開発が必要となるため、運用負荷がかかります。また、EventBridgeとAWS Step Functionsステートマシンを利用する方法では、ConnectからEventBridgeへのイベント送信がベストエフォートであるため、イベントが送信されずメール通知が行われない可能性があります。

今回は、ローコードで確実にメール送信が行われるKDSとAWS Step Functionsステートマシンを使用して実装します。

Connectは、各通話ごとに問い合わせレコード(Contact Trace Record: CTR)として通話記録を保存します。

https://docs.aws.amazon.com/ja_jp/connect/latest/adminguide/ctr-data-model.html

Connectでは、KDSに問い合わせレコードを出力できるため、EventBridge Pipes経由でStep Functionsステートマシンを起動できます。

前提条件

Lex

作成済みのLexボットのスロットfreeに以下のプロンプトを修正します。

修正前

お問い合わせ内容をお伝え下さい。無音の状態が一定時間ある場合、録音が切れますので、ご了承ください。

修正後

ご要件、お名前、住所の3点をお伝え下さい。無音の状態が一定時間ある場合、録音が切れますので、ご了承ください。

cm-hirai-screenshot 2024-11-21 10.51.18

KDS

KDSは、プロビジョンドモード、シャード1で作成します。

cm-hirai-screenshot 2024-12-10 9.52.21

Connectのデータストリーミングを有効化します。

cm-hirai-screenshot 2024-12-10 9.55.19

ステートマシン

EventBridge Pipes内でKDSから渡される内容は、以下のとおりです。

{
  "eventSource": "aws:kinesis",
  "eventVersion": "1.0",
  "eventID": "shardId-000000000000:xxxxxxxxx",
  "eventName": "aws:kinesis:record",
  "invokeIdentityArn": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/Amazon_EventBridge_Pipe_cm-hirai-voicemail-to-email-sf_cbcfa279",
  "awsRegion": "ap-northeast-1",
  "eventSourceARN": "arn:aws:kinesis:ap-northeast-1:xxxxxxxxxxxx:stream/cm-hirai",
  "kinesisSchemaVersion": "1.0",
  "partitionKey": "19a1c22c-399d-4c6b-b766-9832832c9005",
  "sequenceNumber": "xxxxxxxxx",
  "data": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "approximateArrivalTimestamp": 1733793205.123
}

上記のうちdataはbase64でエンコードされているため、デコードしてJSONデータをパースすることで、以下のような値が取得できます。

文字起こし内容のコンタクト属性(recording)も確認できます。

{
  "data": {
    "AWSAccountId": "012345678901",
    "AWSContactTraceRecordFormatVersion": "2017-03-10",
    "Agent": null,
    "AgentConnectionAttempts": 0,
    "AnsweringMachineDetectionStatus": null,
    "Attributes": {
      "recording": "お問い合わせ内容〇〇"
    },
    "Campaign": {
      "CampaignId": null
    },
    "Channel": "VOICE",
    "ConnectedToSystemTimestamp": "2024-12-10T01:11:43Z",
    "ContactDetails": {},
    "ContactId": "19a1c22c-399d-4c6b-b766-9832832c9005",
    "CustomerEndpoint": {
      "Address": "+81xxxxxxxx",
      "Type": "TELEPHONE_NUMBER"
    },
    "CustomerVoiceActivity": null,
    "DisconnectReason": "CONTACT_FLOW_DISCONNECT",
    "DisconnectTimestamp": "2024-12-10T01:12:22Z",
    "InitialContactId": null,
    "InitiationMethod": "INBOUND",
    "InitiationTimestamp": "2024-12-10T01:11:43Z",
    "InstanceARN": "arn:aws:connect:ap-northeast-1:012345678901:instance/3ff2093d-af96-43fd-b038-3c07cdd7609c",
    "LastUpdateTimestamp": "2024-12-10T01:13:24Z",
    "MediaStreams": [
      {
        "Type": "AUDIO"
      }
    ],
    "NextContactId": null,
    "PreviousContactId": null,
    "Queue": null,
    "Recording": null,
    "Recordings": null,
    "References": [],
    "ScheduledTimestamp": null,
    "SegmentAttributes": {
      "connect:Subtype": {
        "ValueInteger": null,
        "ValueMap": null,
        "ValueString": "connect:Telephony"
      }
    },
    "SystemEndpoint": {
      "Address": "+81xxxxxxxx",
      "Type": "TELEPHONE_NUMBER"
    },
    "Tags": {
      "aws:connect:instanceId": "3ff2093d-af96-43fd-b038-3c07cdd7609c",
      "aws:connect:systemEndpoint": "+81xxxxxxxx"
    },
    "TaskTemplateInfo": null,
    "TransferCompletedTimestamp": null,
    "TransferredToEndpoint": null,
    "VoiceIdResult": null
  }
}

以下のステートマシンを作成します。ワークフローとしては、以下の図の通りです。
cm-hirai-screenshot 2024-12-23 14.16.34

以下のコードをそのまま貼り付けてください。

TopicArnは、各自変更ください。

{
  "Comment": "Voicemail to Email Notifier State Machine with JSONata",
  "StartAt": "Base64Decode",
  "QueryLanguage": "JSONata",
  "States": {
    "Base64Decode": {
      "Type": "Pass",
      "Next": "ParseJSON",
      "Assign": {
        "decodedData": "{% $base64decode($states.input.data) %}"
      }
    },
    "ParseJSON": {
      "Type": "Pass",
      "Next": "Choice",
      "Assign": {
        "data": "{% $parse($decodedData) %}",
        "InitialContactId": "{% $parse($decodedData).ContactId %}",
        "Time": "{% $parse($decodedData).InitiationTimestamp %}",
        "CallerPhoneNumber": "{% $parse($decodedData).SystemEndpoint.Address %}"
      }
    },
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Condition": "{% $data.Attributes.recording != null %}",
          "Next": "Bedrock InvokeModel",
          "Assign": {
            "recording": "{% $data.Attributes.recording %}"
          }
        }
      ],
      "Default": "Pass"
    },
    "Bedrock InvokeModel": {
      "Type": "Task",
      "Resource": "arn:aws:states:::bedrock:invokeModel",
      "Arguments": {
        "ModelId": "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0",
        "Body": {
          "anthropic_version": "bedrock-2023-05-31",
          "max_tokens": 1000,
          "temperature": 0,
          "messages": [
            {
              "role": "user",
              "content": [
                {
                  "type": "text",
                  "text": "{% '以下の音声文字起こしテキストから、ご要件、名前、住所の3つの情報を抽出し、以下のフォーマットで整理してください:\n\nご要件:[内容を自然な日本語で記述]\n\nお名前:[名前を記述]\n\nご住所:[住所を記述]\n\nなお、情報が不足している場合は「情報なし」と記載してください。\n余計な説明は不要です。上記フォーマットのみを返してください。\n\nテキスト:\n' & $recording %}"
                }
              ]
            }
          ]
        }
      },
      "Next": "SNS Publish",
      "Assign": {
        "bedrock_text": "{% $states.result.Body.content[type=\"text\"].text %}"
      }
    },
    "SNS Publish": {
      "Type": "Task",
      "Resource": "arn:aws:states:::sns:publish",
      "Arguments": {
        "Message": "{% 'お問い合わせ情報\n\n Contact ID: ' & $InitialContactId & '\n\n・日時:' & $Time & '\n\n・電話番号:' & $CallerPhoneNumber &  '\n\n・お問い合わせ内容(整形済み文章)' & '\n\n' & $bedrock_text & '\n\n・お問い合わせ内容(未整形文章)' & '\n\n' & $recording %}",
        "TopicArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxx:cm-hirai"
      },
      "End": true
    },
    "Pass": {
      "Type": "Pass",
      "Comment": "No recording attribute found, ending workflow.",
      "End": true
    }
  }
}

IAMロールは自動作成されます。
cm-hirai-screenshot 2024-12-10 9.58.32

ステートマシンで処理している内容は以下のとおりです。

  1. KDSからEventBridge Pipes経由でステートマシンをトリガーする
    • ヒアリング内容であるコンタクト属性(recording)やコンタクトID、発信元電話番号などが渡される
  2. コンタクト属性(recording)がnullではないか確認
  3. BedrockのClaudeを利用して、文字起こし内容を整形する
  4. 整形した内容をSNSトピックを通じてメール送信する

ConnectインスタンスからKDSへのストリーミングは、特定のフローに絞ることはできず、Connectインスタンス単位で有効化されます。そのため、コンタクト属性(recording)がnullではないかを確認する必要があります。

EventBridge Pipes

EventBridge Pipesを作成します。

ソースにはKDSを設定します。
cm-hirai-screenshot 2024-12-10 10.01.44

ターゲットにはStep Functionsを設定します。
cm-hirai-screenshot 2024-12-10 10.01.48

IAMロール名は自動で作成されます。

Connectフロー

Connectフローでは、以下の1つのブロックを追加します。
cm-hirai-screenshot 2024-12-10 16.32.47
「コンタクト属性の設定」ブロックを追加します。このブロックでは、recordingキーにLexでヒアリングした内容が格納されます。
cm-hirai-screenshot 2024-12-09 14.13.25

作成したConnectフローのjsonファイル(クリックで展開)
{
  "Version": "2019-10-30",
  "StartAction": "17a389ed-1002-4550-9bbf-efbbe3811cf9",
  "Metadata": {
    "entryPointPosition": {
      "x": 36.8,
      "y": 65.6
    },
    "ActionMetadata": {
      "1990a178-5825-414e-bd24-2bc3dc5dc937": {
        "position": {
          "x": 357.6,
          "y": 61.6
        },
        "children": [
          "dfd9756f-f2b0-42ee-9abd-dbd0c4d41340"
        ],
        "overrideConsoleVoice": true,
        "fragments": {
          "SetContactData": "dfd9756f-f2b0-42ee-9abd-dbd0c4d41340"
        },
        "overrideLanguageAttribute": true
      },
      "dfd9756f-f2b0-42ee-9abd-dbd0c4d41340": {
        "position": {
          "x": 357.6,
          "y": 61.6
        },
        "dynamicParams": []
      },
      "17a389ed-1002-4550-9bbf-efbbe3811cf9": {
        "position": {
          "x": 140.8,
          "y": 60.8
        }
      },
      "0cebb197-9920-4fcc-8410-a2f9e9fb12ff": {
        "position": {
          "x": 572,
          "y": 57.6
        },
        "parameters": {
          "LexV2Bot": {
            "AliasArn": {
              "displayName": "TestBotAlias",
              "useLexBotDropdown": true,
              "lexV2BotName": "cm-hirai-beyond-15seconds"
            }
          }
        },
        "dynamicMetadata": {
          "x-amz-lex:audio:start-timeout-ms:*:*": false,
          "x-amz-lex:audio:end-timeout-ms:*:*": false,
          "x-amz-lex:audio:max-length-ms:*:*": false
        },
        "useLexBotDropdown": true,
        "lexV2BotName": "cm-hirai-beyond-15seconds",
        "lexV2BotAliasName": "TestBotAlias",
        "conditionMetadata": [
          {
            "id": "2e6d6b24-8464-46a6-8170-1e1714c401a3",
            "operator": {
              "name": "Equals",
              "value": "Equals",
              "shortDisplay": "="
            },
            "value": "recording"
          }
        ]
      },
      "465002a2-1f89-476b-8f58-e0e2671632c7": {
        "position": {
          "x": 820.8,
          "y": 271.2
        }
      },
      "1a41bbb2-6b65-4bf0-871b-deaf288ba7a1": {
        "position": {
          "x": 819.2,
          "y": 460.8
        }
      },
      "3c2cba41-f8a9-418f-b428-c573abb3c010": {
        "position": {
          "x": 1259.2,
          "y": 526.4
        }
      },
      "acf5bea6-926a-42f6-a9c7-2f9cf4af97ef": {
        "position": {
          "x": 819.2,
          "y": 53.6
        },
        "parameters": {
          "Attributes": {
            "recording": {
              "useDynamic": true
            }
          }
        },
        "dynamicParams": [
          "recording"
        ]
      },
      "a1441575-8861-43f3-91a3-06ab8a938b2d": {
        "position": {
          "x": 1044.8,
          "y": 51.2
        }
      }
    },
    "Annotations": [],
    "name": "cm-hirai-voicemail-to-email-notifier-for-stepfunctions-from-KDS",
    "description": "",
    "type": "contactFlow",
    "status": "published",
    "hash": {}
  },
  "Actions": [
    {
      "Parameters": {
        "TextToSpeechEngine": "Neural",
        "TextToSpeechStyle": "None",
        "TextToSpeechVoice": "Kazuha"
      },
      "Identifier": "1990a178-5825-414e-bd24-2bc3dc5dc937",
      "Type": "UpdateContactTextToSpeechVoice",
      "Transitions": {
        "NextAction": "dfd9756f-f2b0-42ee-9abd-dbd0c4d41340"
      }
    },
    {
      "Parameters": {
        "LanguageCode": "ja-JP"
      },
      "Identifier": "dfd9756f-f2b0-42ee-9abd-dbd0c4d41340",
      "Type": "UpdateContactData",
      "Transitions": {
        "NextAction": "0cebb197-9920-4fcc-8410-a2f9e9fb12ff",
        "Errors": [
          {
            "NextAction": "0cebb197-9920-4fcc-8410-a2f9e9fb12ff",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "FlowLoggingBehavior": "Enabled"
      },
      "Identifier": "17a389ed-1002-4550-9bbf-efbbe3811cf9",
      "Type": "UpdateFlowLoggingBehavior",
      "Transitions": {
        "NextAction": "1990a178-5825-414e-bd24-2bc3dc5dc937"
      }
    },
    {
      "Parameters": {
        "Text": "申し訳ございません。ただいま電話に出ることができません。メッセージを録音してよければ、はい、とお伝え下さい。",
        "LexSessionAttributes": {
          "x-amz-lex:audio:start-timeout-ms:*:*": "8000",
          "x-amz-lex:audio:end-timeout-ms:*:*": "4000",
          "x-amz-lex:audio:max-length-ms:*:*": "50000"
        },
        "LexV2Bot": {
          "AliasArn": "arn:aws:lex:ap-northeast-1:012345678901:bot-alias/OMRBM5O69M/TSTALIASID"
        }
      },
      "Identifier": "0cebb197-9920-4fcc-8410-a2f9e9fb12ff",
      "Type": "ConnectParticipantWithLexBot",
      "Transitions": {
        "NextAction": "1a41bbb2-6b65-4bf0-871b-deaf288ba7a1",
        "Conditions": [
          {
            "NextAction": "acf5bea6-926a-42f6-a9c7-2f9cf4af97ef",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "recording"
              ]
            }
          }
        ],
        "Errors": [
          {
            "NextAction": "465002a2-1f89-476b-8f58-e0e2671632c7",
            "ErrorType": "NoMatchingCondition"
          },
          {
            "NextAction": "1a41bbb2-6b65-4bf0-871b-deaf288ba7a1",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "録音されないということで承知しました。電話を切ります。"
      },
      "Identifier": "465002a2-1f89-476b-8f58-e0e2671632c7",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
        "Errors": [
          {
            "NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "エラーとなりました。"
      },
      "Identifier": "1a41bbb2-6b65-4bf0-871b-deaf288ba7a1",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
        "Errors": [
          {
            "NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {},
      "Identifier": "3c2cba41-f8a9-418f-b428-c573abb3c010",
      "Type": "DisconnectParticipant",
      "Transitions": {}
    },
    {
      "Parameters": {
        "Attributes": {
          "recording": "$.Lex.Slots.free"
        },
        "TargetContact": "Current"
      },
      "Identifier": "acf5bea6-926a-42f6-a9c7-2f9cf4af97ef",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "a1441575-8861-43f3-91a3-06ab8a938b2d",
        "Errors": [
          {
            "NextAction": "a1441575-8861-43f3-91a3-06ab8a938b2d",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "以上で録音を終了します。電話を切ります。"
      },
      "Identifier": "a1441575-8861-43f3-91a3-06ab8a938b2d",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
        "Errors": [
          {
            "NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    }
  ]
}

試してみた

架電して発話すると、以下のメールが通知されました。

cm-hirai-screenshot 2024-12-06 16.56.58

いくつかのサンプル発話を用意し、それぞれのメール通知内容を確認しました。結果は、Lambdaを使用した以下の記事と同じですので、ぜひご参照ください。

https://dev.classmethod.jp/articles/amazon-connect-lex-bedrock-summary-email/#%25E8%25A9%25A6%25E3%2581%2597%25E3%2581%25A6%25E3%2581%25BF%25E3%2581%259F

最後に

本記事では、Amazon Connect、Amazon Lex、Amazon Bedrockを組み合わせて、自動ヒアリング内容を要約し、メール通知する構築方法について解説しました。

Lambdaを利用せずローコードでできる点がよいですね。

ただし、問い合わせレコードは、確実にKDSへ配信されますが、重複される可能性はありますので、その点はご注意ください。

Amazon Connect は、少なくとも 1 回問い合わせレコードを配信します。
問い合わせレコードは、最初の配信後に新しい情報が到着するなど、複数の理由で再度配信される場合があります。
https://docs.aws.amazon.com/ja_jp/connect/latest/adminguide/ctr-data-model.html#important-things-to-know-ctr-data-model

参考

https://docs.aws.amazon.com/ja_jp/connect/latest/adminguide/ctr-data-model.html

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.