如何继续上一个问题的对话流程
How to continue the flow of dialog from the previous question
我有一个场景,第一个问题是 "How many people are in 2018" 和 returns 2。然后我想通过询问 "How many male and female are there" 来继续对话。
它应该 return 1 男 1 女,不问从上一个问题推断的年份。
Dialogflow 和我的实现代码如何推断年份?
我试过统计总人数,也可以统计男女人数,但不知道如何自然地继续对话。
function countresponders2018(agent){
const year = agent.parameters.year;
return admin.database().ref('data').once('value')
.then((snapshot) => {
let totalCount=0;
snapshot.forEach( childSnapshot => {
var value = childSnapshot.val();
if (value.SurveyYear==year){
totalCount+=1;
}
});
agent.add(`The total responders are ${totalCount}`);
});
这是一个很好的问题,在设计良好的对话时需要考虑。
我将忽略数据库查询,因为听起来您可能已经控制了这部分,并且它与会话部分没有直接关系。
重述问题
要重新表述您想做的事情,您希望以相同的方式处理以下所有短语:
- 2018年有多少人?
- 2019年有多少人?
- 有多少人?
同理,这些应该都是一样的:
- 2018年有多少男性和女性?
- 2017年男女人数分别是多少?
- 一共有多少男多少女?
- 你能按男性和女性来分解吗?
ie - 用户应该能够提出问题,既可以指定年份,也可以不指定年份。
在对话过程中,如果他们指定了年份,除非他们指定了不同的年份,否则这个值应该是进一步问题的假定值。这很自然 - 人类会在我们的对话中建立短期上下文。
这些还有一个你没有在你的问题中提出的问题——如果他们还没有指定年份,如何处理一个问题。良好的对话设计会建议您询问年份,然后执行最初提出的问题。所以对话可能是这样的:
User: How many people are there?
Agent: What year would you like that for?
User: 2018
Agent: There were 2 people in 2018
总体方法:上下文
幸运的是 - Dialogflow 直接通过 Contexts 支持这个概念。这使您可以在用户的陈述之间保存参数,因此这是为以后的问题存储年份的好方法。
您还可以创建仅在特定上下文处于活动状态时才能触发的 Intent。这有助于确保哪些 Intent 在对话的某些部分有意义。
对于这类问题,有两种使用上下文的好方法。虽然每一种都有取舍,但你使用哪一种很大程度上取决于个人风格。如果我们还没有年份,您还可以使用上下文来支持您需要询问年份的情况。
方法 1:每个问题单一意图
使用此方案,您将有一个 Intent 来响应用户的每个问题。您的履行将查看是否设置了年份参数,如果是,则将该参数用作年份并将其设置在上下文的参数中。如果未设置 - 则使用上下文中参数的值。
所以我们的 "askPeople" Intent 可能有我们上面谈到的短语:
- [年]有多少人?
- [年]有多少人?
- 有多少人?
我们定义"year"作为@sys.number-integer
的一个参数。 (其他实体类型也是可能的,但现在就可以了。)
如果您使用的是 dialogflow-fulfillment 库,我们的处理函数可能如下所示:
function askPeople( agent ){
// Try to get the year from the context first
let context = agent.context.get('RememberYear');
let year = context && context.parameters && context.parameters.year;
// If we don't have a year, get it from the phrase parameters
year = year || agent.parameters.year;
if( year ){
// If we do have a value, get the result, reply,
// and save the year in the context.
return getPeople( year )
.then( count => replyPeople( agent, year, count ) );
} else {
// We don't have a value
// FIXME: We'll discuss what to do about this below
}
}
您需要将 getPeople()
函数写入 return 解析为数据库结果的 Promise。我还特意把 replyPeople()
函数去掉了。它可能看起来像这样
function replyPeople( agent, year, count ){
agent.context.set({
name: 'RememberYear',
lifespan: 99,
parameters:{
year: year
}
});
agent.add( `The total count for ${year} was ${count}.` );
}
方法 2:每个问题多个意图
有了这个,我们将有两个不同的 Intent 来处理这个问题。一个接受带有年份的它,而另一个处理没有年份的短语。最大的区别是训练短语中没有年份的那个需要设置 "RememberYear" 上下文才能触发。
基本 Intent ("askPeopleYear") 是相当熟悉的训练短语,例如
- [年]有多少人?
- [年]有多少人?
和"year"参数定义同上。
我们的另一个 Intent ("askPeopleNoYear") 将设置 "RememberYear" 的输入上下文并有一个训练短语,例如
- 有多少人?
并且不会有任何参数。
我们可能需要第三个 Intent,或者至少需要额外的方法来处理如果 "RememberYear" 上下文 未设置 会发生什么,但是它们说那句话。我们将在下面讨论这个问题。
实现代码需要两个不同的处理函数,它们可能看起来像这样
function askPeopleYear( agent ){
// We know the year is in the parameter
let year = agent.parameters.year;
// Get the result, reply, and set the context
return getPeople( year )
.then( count => replyPeople( agent, year, count ) );
}
function askPeopleNoYear( agent ){
// We know the year is in the context
let context = agent.context.get('RememberYear');
let year = context && context.parameters && context.parameters.year;
// Get the result, reply, and set the context
return getPeople( year )
.then( count => replyPeople( agent, year, count ) );
}
getPeople()
和 replyPeople()
函数与我们之前的方法相同。
处理未设置年份
那么,如果他们说 "How many people are there",但我们没有为 "RememberYear" 上下文设置值,会发生什么?
在第一种方法中,这达到了我们用 "FIXME" 标记的 else
条件。在第二种方法中,如果我们不放置其他东西,就会触发 Fallback Intent,这对用户没有真正的帮助。
在任何一种情况下,我们应该做的是询问用户他们想要的年份并创建一个 Intent 来捕获年份。由于我们可能需要针对不同的问题类型执行此操作,因此我们还应该将要执行的函数存储在...您猜对了...上下文中。因此,假设我们设置了一个 "NeedsYear" 上下文,其参数名为 "functionName" 以跟踪我们需要调用哪个函数。
此 Intent(我们将其命名为 "provideYear")将需要 "NeedsYear" 输入上下文并且可能具有训练短语,例如:
- [年份]
- 获取[年]
并采用 "year" 参数,与我们在上面定义的相同。 (我们甚至可能将其标记为必需。)
这个处理程序可能类似于
function provideYear( agent ){
// We know we have the parmeter
let year = agent.parameters.year;
// We also know we have the context with the function name
let context = agent.context.get('NeedsYear');
let functionName = context && context.parameters && context.parameters.functionName;
// We should clear this context, since we no longer need the year
agent.context.set({
name: 'NeedsYear',
lifespan: 0
};
// And then call whichever function is appropriate
if( functionName === 'people' ){
return getPeople( year )
.then( count => replyPeople( agent, year, count ) );
} else if( functionName === 'gender' ){
// ...
}
}
总结
使用上下文。
上下文是可以触发 Intent 的良好守门人,也是在对话轮次之间存储值的好方法。
上下文很强大。
我有一个场景,第一个问题是 "How many people are in 2018" 和 returns 2。然后我想通过询问 "How many male and female are there" 来继续对话。 它应该 return 1 男 1 女,不问从上一个问题推断的年份。
Dialogflow 和我的实现代码如何推断年份?
我试过统计总人数,也可以统计男女人数,但不知道如何自然地继续对话。
function countresponders2018(agent){
const year = agent.parameters.year;
return admin.database().ref('data').once('value')
.then((snapshot) => {
let totalCount=0;
snapshot.forEach( childSnapshot => {
var value = childSnapshot.val();
if (value.SurveyYear==year){
totalCount+=1;
}
});
agent.add(`The total responders are ${totalCount}`);
});
这是一个很好的问题,在设计良好的对话时需要考虑。
我将忽略数据库查询,因为听起来您可能已经控制了这部分,并且它与会话部分没有直接关系。
重述问题
要重新表述您想做的事情,您希望以相同的方式处理以下所有短语:
- 2018年有多少人?
- 2019年有多少人?
- 有多少人?
同理,这些应该都是一样的:
- 2018年有多少男性和女性?
- 2017年男女人数分别是多少?
- 一共有多少男多少女?
- 你能按男性和女性来分解吗?
ie - 用户应该能够提出问题,既可以指定年份,也可以不指定年份。
在对话过程中,如果他们指定了年份,除非他们指定了不同的年份,否则这个值应该是进一步问题的假定值。这很自然 - 人类会在我们的对话中建立短期上下文。
这些还有一个你没有在你的问题中提出的问题——如果他们还没有指定年份,如何处理一个问题。良好的对话设计会建议您询问年份,然后执行最初提出的问题。所以对话可能是这样的:
User: How many people are there?
Agent: What year would you like that for?
User: 2018
Agent: There were 2 people in 2018
总体方法:上下文
幸运的是 - Dialogflow 直接通过 Contexts 支持这个概念。这使您可以在用户的陈述之间保存参数,因此这是为以后的问题存储年份的好方法。
您还可以创建仅在特定上下文处于活动状态时才能触发的 Intent。这有助于确保哪些 Intent 在对话的某些部分有意义。
对于这类问题,有两种使用上下文的好方法。虽然每一种都有取舍,但你使用哪一种很大程度上取决于个人风格。如果我们还没有年份,您还可以使用上下文来支持您需要询问年份的情况。
方法 1:每个问题单一意图
使用此方案,您将有一个 Intent 来响应用户的每个问题。您的履行将查看是否设置了年份参数,如果是,则将该参数用作年份并将其设置在上下文的参数中。如果未设置 - 则使用上下文中参数的值。
所以我们的 "askPeople" Intent 可能有我们上面谈到的短语:
- [年]有多少人?
- [年]有多少人?
- 有多少人?
我们定义"year"作为@sys.number-integer
的一个参数。 (其他实体类型也是可能的,但现在就可以了。)
如果您使用的是 dialogflow-fulfillment 库,我们的处理函数可能如下所示:
function askPeople( agent ){
// Try to get the year from the context first
let context = agent.context.get('RememberYear');
let year = context && context.parameters && context.parameters.year;
// If we don't have a year, get it from the phrase parameters
year = year || agent.parameters.year;
if( year ){
// If we do have a value, get the result, reply,
// and save the year in the context.
return getPeople( year )
.then( count => replyPeople( agent, year, count ) );
} else {
// We don't have a value
// FIXME: We'll discuss what to do about this below
}
}
您需要将 getPeople()
函数写入 return 解析为数据库结果的 Promise。我还特意把 replyPeople()
函数去掉了。它可能看起来像这样
function replyPeople( agent, year, count ){
agent.context.set({
name: 'RememberYear',
lifespan: 99,
parameters:{
year: year
}
});
agent.add( `The total count for ${year} was ${count}.` );
}
方法 2:每个问题多个意图
有了这个,我们将有两个不同的 Intent 来处理这个问题。一个接受带有年份的它,而另一个处理没有年份的短语。最大的区别是训练短语中没有年份的那个需要设置 "RememberYear" 上下文才能触发。
基本 Intent ("askPeopleYear") 是相当熟悉的训练短语,例如
- [年]有多少人?
- [年]有多少人?
和"year"参数定义同上。
我们的另一个 Intent ("askPeopleNoYear") 将设置 "RememberYear" 的输入上下文并有一个训练短语,例如
- 有多少人?
并且不会有任何参数。
我们可能需要第三个 Intent,或者至少需要额外的方法来处理如果 "RememberYear" 上下文 未设置 会发生什么,但是它们说那句话。我们将在下面讨论这个问题。
实现代码需要两个不同的处理函数,它们可能看起来像这样
function askPeopleYear( agent ){
// We know the year is in the parameter
let year = agent.parameters.year;
// Get the result, reply, and set the context
return getPeople( year )
.then( count => replyPeople( agent, year, count ) );
}
function askPeopleNoYear( agent ){
// We know the year is in the context
let context = agent.context.get('RememberYear');
let year = context && context.parameters && context.parameters.year;
// Get the result, reply, and set the context
return getPeople( year )
.then( count => replyPeople( agent, year, count ) );
}
getPeople()
和 replyPeople()
函数与我们之前的方法相同。
处理未设置年份
那么,如果他们说 "How many people are there",但我们没有为 "RememberYear" 上下文设置值,会发生什么?
在第一种方法中,这达到了我们用 "FIXME" 标记的 else
条件。在第二种方法中,如果我们不放置其他东西,就会触发 Fallback Intent,这对用户没有真正的帮助。
在任何一种情况下,我们应该做的是询问用户他们想要的年份并创建一个 Intent 来捕获年份。由于我们可能需要针对不同的问题类型执行此操作,因此我们还应该将要执行的函数存储在...您猜对了...上下文中。因此,假设我们设置了一个 "NeedsYear" 上下文,其参数名为 "functionName" 以跟踪我们需要调用哪个函数。
此 Intent(我们将其命名为 "provideYear")将需要 "NeedsYear" 输入上下文并且可能具有训练短语,例如:
- [年份]
- 获取[年]
并采用 "year" 参数,与我们在上面定义的相同。 (我们甚至可能将其标记为必需。)
这个处理程序可能类似于
function provideYear( agent ){
// We know we have the parmeter
let year = agent.parameters.year;
// We also know we have the context with the function name
let context = agent.context.get('NeedsYear');
let functionName = context && context.parameters && context.parameters.functionName;
// We should clear this context, since we no longer need the year
agent.context.set({
name: 'NeedsYear',
lifespan: 0
};
// And then call whichever function is appropriate
if( functionName === 'people' ){
return getPeople( year )
.then( count => replyPeople( agent, year, count ) );
} else if( functionName === 'gender' ){
// ...
}
}
总结
使用上下文。
上下文是可以触发 Intent 的良好守门人,也是在对话轮次之间存储值的好方法。
上下文很强大。