如何继续上一个问题的对话流程

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

上下文很强大。