티스토리 뷰
예제 따라해보기 - Actions on Google : Build your first App (v2 오류 해결)이 v2가 actions on google 의 v2였을 줄이야..
민동그라미 2018. 1. 26. 13:28공식 다큐먼트 링크 :
https://developers.google.com/actions/dialogflow/first-app
오류 발생
따라하다가 보면 오류가 생긴다. v2 invalid argument 라고...
이유
stackoverflow에서 오류의 원인을 찾으면 [stackoverflow 클릭하면 사이트로!]
1. end conversation 클릭을 안해주었거나 등 예제를 따라하면서 설정을 안해준 것에서 오류가 날 수 도 있지만
2. 가장 큰 근본적인 원인은 v2 베타 버전이 나왔고 그에 맞게 적어주는 형식이 달라졌는데 이에 맞게 적어주지 않아서 같다.
헷갈렸던 v1, v2
중요했던 것은 Dialogflow fulfillment beta api1 api2 와 Actions on google v1, v2 에 있었다.
둘은 다른 것이었고
위에서 v2에 오류였던 것은 바로 Actions on Google v2 문제였다.
이에 대한 공식 문서는 다음을 참조 : https ://developers.google.com/actions/reference/v1/migration
요점은
2017년 5월 17일 이후 v2 버전으로 업데이트, 기본적으로 지원해주는 것은 v2 이다.
v1도 일년동안 지원해줄 것이나 가능한 한 빨리 전에 v1으로 짰던 코드를 v2로 짜길 바란다!
달라진 것
1. snake case 에서 Camel case로 바뀌었다. 예를 들면 input_data 들로 썼던 것을 이제는 inputData 로 적게 되었다.
2. node.js client library 에서 제공해주는 예를 들면, 사용자에게 퍼미션 얻는 거라던가, 업데이트 등록하는 것, 위치,시간 정보 얻는 기능들을 제공해주는 system intent 가 spec 으로 제공해주었었는데 이제는 data 형식으로 사용한다.
(이부분은 일단 아직 감이 안잡혀서 push 알림 만드는 예제에서 더 알아낼 생각이다.)
3. 제공해주던 Permission_request 와 is_ssml 이 이제는 안되나보다
이것보다 중요한 것은 바로 버전을 업데이트 하는 것이었다...!
왜냐하면 공식 문서의 작성방식은 version 1 이기 때문이고
firebase enline editor 의 글은 v2 에 맞추어져있기 떄문인 것 같다.
하지만 나는 오디오 스피치를 사용하기 위해 v2 방식을 써야했고 그렇기 때문에
초간단 해결 (업데이트만 해주면 됨!)
업데이트 하는 것이었다...!
그래서 공식 예제 문서에 package.json 을 보면
{
"name": "silly-name-maker",
"description": "Find out your silly name!",
"version": "0.0.1",
"author": "Google Inc.",
"engines": {
"node": "~4.2"
},
"dependencies": {
"actions-on-google": "^1.0.0",
"firebase-admin": "^4.2.1",
"firebase-functions": "^0.5.7"
}
}
1. actions on google 버전이 1.0.0 으로 되어있는데 이것을 1.1.0 버전으로 올리고
npm install 을 커멘드 창에서 치면 된다! (만약에 공식 문서대로 npm 툴 깔고, firebase init 해서 커맨드 창에서 개발을 하고 있었다면!)
2. 만약 fulfillment inline editor 아래 사진과 같은 곳에서 개발 하고 있었다면
index.js 공식문서와 똑같이 복붙! - 단, 중간에 exports.sillyNameMaker = 이부분을 exports.dialogflowFirebaseFulfillment 로 바꾸어준다.
package.json 에서 공식문서와 똑같이 복붙, 1. 과 같이 버전을 1.1.0으로 바꾸어준다.
deploy 끝!
---------------------------------------------------------------------------------------------------------------------------------------------------------
위에서는 actions on google에 v1 ,v2 에 관한 이야기였다.
내가 헷갈렸던 것은 Dialogflow fulfillment beta api v1, v2 였다.
이에 관한 공식문서는
https://dialogflow.com/docs/reference/v2-comparison 여기서 확인 할 수 있다.
actions on google 문서에서 위와 같이 말하길래, Dialogflow's fulfillment api v2 를 사용하면
app. ask, app.tell 등을 사용한 공식문서 코드에서 오류가 날 줄 알았는데,
Dialogflow's fulfillment api v2 를 사용해도 공식문서에 app.tell app.ask 로 이루어진 전체 코드를 사용해도 문제가 없었다.
현재는 지원해주는 것이려나..?
아래는 Dialogflow's fulfillment api v1, v2 문제 때문에 안되는 것인 줄 알고, app.ask , app.tell 코드를 다 빼고 적은 코드로 해결했던 것!
결론적으로 actions on google에 v1 이 아니라 v2 여야 하고, 그러기 위에서 dependencies 만 버전을 올려줬으면 오류가 안나는 거였다~
해결(막 Dialogflow 시작했을 때 야매로 )
//공식 예제 원본
'use strict';process.env.DEBUG = 'actions-on-google:*'; const App = require('actions-on-google').DialogflowApp; const functions = require('firebase-functions');
// a. the action name from the make_name Dialogflow intent const NAME_ACTION = 'make_name'; // b. the parameters that are parsed from the make_name intent const COLOR_ARGUMENT = 'color'; const NUMBER_ARGUMENT = 'number';
exports.sillyNameMaker = functions.https.onRequest((request, response) => { const app = new App({request, response}); console.log('Request headers: ' + JSON.stringify(request.headers)); console.log('Request body: ' + JSON.stringify(request.body));
// c. The function that generates the silly name function makeName (app) { let number = app.getArgument(NUMBER_ARGUMENT); let color = app.getArgument(COLOR_ARGUMENT); app.tell('Alright, your silly name is ' + color + ' ' + number + '! I hope you like it. See you next time.'); } // d. build an action map, which maps intent names to functions let actionMap = new Map(); actionMap.set(NAME_ACTION, makeName);
app.handleRequest(actionMap); });
바꾸어줄 부분
다음과 같은 내용을 바꾸어 적어주어야만 했다.
0. agent 설정에서 dialog v2 beta api 설정을 on 해주었다.
1. Fulfillment 탭으로 간다.
2. Inline Editor 창에 function precessv2Request 부분으로 간다. (Dialogflow v2 에 맞는 답변을 제시해주는 곳)
3. actionHandlers 에 가서 make_name 액션을 만들어주고 그 액션에 맞는 답변 내용을 생성해준다.
이 부분에 가서
make_name 액션 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 'make_name': () => { let responseToUser = { //fulfillmentMessages: richResponsesV2, // Optional, uncomment to enable //outputContexts: [{ 'name': `${session}/contexts/weather`, 'lifespanCount': 2, 'parameters': {'city': 'Rome'} }], // Optional, uncomment to enable fulfillmentText: 'Alright, your silly name is ' + parameters.color + ' ' + parameters.number + '! I hope you like it. See you next time' }; sendResponse(responseToUser); }, | cs |
간략 설명
make_name 이라는 action을 추가해주고
실행해야 할 코드에
기존(공식 예제)에서 COLOR_ARGUMENT = color 라고 미리 설정해주고 코드에서
name is ' + color + 라고 쓰던 것을 (number 도 같은 원리)
const COLOR_ARGUMENT = 'color';
let color = app.getArgument(COLOR_ARGUMENT);
app.tell('Alright, your silly name is ' + color + ' ' +
v2 버전에서는
Dialogflow 에서 parameter name 과 parameter value를 설정해주었기 때문에
그냥 action 답변부분에서 name is' + parameters.color 라고 사용하면 된다!
즉,
기존(공식 예제)에서
1. 다음부분 작성 생략
const COLOR_ARGUMENT = 'color';
let color = app.getArgument(COLOR_ARGUMENT);
2. 'make_name' 새로 작성한 action을 아래와 같은 기존 코드를 바꾸면 된다.
app.tell('Alright, your silly name is ' + color + ' ' + number + '! I hope you like it. See you next time.'); }
color 를 ---- parameters.color 로
number를 ---- parameters.number 로 바꾸어서
v2 부분 코드 전체
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | /* * Function to handle v2 webhook requests from Dialogflow */ function processV2Request (request, response) { // An action is a string used to identify what needs to be done in fulfillment let action = (request.body.queryResult.action) ? request.body.queryResult.action : 'default'; // Parameters are any entites that Dialogflow has extracted from the request. let parameters = request.body.queryResult.parameters || {}; // https://dialogflow.com/docs/actions-and-parameters // Contexts are objects used to track and store conversation state let inputContexts = request.body.queryResult.contexts; // https://dialogflow.com/docs/contexts // Get the request source (Google Assistant, Slack, API, etc) let requestSource = (request.body.originalDetectIntentRequest) ? request.body.originalDetectIntentRequest.source : undefined; // Get the session ID to differentiate calls from different users let session = (request.body.session) ? request.body.session : undefined; // Create handlers for Dialogflow actions as well as a 'default' handler const actionHandlers = { // The default welcome intent has been matched, welcome the user (https://dialogflow.com/docs/events#default_welcome_intent) 'input.welcome': () => { sendResponse('Hello, Welcome to my Dialogflow agent!'); // Send simple response to user }, // The default fallback intent has been matched, try to recover (https://dialogflow.com/docs/intents#fallback_intents) 'input.unknown': () => { // Use the Actions on Google lib to respond to Google requests; for other requests use JSON sendResponse('I\'m having trouble, can you try that again?'); // Send simple response to user }, 'make_name': () => { let responseToUser = { //fulfillmentMessages: richResponsesV2, // Optional, uncomment to enable //outputContexts: [{ 'name': `${session}/contexts/weather`, 'lifespanCount': 2, 'parameters': {'city': 'Rome'} }], // Optional, uncomment to enable fulfillmentText: 'Alright, your silly name is ' + parameters.color + ' ' + parameters.number + '! I hope you like it. See you next time' }; sendResponse(responseToUser); }, // Default handler for unknown or undefined actions 'default': () => { let responseToUser = { //fulfillmentMessages: richResponsesV2, // Optional, uncomment to enable //outputContexts: [{ 'name': `${session}/contexts/weather`, 'lifespanCount': 2, 'parameters': {'city': 'Rome'} }], // Optional, uncomment to enable fulfillmentText: 'This is from Dialogflow\'s Cloud Functions for Firebase editor! :-)'// displayed response }; sendResponse(responseToUser); } }; // If undefined or unknown action use the default handler if (!actionHandlers[action]) { action = 'default'; } // Run the proper handler function to handle the request from Dialogflow actionHandlers[action](); // Function to send correctly formatted responses to Dialogflow which are then sent to the user function sendResponse (responseToUser) { // if the response is a string send it as a response to the user if (typeof responseToUser === 'string') { let responseJson = {fulfillmentText: responseToUser}; // displayed response response.json(responseJson); // Send response to Dialogflow } else { // If the response to the user includes rich responses or contexts send them to Dialogflow let responseJson = {}; // Define the text response responseJson.fulfillmentText = responseToUser.fulfillmentText; // Optional: add rich messages for integrations (https://dialogflow.com/docs/rich-messages) if (responseToUser.fulfillmentMessages) { responseJson.fulfillmentMessages = responseToUser.fulfillmentMessages; } // Optional: add contexts (https://dialogflow.com/docs/contexts) if (responseToUser.outputContexts) { responseJson.outputContexts = responseToUser.outputContexts; } // Send the response to Dialogflow console.log('Response to Dialogflow: ' + JSON.stringify(responseJson)); response.json(responseJson); } } } const richResponseV2Card = { 'title': 'Title: this is a title', 'subtitle': 'This is an subtitle. Text can include unicode characters including emoji 📱.', 'imageUri': 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png', 'buttons': [ { 'text': 'This is a button', 'postback': 'https://assistant.google.com/' } ] }; const richResponsesV2 = [ { 'platform': 'ACTIONS_ON_GOOGLE', 'simple_responses': { 'simple_responses': [ { 'text_to_speech': 'Spoken simple response', 'display_text': 'Displayed simple response' } ] } }, { 'platform': 'ACTIONS_ON_GOOGLE', 'basic_card': { 'title': 'Title: this is a title', 'subtitle': 'This is an subtitle.', 'formatted_text': 'Body text can include unicode characters including emoji 📱.', 'image': { 'image_uri': 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png' }, 'buttons': [ { 'title': 'This is a button', 'open_uri_action': { 'uri': 'https://assistant.google.com/' } } ] } }, { 'platform': 'FACEBOOK', 'card': richResponseV2Card }, { 'platform': 'SLACK', 'card': richResponseV2Card } ]; | cs |
그러면 성공!
v2 parameters
v2 에서 parameter를 불러오는 방법을 찾는데 가장 시간이 오래 걸렸다.
도움을 주었던 참고자료
Dialogflow 에서 parameter를 전송하는 것에 관한 이야기
https://discuss.api.ai/t/how-to-send-parameters-to-api-ai-using-nodejs-sdk/9876/7
다만, v2 알아둘 것은 v2에서 parameters는 result 가 아니라 queryResult. 이다.
Dialogflow v1과 v2 비교 공식 문서를 가면 알 수 있다. [클릭하면 비교 공식문서로]
request.body.result.parameters.color 가 아니라 parameters.color 라고 쓴 이유
여기서 받아주었기 때문에 밑에서 그냥 parameters.color 라고 써도 된다!
추가
v1 버전
action handler 에 똑같이 make_name action 만들어주고 코드를 다음과 같이!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 'make_name': () => { // Use the Actions on Google lib to respond to Google requests; for other requests use JSON if (requestSource === googleAssistantRequest) { let responseToUser = { //googleRichResponse: googleRichResponse, // Optional, uncomment to enable //googleOutputContexts: ['weather', 2, { ['city']: 'rome' }], // Optional, uncomment to enable speech: 'Alright, your silly name is ' + parameters.color + ' ' + parameters.number + '! I hope you like it. See you next time' , // spoken response text: 'Alright, your silly name is ' + parameters.color + ' ' + parameters.number + '! I hope you like it. See you next time'// displayed response }; sendGoogleResponse(responseToUser); } else { let responseToUser = { //data: richResponsesV1, // Optional, uncomment to enable //outputContexts: [{'name': 'weather', 'lifespan': 2, 'parameters': {'city': 'Rome'}}], // Optional, uncomment to enable speech: 'Alright, your silly name is ' + parameters.color + ' ' + parameters.number + '! I hope you like it. See you next time', // spoken response text: 'Alright, your silly name is ' + parameters.color + ' ' + parameters.number + '! I hope you like it. See you next time' // displayed response }; sendResponse(responseToUser); } }, | cs |
끝!
'Dialogflow+ Actions on Google ' 카테고리의 다른 글
google assistant Dialogflow event에 관하여 (0) | 2018.04.22 |
---|