Google 助理的事实应用程序
Fact App for Google Assistant
我正在摆弄我的 Google 家并创建一个应用程序来读取有关树懒的事实。我使用 API.AI 创建了代理,我在 Firebase 上托管我的函数并通过网络挂接将它连接到 API.AI。你让 Google 告诉你一个关于树懒的事实,它会回答你是想听 "fun fact" 还是 "science fact." 你的回答决定了什么样的事实 Google会为你朗读。
在 API.AI 上进行测试时,我收到默认的失败响应,但当我查看 JSON 时,它显然是在解析事实类别。我的 javascript 代码基于他们教程使用的 Google 示例 "Facts about Google" 应用程序。下面是 API.AI 测试中的 JSON 以及我的 tellFact()
函数。
如果 JSON 清楚地表明它正在解析正确的类别,为什么我会遇到失败子句?
JSON
{
"id": "2b920a5b-0d17-4c5a-9ac1-18071f078464",
"timestamp": "2017-07-13T20:43:33.307Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "tell me something scientific about sloths",
"action": "tell.fact",
"actionIncomplete": false,
"parameters": {
"fact-category": "science"
},
"contexts": [
{
"name": "_actions_on_google_",
"parameters": {
"fact-category.original": "scientific",
"fact-category": "science"
},
"lifespan": 100
}
],
"metadata": {
"intentId": "ca4fa7f1-aceb-4867-b7c3-cf16d1ce4d79",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false",
"webhookResponseTime": 195,
"intentName": "tell_fact"
},
"fulfillment": {
"speech": "Sorry, I didn't understand. I can tell you fun facts or science facts about sloths. Which one do you want to hear about?",
"messages": [
{
"type": 0,
"speech": "Sorry, I didn't understand. I can tell you fun facts or science facts about sloths. Which one do you want to hear about?"
}
],
...
index.js
const App = require('actions-on-google').ApiAiApp;
const functions = require('firebase-functions');
const TELL_FACT = 'tell.fact';
// API.AI parameter names
const CATEGORY_ARGUMENT = 'category';
const FACT_TYPE = {
FUN: 'fun',
SCIENCE: 'science'
};
const FUN_FACTS = new Set([...]);
const SCIENCE_FACTS = new Set([...]);
...
exports.factsAboutSloths = 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));
// Greet the user and direct them to next turn
function unhandledDeepLinks(app) {
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
app.ask(app.buildRichResponse()
.addSimpleResponse(`Welcome to Facts about Sloths! I'd really rather not talk about ${app.getRawInput()}.` +
`Wouldn't you rather talk about Sloths? I can tell you a fun fact or a science fact about sloths.` +
`Which do you want to hear about?`).addSuggestions(['Fun', 'Science']));
} else {
app.ask(`Welcome to Facts about Sloths! I'd really rather not talk about ${app.getRawInput()}.` +
`Wouldn't you rather talk about Sloths? I can tell you a fun fact or a science fact about sloths.` +
`Which do you want to hear about?`, NO_INPUTS);
}
}
// Say a fact
function tellFact(app) {
let funFacts = app.data.funFacts ? new Set(app.data.funFacts) : FUN_FACTS;
let scienceFacts = app.data.scienceFacts ? new Set(app.data.scienceFacts) : SCIENCE_FACTS;
if (funFacts.size === 0 && scienceFacts.size === 0) {
app.tell('Actually it looks like you heard it all. Thanks for listening!');
return;
}
let factCategory = app.getArgument(CATEGORY_ARGUMENT);
if (factCategory === FACT_TYPE.FUN) {
let fact = getRandomFact(funFacts);
if (fact === null) {
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
let suggestions = ['Science'];
app.ask(app.buildRichResponse()
.addSimpleResponse(noFactsLeft(app, factCategory, FACT_TYPE.SCIENCE))
.addSuggestions(suggestions));
} else {
app.ask(noFactsLeft(app, factCategory, FACT_TYPE.SCIENCE), NO_INPUTS);
}
return;
}
let factPrefix = 'Sure, here\'s a fun fact. ';
app.data.funFacts = Array.from(funFacts);
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
let image = getRandomImage(SLOTH_IMAGES);
app.ask(app.buildRichResponse()
.addSimpleResponse(factPrefix)
.addBasicCard(app.buildBasicCard(fact)
.addButton(LINK_OUT_TEXT, WIKI_LINK)
.setImage(image[0], image[1]))
.addSimpleResponse(NEXT_FACT_DIRECTIVE)
.addSuggestions(CONFIRMATION_SUGGESTIONS));
} else {
app.ask(factPrefix + fact + NEXT_FACT_DIRECTIVE, NO_INPUTS);
}
return;
} else if (factCategory === FACT_TYPE.SCIENCE) {
let fact = getRandomFact(scienceFacts);
if (fact === null) {
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
let suggestions = ['Fun'];
app.ask(app.buildRichResponse()
.addSimpleResponse(noFactsLeft(app, factCategory, FACT_TYPE.FUN))
.addSuggestions(suggestions));
} else {
app.ask(noFactsLeft(app, factCategory, FACT_TYPE.FUN), NO_INPUTS);
}
return;
}
let factPrefix = 'Okay, here\'s a science fact. ';
app.data.scienceFacts = Array.from(scienceFacts);
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
let image = getRandomImage(SLOTH_IMAGES);
app.ask(app.buildRichResponse()
.addSimpleResponse(factPrefix)
.addBasicCard(app.buildBasicCard(fact)
.setImage(image[0], image[1])
.addButton(LINK_OUT_TEXT, WIKI_LINK))
.addSimpleResponse(NEXT_FACT_DIRECTIVE)
.addSuggestions(CONFIRMATION_SUGGESTIONS));
} else {
app.ask(factPrefix + fact + NEXT_FACT_DIRECTIVE, NO_INPUTS);
}
return;
} else {
// Conversation repair is handled in API.AI, but this is a safeguard
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
app.ask(app.buildRichResponse()
.addSimpleResponse(`Sorry, I didn't understand. ` +
`I can tell you fun facts or science facts about sloths. ` +
`Which one do you want to hear about?`)
.addSuggestions(['Fun', 'Science']));
} else {
app.ask(`Sorry, I didn't understand. ` +
`I can tell you fun facts or science facts about sloths. ` +
`Which one do you want to hear about?`, NO_INPUTS);
}
}
}
// Say they've heard it all about this category
function noFactsLeft(app, currentCategory, redirectCategory) {
let parameters = {};
parameters[CATEGORY_ARGUMENT] = redirectCategory;
// Replace the outgoing facts context with different parameters
app.setContext(FACTS_CONTEXT, DEFAULT_LIFESPAN, parameters);
let response = `Looks like you've heard all there is to know about the ${currentCategory} facts of sloths. ` +
`I could tell you about its ${redirectCategory} instead. So what would you like to hear about?`;
return response;
}
let actionMap = new Map();
actionMap.set(UNRECOGNIZED_DEEP_LINK, unhandledDeepLinks);
actionMap.set(TELL_FACT, tellFact);
app.handleRequest(actionMap);
});
tell.fact 意图截图
啊哈!一个容易被忽视的偷偷摸摸的问题。
在 API.AI 中,您已将参数命名为 "fact-category"。
您要求的参数是
let factCategory = app.getArgument(CATEGORY_ARGUMENT);
并将CATEGORY_ARGUMENT定义为
const CATEGORY_ARGUMENT = 'category';
所以它陷入了错误状态,因为你得到了名为 "category" 的参数,它没有被设置,所以 factCategory
最终在每种情况下都是未定义的。
我正在摆弄我的 Google 家并创建一个应用程序来读取有关树懒的事实。我使用 API.AI 创建了代理,我在 Firebase 上托管我的函数并通过网络挂接将它连接到 API.AI。你让 Google 告诉你一个关于树懒的事实,它会回答你是想听 "fun fact" 还是 "science fact." 你的回答决定了什么样的事实 Google会为你朗读。
在 API.AI 上进行测试时,我收到默认的失败响应,但当我查看 JSON 时,它显然是在解析事实类别。我的 javascript 代码基于他们教程使用的 Google 示例 "Facts about Google" 应用程序。下面是 API.AI 测试中的 JSON 以及我的 tellFact()
函数。
如果 JSON 清楚地表明它正在解析正确的类别,为什么我会遇到失败子句?
JSON
{
"id": "2b920a5b-0d17-4c5a-9ac1-18071f078464",
"timestamp": "2017-07-13T20:43:33.307Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "tell me something scientific about sloths",
"action": "tell.fact",
"actionIncomplete": false,
"parameters": {
"fact-category": "science"
},
"contexts": [
{
"name": "_actions_on_google_",
"parameters": {
"fact-category.original": "scientific",
"fact-category": "science"
},
"lifespan": 100
}
],
"metadata": {
"intentId": "ca4fa7f1-aceb-4867-b7c3-cf16d1ce4d79",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false",
"webhookResponseTime": 195,
"intentName": "tell_fact"
},
"fulfillment": {
"speech": "Sorry, I didn't understand. I can tell you fun facts or science facts about sloths. Which one do you want to hear about?",
"messages": [
{
"type": 0,
"speech": "Sorry, I didn't understand. I can tell you fun facts or science facts about sloths. Which one do you want to hear about?"
}
],
...
index.js
const App = require('actions-on-google').ApiAiApp;
const functions = require('firebase-functions');
const TELL_FACT = 'tell.fact';
// API.AI parameter names
const CATEGORY_ARGUMENT = 'category';
const FACT_TYPE = {
FUN: 'fun',
SCIENCE: 'science'
};
const FUN_FACTS = new Set([...]);
const SCIENCE_FACTS = new Set([...]);
...
exports.factsAboutSloths = 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));
// Greet the user and direct them to next turn
function unhandledDeepLinks(app) {
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
app.ask(app.buildRichResponse()
.addSimpleResponse(`Welcome to Facts about Sloths! I'd really rather not talk about ${app.getRawInput()}.` +
`Wouldn't you rather talk about Sloths? I can tell you a fun fact or a science fact about sloths.` +
`Which do you want to hear about?`).addSuggestions(['Fun', 'Science']));
} else {
app.ask(`Welcome to Facts about Sloths! I'd really rather not talk about ${app.getRawInput()}.` +
`Wouldn't you rather talk about Sloths? I can tell you a fun fact or a science fact about sloths.` +
`Which do you want to hear about?`, NO_INPUTS);
}
}
// Say a fact
function tellFact(app) {
let funFacts = app.data.funFacts ? new Set(app.data.funFacts) : FUN_FACTS;
let scienceFacts = app.data.scienceFacts ? new Set(app.data.scienceFacts) : SCIENCE_FACTS;
if (funFacts.size === 0 && scienceFacts.size === 0) {
app.tell('Actually it looks like you heard it all. Thanks for listening!');
return;
}
let factCategory = app.getArgument(CATEGORY_ARGUMENT);
if (factCategory === FACT_TYPE.FUN) {
let fact = getRandomFact(funFacts);
if (fact === null) {
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
let suggestions = ['Science'];
app.ask(app.buildRichResponse()
.addSimpleResponse(noFactsLeft(app, factCategory, FACT_TYPE.SCIENCE))
.addSuggestions(suggestions));
} else {
app.ask(noFactsLeft(app, factCategory, FACT_TYPE.SCIENCE), NO_INPUTS);
}
return;
}
let factPrefix = 'Sure, here\'s a fun fact. ';
app.data.funFacts = Array.from(funFacts);
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
let image = getRandomImage(SLOTH_IMAGES);
app.ask(app.buildRichResponse()
.addSimpleResponse(factPrefix)
.addBasicCard(app.buildBasicCard(fact)
.addButton(LINK_OUT_TEXT, WIKI_LINK)
.setImage(image[0], image[1]))
.addSimpleResponse(NEXT_FACT_DIRECTIVE)
.addSuggestions(CONFIRMATION_SUGGESTIONS));
} else {
app.ask(factPrefix + fact + NEXT_FACT_DIRECTIVE, NO_INPUTS);
}
return;
} else if (factCategory === FACT_TYPE.SCIENCE) {
let fact = getRandomFact(scienceFacts);
if (fact === null) {
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
let suggestions = ['Fun'];
app.ask(app.buildRichResponse()
.addSimpleResponse(noFactsLeft(app, factCategory, FACT_TYPE.FUN))
.addSuggestions(suggestions));
} else {
app.ask(noFactsLeft(app, factCategory, FACT_TYPE.FUN), NO_INPUTS);
}
return;
}
let factPrefix = 'Okay, here\'s a science fact. ';
app.data.scienceFacts = Array.from(scienceFacts);
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
let image = getRandomImage(SLOTH_IMAGES);
app.ask(app.buildRichResponse()
.addSimpleResponse(factPrefix)
.addBasicCard(app.buildBasicCard(fact)
.setImage(image[0], image[1])
.addButton(LINK_OUT_TEXT, WIKI_LINK))
.addSimpleResponse(NEXT_FACT_DIRECTIVE)
.addSuggestions(CONFIRMATION_SUGGESTIONS));
} else {
app.ask(factPrefix + fact + NEXT_FACT_DIRECTIVE, NO_INPUTS);
}
return;
} else {
// Conversation repair is handled in API.AI, but this is a safeguard
if (app.hasSurfaceCapability(app.SurfaceCapabilities.SCREEN_OUTPUT)) {
app.ask(app.buildRichResponse()
.addSimpleResponse(`Sorry, I didn't understand. ` +
`I can tell you fun facts or science facts about sloths. ` +
`Which one do you want to hear about?`)
.addSuggestions(['Fun', 'Science']));
} else {
app.ask(`Sorry, I didn't understand. ` +
`I can tell you fun facts or science facts about sloths. ` +
`Which one do you want to hear about?`, NO_INPUTS);
}
}
}
// Say they've heard it all about this category
function noFactsLeft(app, currentCategory, redirectCategory) {
let parameters = {};
parameters[CATEGORY_ARGUMENT] = redirectCategory;
// Replace the outgoing facts context with different parameters
app.setContext(FACTS_CONTEXT, DEFAULT_LIFESPAN, parameters);
let response = `Looks like you've heard all there is to know about the ${currentCategory} facts of sloths. ` +
`I could tell you about its ${redirectCategory} instead. So what would you like to hear about?`;
return response;
}
let actionMap = new Map();
actionMap.set(UNRECOGNIZED_DEEP_LINK, unhandledDeepLinks);
actionMap.set(TELL_FACT, tellFact);
app.handleRequest(actionMap);
});
tell.fact 意图截图
啊哈!一个容易被忽视的偷偷摸摸的问题。
在 API.AI 中,您已将参数命名为 "fact-category"。
您要求的参数是
let factCategory = app.getArgument(CATEGORY_ARGUMENT);
并将CATEGORY_ARGUMENT定义为
const CATEGORY_ARGUMENT = 'category';
所以它陷入了错误状态,因为你得到了名为 "category" 的参数,它没有被设置,所以 factCategory
最终在每种情况下都是未定义的。