AWS Step FunctionsでAmazon Connectでの会話内容を要約し、コンタクト詳細に表示する方法
はじめに
本記事では、AWS Step Functionsを使用して、Amazon Connect Contact Lensで文字起こしした内容を日本語で要約し、コンタクト詳細画面に表示する方法を紹介します。
Contact Lensには、会話内容の文字起こしと要約機能が標準で搭載されています。
ただし、要約機能は日本語に対応していません。
そこで、Contact Lensが文字起こしデータをS3に保存した際のトリガーで、Step Functions ステートマシンからAmazon Bedrockを利用して日本語の要約を生成します。
生成した要約は、コンタクト詳細画面の属性として保存することで、以下のように表示できます。
コンタクト詳細に要約文を表示
構成は以下の通りです。
処理の流れは以下の通りです。
- Contact Lens が会話を文字起こしし、S3 に保存
- S3 の PUT イベントが CloudTrail を通じて EventBridge にイベントを送信
- EventBridge が Step Functions を起動
- Step Functions が S3 から文字起こしデータを取得
- Bedrock (Claude) で会話の要約を生成
- Connect UpdateContactAttributes API で要約をコンタクト属性として保存
- コンタクト詳細画面に要約が表示される
以前、AWS Lambdaを利用した方法を執筆しましたが、今回はコード保守の負担低減のため、Step Functionsを採用しました。
前提条件
- Connect インスタンスと S3 バケットを作成済み
- Contact Lens の会話分析を有効化
ステートマシン用IAMポリシー作成
以下のIAMポリシーを作成します。Connectインスタンスの文字起こしが保存されているS3バケット名とConnectインスタンスARNは各自変更ください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::amazon-connect-xxxxxxxxxx/Analysis/Voice/*"
]
},
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel"
],
"Resource": [
"arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0"
]
},
{
"Effect": "Allow",
"Action": [
"connect:UpdateContactAttributes"
],
"Resource": [
"arn:aws:connect:ap-northeast-1:111111111111:instance/3ff2093d-af96-43fd-b038-3c07cdd7609c/contact/*"
]
}
]
}
ステートマシン作成
ステートマシンを作成します。
cm-hirai-connect-transcript-summarizer
{
"Comment": "Amazon Connect 会話要約ワークフロー",
"StartAt": "GetTranscriptFromS3",
"QueryLanguage": "JSONata",
"States": {
"GetTranscriptFromS3": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:s3:getObject",
"Arguments": {
"Bucket": "{% $states.context.Execution.Input.detail.requestParameters.bucketName %}",
"Key": "{% $states.context.Execution.Input.detail.requestParameters.key %}"
},
"Assign": {
"transcriptData": "{% $parse($states.result.Body) %}"
},
"Next": "ExtractTranscriptText"
},
"ExtractTranscriptText": {
"Type": "Pass",
"Assign": {
"transcriptText": "{% $join($transcriptData.Transcript.(Content), ' ') %}",
"contactId": "{% $transcriptData.CustomerMetadata.ContactId %}"
},
"Next": "CheckTranscriptText"
},
"CheckTranscriptText": {
"Type": "Choice",
"Choices": [
{
"Condition": "{% $length($transcriptText) > 0 %}",
"Next": "GenerateSummary"
}
],
"Default": "NoTranscriptFound"
},
"NoTranscriptFound": {
"Type": "Fail",
"Error": "NoTranscriptFound",
"Cause": "No transcript text found in the file."
},
"GenerateSummary": {
"Type": "Task",
"Resource": "arn:aws:states:::bedrock:invokeModel",
"Arguments": {
"ModelId": "anthropic.claude-3-5-sonnet-20240620-v1:0",
"ContentType": "application/json",
"Accept": "application/json",
"Body": {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1000,
"temperature": 0.5,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "{% '以下の通話内容を要約してください。\n\n【要約のフォーマット】\n1. 問い合わせ概要:顧客が何について問い合わせたか、簡潔に記載\n2. 詳細確認:エージェントがどのような情報を確認したか\n3. 対応内容:エージェントがどのように対応したか\n4. 結果:最終的な解決策や顧客の反応\n\n【通話内容】\n' & $transcriptText & '\n\n【出力のルール】\n- 「要約: 」で始め、一段落の自然な文章で記述する\n- 重要な情報(日付、注文番号、金額など)は具体的に含める\n- 専門用語や略語は避け、誰にでも分かりやすい表現を使用する\n- 顧客の感情の変化(不安→安心など)も可能な限り含める\n- 全体で100〜200文字程度に収める\n- 「まもなく解決予定」「対応完了」など、明確な結論で締めくくる\n\n【出力例】\n要約: 顧客が先週購入したヘッドフォン(注文番号BT-2023)の不具合について問い合わせ。エージェントは購入履歴と保証内容を確認し、製品が保証期間内であることを確認。無償交換または返金の選択肢を提案し、顧客は返金を希望。エージェントは返金手続きを開始し、5営業日以内に全額が返金される予定と案内。顧客は対応の迅速さに満足し、手続き完了。' %}"
}
]
}
]
}
},
"Assign": {
"summary": "{% $states.result.Body.content[type=\"text\"].text %}"
},
"Next": "UpdateContactAttributes"
},
"UpdateContactAttributes": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:connect:updateContactAttributes",
"Arguments": {
"InstanceId": "arn:aws:connect:ap-northeast-1:111111111111:instance/3ff2093d-af96-43fd-b038-3c07cdd7609c",
"InitialContactId": "{% $contactId %}",
"Attributes": {
"Summary": "{% $summary %}"
}
},
"End": true
}
}
}
InstanceId
は、EventBridgeから渡されないため、指定が必要です。
各ステートごとの処理概要は以下の通りです
- GetTranscriptFromS3:S3から文字起こしJSONを取得
- ExtractTranscriptText:JSONから会話テキストとコンタクトIDを抽出
- CheckTranscriptText:会話テキストが存在するか確認
- GenerateSummary:Bedrockで会話要約を生成
- UpdateContactAttributes:要約をConnectIDのコンタクト属性に保存
プロンプト内容は以下の通りです。
以下の通話内容を要約してください。
【要約のフォーマット】
1. 問い合わせ概要:顧客が何について問い合わせたか、簡潔に記載
2. 詳細確認:エージェントがどのような情報を確認したか
3. 対応内容:エージェントがどのように対応したか
4. 結果:最終的な解決策や顧客の反応
【通話内容】
& $transcriptText &
【出力のルール】
- 「要約: 」で始め、一段落の自然な文章で記述する
- 重要な情報(日付、注文番号、金額など)は具体的に含める
- 専門用語や略語は避け、誰にでも分かりやすい表現を使用する
- 顧客の感情の変化(不安→安心など)も可能な限り含める
- 全体で100〜200文字程度に収める
- 「まもなく解決予定」「対応完了」など、明確な結論で締めくくる
【出力例】
要約: 顧客が先週購入したヘッドフォン(注文番号A-1)の不具合について問い合わせ。エージェントは購入履歴と保証内容を確認し、製品が保証期間内であることを確認。無償交換または返金の選択肢を提案し、顧客は返金を希望。エージェントは返金手続きを開始し、5営業日以内に全額が返金される予定と案内。顧客は対応の迅速さに満足し、手続き完了。
ステートマシン作成時に IAM ロールとポリシーが自動作成されますが、一部の IAM ポリシーは手動で作成する必要があります。
そのため、先ほど作成したポリシーを適用しましょう。
EventBridge ルール作成
EventBridge ルールを作成します。
イベントパターンは以下の通りです。
文字起こしが保存されているS3バケット名は各自で変更してください。会話内容が保存されるS3バケットのS3 URIは例えば以下のとおりです。
- s3://amazon-connect-xxxxxxxxxx/Analysis/Voice/2025/01/01/aa165854-149e-460f-94b3-facf03ae934e_analysis_2025-01-01T01_01_01Z.json
{
"source": ["aws.s3"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["s3.amazonaws.com"],
"eventName": ["PutObject"],
"requestParameters": {
"bucketName": ["amazon-connect-xxxxxxxxxx"],
"key": [{
"prefix": "Analysis/Voice/"
}, {
"suffix": ".json"
}]
}
}
}
ターゲットは先程作成したステートマシンにします。
試してみる
実際に電話での会話を行い、システムの動作を確認します。
電話での会話が終了すると、システムが自動的に会話を分析し、以下のようにコンタクト詳細画面に要約文が表示されました。
会話を2分程度した場合、S3バケットに保存されるまでに7分程度かかりました。そのため、会話終了後、要約文が表示されるまでに7分程度かかりました。
コンタクト詳細に要約文を表示
要約: 顧客が最近注文した商品の未着について問い合わせ。エージェントは注文番号(123-456789-01234)を確認し、商品が3日前に発送済みで2025年2月14日配達予定と回答。顧客が配送遅延の表示を指摘したため、エージェントは天候の影響による1〜2日の遅延可能性を説明。顧客は状況を理解し、安心した様子。配送は進行中で、まもなく到着予定。
なお、EventBridgeからステートマシンに渡されるイベント内容は、以下の通りです。一部省略しています。
{
"detail-type": "AWS API Call via CloudTrail",
"source": "aws.s3",
"account": "111111111111",
"time": "2025-03-24T00:17:16Z",
"region": "ap-northeast-1",
"resources": [],
"detail": {
"eventTime": "2025-03-24T00:17:16Z",
"eventSource": "s3.amazonaws.com",
"eventName": "PutObject",
"awsRegion": "ap-northeast-1",
"requestParameters": {
"bucketName": "amazon-connect-xxxxxxxxxx",
"key": "Analysis/Voice/2025/02/13/aa165854-149e-460f-94b3-facf03ae934e_analysis_2025-02-13T02_47_18Z.json",
},
"readOnly": false,
"resources": [
{
"type": "AWS::S3::Object",
"ARN": "arn:aws:s3:::amazon-connect-xxxxxxxxxx/Analysis/Voice/2025/02/13/aa165854-149e-460f-94b3-facf03ae934e_analysis_2025-02-13T02_47_18Z.json"
},
{
"accountId": "111111111111",
"type": "AWS::S3::Bucket",
"ARN": "arn:aws:s3:::amazon-connect-xxxxxxxxxx"
}
],
"eventType": "AwsApiCall"
}
}