1

我有一个场景,第一个问题是“2018 年有多少人”并返回 2。然后我想通过询问“有多少男性和女性”来继续对话。它应该返回 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}`);
        
    });

4

1 回答 1

2

这是一个很好的问题,在设计好的对话时需要考虑。

我将忽略数据库查询,因为听起来您可以控制这部分,并且它与会话部分没有直接关系。

重述问题

要重新表述您想要做的事情,您希望以相同的方式处理以下所有短语:

  • 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 在对话的某些部分有意义是很有用的。

对于这类问题,有两种使用上下文的好方法。尽管每个都有取舍,但您使用哪一个很大程度上取决于个人风格。如果我们还没有年份,您还可以使用上下文来支持您需要询问年份的场景。

方法 1:每个问题的单一意图

使用此方案,您将拥有一个响应用户的每个问题的 Intent。您的履行将查看是否设置了年份参数,如果设置了,则使用该参数作为年份并将其设置在上下文的参数中。如果未设置 - 然后使用上下文中参数的值。

所以我们的“askPeople”意图可能有我们上面谈到的短语:

  • [年] 有多少人?
  • [年],有多少人?
  • 有多少人?

我们将“年”定义@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()函数以返回一个 Promise,该 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:每个问题的多个意图

有了这个,我们将有两个不同的意图来处理这个问题。一个接受带有年份的短语,而另一个则处理不带年份的短语。最大的区别在于,训练短语中没有年份的将需要设置“RememberYear”上下文才能触发。

基本意图(“askPeopleYear”)是相当熟悉的训练短语,例如

  • [年] 有多少人?
  • [年],有多少人?

而“年份”参数的定义方式与上述相同。

我们的另一个意图(“askPeopleNoYear”)将设置“RememberYear”的输入上下文并有一个训练短语,例如

  • 有多少人?

并且不会有任何参数。

如果没有设置“RememberYear”上下文,我们可能需要第三个 Intent,或者至少是额外的方法来处理,但他们会说那个短语。我们将在下面讨论这个。

实现代码需要两个不同的处理函数,看起来像这样

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()与我们之前的方法相同。

处理无年份设置

那么如果他们说“有多少人”,但我们没有设置“RememberYear”上下文的值,会发生什么?

在第一种方法中,这达到了else我们用“FIXME”标记的条件。在第二种方法中,如果我们不放置其他东西,则会触发一个 Fallback Intent,这并不能真正帮助用户。

无论哪种情况,我们应该做的是询问用户他们想要的年份并创建一个 Intent 来捕获年份。由于我们可能需要针对不同的问题类型执行此操作,因此我们还应该将要执行的函数存储在……您猜对了……上下文中。因此,假设我们设置了一个“NeedsYear”上下文,并带有一个名为“functionName”的参数来跟踪我们需要调用哪个函数。

此 Intent(我们将其命名为“provideYear”)将需要“NeedsYear”输入上下文,并且可能具有训练短语,例如:

  • [年]
  • 得到它[年]

并取一个“年份”参数,与我们上面定义的相同。(我们甚至可以将其标记为必需。)

这个处理程序可能看起来像

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 的良好守门人,也是在对话轮次之间存储值的好方法。

上下文是强大的。

于 2020-04-05T14:19:52.387 回答