使用对话模型对类型 AMAZON.NUMBER 进行 Alexa 输入验证

Alexa input validation for type AMAZON.NUMBER using dialog model

我正在为 Node.js 使用 ASK SDK 2.0。 我有一项技能,使用对话模型来提示用户进行一系列输入,所有这些都是 AMAZON.NUMBER 类型的必需插槽。当用户给出数字响应时,一切正常。但是,如果用户给出非数字的响应,例如 "yellow",则槽值填充为:

"value": "?"

并继续提示用户输入下一个插槽。如果他们提供无效响应,我怎样才能让它再次提示用户使用该插槽?我已经翻阅了文档,但找不到任何东西。理想情况下,我希望该技能能够重新提示用户,直到给出有效输入(即该值不是 "?"

(奇怪的是,如果我将类型设置为AMAZON.DATE,它会自动重新提示一次,然后如果第二次提供无效类型,技能就会退出。)

我的回复是这样的:

"response": {
    "directives": [{
        "type": "Dialog.Delegate",
        "updatedIntent": {
            "name": "MRRIntent",
            "confirmationStatus": "NONE",
            "slots": {
                "growthValue": {
                    "name": "growthValue",
                    "value": "?",
                    "confirmationStatus": "NONE"
                },
                "churnValue": {
                    "name": "churnValue",
                    "value": "?",
                    "confirmationStatus": "NONE"
                },
                "startingValue": {
                    "name": "startingValue",
                    "value": "10",
                    "confirmationStatus": "NONE"
                }
            }
        }
    }]
}

在此示例中,startingValue 给出了数字响应,但其他两个(growthValuechurnValue)给出了非数字响应。

我可以在意图处理程序的哪个位置检查此值并在出现故障的特定插槽上重新提示?我正在使用 Dialog.Directive 文档说不要将 reprompt 与 ( https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#details 一起使用),除非我误解了它。

我的处理程序是这样的:

const InProgressPlanMyTripHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest' &&
            request.intent.name === 'MRRIntent' &&
            request.dialogState !== 'COMPLETED';
    },
    handle(handlerInput) {
        const currentIntent = handlerInput.requestEnvelope.request.intent;
        return handlerInput.responseBuilder
            .addDelegateDirective(currentIntent)
            .getResponse();
    },
};

const CompletedPlanMyTripHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest' && request.intent.name === 'MRRIntent';
    },
    handle(handlerInput) {

        const responseBuilder = handlerInput.responseBuilder;
        const filledSlots = handlerInput.requestEnvelope.request.intent.slots;
        const slotValues = getSlotValues(filledSlots);

        let speechOutput = `Your values are: startingValue: ${slotValues.startingValue.synonym} growthValue: ${slotValues.growthValue.synonym}, and churnValue: ${slotValues.churnValue.synonym}`

        return responseBuilder
            .speak(speechOutput)
            .getResponse();
    },
};

我以“计划我的旅行”示例作为起点,因此我的绝大部分代码将与其相同:https://github.com/alexa/alexa-cookbook/tree/master/feature-demos/skill-demo-plan-my-trip

我缺少/不理解什么?谢谢

我相信我找到了解决方案 -- 我不确定它是否是最佳解决方案,但它似乎有效。

CompletedPlanMyTripHandler 中,我在 handle 方法中添加了以下检查:

handle(handlerInput) {
    const currentIntent = handlerInput.requestEnvelope.request.intent;

    let slots = currentIntent.slots;
    let badInputSlot;
    for (let x in slots) {
        if (slots.hasOwnProperty(x) && slots[x].hasOwnProperty('value')) {
            if (isNaN(slots[x].value)) {
                badInputSlot = x;
                break;
            }
        }
    }
    if (badInputSlot) {
        return handlerInput.responseBuilder.speak('I do not understand. Please respond with a number.').reprompt('Please respond with a number').addElicitSlotDirective(badInputSlot, currentIntent).getResponse();
    } else {
        return handlerInput.responseBuilder
            .addDelegateDirective(currentIntent)
            .getResponse();
    }
},

它正在做的是在 for 循环中检查意图上的插槽,看看每个插槽是否都有 value 属性(仅在提供响应后才添加) ,然后对其进行 isNaN 检查以查看该值实际上是否有效。如果不是,我们需要再次询问用户该值,所以我们将插槽的名称存储在 badInputSlot 中并跳出循环。

现在,我们继续 if 语句,如果有一个值分配给 badInputSlot 我们 return 为具有错误值的特定插槽引出一个插槽指令。

然后,在用户提供新值后,我们重复该过程,直到 handlerInput.requestEnvelope.request.intent.slots 中的每个槽都有一个 value 属性 通过我们的 isNaN 验证检查.一旦发生这种情况,badInputSlot 将是未定义的,我们的 if 语句将转到 else 块和 return 委托指令以完成意图。

我也在 Alexa Slack 上回答了你,但是我会post在这里再次给其他人看:

您仍然需要在代码中进行错误处理,这在文档中多次提到。您的代码中有以下内容来检索插槽值:

const filledSlots = handlerInput.requestEnvelope.request.intent.slots;
const slotValues = getSlotValues(filledSlots);
const startingValue = slotValues.startingValue.synonym;

您需要做的是检查代码的格式是否正确,例如:

if (!startingValue || startingValue === '?') {
    return handlerInput.responseBuilder
        .speak('This is not a number, please try again')
        .reprompt('Please try again.')
        .addElicitSlotDirective('SLOTNAMEGOESHERE')
        .getResponse();
}

如果 userInputundefined(这意味着我们仍然缺少对话框中的那个槽)或 userInput 等于 ?.使用上面的内容,您可以通过一个接一个地引出一个槽来很好地设计对话。

始终在后端验证插槽,并且每当您的数字插槽未返回数值时,不要委托,而是使用 Dialog.ElicitSlot 指令让 Alexa 请求该特定插槽。

例如:

// if number-slot validation fails

return handlerInput.responseBuilder
   .addElicitSlotDirective(slotToElicit)
   .speak("Please provide a number")
   .reprompt("Please provide a number")
   .getResponse();

使用 Dialog.Delegate 指令,您无法从代码中发送 outputSpeechreprompt。相反,将使用交互模型中定义的那些。但是,在任何时候,您都可以接管对话而不是继续委托给 Alexa。

有关对话框指令的更多信息here