AWS Step FunctionsでAmazon Connectの通話会話内容を要約し、Backlogのコメントに自動追加してみた

AWS Step FunctionsでAmazon Connectの通話会話内容を要約し、Backlogのコメントに自動追加してみた

Clock Icon2025.05.08

はじめに

これまでに、Amazon Connect で受けた問い合わせを Backlog の課題として自動起票する仕組みに関して 3 回にわたり紹介してきました。

  1. Amazon Connectの通話記録をStep Functionsで処理し、Backlogに自動起票させてみた
  2. Amazon Connectの通話記録をKinesis Data Streamsに配信時、Step Functionsでデータの重複対策を実装する
  3. AWS Step FunctionsでConnectの通話記録をBacklogに自動起票する際、課題の担当者を対応エージェントに設定する

本記事では、通話の文字起こしをもとに会話内容を要約し、その結果を Backlog のコメントとして自動追記する方法を解説します。

Amazon Connect の Contact Lens には文字起こしと要約機能がありますが、残念ながら現時点では日本語の要約に対応していません。

https://docs.aws.amazon.com/ja_jp/connect/latest/adminguide/supported-languages.html#supported-languages-contact-lens

そこで今回は、Contact Lens が S3バケット に保存した文字起こしファイルをトリガーに Step Functions を起動し、Amazon Bedrock(Claude)で日本語要約を生成します。生成した要約を、前回までの仕組みで自動起票された Backlog の課題へコメントとして追加します。

以降では、前回までに構築したリソース(図中の緑枠)を前提に、青枠で示した EventBridge と Step Functions の実装を中心に説明します。

cm-hirai-screenshot 2025-04-25 9.01.41

システム全体像は下図のとおりです(緑枠が既存、青枠が今回構築)。

cm-hirai-screenshot 2025-04-25 15.39.53

処理フローは次の 7 ステップです。

  1. Contact Lens が通話を文字起こしし、結果を S3バケット に保存
  2. S3 の PutObject イベントが CloudTrail を経由して EventBridge に送信
  3. EventBridge が Step Functions を起動
  4. Step Functions が S3 から文字起こしデータとコンタクト ID を取得
  5. Amazon Bedrock(Claude)で要約を生成
  6. DynamoDB でコンタクト ID から Backlog の課題キーを取得
  7. Backlog の課題更新 API を呼び出し、要約をコメントとして追加

https://developer.nulab.com/ja/docs/backlog/api/2/update-issue/

ステートマシン用IAMポリシー作成

Step Functions が文字起こしファイル(JSON)を S3 から取得できるように、以下の IAM ポリシーを用意します。
バケット名「amazon-connect-xxxxxxxxxx」は、ご自身の Amazon Connect インスタンスで設定しているバケット名に置き換えてください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::amazon-connect-xxxxxxxxxx/Analysis/Voice/*"
            ]
        }
    ]
}

ステートマシン作成

ステートマシンを作成します。
cm-hirai-connect-backlog-transcript

cm-hirai-screenshot 2025-04-25 10.58.20

cm-hirai-screenshot 2025-04-25 10.58.44

{
  "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($map($transcriptData.Transcript,function($t){'[' & ($t.ParticipantId = 'AGENT' ? 'AGENT' : 'CUSTOMER') & ']' & $t.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": "GetBacklogIssueKey"
    },
    "GetBacklogIssueKey": {
      "Type": "Task",
      "Resource": "arn:aws:states:::dynamodb:getItem",
      "Arguments": {
        "TableName": "cm-hirai-connect-backlog-tracker",
        "Key": {
          "contact_id": {
            "S": "{% $contactId %}"
          }
        }
      },
      "Assign": {
        "backlogIssueKey": "{% $states.result.Item.backlog_issue_id.S %}"
      },
      "Next": "UpdateBacklogIssueComment"
    },
    "UpdateBacklogIssueComment": {
      "Type": "Task",
      "Resource": "arn:aws:states:::http:invoke",
      "Arguments": {
        "ApiEndpoint": "{% 'https://<xxx.backlog.jp>/api/v2/issues/' & $backlogIssueKey %}",
        "Method": "PATCH",
        "InvocationConfig": {
          "ConnectionArn": "<ConnectionArn>"
        },
        "RequestBody": {
          "comment": "{% $summary %}"
        }
      },
      "Retry": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "BackoffRate": 2,
          "IntervalSeconds": 1,
          "MaxAttempts": 3,
          "JitterStrategy": "FULL"
        }
      ],
      "End": true
    }
  }
}

以下のプレースホルダーを実際の値に置き換えてください

  • <xxx.backlog.jp>:Backlogドメイン
  • <ConnectionArn>:作成したEventBridge ConnectionのARN

各ステートごとの処理概要は以下の通りです

  1. GetTranscriptFromS3:S3から文字起こしJSONを取得
  2. ExtractTranscriptText:JSONから会話テキストとContact IDを抽出
  3. CheckTranscriptText:会話テキストが存在するか確認
  4. GenerateSummary:Bedrockで会話要約を生成
  5. GetBacklogIssueKey: DynamoDB から Backlog 課題キーを取得
  6. UpdateBacklogIssueComment:Backlog API でコメントを追加

プロンプト内容は以下の通りです。

以下の通話内容を要約してください。

【要約のフォーマット】
1. 問い合わせ概要:顧客が何について問い合わせたか、簡潔に記載
2. 詳細確認:エージェントがどのような情報を確認したか
3. 対応内容:エージェントがどのように対応したか
4. 結果:最終的な解決策や顧客の反応

【通話内容】
 & $transcriptText & 

【出力のルール】
- 「要約: 」で始め、一段落の自然な文章で記述する
- 重要な情報(日付、注文番号、金額など)は具体的に含める
- 専門用語や略語は避け、誰にでも分かりやすい表現を使用する
- 顧客の感情の変化(不安→安心など)も可能な限り含める
- 全体で100〜200文字程度に収める
- 「まもなく解決予定」「対応完了」など、明確な結論で締めくくる

【出力例】
要約: 顧客が先週購入したヘッドフォン(注文番号A-1)の不具合について問い合わせ。エージェントは購入履歴と保証内容を確認し、製品が保証期間内であることを確認。無償交換または返金の選択肢を提案し、顧客は返金を希望。エージェントは返金手続きを開始し、5営業日以内に全額が返金される予定と案内。顧客は対応の迅速さに満足し、手続き完了。

ステートマシンを作成すると自動で実行ロールが生成されますが、S3 から文字起こしファイルを取得する権限は手動で追加する必要があります。
前セクションで作成したポリシーをロールにアタッチしてください。

cm-hirai-screenshot 2025-04-24 16.22.28

EventBridge ルール作成

Contact Lens が S3 に文字起こしファイル(JSON)を保存したことをトリガーに、Step Functions を起動する 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"
      }]
    }
  }
}

ターゲットは先程作成したステートマシンにします。

試してみる

実際に電話をかけ、システムが期待どおり動作することを確認できました。

  1. 通話が終了すると Contact Lens が音声を解析し、文字起こし JSON を S3 に保存
  2. EventBridge ルールが PutObject イベントを検知し、Step Functions を起動
  3. 約 7 分後(※ 2 分程度の通話の場合)に Backlog の課題へ要約コメントが自動追加

下図のように、コンタクト詳細画面で生成された要約を確認できます。

cm-hirai-screenshot 2025-04-25 9.01.41

要約: 顧客が最近注文した商品の未着について問い合わせ。エージェントは注文番号(123-4567890-1234-5)を確認し、3日前に発送済みで2025年2月14日配達予定と回答。顧客が配送遅延の表示を指摘したため、エージェントは天候の影響による1-2日の遅延可能性を説明。顧客は状況を理解し、安心した様子。配送は進行中で、まもなく到着予定。対応完了。

なお、EventBridgeからステートマシンに渡されるイベント内容は、以下の通りです。一部省略しています。

{
  "detail-type": "AWS API Call via CloudTrail",
  "source": "aws.s3",
  "account": "111111111111",
  "time": "2025-04-24T00:17:16Z",
  "region": "ap-northeast-1",
  "resources": [],
  "detail": {
    "eventTime": "2025-04-24T00:17:16Z",
    "eventSource": "s3.amazonaws.com",
    "eventName": "PutObject",
    "awsRegion": "ap-northeast-1",
    "requestParameters": {
      "bucketName": "amazon-connect-xxxxxxxxxx",
      "key": "Analysis/Voice/2025/04/24/aa165854-149e-460f-94b3-facf03ae934e_analysis_2025-04-24T02_47_18Z.json",
    },
    "readOnly": false,
    "resources": [
      {
        "type": "AWS::S3::Object",
        "ARN": "arn:aws:s3:::amazon-connect-xxxxxxxxxx/Analysis/Voice/2025/04/24/aa165854-149e-460f-94b3-facf03ae934e_analysis_2025-04-24T02_47_18Z.json"
      },
      {
        "accountId": "111111111111",
        "type": "AWS::S3::Bucket",
        "ARN": "arn:aws:s3:::amazon-connect-xxxxxxxxxx"
      }
    ],
    "eventType": "AwsApiCall"
  }
}

参考

https://developer.nulab.com/ja/docs/backlog/api/2/update-issue/

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.