

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# Amazon Chime SDK의 알림 이해
<a name="va-notification-targets"></a>

음성 분석은 화자 검색 또는 음성 톤 분석 작업이 시작될 때, 실행 중일 때, 완료될 때 자동으로 이벤트를 대상으로 전송합니다. 알림 대상을 사용하여 이러한 이벤트를 수신합니다. 워크플로 또는 애플리케이션에 고가용성이 필요한 경우 여러 알림 대상을 사용하는 것이 좋습니다.

또한 알림 대상에 액세스하는 데 필요한 정책이 포함된 IAM 역할을 사용해야 합니다. 자세한 내용은 [Amazon Chime SDK의 통화 분석 리소스 액세스 역할 사용](call-analytics-resource-access-role.md) 단원을 참조하십시오.

**참고**  
Amazon SQS 및 Amazon SNS의 경우 선입선출 대기열이 지원되지 않습니다. 따라서 메시지가 잘못된 순서로 도착할 수 있습니다. 타임스탬프를 확인하여 필요에 따라 메시지를 정렬하고 Amazon DynamoDB와 같은 데이터 스토어에 메시지를 보관하는 것이 좋습니다. [Amazon Chime SDK의 작업 결과 폴링](va-task-result-poll.md)에 설명된 Get API를 사용하여 최신 결과를 받을 수도 있습니다.

다음 표에는 이벤트와 해당 세부 정보 유형이 나와 있습니다.


| 알림 이벤트 | 세부 정보 유형 | 
| --- | --- | 
| 음성 분석 메타데이터 | `VoiceAnalyticsStatus` | 
| 화자 검색 | `SpeakerSearchStatus` | 
| 음성 톤 분석 | `VoiceToneAnalysisStatus` | 

# Amazon Chime SDK의 알림 대상에 대한 IAM 정책 이해
<a name="va-iam-target-policies"></a>

Amazon SQS, Amazon SNS, AWS Lambda 또는 Amazon KDS 알림 대상에 대한 액세스를 허용하는 호출 분석 구성의 IAM 역할 정책을 사용해야 합니다. 자세한 내용은 이 안내서의 [Amazon Chime SDK의 통화 분석 리소스 액세스 역할 사용](call-analytics-resource-access-role.md) 섹션을 참조하세요.

## 화자 검색 이벤트
<a name="va-speaker-search-events"></a>

화자 검색 이벤트에는 `SpeakerSearchStatus` 세부 유형이 있습니다.

Amazon Chime SDK 음성 커넥터는 다음과 같은 화자 검색 이벤트를 전송합니다.
+ 식별 일치
+ 음성 임베딩 생성

이벤트에는 다음과 같은 상태가 있을 수 있습니다:
+ `IdentificationSuccessful` – 주어진 음성 프로필 도메인에서 높은 신뢰도 점수로 일치하는 음성 프로필 ID를 하나 이상 식별하는 데 성공했습니다.
+ `IdentificationFailure` – 식별을 수행하지 못했습니다. 원인: 발신자가 10초 이상 말을 하지 않았으며, 음질이 좋지 않습니다.
+ `IdentificationNoMatchesFound` – 해당 음성 프로필 도메인에서 신뢰도가 높은 일치 항목을 찾을 수 없습니다. 전화를 건 사람이 처음이거나 목소리가 바뀌었을 수 있습니다.
+ `VoiceprintGenerationSuccessful` – 시스템이 20초 분량의 무음이 아닌 오디오를 사용하여 음성 임베딩을 생성했습니다.
+ `VoiceprintGenerationFailure` – 시스템에서 음성 임베딩을 생성하지 못했습니다. 원인: 발신자가 20초 이상 말을 하지 않았으며, 음질이 좋지 않습니다.

### 식별 일치
<a name="va-id-matches"></a>

특정 `transactionId`로 [https://docs.aws.amazon.com/chime-sdk/latest/APIReference/API_voice-chime_StartSpeakerSearchTask](https://docs.aws.amazon.com/chime-sdk/latest/APIReference/API_voice-chime_StartSpeakerSearchTask) API를 직접 호출한 후 음성 커넥터 서비스는 10초 동안 무음이 아닌 발화가 지속되면 식별 일치 알림을 반환합니다. 이 서비스는 음성 프로필 ID와 [0, 1] 범위의 신뢰도 점수와 함께 상위 10개의 매칭을 반환합니다. 신뢰도 점수가 높을수록 통화 중인 화자가 음성 프로필 ID와 일치할 가능성이 높아집니다. 기계 학습 모델에서 일치하는 항목이 없는 경우 알림의 `detailStatus` 필드에 `IdentificationNoMatchesFound`가 포함됩니다.

다음 예제는 매칭 성공 알림을 보여줍니다.

```
{    
    "version": "0",
    "id": "12345678-1234-1234-1234-111122223333",
    "detail-type": "SpeakerSearchStatus",
    "service-type": "VoiceAnalytics",
    "source": "aws.chime",
    "account": "111122223333",
    "time": "yyyy-mm-ddThh:mm:ssZ",
    "region": "us-east-1",
    "resources": [],
    "detail": {
        "taskId": "uuid",
        "detailStatus": "IdentificationSuccessful",
        "speakerSearchDetails" : {
            "results": [
                {
                    "voiceProfileId": "vp-505e0992-82da-49eb-9d4a-4b34772b96b6",
                    "confidenceScore": "0.94567856",
                },
                {
                    "voiceProfileId": "vp-fba9cbfa-4b8d-4f10-9e41-9dfdd66545ab",
                    "confidenceScore": "0.82783350",
                },
                {
                    "voiceProfileId": "vp-746995fd-16dc-45b9-8965-89569d1cf787",
                    "confidenceScore": "0.77136436",
                }
            ]
        },
        "mediaInsightsPipelineId": "87654321-33ca-4dc6-9cdf-abcde6612345",
        "sourceArn": "arn:aws:chime:us-east-1:111122223333:media-pipeline/87654321-33ca-4dc6-9cdf-abcde6612345",
        "streamArn": "arn:aws:kinesisvideo:us-east-1:111122223333:stream/my-stream/0123456789012",
        "channelId": 0
    }
}
```

### 음성 임베딩 생성
<a name="va-voice-print-generation"></a>

추가로 10초 동안 무음이 아닌 발화가 지속되면 음성 커넥터는 알림 대상에 음성 임베딩 생성 알림을 보냅니다. 음성 프로필에 새 음성 임베딩을 등록하거나 이미 음성 프로필에 있는 성문을 업데이트할 수 있습니다.

다음 예제는 매칭에 성공하면 관련 음성 프로필을 업데이트할 수 있다는 의미의 알림을 보여줍니다.

```
{
    "version": "0",
    "id": "12345678-1234-1234-1234-111122223333",
    "detail-type": "SpeakerSearchStatus",
    "service-type": "VoiceAnalytics",
    "source": "aws.chime",
    "account": "111122223333",
    "time": "yyyy-mm-ddThh:mm:ssZ",
    "region": "us-east-1",
    "resources": [],
    "detail": {
        "taskId": "guid",
        "detailStatus": "VoiceprintGenerationSuccess",
        "mediaInsightsPipelineId": "87654321-33ca-4dc6-9cdf-abcde6612345",
        "sourceArn": "arn:aws:chime:us-east-1:111122223333:media-pipeline/87654321-33ca-4dc6-9cdf-abcde6612345",
        "streamArn": "arn:aws:kinesisvideo:us-east-1:111122223333:stream/my-stream/0123456789012",
        "channelId": 0
    }
}
```

## 음성 톤 분석 이벤트
<a name="va-tone-status"></a>

음성 톤 분석 이벤트에는 `VoiceToneAnalysisStatus` 세부 유형이 있습니다. 분석 결과 다음과 같은 상태가 반환될 수 있습니다.
+ `VoiceToneAnalysisSuccessful` – 발신자와 상담원의 음성을 긍정, 부정, 중립 등 감정의 확률로 분석하는 데 성공했습니다.
+ `VoiceToneAnalysisFailure` – 톤 분석을 수행하지 못했습니다. 발신자가 10초 동안 말을 하지 않고 전화를 끊거나 오디오 품질이 너무 나빠지면 이런 일이 발생할 수 있습니다.
+ `VoiceToneAnalysisCompleted` – 사용자와 상담원의 음성을 전체 통화에 대한 감정 확률로 분석하는 데 성공했습니다. 음성 톤 분석이 완료될 때 전송되는 마지막 이벤트입니다.

다음 예제에서는 일반적인 음성 톤 분석 이벤트를 보여줍니다.

```
{
  "detail-type": "VoiceToneAnalysisStatus",
  "service-type": "VoiceAnalytics",
  "source": "aws.chime",
  "account": "216539279014",
  "time": "2022-08-26T17:55:15.563441Z",
  "region": "us-east-1",
  "detail": {
    "taskId": "uuid",
    "detailStatus": "VoiceToneAnalysisSuccessful",
    "voiceToneAnalysisDetails": {
      "currentAverageVoiceTone": {
          "startTime": "2022-08-26T17:55:15.563Z",
          "endTime": "2022-08-26T17:55:45.720Z",
          "voiceToneLabel": "neutral",
          "voiceToneScore": {    
            "neutral": "0.83",    
            "positive": "0.13",    
            "negative": "0.04"
          }
      },
      "overallAverageVoiceTone": {
          "startTime": "2022-08-26T16:23:13.344Z",
          "endTime": "2022-08-26T17:55:45.720Z",
          "voiceToneLabel": "positive",
          "voiceToneScore": {    
            "neutral": "0.25",    
            "positive": "0.65",    
            "negative": "0.1"
          }
      }
    },
        "startFragmentNumber": "01234567890123456789",
        "mediaInsightsPipelineId": "87654321-33ca-4dc6-9cdf-abcde6612345",
        "sourceArn": "arn:aws:chime:us-east-1:111122223333:media-pipeline/87654321-33ca-4dc6-9cdf-abcde6612345",
        "streamArn": "arn:aws:kinesisvideo:us-east-1:111122223333:stream/my-stream/0123456789012",
        "channelId": 0
  },
  "version": "0",
  "id": "Id-f928dfe3-f44b-4965-8a17-612f9fb92d59"
}
```

## 통화 후 요약 이벤트
<a name="va-post-call-summary-events"></a>

통화 종료 후 5분 후에 통화 요약 이벤트가 전송됩니다. 이 요약은 통화 중에 발생한 화자 검색 작업에 대한 개요를 제공합니다.

다음 예에서는 가장 일치하는 음성 프로필, 확인된 화자 ID, 통화 중에 이루어진 `CreateVoiceProfile` 및 `UpdateVoiceProfile` API 호출을 통해 생성되거나 업데이트된 음성 프로필 목록을 포함한 통화 후 요약 정보를 보여줍니다.

```
{
    "version": "0",
    "id": "12345678-1234-1234-1234-111122223333",
    "detail-type": "VoiceAnalyticsStatus",
    "service-type": "VoiceAnalytics",
    "source": "aws.chime",
    "account": "111122223333",
    "time": "yyyy-mm-ddThh:mm:ssZ",    
    "region": "us-east-1",
    "resources": [],
    "detail": {
        "detailStatus": "PostCallVoiceAnalytics",
        "callId": "22e8dee8-bbd7-4f94-927b-2d0ebaeddc1c",
        "transactionId": "daaeb6bf-2fe2-4e51-984e-d0fbf2f09436",
        "voiceConnectorId": "abcdef1ghij2klmno3pqr4",
        "isCaller": true | false,
        "speakerSearchResults": {
            "bestMatchedVoiceProfileId": "vp-04c25ba1-a059-4fd3-8495-4ac91b55e2bf",
            "customerValidatedCallerIdentity": "vp-04c25ba1-a059-4fd3-8495-4ac91b55e2bf",
            "createVoiceProfileTransactions": [
                {
                    "voiceProfileId": "vp-04c25ba1-a059-4fd3-8495-4ac91b55e2bf",
                    "requestTimestamp": "2022-12-14T18:38:38.796Z"
                },
                {
                    "voiceProfileId": "vp-04c25ba1-a059-4fd3-8495-4ac91b55e2bf",
                    "requestTimestamp": "2022-12-14T18:38:38.796Z",
                }
            ],
            "updateVoiceProfileTransactions": [
                {
                    "voiceProfileId": "vp-04c25ba1-a059-4fd3-8495-4ac91b55e2bf",
                    "requestTimestamp": "2022-12-14T18:38:38.796Z",
                },
                {
                    "voiceProfileId": "vp-04c25ba1-a059-4fd3-8495-4ac91b55e2bf",
                    "requestTimestamp": "2022-12-14T18:38:38.796Z",
                }
            ]
        }
    }
}
```

# Amazon Chime SDK용 음성 분석 예제 Lambda 함수
<a name="va-sample-lambda"></a>

다음 예제의 Python 코드는 음성 커넥터에서 받은 알림을 처리합니다. AWS Lambda 함수에 코드를 추가할 수 있습니다. 또한 이를 사용하여 Amazon SQS 대기열, Amazon SNS 주제 또는 Amazon Kinesis Data Stream을 트리거할 수 있습니다. 그런 다음 향후 처리를 위해 알림을 `EventTable`에 저장할 수 있습니다. 정확한 알림 형식은 [Amazon Chime SDK의 알림 이해](va-notification-targets.md) 섹션을 참조하세요.

```
import base64
import boto3
import json
import logging
import time

from datetime import datetime
from enum import Enum

log = logging.getLogger()
log.setLevel(logging.INFO)

dynamo = boto3.client("dynamodb")

EVENT_TABLE_NAME = "EventTable"

class EventType(Enum):
    """
    This example code uses a single Lambda processor to handle either
    triggers from SQS, SNS, Lambda, or Kinesis. You can adapt it to fit your
    desired infrastructure depending on what you prefer. To distinguish
    where we get events from, we use an EventType enum as an
    example to show the different ways of parsing the notifications.
    """
    SQS = "SQS"
    SNS = "SNS"
    LAMBDA = "LAMBDA"
    KINESIS = "KINESIS"


class AnalyticsType(Enum):
    """
    Define the various analytics event types that this Lambda will
    handle.
    """
    SPEAKER_SEARCH = "SpeakerSearch"
    VOICE_TONE_ANALYSIS = "VoiceToneAnalysis"
    ANALYTICS_READY = "AnalyticsReady"
    UNKNOWN = "UNKNOWN"
 
   
class DetailType(Enum):
    """
    Define the  various detail types that Voice Connector's voice
    analytics feature can return.
    """
    SPEAKER_SEARCH_TYPE = "SpeakerSearchStatus"
    VOICE_TONE_ANALYSIS_TYPE = "VoiceToneAnalysisStatus"
    ANALYTICS_READY = "VoiceAnalyticsStatus"
 

def handle(event, context):
    """
    Example of how to handle incoming Voice Analytics notification messages
    from Voice Connector.
    """
    logging.info(f"Received event of type {type(event)} with payload {event}")
    is_lambda = True
    
    # Handle triggers from SQS, SNS, and KDS. Use the below code if you would like
    # to use this Lambda as a trigger for an existing SQS queue, SNS topic or Kinesis
    # stream.
    if "Records" in event:
        logging.info("Handling event from SQS or SNS since Records exists")
        is_lambda = False
        for record in event.get("Records", []):
            _process_record(record)
    
    # If you would prefer to have your Lambda invoked directly, use the
    # below code to have the Voice Connector directly invoke your Lambda.
    # In this scenario, there are no "Records" passed.
    if is_lambda:
        logging.info(f"Handling event from Lambda")
        event_type = EventType.LAMBDA
        _process_notification_event(event_type, event)


def _process_record(record):
    # SQS and Kinesis use eventSource.
    event_source = record.get("eventSource")
    
    # SNS uses EventSource.
    if not event_source:
        event_source = record.get("EventSource")

    # Assign the event type explicitly based on the event source value.
    event_type = None
    if event_source == "aws:sqs":
        event = record["body"]
        event_type = EventType.SQS
    elif event_source == "aws:sns":
        event = record["Sns"]["Message"]
        event_type = EventType.SNS
    elif event_source == "aws:kinesis":
        raw_data = record["kinesis"]["data"]
        raw_message = base64.b64decode(raw_data).decode('utf-8')
        event = json.loads(raw_message)
        event_type = EventType.KINESIS
    else:
        raise Exception(f"Event source {event_source} is not supported")

    _process_notification_event(event_type, event)


def _process_notification_event(
    event_type: EventType,
    event: dict
):
    """
    Extract the attributes from the Voice Analytics notification message
    and store it as a DynamoDB item to process later.
    """
    message_id = event.get("id")
    analytics_type = _get_analytics_type(event.get("detail-type"))
    pk = None
    if analytics_type == AnalyticsType.ANALYTICS_READY.value or analytics_type == AnalyticsType.UNKNOWN.value:
        transaction_id = event.get("detail").get("transactionId")
        pk = f"transactionId#{transaction_id}#notificationType#{event_type.value}#analyticsType#{analytics_type}"
    else:
        task_id = event.get("detail").get("taskId")
        pk = f"taskId#{task_id}#notificationType#{event_type.value}#analyticsType#{analytics_type}"
    logging.info(f"Generated PK {pk}")
    _create_request_record(pk, message_id, json.dumps(event))


def _create_request_record(pk: str, sk: str, body: str):
    """
    Record this notification message into the Dynamo db table
    """
    try:
        # Use consistent ISO8601 date format.
        # 2019-08-01T23:09:35.369156 -> 2019-08-01T23:09:35.369Z
        time_now = (
            datetime.utcnow().isoformat()[:-3] + "Z"
        )
        response = dynamo.put_item(
            Item={
                "PK": {"S": pk},
                "SK": {"S": sk},
                "body": {"S": body},
                "createdOn": {"S": time_now},
            },
            TableName=EVENT_TABLE_NAME,
        )
        logging.info(f"Added record in table {EVENT_TABLE_NAME}, response : {response}")
    except Exception as e:
        logging.error(f"Error in adding record: {e}")


def _get_analytics_type(detail_type: str):
    """
    Get analytics type based on message detail type value.
    """
    if detail_type == DetailType.SPEAKER_SEARCH_TYPE.value:
        return AnalyticsType.SPEAKER_SEARCH.value
    elif detail_type == DetailType.VOICE_TONE_ANALYSIS_TYPE.value:
        return AnalyticsType.VOICE_TONE_ANALYSIS.value
    elif detail_type == DetailType.ANALYTICS_READY.value:
        return AnalyticsType.ANALYTICS_READY.value
    else:
        return AnalyticsType.UNKNOWN.value
```

**중요**  
[https://docs.aws.amazon.com/chime-sdk/latest/APIReference/API_voice-chime_StartSpeakerSearchTask](https://docs.aws.amazon.com/chime-sdk/latest/APIReference/API_voice-chime_StartSpeakerSearchTask) 또는 [https://docs.aws.amazon.com/chime-sdk/latest/APIReference/API_voice-chime_StartVoiceToneAnalysis.html](https://docs.aws.amazon.com/chime-sdk/latest/APIReference/API_voice-chime_StartVoiceToneAnalysis.html) API를 직접 호출하려면 먼저 동의를 받아야 합니다. 동의를 받을 때까지 Amazon DynamoDB와 같은 보류 영역에서 이벤트를 지속하는 것이 좋습니다.