6

image

不知道从什么时候开始,当我们联系一些企业的客服时,越来越多的联系和支持工作都开始由AI机器人程序代为完成了。无论是电话下单,查询订单状态,咨询业务问题,或者其他什么类型的求助,这些机器人通常都能很好地完成,而一些比较「黑科技」的机器人,甚至在成功解决问题之后,我们都无法判断电话那头的到底是人还是程序……

就是因为效果太好,很多企业甚至已经开始主要通过这种方式向用户提供服务,甚至开始缩减人工客服的岗位。那么这就可能产生一个问题:如果机器人客服的程序挂了,你的客户该由谁来提供服务?

如果你的智能客服是基于Amazon Lex构建的,那么可以考虑创建高可用性的多区域机器人,这样就算一个区域的机器人故障,也可以自动交由其他区域来接待客户,从根本上解决这种问题。

众多亚马逊云科技 (Amazon Web Services)客户已经开始利用 Amazon Lex 机器人在电话及其他多种渠道上增强 Amazon Connect 自助服务的对话体验。借助 Amazon Lex ,呼叫方(用 Amazon Connect 术语描述,即客户)能够快速获取问题的答案,几乎不需要人工客服的介入。但这同时也给服务可用性提出了更高的要求,因此引发了新的问题:我们该使用哪种架构模式提升机器人可用性?在本文中,我们将探讨一种跨区域方法,通过在多个区域中部署 Amazon Lex 机器人以提高服务可用性。

架构概述

在这套解决方案中,一旦 Amazon Lex 出现服务可用性问题,Amazon Connect 流能够将中断影响控制在最低程度,借此实现业务连续性。此架构模式使用以下组件:

  • 两个 Amazon Lex机器人,各自处于不同的区域内。
  • 两个机器人由负责区域检查的 Amazon Lambda 函数进行触发,并集成至 Amazon Connect 流内。
  • 用于检查机器人运行状况的 Lambda 函数。
  • 为 Amazon Connect 区域中的主机器人创建一套 Amazon DynamoDB 表,并通过 Lambda 函数进行读取。
  • 使用 DynamoDB 表保存 Amazon Connect 与 Amazon Lex 之间的区域映射。由之前提到的运行状态检查函数负责更新此表。区域检查函数读取此表,以获取 Amazon Connect 与 Amazon Lex 的最新主区域映射。

之所以要在两个区域内设置完成相同的 Amazon Lex 机器人,是为了能够随时在辅助区域中启动该机器人,进而在主区域发生故障时及时替换。
image

Amazon Lex的多区域模式

随后的两节,主要描述集成有 Amazon Lex 机器人的 Amazon Connect 流如何在主区域发生服务故障或中断的情况下,快速实现恢复并使用辅助区域内的 Amazon Lex 正常响应客户呼叫。

运行状况检查函数将根据TEST_METHOD Lambda环境变量对两个 Amazon Lex 运行时API之一进行调用(PutSession或PostText)。我们可以根据自己的喜好及用例要求选择其中一个。 PutSession API 调用不会产生任何额外的 Amazon Lex 关联费用,但无法测试 Amazon Lex 提供的自然语言理解(NLU)功能。 PostTextAPI 则允许我们检查 Amazon Lex 的NLU功能,且额外成本不高。

运行状况检查函数会将通过测试的区域名称,更新至 DynamoDB 表(lexDR)中的 lexRegion 列。如果主区域正常通过运行状况检查,则 lexRegion 将被更新为主区域名称。如果运行状况检查失败,则该函数将基于辅助区域内的 TEST_METHOD 环境变量向相应的运行时 API 发出调用。如果测试成功,则 DynamoDB 表中的 lexRegion 列将被更新为辅助区域;如果测试仍然失败,则更新为 err ,代表两个区域皆已发生服务中断。

在 Amazon Connect收到的每项呼叫中,都会发出区域运行状况检测函数调用,借此获取当前 Amazon Connect区域中的活动Amazon Lex区域。由区域运行状况检查函数返回的主区域将作为最新条目,由该检查函数写入至DyanmoDB表。由区域检查函数返回的可用区域内Amazon Lex机器人,将通过Get Customer Input Block配置接受Amazon Connect的调用。如果函数返回的是辅助区域,则Amazon Connect将调用辅助区域中的机器人。

部署Amazon Lex机器人

大家需要在主区域与辅助区域中创建相同的对话机器人。在本文中,我们将us-east-1作为主区域,us-west-2作为辅助区域。接下来,先使用主区域us-east-1创建机器人:

  • 在Amazon Lex控制台上点击Create。
  • 在Try a Sample部分,选择OrderFlowers,而后在COPPA中选择No。
  • 其他设置项皆保留默认值,点击Create。
  • 此机器人的创建与构建操作将自动进行。
  • 在机器人构建完成(约需1至2分钟)后,选择Publish。
  • 创建一个别名,名称为ver_one。

对us-west-2区域重复上述步骤。现在,我们已经在us-east-1与us-west-2中建立起能够正常运行的Amazon Lex机器人。

创建DynamoDB

请确保当前处于us-east-1区域内。

  • 在DynamoDB控制台选择Create。
  • 在Table name部分,输入lexDR。
  • 在Primary key部分,输入connectRegion且类型为String。
  • 其他各项保留默认值,而后选择Create。
  • 在Items选项卡中,选择Create item。
  • 将connectRegion的值设置为us-east-1,而后Append一个类型为String、名称为lexRegion的新列,并将其值设置为us-east-1。

image

  • 点击Save。

image

为Lambda函数创建IAM角色

在此步骤中,我们将为两项Lambda函数创建一个亚马逊云科技身份与访问管理(Amazon Identity and Access Management,简称IAM)角色。

  • 在IAM控制台上,点击Access management并选择Policies。
  • 点击Create Policy。
  • 点击JSON。
  • 粘贴以下自定义IAM策略,此策略允许对DynamoDB表lexDR进行读取/写入访问。请将策略中的“xxxxxxxxxxxx”部分替换为我们的亚马逊云科技账户编号。
{
 "Version": "2012-10-17",
 "Statement": [{
 "Sid": "VisualEditor0",
 "Effect": "Allow",
 "Action": ["dynamodb:GetItem", "dynamodb:UpdateItem"],
 "Resource": "arn:aws:dynamodb:us-east-1:xxxxxxxxxxxx:table/lexDR"
 }]
}
  • 点击Review Policy。
  • 将其命名为DynamoDBReadWrite,而后点击Create Policy。
  • 在IAM控制台上,点击Access management下的Roles,而后点击Create Role。
  • 为该服务选择Lambda,而后点击Next。
  • 附加以下权限策略:

    • AmazonLambdaBasicExecutionRole
    • AmazonLexRunBotsOnly
    • DynamoDBReadWrite
  • 点击Next: Tags。接下来点击Next: Review以跳过Tags页面。
  • 将角色命名为lexDRRole,而后点击Save。

部署区域检查函数

我们首先需要创建一项Lambda函数,用于从DynamoDB表中读取记录,借此判断哪个Amazon Lex机器人与Amazon Connect实例处于同一区域当中。Amazon Connect或者使用此机器人的应用程序后续将调用此函数。

  • 在Lambda控制台上,选择Create function。
  • 在Function name部分,输入lexDRGetRegion。
  • 在Runtime部分,选择Python 3.8。
  • 在Permissions之下,选择Use an existing role。
  • 选择角色lexDRRole。
  • 选择Create function。
  • 在Lambda代码编辑器中,输入以下代码(下载自lexDRGetRegion.zip):
import json
import boto3
import os
import logging
dynamo_client=boto3.client('dynamodb')
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
def getCurrentPrimaryRegion(key):
 result = dynamo_client.get_item(
 TableName=os.environ['TABLE_NAME'],
 Key = {
 "connectRegion": {"S": key }
 }
 )
 logger.debug(result['Item']['lexRegion']['S'] )
 return result['Item']['lexRegion']['S']
def lambda_handler(event, context):
 logger.debug(event)
 region = event["Details"]["Parameters"]["region"]
 return {
 'statusCode': 200,
 'primaryCode': getCurrentPrimaryRegion(region)
 }
  • 在Environment variables部分,选择Edit。
  • 添加一项环境变量,其中Key为TABLE_NAME,Value为lexDR。
  • 点击Save以保存该环境变量。
  • 点击Save以保存该Lambda函数。

image

部署运行状况检查函数

在us-east-1当中创建另一项Lambda函数,用以实现运行状况检查功能。

  • 在Lambda控制台上,选择Create function。
  • 在Function name部分,输入lexDRTest。
  • 在Runtime部分,选择Python 3.8。
  • 在Permissions之下,选择Use an existing role。
  • 选择lexDRRole。
  • 选择Create function。
  • 在Lambda代码编辑器中,输入以下代码(下载自lexDRTest.zip):
import json
import boto3
import sys
import os
dynamo_client = boto3.client('dynamodb')
primaryRegion = os.environ['PRIMARY_REGION']
secondaryRegion = os.environ['SECONDARY_REGION']
tableName = os.environ['TABLE_NAME']
primaryRegion_client = boto3.client('lex-runtime',region_name=primaryRegion)
secondaryRegion_client = boto3.client('lex-runtime',region_name=secondaryRegion)
def getCurrentPrimaryRegion():
 result = dynamo_client.get_item(
 TableName=tableName,
 Key={ 
 'connectRegion': {'S': primaryRegion} 
 }
 )
 return result['Item']['lexRegion']['S']
def updateTable(region):
 result = dynamo_client.update_item(
 TableName= tableName,
 Key={ 
 'connectRegion': {'S': primaryRegion }
 }, 
 UpdateExpression='set lexRegion = :region',
 ExpressionAttributeValues={
 ':region': {'S':region}
 }
 )
#SEND MESSAGE/PUT SESSION ENV VA
def put_session(botname, botalias, user, region):
 print(region,botname, botalias)
 client = primaryRegion_client
 if region == secondaryRegion:
 client = secondaryRegion_client
 try:
 response = client.put_session(botName=botname, botAlias=botalias, userId=user)
 if (response['ResponseMetadata'] and response['ResponseMetadata']['HTTPStatusCode'] and response['ResponseMetadata']['HTTPStatusCode'] != 200) or (not response['sessionId']): 
 return 501
 else:
 if getCurrentPrimaryRegion != region:
 updateTable(region)
 return 200
 except:
 print('ERROR: {}',sys.exc_info()[0])
 return 501
def send_message(botname, botalias, user, region):
 print(region,botname, botalias)
 client = primaryRegion_client
 if region == secondaryRegion:
 client = secondaryRegion_client
 try:
 message = os.environ['SAMPLE_UTTERANCE']
 expectedOutput = os.environ['EXPECTED_RESPONSE']
 response = client.post_text(botName=botname, botAlias=botalias, userId=user, inputText=message)
 if response['message']!=expectedOutput:
 print('ERROR: Expected_Response=Success, Response_Received='+response['message'])
 return 500
 else:
 if getCurrentPrimaryRegion != region:
 updateTable(region)
 return 200
 except:
 print('ERROR: {}',sys.exc_info()[0])
 return 501
def lambda_handler(event, context):
 print(event)
 botName = os.environ['BOTNAME']
 botAlias = os.environ['BOT_ALIAS']
 testUser = os.environ['TEST_USER']
 testMethod = os.environ['TEST_METHOD']
 if testMethod == 'send_message':
 primaryRegion_response = send_message(botName, botAlias, testUser, primaryRegion)
 else:
 primaryRegion_response = put_session(botName, botAlias, testUser, primaryRegion)
 if primaryRegion_response != 501:
 primaryRegion_client.delete_session(botName=botName, botAlias=botAlias, userId=testUser)
 if primaryRegion_response != 200:
 if testMethod == 'send_message':
 secondaryRegion_response = send_message(botName, botAlias, testUser, secondaryRegion)
 else:
 secondaryRegion_response = put_session(botName, botAlias, testUser, secondaryRegion)
 if secondaryRegion_response != 501:
 secondaryRegion_client.delete_session(botName=botName, botAlias=botAlias, userId=testUser)
 if secondaryRegion_response != 200:
 updateTable('err')
 #deleteSessions(botName, botAlias, testUser)
 return {'statusCode': 200,'body': 'Success'}
  • 在Environment variables部分,选择Edit,而后添加以下环境变量:

    • BOTNAME – OrderFlowers
    • BOT_ALIAS – ver_one
    • SAMPLE_UTTERANCE – I would like to order some flowers. (向机器人发送的示例话语。)
    • EXPECTED_RESPONSE – What type of flowers would you like to order? (机器人在收到以上示例话语后应做出的预期响应。)
    • PRIMARY_REGION – us-east-1
    • SECONDARY_REGION – us-west-2
    • TABLE_NAME – lexDR
    • TEST_METHOD – put_session或send_message

      • send_message:此方法将调用Lex运行时函数postText,该函数将提取语音并将其映射至训练得出的某一intent。postText将测试Lex的自然语言理解能力,每项请求的使用成本为0.00075美元,几乎可以忽略不计。
      • put_session:此方法将调用Lex运行时函数put_session,该函数为用户创建一个新的会话。put_session不会测试Lex的自然语言理解能力。
    • TEST_USER – test
  • 点击Save以保存此环境变量。
  • 在Basic Settings section当中,将Timeout的值更新为15秒。
  • 点击Save以保存此Lambda函数。

image

创建一条Amazon CloudWatch规则

为了每5分钟触发一次运行状况检查函数,我们需要创建一条Amazon CloudWatch规则。

  • 在CloudWatch控制台的Events之下,选择Rules。
  • 选择Create rule。
  • 在Event Source之下,将选项切换为Schedule。
  • 将Fixed rate of设置为5 minutes。
  • 在Targets之下,选择Add target。
  • 选择目标Lambda function。
  • 在Function部分,选择lexDRTest。
  • 在Configure input之下,选择Constant(JSON text),而后输入{}。
  • 选择Configure details。
  • 在Rule definition之下的Name部分,输入lexHealthCheckRule。
  • 选择Create rule。

现在,我们应该已经建立起lexHealthCheckRule CloudWatch规则,能够每5分钟调用一次lexDRTest函数。这项操作将检查主机器人的运行状况是否正常,并将结果对应更新至DynamoDB表。

创建Amazon Connect实例

随后我们需要创建一个Amazon Connect实例,借此在创建lexDRTest函数的同一区域之内测试机器人的多区域模式。

  • 如果还没有Amazon Connect实例,请首先创建一个
  • 在Amazon Connect控制台上,选择作为Amazon Connect传输流目标的实例别名。
  • 选择Contact flows。
  • 在Amazon Lex之下,从us-east-1区域中选择OrderFlowers机器人,而后点击Add Lex Bot。
  • 在us-west-2区域中选择OrderFlowers机器人,而后点击Add Lex Bot。

image

  • 在Amazon Lambda之下,选择lexDRGetRegion并点击Add Lambda Function。
  • 点击左侧面板中的Overview再点击登录链接,借此登录至Amazon Connect实例。
  • 点击左侧面板中的Routing,而后点击下拉菜单中的Contact Flows。
  • 点击Create Contact Flow按钮。
  • 点击Save按钮旁的向下箭头按钮,再点击Import Flow。
  • 下载联系流程Flower DR Flow。在Import Flow对话框中上传此文件。

image

  • 在Contact Flow中,点击Inovke Amazon Lambda Function部分,借此在屏幕右侧打开一个属性面板。
  • 选择lexDRGetRegion并点击Save。
  • 点击Publish按钮,发布当前联系流程。

将电话号码关联至联系流程

接下来,我们需要将电话号码关联至联系流程,借此调用并测试OrderFlowers机器人。

  • 点击左侧导航栏中的Routing选项。
  • 点击Phone Numbers。
  • 点击Claim Number。
  • 选择国家代码并选择电话号码。
  • 在Contact flow/IVR选择框中,选择我们在之前步骤中导入的联系流程Flower DR Flow。
  • 等待几分钟,而后呼叫该号码以与OrderFlowers机器人交互。

测试集成效果

要测试这套解决方案,可以执行以下操作以模拟us-east-1区域发生故障的场景:

  • 在us-east-1区域中打开Amazon Lex控制台。
  • 选择OrderFlowers机器人。
  • 点击Settings。
  • 删除机器人别名ver_one。

在下一次进行运行状况检查时,解决方案将尝试与us-east-1区域的Lex机器人进行通信。由于机器人别名不存在,因此无法获得成功响应。因此,本示例随后会呼叫辅助区域us-west-2并收到成功响应。在收到响应后,示例将使用us-west-2更新lexDR以及DynamoDB表中的lexRegion列。

接下来,所有指向us-east-1区域内Connect的后续呼叫都将与us-west-2区域内的Lex机器人进行实际交互。通过这一自动切换,可以证明当前架构模式确实能够在发生服务故障时保障业务连续性。

在删除机器人别名到下一次运行状况检查之间的时段内,对Amazon Connect的呼叫都将失败。但在运行状况检查之后,系统将自动实现业务连续性保障。因此,每一轮运行状况检查之间的间隔越短,则停机时间越短。我们可以通过编辑Amazon CloudWatch规则lexHealthCheckRule以调整每次运行状况检查之间的间隔时长。

要让us-east-1区域再次通过运行状况检查,请在us-east-1中重新创建OrderFlowers机器人的别名ver_one。

资源清理

为了避免产生不必要的额外成本,请删除本示例中创建的所有资源。

  • 创建在us-east-1与us-west-2当中的Amazon Lex机器人OrderFlowers
  • CloudWatch规则lexHealthCheckRule
  • DynamoDB表lexDR
  • Lambda函数lexDRTest与lexDRGetRegion
  • IAM角色lexDRRole
  • 联系流程Flower DR Flow

总结

配合Amazon Lex提供的自助服务,Amazon Connect将帮助我们轻松创建便捷直观的客户服务体验。本文提供一种跨区域形式的高可用性实现方法,保证能够在某一区域中的机器人或支持实现API不可用时,使用来自其他区域的资源以继续响应客户呼叫。

image


亚马逊云开发者
2.9k 声望9.6k 粉丝

亚马逊云开发者社区是面向开发者交流与互动的平台。在这里,你可以分享和获取有关云计算、人工智能、IoT、区块链等相关技术和前沿知识,也可以与同行或爱好者们交流探讨,共同成长。