

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 了解 Amazon Chime SDK 的通知
<a name="va-notification-targets"></a>

語音分析會在發言者搜尋或語音音調分析任務開始時、執行期間以及完成時，自動將事件傳送至目標。您可以使用通知目標來接收這些事件。如果您的工作流程或應用程式需要高可用性，我們建議您使用多個通知目標。

此外，您必須使用 IAM 角色搭配存取通知目標所需的政策。如需詳細資訊，請參閱[使用 Amazon Chime SDK 的呼叫分析資源存取角色](call-analytics-resource-access-role.md)。

**注意**  
對於 Amazon SQS 和 Amazon SNS，我們不支援first-in-first-out佇列。因此，訊息可能無法按順序送達。建議您檢查時間戳記以視需要排序訊息，並在 Amazon DynamoDB 等資料存放區中保留訊息。您也可以使用中所述APIs [輪詢 Amazon Chime SDK 的任務結果](va-task-result-poll.md) 來接收最新結果。

下表列出事件及其對應的詳細資訊類型。


| 通知事件 | 詳細資訊類型 | 
| --- | --- | 
| 語音分析中繼資料 | `VoiceAnalyticsStatus` | 
| 發言者搜尋 | `SpeakerSearchStatus` | 
| 語音音調分析 | `VoiceToneAnalysisStatus` | 

# 了解 Amazon Chime SDK 通知目標的 IAM 政策
<a name="va-iam-target-policies"></a>

您必須在通話分析組態中使用 IAM 角色中的政策，以允許存取您的 Amazon SQS、Amazon SNS、AWS Lambda 或 Amazon KDS 通知目標。如需詳細資訊，請參閱本指南中的 [使用 Amazon Chime SDK 的呼叫分析資源存取角色](call-analytics-resource-access-role.md)。

## 發言者搜尋事件
<a name="va-speaker-search-events"></a>

發言者搜尋事件具有`SpeakerSearchStatus`詳細資訊類型。

Amazon Chime SDK Voice Connectors 會傳送下列發言者搜尋事件：
+ 識別相符項目
+ 語音內嵌產生

事件可以有下列狀態：
+ `IdentificationSuccessful` – 成功識別至少一個在指定語音設定檔網域中具有高可信度分數的相符語音設定檔 ID。
+ `IdentificationFailure` – 無法執行識別。原因：來電者至少 10 秒沒有說話，音訊品質不佳。
+ `IdentificationNoMatchesFound` – 在指定的語音設定檔網域中找不到高度可信度相符項目。發起人可能是新的，或其語音可能已變更。
+ `VoiceprintGenerationSuccessful` – 系統使用 20 秒的非靜音音訊產生語音內嵌。
+ `VoiceprintGenerationFailure` – 系統無法產生語音內嵌。原因：來電者至少 20 秒沒有說話，音訊品質不佳。

### 識別相符項目
<a name="va-id-matches"></a>

為指定的 呼叫 [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 之後`transactionId`，語音連接器服務會在 10 秒的非無提示語音之後傳回識別比對通知。服務會傳回前 10 個相符項目，以及 【0， 1】 範圍內的語音設定檔 ID 和可信度分數。可信度分數越高，通話的發言者越有可能符合語音設定檔 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 分鐘會傳送通話後摘要事件。這些摘要提供在整個通話中發生的發言者搜尋任務的概觀。

下列範例顯示通話後摘要，其中包含最佳語音設定檔相符項目、已確認的發言者身分，以及透過 `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) APIs。我們建議您將事件保留在保留區域中，例如 Amazon DynamoDB，直到您取得同意為止。