Alexa 开发者控制台回复 "There was a problem with the requested skill's response"

Alexa Developer Console replying with "There was a problem with the requested skill's response"

过去几个小时我一直在尝试解决这个问题,但没有成功。这是我的代码:

Lambda 代码:

/*eslint-disable  func-names */
/* eslint quote-props: ["error", "consistent"]*/

// There are three sections, Text Strings, Skill Code, and Helper Function(s).
// You can copy and paste the contents as the code for a new Lambda function, using the alexa-skill-kit-sdk-factskill template.
// This code includes helper functions for compatibility with versions of the SDK prior to 1.0.9, which includes the dialog directives.



 // 1. Text strings =====================================================================================================
 //    Modify these strings and messages to change the behavior of your Lambda function


let speechOutput;
let reprompt;
let welcomeOutput = "Welcome to Tanpura! You can ask me to play at any pitch. How can I help you today?";
let welcomeReprompt = "sample re-prompt text";
// 2. Skill Code =======================================================================================================
"use strict";
const Alexa = require('alexa-sdk');
const APP_ID = undefined;  // TODO replace with your app ID (OPTIONAL).
speechOutput = '';
const handlers = {
    'LaunchRequest': function () {
        this.emit(':ask', welcomeOutput, welcomeReprompt);
    },
    'AMAZON.HelpIntent': function () {
        speechOutput = 'You can ask me to play at any pitch, such as C, C sharp, D, D sharp, and so on. How can I help you?';
        reprompt = '';
        this.emit(':ask', speechOutput, reprompt);
    },
   'AMAZON.CancelIntent': function () {
        speechOutput = 'Would you like me to play another pitch?';
        this.emit(':tell', speechOutput);
    },
   'AMAZON.StopIntent': function () {
        speechOutput = 'I hope I helped you. Goodbye!';
        this.emit(':tell', speechOutput);
   },
   'SessionEndedRequest': function () {
        speechOutput = '';
        //this.emit(':saveState',true);//uncomment to save attributes to db on session end
        this.emit(':tell', speechOutput);
   },
    'AMAZON.FallbackIntent': function () {
        speechOutput = '';

        //any intent slot variables are listed here for convenience


        //Your custom intent handling goes here
        speechOutput = "I currently do not support your request. However, you can ask me to play at any pitch, and I will do so. How can I help?";
        this.emit(":ask", speechOutput, speechOutput);
    },
    'AMAZON.NavigateHomeIntent': function () {
        speechOutput = '';

        //any intent slot variables are listed here for convenience


        //Your custom intent handling goes here
        speechOutput = "Welcome to Tanpura! You can ask me to play at any pitch. How can I help you today?";
        this.emit(":ask", speechOutput, speechOutput);
    },
    'PlayNoteIntent': function () {
        speechOutput = '';

        //any intent slot variables are listed here for convenience

        let noteSlot = resolveCanonical(this.event.request.intent.slots.note);
        console.log("User selected pitch: " + noteSlot);

        let accidentalSlot = resolveCanonical(this.event.request.intent.slots.accidental);
        console.log("User selected accidental: " + accidentalSlot);
        var notes = {
            'a': {
                'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Gsharp.mp3',
                'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_A.mp3',
                'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Asharp.mp3'
            },
            'b': {
                'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Asharp.mp3',
                'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_B.mp3',
                'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_C.mp3'
            },
            'c': {
                'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_B.mp3',
                'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_C.mp3',
                'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Csharp.mp3'
            },
            'd': {
                'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Csharp.mp3',
                'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_D.mp3',
                'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Dsharp.mp3'
            },
            'e': {
                'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Dsharp.mp3',
                'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_E.mp3',
                'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_F.mp3'
            },
            'f': {
                'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_E.mp3',
                'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_F.mp3',
                'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Fsharp.mp3'
            },
            'g': {
                'flat': 'https://s3.amazonaws.com/tanpuranew/tanpura_Fsharp.mp3',
                'natural': 'https://s3.amazonaws.com/tanpuranew/tanpura_G.mp3',
                'sharp': 'https://s3.amazonaws.com/tanpuranew/tanpura_Gsharp.mp3'
            }

        }
        var note = noteSlot.toLowerCase();
            var speechReprompt = "";
        if (noteSlot && notes[note]){
            var audio = '';
            var accidental = 'natural';

            if(accidentalSlot && accidental.indexOf(accidentalSlot) > -1){
                accidental = accidentalSlot;
            }

            var audioSrc = notes[note][accidental];
            speechOutput = "Ok. I will play " + noteSlot + accidental + <audio src="' + audioSrc + '" />;
            speechReprompt = "Would you like me to continue playing?";

        }

        else{
            speechOutput = "The note you have requested is not supported yet.";
            speechReprompt = "However, Tanpura does support A, B, C, D, E, F, G, and all of the accidentals in between.";
        }

        //Your custom intent handling goes here

        this.emit(":ask", speechOutput, speechOutput);
    },  
    'Unhandled': function () {
        speechOutput = "Tanpura didn't quite understand what you wanted. Please try rephrasing your request.";
        this.emit(':ask', speechOutput, speechOutput);
    }
};

exports.handler = (event, context) => {
    const alexa = Alexa.handler(event, context);
    alexa.appId = APP_ID;
    // To enable string internationalization (i18n) features, set a resources object.
    //alexa.resources = languageStrings;
    alexa.registerHandlers(handlers);
    //alexa.dynamoDBTableName='DYNAMODB_TABLE_NAME';//uncomment this line to save attributes to DB
    alexa.execute();
};

//    END of Intent Handlers {} ========================================================================================
// 3. Helper Function  =================================================================================================

function resolveCanonical(slot){
    //this function looks at the entity resolution part of request and returns the slot value if a synonyms is provided
    let canonical;
    try{
        canonical = slot.resolutions.resolutionsPerAuthority[0].values[0].value.name;
    }catch(err){
        console.log(err.message);
        canonical = slot.value;
    };
    return canonical;
};

function delegateSlotCollection(){
  console.log("in delegateSlotCollection");
  console.log("current dialogState: "+this.event.request.dialogState);
    if (this.event.request.dialogState === "STARTED") {
      console.log("in Beginning");
      let updatedIntent= null;
      // updatedIntent=this.event.request.intent;
      //optionally pre-fill slots: update the intent object with slot values for which
      //you have defaults, then return Dialog.Delegate with this updated intent
      // in the updatedIntent property
      //this.emit(":delegate", updatedIntent); //uncomment this is using ASK SDK 1.0.9 or newer

      //this code is necessary if using ASK SDK versions prior to 1.0.9 
      if(this.isOverridden()) {
            return;
        }
        this.handler.response = buildSpeechletResponse({
            sessionAttributes: this.attributes,
            directives: getDialogDirectives('Dialog.Delegate', updatedIntent, null),
            shouldEndSession: false
        });
        this.emit(':responseReady', updatedIntent);

    } else if (this.event.request.dialogState !== "COMPLETED") {
      console.log("in not completed");
      // return a Dialog.Delegate directive with no updatedIntent property.
      //this.emit(":delegate"); //uncomment this is using ASK SDK 1.0.9 or newer

      //this code necessary is using ASK SDK versions prior to 1.0.9
        if(this.isOverridden()) {
            return;
        }
        this.handler.response = buildSpeechletResponse({
            sessionAttributes: this.attributes,
            directives: getDialogDirectives('Dialog.Delegate', null, null),
            shouldEndSession: false
        });
        this.emit(':responseReady');

    } else {
      console.log("in completed");
      console.log("returning: "+ JSON.stringify(this.event.request.intent));
      // Dialog is now complete and all required slots should be filled,
      // so call your normal intent handler.
      return this.event.request.intent;
    }
}


function randomPhrase(array) {
    // the argument is an array [] of words or phrases
    let i = 0;
    i = Math.floor(Math.random() * array.length);
    return(array[i]);
}
function isSlotValid(request, slotName){
        let slot = request.intent.slots[slotName];
        //console.log("request = "+JSON.stringify(request)); //uncomment if you want to see the request
        let slotValue;

        //if we have a slot, get the text and store it into speechOutput
        if (slot && slot.value) {
            //we have a value in the slot
            slotValue = slot.value.toLowerCase();
            return slotValue;
        } else {
            //we didn't get a value in the slot.
            return false;
        }
}

//These functions are here to allow dialog directives to work with SDK versions prior to 1.0.9
//will be removed once Lambda templates are updated with the latest SDK

function createSpeechObject(optionsParam) {
    if (optionsParam && optionsParam.type === 'SSML') {
        return {
            type: optionsParam.type,
            ssml: optionsParam['speech']
        };
    } else {
        return {
            type: optionsParam.type || 'PlainText',
            text: optionsParam['speech'] || optionsParam
        };
    }
}

function buildSpeechletResponse(options) {
    let alexaResponse = {
        shouldEndSession: options.shouldEndSession
    };

    if (options.output) {
        alexaResponse.outputSpeech = createSpeechObject(options.output);
    }

    if (options.reprompt) {
        alexaResponse.reprompt = {
            outputSpeech: createSpeechObject(options.reprompt)
        };
    }

    if (options.directives) {
        alexaResponse.directives = options.directives;
    }

    if (options.cardTitle && options.cardContent) {
        alexaResponse.card = {
            type: 'Simple',
            title: options.cardTitle,
            content: options.cardContent
        };

        if(options.cardImage && (options.cardImage.smallImageUrl || options.cardImage.largeImageUrl)) {
            alexaResponse.card.type = 'Standard';
            alexaResponse.card['image'] = {};

            delete alexaResponse.card.content;
            alexaResponse.card.text = options.cardContent;

            if(options.cardImage.smallImageUrl) {
                alexaResponse.card.image['smallImageUrl'] = options.cardImage.smallImageUrl;
            }

            if(options.cardImage.largeImageUrl) {
                alexaResponse.card.image['largeImageUrl'] = options.cardImage.largeImageUrl;
            }
        }
    } else if (options.cardType === 'LinkAccount') {
        alexaResponse.card = {
            type: 'LinkAccount'
        };
    } else if (options.cardType === 'AskForPermissionsConsent') {
        alexaResponse.card = {
            type: 'AskForPermissionsConsent',
            permissions: options.permissions
        };
    }

    let returnResult = {
        version: '1.0',
        response: alexaResponse
    };

    if (options.sessionAttributes) {
        returnResult.sessionAttributes = options.sessionAttributes;
    }
    return returnResult;
}

function getDialogDirectives(dialogType, updatedIntent, slotName) {
    let directive = {
        type: dialogType
    };

    if (dialogType === 'Dialog.ElicitSlot') {
        directive.slotToElicit = slotName;
    } else if (dialogType === 'Dialog.ConfirmSlot') {
        directive.slotToConfirm = slotName;
    }

    if (updatedIntent) {
        directive.updatedIntent = updatedIntent;
    }
    return [directive];
}

Alexa 开发人员JSON 输入:

{
    "version": "1.0",
    "session": {
        "new": true,
        "sessionId": "amzn1.echo-api.session.1456bfda-a9d3-457f-88a7-bc5387d774db",
        "application": {
            "applicationId": "amzn1.ask.skill.0c237132-8815-4025-a23f-ca6df688bcd2"
        },
        "user": {
            "userId": "amzn1.ask.account.AGD7V7GZTLU4DQH623OMU5MUBR2FGWXKDVW2OPNYYRWKIYJCHQBCSKVNQHEPOEXQWO33Q4OTJ6LSIRLYT3TN33OAK3W7LLNNYPU5S3MVKPMPNH2XDWYJ7DBWCFZRXY4STCPFKVL2FADYZE4TXS53Z5AXBPN6344R6VG6GD365TFQTCPPKABC5IKM46UZXUX3BPR4TQ4KEYO6LTA"
        }
    },
    "context": {
        "System": {
            "application": {
                "applicationId": "amzn1.ask.skill.0c237132-8815-4025-a23f-ca6df688bcd2"
            },
            "user": {
                "userId": "amzn1.ask.account.AGD7V7GZTLU4DQH623OMU5MUBR2FGWXKDVW2OPNYYRWKIYJCHQBCSKVNQHEPOEXQWO33Q4OTJ6LSIRLYT3TN33OAK3W7LLNNYPU5S3MVKPMPNH2XDWYJ7DBWCFZRXY4STCPFKVL2FADYZE4TXS53Z5AXBPN6344R6VG6GD365TFQTCPPKABC5IKM46UZXUX3BPR4TQ4KEYO6LTA"
            },
            "device": {
                "deviceId": "amzn1.ask.device.AFHBRIBVUWYIR2ESXPKWP3G3PHYK4W5VW4NF55KH5ZXD27WMSPBPU7YLJQJWM2YQDZBH7VWGXCLNQKESUNWWGI6CJUWUUSWUKVBZWZC5LBNXMCDY2IOZAZUYWHYXT5VLLA7XC3OP2WY7RXE2LPRHM5E4BIMR662M5MZKJH4WRPUFS3HVIFRDK",
                "supportedInterfaces": {}
            },
            "apiEndpoint": "https://api.amazonalexa.com",
            "apiAccessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJhdWQiOiJodHRwczovL2FwaS5hbWF6b25hbGV4YS5jb20iLCJpc3MiOiJBbGV4YVNraWxsS2l0Iiwic3ViIjoiYW16bjEuYXNrLnNraWxsLjBjMjM3MTMyLTg4MTUtNDAyNS1hMjNmLWNhNmRmNjg4YmNkMiIsImV4cCI6MTUzOTU1MzE2NywiaWF0IjoxNTM5NTQ5NTY3LCJuYmYiOjE1Mzk1NDk1NjcsInByaXZhdGVDbGFpbXMiOnsiY29uc2VudFRva2VuIjpudWxsLCJkZXZpY2VJZCI6ImFtem4xLmFzay5kZXZpY2UuQUZIQlJJQlZVV1lJUjJFU1hQS1dQM0czUEhZSzRXNVZXNE5GNTVLSDVaWEQyN1dNU1BCUFU3WUxKUUpXTTJZUURaQkg3VldHWENMTlFLRVNVTldXR0k2Q0pVV1VVU1dVS1ZCWldaQzVMQk5YTUNEWTJJT1pBWlVZV0hZWFQ1VkxMQTdYQzNPUDJXWTdSWEUyTFBSSE01RTRCSU1SNjYyTTVNWktKSDRXUlBVRlMzSFZJRlJESyIsInVzZXJJZCI6ImFtem4xLmFzay5hY2NvdW50LkFHRDdWN0daVExVNERRSDYyM09NVTVNVUJSMkZHV1hLRFZXMk9QTllZUldLSVlKQ0hRQkNTS1ZOUUhFUE9FWFFXTzMzUTRPVEo2TFNJUkxZVDNUTjMzT0FLM1c3TExOTllQVTVTM01WS1BNUE5IMlhEV1lKN0RCV0NGWlJYWTRTVENQRktWTDJGQURZWkU0VFhTNTNaNUFYQlBONjM0NFI2Vkc2R0QzNjVURlFUQ1BQS0FCQzVJS000NlVaWFVYM0JQUjRUUTRLRVlPNkxUQSJ9fQ.KAHvIOOUP4k-73lNMxRnOToYjrUbeHuLRDQGzMFi9dVEiwc2QpvpMZpLNpG5rCtoqB2-OfC48KbK5u67nW6X9QO6DSoNTBfPKUatIHB6pUWbArdv-FliUO69SQMomjLtLzC86_jnZ8TqvNavjb5I5hOGnmCe5Fv2IY5HgBw0h07Dq3ZT4i_4edcnhX9zYJretTEydF0L3JA7GTithgtAGFxbBqbiDTKRMlaGUGBWAkZkHy8FPWsAmvfTwRaNL7F3LAEbGH2QJlyoPQR7jYij7CsnlRAEv-3Ur1kFaMEdhDNA9fcn2JI4TVf1umy0fL66dHWq3omk2p5I4FyrJ3a8SQ"
        }
    },
    "request": {
        "type": "IntentRequest",
        "requestId": "amzn1.echo-api.request.80cc2899-2fa2-4828-99ba-1c25d8cce05b",
        "timestamp": "2018-10-14T20:39:27Z",
        "locale": "en-US",
        "intent": {
            "name": "PlayNoteIntent",
            "confirmationStatus": "NONE",
            "slots": {
                "note": {
                    "name": "note",
                    "value": "an",
                    "resolutions": {
                        "resolutionsPerAuthority": [
                            {
                                "authority": "amzn1.er-authority.echo-sdk.amzn1.ask.skill.0c237132-8815-4025-a23f-ca6df688bcd2.Note",
                                "status": {
                                    "code": "ER_SUCCESS_NO_MATCH"
                                }
                            }
                        ]
                    },
                    "confirmationStatus": "NONE",
                    "source": "USER"
                },
                "accidental": {
                    "name": "accidental",
                    "value": "e",
                    "resolutions": {
                        "resolutionsPerAuthority": [
                            {
                                "authority": "amzn1.er-authority.echo-sdk.amzn1.ask.skill.0c237132-8815-4025-a23f-ca6df688bcd2.accidental",
                                "status": {
                                    "code": "ER_SUCCESS_NO_MATCH"
                                }
                            }
                        ]
                    },
                    "confirmationStatus": "NONE",
                    "source": "USER"
                }
            }
        }
    }
}

最后是技能的JSON:

{
    "interactionModel": {
        "languageModel": {
            "invocationName": "tanpura",
            "intents": [
                {
                    "name": "AMAZON.FallbackIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.NavigateHomeIntent",
                    "samples": []
                },
                {
                    "name": "PlayNoteIntent",
                    "slots": [
                        {
                            "name": "note",
                            "type": "Note"
                        },
                        {
                            "name": "accidental",
                            "type": "accidental"
                        }
                    ],
                    "samples": [
                        "for an {note} {accidental}",
                        "for a {note} {accidental}",
                        "play {note} {accidental}",
                        "Start an {note} {accidental}",
                        "Give me an {note} {accidental}",
                        "Make an {note} {accidental}",
                        "Put on an {note} {accidental}",
                        "Sing at an {note} {accidental}",
                        "Sing at a {note} {accidental}",
                        "Create an {note} {accidental}",
                        "Lets hear an {note} {accidental}",
                        "Play an {note} {accidental}",
                        "Lets hear a {note} {accidental}",
                        "Sing at {note} {accidental}",
                        "Create a {note} {accidental}",
                        "Make a {note} {accidental}",
                        "Put on a {note} {accidental}",
                        "Initiate a {note} {accidental}",
                        "Give me a {note} {accidental}",
                        "Start a {note} {accidental}",
                        "Play a {note} {accidental}"
                    ]
                }
            ],
            "types": [
                {
                    "name": "Note",
                    "values": [
                        {
                            "name": {
                                "value": "B"
                            }
                        },
                        {
                            "name": {
                                "value": "A#"
                            }
                        },
                        {
                            "name": {
                                "value": "A"
                            }
                        },
                        {
                            "name": {
                                "value": "G#"
                            }
                        },
                        {
                            "name": {
                                "value": "G"
                            }
                        },
                        {
                            "name": {
                                "value": "F"
                            }
                        },
                        {
                            "name": {
                                "value": "E"
                            }
                        },
                        {
                            "name": {
                                "value": "D#"
                            }
                        },
                        {
                            "name": {
                                "value": "D"
                            }
                        },
                        {
                            "name": {
                                "value": "C#"
                            }
                        },
                        {
                            "name": {
                                "value": "C"
                            }
                        }
                    ]
                },
                {
                    "name": "accidental",
                    "values": [
                        {
                            "name": {
                                "value": "natural"
                            }
                        },
                        {
                            "name": {
                                "value": "flat"
                            }
                        },
                        {
                            "name": {
                                "value": "sharp"
                            }
                        }
                    ]
                }
            ]
        }
    }
}

该代码最初可以正常工作,但在我对 Lambda 代码进行了几次编辑之后,我一次又一次地收到相同的回复,这毫无意义。我认为问题可能在于我为意外值添加了一个额外的值,并且我在 skillinator.io 站点重建我的 Lambda 代码之前添加了一个自然值。任何帮助将不胜感激,因为我整天都在为这段代码苦苦挣扎。

"There was a problem with the requested skill's response"表示响应有问题json。它可能为空或无效。

对于您的情况,此行会引发错误,因为您的字符串连接不正确。

speechOutput = "Ok. I will play " + noteSlot + accidental + <audio src="' + audioSrc + '" />;

改为:

speechOutput = "Ok. I will play " + noteSlot + accidental + "<audio src=\"" + audioSrc + "\" />";