Amazon Connectのフローでの離脱箇所と放棄呼をKinesis Data Streamsを用いて取得し、DynamoDBに保存してみた

2024.04.22

はじめに

Amazon Connectのフローで離脱箇所や放棄呼をAmazon Kinesis Data Streams(以降、KDS)を用いて取得し、DynamoDBに保存する方法をまとめました。

利用用途は以下が挙げられます。

  • IVRでの途中離脱箇所を知りたい
  • オペレーターにつながる前に切られる放棄呼の有無を知りたい

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

Connectでは、KDSに問い合わせレコードを出力することができます。通常は問い合わせレコードは、どのフローで切断されたか情報はありませんが、フロー内で工夫すると取得ができます。工夫内容は後述します。

以下の構成図をもとに処理の流れを説明します。

  1. Connectのフロー内で、図の青丸箇所のいずれかで切断したとします
  2. 切断直後、問い合わせレコードがKDSにストリーミングされます
  3. KDSに保存されたことをトリガーにLambdaが起動し、問い合わせレコードから切断情報などを抽出し、DynamoDBに保存します

構築

以下の構築を解説します。

  • DynamoDB
  • KDS
  • Lambda
  • Connectフロー

DynamoDB

以下の設定で作成します

  • テーブル名:call-records
  • パーティションキー:contact_id

今回の切断箇所だけではなく、他の情報もDynamoDBに保存することを想定し、パーティションキーはContact IDにします。

Lambdaによって、DynamoDBには以下の属性を保存する想定です。

  • コンタクトID
  • 放棄呼の有無
  • 発信者の通話時間
  • 発信日時
  • フロー離脱箇所
  • 発信元電話番号

KDS

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

Lambda

ランタイムPython3.12を使用して作成します。

  • IAMポリシーは、以下を追加します。
    • AmazonKinesisReadOnlyAccess
    • AmazonDynamoDBFullAccess
import json
import base64
import boto3
from datetime import datetime, timezone, timedelta

JST = timezone(timedelta(hours=+9), 'JST')

def decode_kinesis_record(kinesis_record):
    data_string = kinesis_record['data']
    decoded_data = base64.b64decode(data_string)
    decoded_string = decoded_data.decode('utf-8')
    print('decode_kinesis_record:' + decoded_string)
    return json.loads(decoded_string)

def extract_call_details(data_json):
    return {
        'phone_number': data_json['CustomerEndpoint']['Address'],
        'call_route': data_json['Attributes']['call_route'],
        'start_time_utc': data_json['InitiationTimestamp'],
        'end_time_utc': data_json['DisconnectTimestamp'],
        'contact_id': data_json['ContactId'],
        'call_completed': data_json['Attributes']['call_completed']
    }

def calculate_call_duration(start_time_utc, end_time_utc):
    start_time_utc = datetime.strptime(start_time_utc, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
    end_time_utc = datetime.strptime(end_time_utc, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
    return end_time_utc - start_time_utc

def convert_to_jst(start_time_utc):
    start_time_utc = datetime.strptime(start_time_utc, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc)
    start_time_jst = start_time_utc.astimezone(JST)
    return start_time_jst.strftime("%Y-%m-%dT%H:%M:%S%z")

def save_call_record(call_details, call_duration, start_time_jst):
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('call-records')

    response = table.update_item(
        Key={
            'contact_id': call_details['contact_id']
        },
        UpdateExpression="SET phone_number = :phone, call_route = :route, start_time = :start, call_duration = :duration, call_completed = :completed",
        ExpressionAttributeValues={
            ':phone': call_details['phone_number'],
            ':route': call_details['call_route'],
            ':start': start_time_jst,
            ':duration': str(call_duration),
            ':completed': call_details['call_completed']
        },
        ReturnValues="UPDATED_NEW"
    )
    print('response:' + json.dumps(response, ensure_ascii=False))

def lambda_handler(event, context):
    print('event:' + json.dumps(event, ensure_ascii=False))
    kinesis_record = event['Records'][0]['kinesis']
    data_json = decode_kinesis_record(kinesis_record)
    call_details = extract_call_details(data_json)
    call_duration = calculate_call_duration(call_details['start_time_utc'], call_details['end_time_utc'])
    start_time_jst = convert_to_jst(call_details['start_time_utc'])
    save_call_record(call_details, call_duration, start_time_jst)

    return {
        'statusCode': 200,
    }

DynamoDBに保存する属性は以下です

  • コンタクトID:contact_id
  • 放棄呼の有無:call_completed
  • 発信者の通話時間:call_duration
  • 発信日時:start_time
  • フロー離脱箇所:call_route
  • 発信元電話番号:phone_number

問い合わせレコードがKDSにストリーミングされると、Lambdaをトリガーさせるため、トリガーを以下の設定で行います

設定値については、以下が参考になります

Lambdaのevent

例として、KDSから受け取るLambdaのevent値を記載します。

{
    "Records": [
        {
            "kinesis": {
                "kinesisSchemaVersion": "1.0",
                "partitionKey": "9a5b35b2-3210-4873-8cd3-d83dd170204b",
                "sequenceNumber": "49651139538302143095151021919781625570957189547772870658",
                "data": "xxxxx",
                "approximateArrivalTimestamp": 1713397730.084
            },
            "eventSource": "aws:kinesis",
            "eventVersion": "1.0",
            "eventID": "shardId-000000000000:xxxxxx",
            "eventName": "aws:kinesis:record",
            "invokeIdentityArn": "arn:aws:iam::xxxxxxxxxxxx:role/service-role/cm-hirai-call-route-role-xxx",
            "awsRegion": "ap-northeast-1",
            "eventSourceARN": "arn:aws:kinesis:ap-northeast-1:xxxxxxxxxxxx:stream/cm-hirai-call-route"
        }
    ]
}

上記のevent['Records'][0]['kinesis']['data']は、base64でエンコードされているため、デコードすると、以下の値が取得できます。

{
    "AWSAccountId": "xxxxxxxxxxx",
    "AWSContactTraceRecordFormatVersion": "2017-03-10",
    "Agent": null,
    "AgentConnectionAttempts": 0,
    "AnsweringMachineDetectionStatus": null,
    "Attributes": {
        "call_completed": "false",
        "call_route": "start"
    },
    "Campaign": {
        "CampaignId": null
    },
    "Channel": "VOICE",
    "ConnectedToSystemTimestamp": "2024-04-17T23:47:52Z",
    "ContactDetails": {},
    "ContactId": "9c905123-6240-4484-b9d1-4e3e40660d8e",
    "CustomerEndpoint": {
        "Address": "+81xxxxxxxxx",
        "Type": "TELEPHONE_NUMBER"
    },
    "CustomerVoiceActivity": null,
    "DisconnectReason": "CUSTOMER_DISCONNECT",
    "DisconnectTimestamp": "2024-04-17T23:47:56Z",
    "InitialContactId": null,
    "InitiationMethod": "INBOUND",
    "InitiationTimestamp": "2024-04-17T23:47:52Z",
    "InstanceARN": "arn:aws:connect:ap-northeast-1:xxxxxxxxxxx:instance/3ff2093d-af96-43fd-b038-3c07cdd7609c",
    "LastUpdateTimestamp": "2024-04-17T23:49:03Z",
    "MediaStreams": [
        {
            "Type": "AUDIO"
        }
    ],
    "NextContactId": null,
    "PreviousContactId": null,
    "Queue": null,
    "Recording": null,
    "Recordings": null,
    "References": [],
    "ScheduledTimestamp": null,
    "SegmentAttributes": {
        "connect:Subtype": {
            "ValueString": "connect:Telephony"
        }
    },
    "SystemEndpoint": {
        "Address": "+81xxxxxxxxx",
        "Type": "TELEPHONE_NUMBER"
    },
    "Tags": {
        "aws:connect:instanceId": "3ff2093d-af96-43fd-b038-3c07cdd7609c",
        "aws:connect:systemEndpoint": "+81xxxxxxxxx"
    },
    "TaskTemplateInfo": null,
    "TransferCompletedTimestamp": null,
    "TransferredToEndpoint": null,
    "VoiceIdResult": null
}

上記からコンタクトIDやcall_routecall_completedなどを取得し、DynamoDBに保存します。

Connect

Amazon Connect から問い合わせレコードをKDSにストリーミングする設定を行います。

コンソールから[データストリーミング]に遷移し、[Kinesis ストリーム]から[問い合わせ追跡レコード]で作成したKDSを設定するだけです。

Connect フロー

今回利用するConnect フローは以下の通りです。

フローは以下の流れです。

  1. 発信する
  2. アナウンスが流れるので、ユーザーがプッシュ式で「1」または「2」を入力します。
  3. 再度、アナウンスが流れるので、ユーザーがプッシュ式で「1」または「2」を入力します。
  4. 入力された内容がアナウンスされる
  5. 切断される
フロー (クリックすると展開します)
{
  "Version": "2019-10-30",
  "StartAction": "83796d04-0550-4e7e-aa99-fce8b18c3f98",
  "Metadata": {
    "entryPointPosition": {
      "x": 241.6,
      "y": -47.2
    },
    "ActionMetadata": {
      "791eef75-931f-4116-8898-300cb47711db": {
        "position": {
          "x": 875.2,
          "y": 663.2
        }
      },
      "d6f52dbe-f77b-49ee-913e-11af21cf4f3b": {
        "position": {
          "x": 867.2,
          "y": 381.6
        },
        "dynamicParams": []
      },
      "0534b033-6e91-4b33-97f5-708c19b7aeec": {
        "position": {
          "x": 1324,
          "y": 138.4
        }
      },
      "d8f00f42-7a09-4ff9-b8d8-ef6d03ce172a": {
        "position": {
          "x": 2035.2,
          "y": 364
        }
      },
      "83796d04-0550-4e7e-aa99-fce8b18c3f98": {
        "position": {
          "x": 344,
          "y": -62.4
        }
      },
      "8ebcf8ff-9525-4b7e-b07e-b6699b340e2b": {
        "position": {
          "x": 859.2,
          "y": 200
        },
        "dynamicParams": []
      },
      "26cd035e-247c-4388-bff9-ba756df8235c": {
        "position": {
          "x": 1323.2,
          "y": 744.8
        }
      },
      "400c5522-9cb0-4054-93c1-1c813d715dee": {
        "position": {
          "x": 1084.8,
          "y": -184
        },
        "conditionMetadata": [
          {
            "id": "f52c7bdd-2d72-468e-926f-e37f0fc37aab",
            "value": "1"
          },
          {
            "id": "d6d3b356-0118-4876-97b7-4bd688c7f21b",
            "value": "2"
          }
        ]
      },
      "a1972ceb-07b7-483e-b03f-2bd3dd6063fd": {
        "position": {
          "x": 1090.4,
          "y": 289.6
        },
        "conditionMetadata": [
          {
            "id": "79e1cb23-1aa4-4841-8b71-b4723557d5f3",
            "value": "1"
          },
          {
            "id": "d618d60a-c533-46dd-a605-c55459e451e1",
            "value": "2"
          }
        ]
      },
      "0bb60df6-c203-413e-930c-0fe3eb2677ea": {
        "position": {
          "x": 349.6,
          "y": 110.4
        },
        "children": [
          "c276029c-6058-4b01-91e5-159cf7c622f4"
        ],
        "overrideConsoleVoice": true,
        "fragments": {
          "SetContactData": "c276029c-6058-4b01-91e5-159cf7c622f4"
        },
        "overrideLanguageAttribute": true
      },
      "c276029c-6058-4b01-91e5-159cf7c622f4": {
        "position": {
          "x": 349.6,
          "y": 110.4
        },
        "dynamicParams": []
      },
      "1046bd11-9a7f-4746-a1eb-2ab571ceda91": {
        "position": {
          "x": 591.2,
          "y": 301.6
        },
        "conditionMetadata": [
          {
            "id": "6b52ff60-18f7-4b45-81f3-02d69bb14d7f",
            "value": "1"
          },
          {
            "id": "d3298e8f-e0bd-4aea-bad9-2ea2bc28c4cd",
            "value": "2"
          }
        ]
      },
      "02b1970e-5964-40f3-8f02-a8a572528a8a": {
        "position": {
          "x": 349.6,
          "y": 289.6
        },
        "dynamicParams": []
      },
      "beb20743-c289-44c4-b15e-4296cd1524dc": {
        "position": {
          "x": 1323.2,
          "y": -48
        },
        "dynamicParams": []
      },
      "f8b767e9-6ce8-4d62-abe1-99ddb29a047b": {
        "position": {
          "x": 1823.2,
          "y": 292
        }
      },
      "2437cce9-a1c7-4b54-bd5e-48ee06780fcb": {
        "position": {
          "x": 1326.4,
          "y": -231.2
        },
        "dynamicParams": []
      },
      "9a65bea6-f010-4201-9b80-ba5fd949a730": {
        "position": {
          "x": 1322.4,
          "y": 343.2
        },
        "dynamicParams": []
      },
      "6ad4c36f-cde9-4895-9e2c-e3f0058300b0": {
        "position": {
          "x": 1324.8,
          "y": 553.6
        },
        "dynamicParams": []
      },
      "8cd3631c-4c50-486f-b1d0-0d19be958468": {
        "position": {
          "x": 1600.8,
          "y": 295.2
        },
        "dynamicParams": []
      }
    },
    "Annotations": [],
    "name": "cm-hirai-call-route",
    "description": "",
    "type": "contactFlow",
    "status": "published",
    "hash": {}
  },
  "Actions": [
    {
      "Parameters": {
        "Text": "有効な番号以外が入力されました。"
      },
      "Identifier": "791eef75-931f-4116-8898-300cb47711db",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "1046bd11-9a7f-4746-a1eb-2ab571ceda91",
        "Errors": [
          {
            "NextAction": "1046bd11-9a7f-4746-a1eb-2ab571ceda91",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Attributes": {
          "call_route": "2"
        },
        "TargetContact": "Current"
      },
      "Identifier": "d6f52dbe-f77b-49ee-913e-11af21cf4f3b",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "a1972ceb-07b7-483e-b03f-2bd3dd6063fd",
        "Errors": [
          {
            "NextAction": "a1972ceb-07b7-483e-b03f-2bd3dd6063fd",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "有効な番号以外が入力されました。"
      },
      "Identifier": "0534b033-6e91-4b33-97f5-708c19b7aeec",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "400c5522-9cb0-4054-93c1-1c813d715dee",
        "Errors": [
          {
            "NextAction": "400c5522-9cb0-4054-93c1-1c813d715dee",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {},
      "Identifier": "d8f00f42-7a09-4ff9-b8d8-ef6d03ce172a",
      "Type": "DisconnectParticipant",
      "Transitions": {}
    },
    {
      "Parameters": {
        "FlowLoggingBehavior": "Enabled"
      },
      "Identifier": "83796d04-0550-4e7e-aa99-fce8b18c3f98",
      "Type": "UpdateFlowLoggingBehavior",
      "Transitions": {
        "NextAction": "0bb60df6-c203-413e-930c-0fe3eb2677ea"
      }
    },
    {
      "Parameters": {
        "Attributes": {
          "call_route": "1"
        },
        "TargetContact": "Current"
      },
      "Identifier": "8ebcf8ff-9525-4b7e-b07e-b6699b340e2b",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "400c5522-9cb0-4054-93c1-1c813d715dee",
        "Errors": [
          {
            "NextAction": "400c5522-9cb0-4054-93c1-1c813d715dee",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "有効な番号以外が入力されました。"
      },
      "Identifier": "26cd035e-247c-4388-bff9-ba756df8235c",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "a1972ceb-07b7-483e-b03f-2bd3dd6063fd",
        "Errors": [
          {
            "NextAction": "a1972ceb-07b7-483e-b03f-2bd3dd6063fd",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "StoreInput": "False",
        "InputTimeLimitSeconds": "10",
        "Text": "1、もしくは、2、を押して下さい"
      },
      "Identifier": "400c5522-9cb0-4054-93c1-1c813d715dee",
      "Type": "GetParticipantInput",
      "Transitions": {
        "NextAction": "0534b033-6e91-4b33-97f5-708c19b7aeec",
        "Conditions": [
          {
            "NextAction": "2437cce9-a1c7-4b54-bd5e-48ee06780fcb",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "1"
              ]
            }
          },
          {
            "NextAction": "beb20743-c289-44c4-b15e-4296cd1524dc",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "2"
              ]
            }
          }
        ],
        "Errors": [
          {
            "NextAction": "0534b033-6e91-4b33-97f5-708c19b7aeec",
            "ErrorType": "InputTimeLimitExceeded"
          },
          {
            "NextAction": "0534b033-6e91-4b33-97f5-708c19b7aeec",
            "ErrorType": "NoMatchingCondition"
          },
          {
            "NextAction": "0534b033-6e91-4b33-97f5-708c19b7aeec",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "StoreInput": "False",
        "InputTimeLimitSeconds": "10",
        "Text": "1、もしくは、2、を押して下さい"
      },
      "Identifier": "a1972ceb-07b7-483e-b03f-2bd3dd6063fd",
      "Type": "GetParticipantInput",
      "Transitions": {
        "NextAction": "26cd035e-247c-4388-bff9-ba756df8235c",
        "Conditions": [
          {
            "NextAction": "9a65bea6-f010-4201-9b80-ba5fd949a730",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "1"
              ]
            }
          },
          {
            "NextAction": "6ad4c36f-cde9-4895-9e2c-e3f0058300b0",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "2"
              ]
            }
          }
        ],
        "Errors": [
          {
            "NextAction": "26cd035e-247c-4388-bff9-ba756df8235c",
            "ErrorType": "InputTimeLimitExceeded"
          },
          {
            "NextAction": "26cd035e-247c-4388-bff9-ba756df8235c",
            "ErrorType": "NoMatchingCondition"
          },
          {
            "NextAction": "26cd035e-247c-4388-bff9-ba756df8235c",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "TextToSpeechEngine": "Neural",
        "TextToSpeechStyle": "None",
        "TextToSpeechVoice": "Kazuha"
      },
      "Identifier": "0bb60df6-c203-413e-930c-0fe3eb2677ea",
      "Type": "UpdateContactTextToSpeechVoice",
      "Transitions": {
        "NextAction": "c276029c-6058-4b01-91e5-159cf7c622f4"
      }
    },
    {
      "Parameters": {
        "LanguageCode": "ja-JP"
      },
      "Identifier": "c276029c-6058-4b01-91e5-159cf7c622f4",
      "Type": "UpdateContactData",
      "Transitions": {
        "NextAction": "02b1970e-5964-40f3-8f02-a8a572528a8a",
        "Errors": [
          {
            "NextAction": "02b1970e-5964-40f3-8f02-a8a572528a8a",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "StoreInput": "False",
        "InputTimeLimitSeconds": "10",
        "Text": "1、もしくは、2、を押して下さい"
      },
      "Identifier": "1046bd11-9a7f-4746-a1eb-2ab571ceda91",
      "Type": "GetParticipantInput",
      "Transitions": {
        "NextAction": "791eef75-931f-4116-8898-300cb47711db",
        "Conditions": [
          {
            "NextAction": "8ebcf8ff-9525-4b7e-b07e-b6699b340e2b",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "1"
              ]
            }
          },
          {
            "NextAction": "d6f52dbe-f77b-49ee-913e-11af21cf4f3b",
            "Condition": {
              "Operator": "Equals",
              "Operands": [
                "2"
              ]
            }
          }
        ],
        "Errors": [
          {
            "NextAction": "791eef75-931f-4116-8898-300cb47711db",
            "ErrorType": "InputTimeLimitExceeded"
          },
          {
            "NextAction": "791eef75-931f-4116-8898-300cb47711db",
            "ErrorType": "NoMatchingCondition"
          },
          {
            "NextAction": "791eef75-931f-4116-8898-300cb47711db",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Attributes": {
          "call_route": "start",
          "call_completed": "false"
        },
        "TargetContact": "Current"
      },
      "Identifier": "02b1970e-5964-40f3-8f02-a8a572528a8a",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "1046bd11-9a7f-4746-a1eb-2ab571ceda91",
        "Errors": [
          {
            "NextAction": "1046bd11-9a7f-4746-a1eb-2ab571ceda91",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Attributes": {
          "call_route": "1-2"
        },
        "TargetContact": "Current"
      },
      "Identifier": "beb20743-c289-44c4-b15e-4296cd1524dc",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "8cd3631c-4c50-486f-b1d0-0d19be958468",
        "Errors": [
          {
            "NextAction": "8cd3631c-4c50-486f-b1d0-0d19be958468",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Text": "$.Attributes.call_route、が入力されました。終了します。"
      },
      "Identifier": "f8b767e9-6ce8-4d62-abe1-99ddb29a047b",
      "Type": "MessageParticipant",
      "Transitions": {
        "NextAction": "d8f00f42-7a09-4ff9-b8d8-ef6d03ce172a",
        "Errors": [
          {
            "NextAction": "d8f00f42-7a09-4ff9-b8d8-ef6d03ce172a",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Attributes": {
          "call_route": "1-1"
        },
        "TargetContact": "Current"
      },
      "Identifier": "2437cce9-a1c7-4b54-bd5e-48ee06780fcb",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "8cd3631c-4c50-486f-b1d0-0d19be958468",
        "Errors": [
          {
            "NextAction": "8cd3631c-4c50-486f-b1d0-0d19be958468",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Attributes": {
          "call_route": "2-1"
        },
        "TargetContact": "Current"
      },
      "Identifier": "9a65bea6-f010-4201-9b80-ba5fd949a730",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "8cd3631c-4c50-486f-b1d0-0d19be958468",
        "Errors": [
          {
            "NextAction": "8cd3631c-4c50-486f-b1d0-0d19be958468",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Attributes": {
          "call_route": "2-2"
        },
        "TargetContact": "Current"
      },
      "Identifier": "6ad4c36f-cde9-4895-9e2c-e3f0058300b0",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "8cd3631c-4c50-486f-b1d0-0d19be958468",
        "Errors": [
          {
            "NextAction": "8cd3631c-4c50-486f-b1d0-0d19be958468",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "Attributes": {
          "call_completed": "true"
        },
        "TargetContact": "Current"
      },
      "Identifier": "8cd3631c-4c50-486f-b1d0-0d19be958468",
      "Type": "UpdateContactAttributes",
      "Transitions": {
        "NextAction": "f8b767e9-6ce8-4d62-abe1-99ddb29a047b",
        "Errors": [
          {
            "NextAction": "f8b767e9-6ce8-4d62-abe1-99ddb29a047b",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    }
  ]
}

フローの離脱箇所

コンタクト属性は問い合わせレコードに保存されるため、分岐先情報をコンタクト属性として設定すると、フロー離脱箇所をDynamoDBに保存することができます。

[コンタクト属性の設定]ブロックでフローの分岐情報として、キー:call_route、値(分岐先)を付与します。

今回は以下のように、キー:call_routeの値は、数字にしていますが、問い合わせの種別(例えば、カードの紛失や口座開設希望など)を設定するとよいでしょう。

例えば、1を入力後、切断すると、切断箇所は1になります。1を押して次のフローで2を押して切断すると、1-2が切断箇所になります。

放棄呼

放棄呼の有無は、発信直後とフローの最後に[コンタクト属性の設定]ブロックとして、キー:call_completed、値(true or false)を付与することで確認できます。

オペレーターにつながるまでに電話を切られることを、放棄呼と指しますが、今回は最後まで進めたかどうかでtrue or falseと判定します。フローの最後にオペレーターに繋がるようにしてもよいです。

例えば、発信し、最初の音声が流れた瞬間に切断すると、放棄呼がありとなりコンタクト属性はfalseとなります。最後まで進めると、放棄呼なしであり、コンタクト属性はtrueになります。

試してみる

それでは電話をかけて試してみます。

電話を終了してからDynamoDBに反映されるまでに、1分ほどかかります。

以下の結果となりました。

フローの離脱箇所はcall_routeで確認できます。例えば、call_route1の場合は、最初の選択肢で1を選び、次の選択で切断されたということです。call_route2-1の場合は、最初の選択で2を選び、次の選択で1を選んだ後に切断された場合です。

最後まで進めた場合(call_route1-21-1)、call_completedtrueとなり、放棄呼ではないということです。一方、発信直後に切断された場合は、call_completedfalseで放棄呼があったことがわかります。

最後

Amazon Connectのフローで離脱箇所や放棄呼をKDSで取得できるようにし、DynamoDBに保存する方法をまとめました。

Amazon Connect フローのコンタクト属性に分岐先情報を設定することで、比較的容易に離脱箇所や放棄呼の情報を取得できます。参考になれば幸いです。