使用语音转录信心分数来改善与 Lex V2 机器人的对话 - Amazon Lex

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

使用语音转录信心分数来改善与 Lex V2 机器人的对话

当用户说出语音时,Amazon Lex V2 会使用自动语音识别 (ASR) 在用户请求被解释之前对其进行转录。默认情况下,Amazon Lex V2 使用最有可能的音频转录进行解释。

在某些情况下,可能有多种可能的音频转录。例如,用户说出的言语可能模棱两可,例如用户说“My name is John”(我叫约翰),听起来可能像是“My name is Juan”(我叫胡安)。在这种情况下,您可以使用消除歧义的技术或将您的专有领域知识与转录置信度分数相结合,以帮助确定转录列表中正确的转录。

Amazon Lex V2 在对 Lambda 代码挂钩函数的请求中包括用户输入的最优转录和最多两个备用转录。每个转录都包含一个评估该转录的正确性的可信度分数。每个转录还包括从用户输入中推断出的任何槽位值。

您可以比较两个转录的置信度分数,以确定这两者之间是否存在歧义。例如,如果一个转录的置信度分数为 0.95,而另一个转录的置信度分数为 0.65,则第一个转录可能是正确的,并且这两者之间的歧义较低。如果两个转录的置信度分数分别为 0.75 和 0.72,则这两者之间的歧义性较高。您可以使用自己的专有领域知识加以区分。

例如,如果置信度分数分别为 0.75 和 0.72 的两个转录中推断的槽位值是“John”和“Juan”,则可以在数据库中执行用户查询以确定这些名称是否存在并删除其中一个转录。如果“John”不是数据库中的用户,而“Juan”是数据库中的用户,则可以使用对话框代码挂钩将名字的推断槽位值更改为“Juan”。

Amazon Lex V2 返回的置信度分数是比较值。请勿将其视作绝对分数进行信任。根据对 Amazon Lex V2 的改进,这些值可能会发生变化。

音频转录置信度分数仅适用于英语(英国)(en_GB) 和英语(美国)(en_US)。仅支持 8 个 kHz 音频输入的置信度分数。Amazon Lex V2 主机上测试窗口的音频输入不提供转录置信度分数,因为它使用 16 个 kHz 音频输入。

注意

在对现有机器人使用音频转录置信度分数之前,必须先重新构建该机器人。现有版本的机器人不支持转录置信度分数。要使用转录置信度分数,必须创建新版本的机器人。

您可以将置信度分数用于多种对话设计模式:

  • 如果由于环境嘈杂或信号质量差而导致最高置信度分数低于阈值,则可以向用户发送同样问题的提示以便捕获质量更好的音频。

  • 如果多个转录对槽位值的置信度分数相似,例如“John”和“Juan”,则可以将这些值与预先存在的数据库进行比较以消除输入,也可以提示用户从这两个值中选择一个。例如,“say 1 for John or say 2 for Juan.”(如果是约翰,请说 1,如果是胡安,请说 2。)

  • 如果您的业务逻辑要求在置信度分数接近最优转录的情况下,根据备用转录中的特定关键字进行意图切换,则可以使用对话框代码挂钩 Lambda 函数或使用会话管理操作来更改意图。有关更多信息,请参阅 会话管理

Amazon Lex V2 发送以下JSON结构,其中包含最多三个转录,供用户输入您的 Lambda 代码挂钩函数:

"transcriptions": [ { "transcription": "string", "rawTranscription": "string", "transcriptionConfidence": "number", }, "resolvedContext": { "intent": "string" }, "resolvedSlots": { "string": { "shape": "List", "value": { "originalValue": "string", "resolvedValues": [ "string" ] }, "values": [ { "shape": "Scalar", "value": { "originalValue": "string", "resolvedValues": [ "string" ] } }, { "shape": "Scalar", "value": { "originalValue": "string", "resolvedValues": [ "string" ] } } ] } } } ]

该JSON结构包含转录文本、为话语解析的意图以及在话语中检测到的任何插槽的值。对于文本用户输入,转录包含一个置信度分数为 1.0 的单个转录。

转录的内容取决于对话的回合以及所识别的意图。

就第一回合意图引发而言,Amazon Lex V2 确定前三个转录。对于最优转录,Amazon Lex V2 返回转录中的意图以及任何推断的槽位值。

对于随后的回合,即槽位引发回合,结果取决于每个转录的推断意图,如下所示。

  • 如果最优转录的推断意图与上一回合相同,并且所有其他转录的意图相同,则

    • 所有转录都包含推断出的槽位值。

       

  • 如果最优转录的推断意图与上一回合不同,并且所有其他转录具有上一个意图,则

    • 该最优转录包含新意图的推断槽位值。

    • 其他转录具有上一个意图以及该上一个意图的推断槽位值。

       

  • 如果最优转录的推断意图与上一回合不同,一个转录与上一个意图相同,并且一个转录具有不同的意图,则

    • 该最优转录包含新的推断意图以及言语中的任何推断槽位值。

    • 具有上一个推断意图的转录包含该意图的推断槽位值。

    • 具有不同意图的转录不包含推断的意图名称,也不包含推断的槽位值。

       

  • 如果最优转录的推断意图与上一回合不同,并且所有其他转录具有不同的意图,则

    • 该最优转录包含新的推断意图以及言语中的任何推断槽位值。

    • 其他转录不包含推断的意图,也不包含推断的槽位值。

       

  • 如果前两个转录的推断意图与上一回合相同和不同,并且第三个转录具有不同的意图,则

    • 前两个转录包含新的推断意图以及言语中的任何推断槽位值。

    • 第三个转录不包含意图名称,也不包含已解析的槽位值。

会话管理

要更改 Amazon Lex V2 在与用户对话中使用的意图,请使用对话框代码挂钩 Lambda 函数中的响应。或者,也可以在自定义应用程序APIs中使用会话管理。

在 Lex V2 机器人中使用 Lambda 函数

当您使用 Lambda 函数时,Amazon Lex V2 会使用包含该函数输入的JSON结构来调用该函数。该JSON结构包含一个名为的字段transcriptions,该字段包含 Amazon Lex V2 为该话语确定的可能转录。该 transcriptions 字段包含一到三个可能的转录,每个转录都有对应的置信度分数。

要使用备用转录中的意图,请在 Lambda 函数的 ConfirmIntentElicitSlot 对话操作中指定该意图。要使用备用转录中的槽位值,请在 Lambda 函数响应的 intent 字段中设置该值。有关更多信息,请参阅 整合一个 AWS Lambda 在你的机器人中发挥作用

使用 Lambda 和 Lex V2 的示例代码

以下代码示例是一个 Python Lambda 函数,使用音频转录来改善用户的对话体验。

要使用示例代码,您必须满足以下条件:

  • 拥有单语言的机器人,语言可以是英语(英国)(en_GB)或英语(美国)(en_US)。

  • 一个意图, OrderBirthStone。确保在意图定义的代码挂钩部分中选中使用 Lambda 函数进行初始化和验证

  • Intent 应有两个插槽,“BirthMonth” 和 “名称”,均为类型AMAZON.AlphaNumeric

  • 拥有定义了 Lambda 函数的别名。有关更多信息,请参阅 创建一个 AWS Lambda 适用于你的机器人的功能

import time import os import logging logger = logging.getLogger() logger.setLevel(logging.DEBUG) # --- Helpers that build all of the responses --- def elicit_slot(session_attributes, intent_request, slots, slot_to_elicit, message): return { 'sessionState': { 'dialogAction': { 'type': 'ElicitSlot', 'slotToElicit': slot_to_elicit }, 'intent': { 'name': intent_request['sessionState']['intent']['name'], 'slots': slots, 'state': 'InProgress' }, 'sessionAttributes': session_attributes, 'originatingRequestId': 'e3ab4d42-fb5f-4cc3-bb78-caaf6fc7cccd' }, 'sessionId': intent_request['sessionId'], 'messages': [message], 'requestAttributes': intent_request['requestAttributes'] if 'requestAttributes' in intent_request else None } def close(intent_request, session_attributes, fulfillment_state, message): intent_request['sessionState']['intent']['state'] = fulfillment_state return { 'sessionState': { 'sessionAttributes': session_attributes, 'dialogAction': { 'type': 'Close' }, 'intent': intent_request['sessionState']['intent'], 'originatingRequestId': '3ab4d42-fb5f-4cc3-bb78-caaf6fc7cccd' }, 'messages': [message], 'sessionId': intent_request['sessionId'], 'requestAttributes': intent_request['requestAttributes'] if 'requestAttributes' in intent_request else None } def delegate(intent_request, session_attributes): return { 'sessionState': { 'dialogAction': { 'type': 'Delegate' }, 'intent': intent_request['sessionState']['intent'], 'sessionAttributes': session_attributes, 'originatingRequestId': 'abc' }, 'sessionId': intent_request['sessionId'], 'requestAttributes': intent_request['requestAttributes'] if 'requestAttributes' in intent_request else None } def get_session_attributes(intent_request): sessionState = intent_request['sessionState'] if 'sessionAttributes' in sessionState: return sessionState['sessionAttributes'] return {} def get_slots(intent_request): return intent_request['sessionState']['intent']['slots'] """ --- Functions that control the behavior of the bot --- """ def order_birth_stone(intent_request): """ Performs dialog management and fulfillment for ordering a birth stone. Beyond fulfillment, the implementation for this intent demonstrates the following: 1) Use of N best transcriptions to re prompt user when confidence for top transcript is below a threshold 2) Overrides resolved slot for birth month from a known fixed list if the top transcript is not accurate. """ transcriptions = intent_request['transcriptions'] if intent_request['invocationSource'] == 'DialogCodeHook': # Disambiguate if there are multiple transcriptions and the top transcription # confidence is below a threshold (0.8 here) if len(transcriptions) > 1 and transcriptions[0]['transcriptionConfidence'] < 0.8: if transcriptions[0]['resolvedSlots'] is not {} and 'Name' in transcriptions[0]['resolvedSlots'] and \ transcriptions[0]['resolvedSlots']['Name'] is not None: return prompt_for_name(intent_request) elif transcriptions[0]['resolvedSlots'] is not {} and 'BirthMonth' in transcriptions[0]['resolvedSlots'] and \ transcriptions[0]['resolvedSlots']['BirthMonth'] is not None: return validate_month(intent_request) return continue_conversation(intent_request) def prompt_for_name(intent_request): """ If the confidence for the name is not high enough, re prompt the user with the recognized names so it can be confirmed. """ resolved_names = [] for transcription in intent_request['transcriptions']: if transcription['resolvedSlots'] is not {} and 'Name' in transcription['resolvedSlots'] and \ transcription['resolvedSlots']['Name'] is not None: resolved_names.append(transcription['resolvedSlots']['Name']['value']['originalValue']) if len(resolved_names) > 1: session_attributes = get_session_attributes(intent_request) slots = get_slots(intent_request) return elicit_slot(session_attributes, intent_request, slots, 'Name', {'contentType': 'PlainText', 'content': 'Sorry, did you say your name is {} ?'.format(" or ".join(resolved_names))}) else: return continue_conversation(intent_request) def validate_month(intent_request): """ Validate month from an expected list, if not valid looks for other transcriptions and to see if the month recognized there has an expected value. If there is, replace with that and if not continue conversation. """ expected_months = ['january', 'february', 'march'] resolved_months = [] for transcription in intent_request['transcriptions']: if transcription['resolvedSlots'] is not {} and 'BirthMonth' in transcription['resolvedSlots'] and \ transcription['resolvedSlots']['BirthMonth'] is not None: resolved_months.append(transcription['resolvedSlots']['BirthMonth']['value']['originalValue']) for resolved_month in resolved_months: if resolved_month in expected_months: intent_request['sessionState']['intent']['slots']['BirthMonth']['resolvedValues'] = [resolved_month] break return continue_conversation(intent_request) def continue_conversation(event): session_attributes = get_session_attributes(event) if event["invocationSource"] == "DialogCodeHook": return delegate(event, session_attributes) # --- Intents --- def dispatch(intent_request): """ Called when the user specifies an intent for this bot. """ logger.debug('dispatch sessionId={}, intentName={}'.format(intent_request['sessionId'], intent_request['sessionState']['intent']['name'])) intent_name = intent_request['sessionState']['intent']['name'] # Dispatch to your bot's intent handlers if intent_name == 'OrderBirthStone': return order_birth_stone(intent_request) raise Exception('Intent with name ' + intent_name + ' not supported') # --- Main handler --- def lambda_handler(event, context): """ Route the incoming request based on intent. The JSON body of the request is provided in the event slot. """ # By default, treat the user request as coming from the America/New_York time zone. os.environ['TZ'] = 'America/New_York' time.tzset() logger.debug('event={}'.format(event)) return dispatch(event)

使用会话管理API来选择不同的意图或时段值

要使用与当前意图不同的意图,请使用PutSession操作。例如,如果您认为第一个替代意图比 Amazon Lex V2 选择的意图更可取,则可以使用 PutSession 操作来更改意图。这样,用户与之交互的下一个意图即为您选择的意图。

您也可以使用 PutSession 操作来更改 intent 结构中的槽位值,以使用替代转录中的值。

有关更多信息,请参阅 了解 Amazon Lex V2 机器人会话