

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

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

当发言人搜索或语音音调分析任务开始、运行和完成时，语音分析会自动向目标发送事件。您可以使用通知目标来接收这些事件。如果您的工作流程或应用程序需要高可用性，我们建议使用多个通知目标。

此外，您必须使用具有访问您的通知目标所需策略的 IAM 角色。有关更多信息，请参阅 [针对 Amazon Chime SDK 使用通话分析资源访问角色](call-analytics-resource-access-role.md)。

**注意**  
对于亚马逊 SQS 和亚马逊 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>

您必须在呼叫分析配置中使用允许访问您的 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 秒钟的非静默话音后返回识别匹配通知。该服务会返回前 10 个匹配项，以及语音配置文件 ID 和范围为 [0, 1] 的置信度分数。置信度分数越高，呼叫中的发言人与语音配置文件 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 数据流。然后，您可以存储通知到 `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）。