はじめに
- 以前、これまで作成を進めてきた「面接練習」スキルをストアへ申請したところ、リジェクトされた
- リジェクトされた理由が詳しくフィードバックされたため、対応を行なおうとしたが、「どのインテントにも該当しない場合には、StartIntentにマッチしてしまう」という問題が解消できないまま、一旦作業を中断していた
- 解消方法を有識者から伺ったので、対応を再開することにした
やってみたこと
有識者からのアドバイスを整理
現状の対話モデルの設定などを見てもらったところ、次の問題が発生しているとのことだった
- AMAZON.FallbackIntentが定義されていないので、StartIntentに該当しない場合に、FallbackIntentHandlerに処理が渡らない状態になっていた。
また、上記とは別に、Amazon.NoIntentやAmazon.YesIntent等を活用すればもっとシンプルになるのではということだった。
やることをチームで相談
前述の内容を踏まえて、チームでこれをやることにした
- Amazon.FallbackIntentを対話モデルに追加
- Amazon.NoIntentを対話モデルに追加
- Amazon.YesIntentを対話モデルに追加
- EtcIntentは対話モデルから削除
- コードエディタ上で、StartIntentHandlerのcanHandle内の条件式を「Amazon. YesIntent」もしくは「StartIntent」にマッチするかどうか、という条件に変更
なお、ここに至るまでにいくつか躓いた点があった。
- StartIntentを対話モデルから削除すると、モデルをビルドした際に次のエラーが表示された。カスタムインテントは1つ以上必要であるとのことだった。そのため、StartIntentは残すことにした
Interaction Model does not include Custom Intents. Add a Custom Intent with at least one Sample Utterance to your Interaction Model.
- ケアレスミスで、canHandleの条件で「Amazon.YesIntent」ではなく「YesIntent」としていたため、マッチせずに「IntentReflectorHandler」が実行されてしまう状態になっていた。
コードの修正
最終的に、細かい修正も含めて、前回の申請でいただいたフィードバック(その5を参照)にすべて対応し、以下のようなコードとして再申請した。
コード
/* *
* This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
* Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
* session persistence, api calls, and more.
* */
const Alexa = require('ask-sdk-core');
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
const speakOutput = 'これから面接練習をします。このスキルでは、質問をランダムに生成します。質問を返した時点でこのスキルは終了します。準備はよろしいですか?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const StartIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& ( Alexa.getIntentName(handlerInput.requestEnvelope) === 'StartIntent' || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.YesIntent');
},
handle(handlerInput) {
// 質問リスト
const array = [
"志望理由は何ですか?",
"あなたの強みはなんですか?",
"あなたの弱みはなんですか?",
"最近苦労したことはなんですか?",
"最近楽しかったことはなんですか?",
"今後やりたいことはなんですか?",
"何か質問ありますか?ふふふ",
"いまのあなたの課題はなんですか?",
"どのようなリーダーシップの経験がありますか?",
"仕事で大事にしていることはなんですか?",
]
const after = "では、お答えください。さようなら。"
const min = Math.ceil(0);
const max = Math.floor(array.length - 1);
let randomInt = Math.floor(Math.random() * (max - min) + min);
const speakOutput = array[randomInt] + after;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
const NoIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.NoIntent';
},
handle(handlerInput) {
const speakOutput = "面接練習を終了します";
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speakOutput = 'このスキルでは、質問をランダムに生成します。質問を返した時点でこのスキルは終了します。開始する場合には、「面接練習をはじめて」と言ってください';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
|| Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speakOutput = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
/* *
* FallbackIntent triggers when a customer says something that doesn’t map to any intents in your skill
* It must also be defined in the language model (if the locale supports it)
* This handler can be safely added but will be ingnored in locales that do not support it yet
* */
const FallbackIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.FallbackIntent';
},
handle(handlerInput) {
const speakOutput = 'すみません、おっしゃっていることが理解できませんでした。このスキルでは、質問をランダムに生成します。質問を返した時点でこのスキルは終了します。開始する場合には、「面接練習をはじめて」と言ってください';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
/* *
* SessionEndedRequest notifies that a session was ended. This handler will be triggered when a currently open
* session is closed for one of the following reasons: 1) The user says "exit" or "quit". 2) The user does not
* respond or says something that does not match an intent defined in your voice model. 3) An error occurs
* */
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
},
handle(handlerInput) {
console.log(`~~~~ Session ended: ${JSON.stringify(handlerInput.requestEnvelope)}`);
// Any cleanup logic goes here.
return handlerInput.responseBuilder.getResponse(); // notice we send an empty response
}
};
/* *
* The intent reflector is used for interaction model testing and debugging.
* It will simply repeat the intent the user said. You can create custom handlers for your intents
* by defining them above, then also adding them to the request handler chain below
* */
const IntentReflectorHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';
},
handle(handlerInput) {
const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);
const speakOutput = `You just triggered ${intentName}`;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
/**
* Generic error handling to capture any syntax or routing errors. If you receive an error
* stating the request handler chain is not found, you have not implemented a handler for
* the intent being invoked or included it in the skill builder below
* */
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
const speakOutput = 'Sorry, I had trouble doing what you asked. Please try again.';
console.log(`~~~~ Error handled: ${JSON.stringify(error)}`);
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
/**
* This handler acts as the entry point for your skill, routing all request and response
* payloads to the handlers above. Make sure any new handlers or interceptors you've
* defined are included below. The order matters - they're processed top to bottom
* */
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
HelpIntentHandler,
StartIntentHandler,
NoIntentHandler,
CancelAndStopIntentHandler,
FallbackIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler)
.addErrorHandlers(
ErrorHandler)
.withCustomUserAgent('sample/hello-world/v1.2')
.lambda();
最後に
これで申請が通るかどうか、待つ。
参考
特に無し。
というか、ドキュメントからうまく必要な情報を探せなかった。結構手探りで解決してた。