Как продолжить диалог с предыдущего вопроса

У меня есть сценарий, в котором первый вопрос - "Сколько людей в 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}`);
        
    });

1 ответ

Решение

Это отличный вопрос, и о нем важно подумать при разработке хороших разговоров.

Я собираюсь игнорировать запросы к базе данных, поскольку похоже, что вы можете контролировать эту часть, и она не имеет прямого отношения к диалоговым частям.

Повторяю проблему

Чтобы перефразировать то, что вы хотите сделать, вы бы хотели обработать все следующие фразы одинаково:

  • Сколько человек в 2018 году?
  • Сколько там людей в 2019 году?
  • Сколько людей там?

Точно так же все они должны быть одинаковыми:

  • Сколько мужчин и женщин будет в 2018 году?
  • Сколько мужчин и женщин в 2017 году?
  • Сколько здесь мужчин и женщин?
  • Можете ли вы разбить это на мужчин и женщин?

т.е. - пользователи должны иметь возможность задавать вопросы, как с указанием года, так и, возможно, без него.

During the conversation, if they specify the year, this value should be the one assumed for further questions unless they specify a different year. This is pretty natural - humans build up short-term context in our conversations.

These also have an issue you didn't raise in your question - how to handle a question if they haven't yet specified a year. Good conversation design would suggest that you should ask for the year, and then execute the question that was originally asked. So the conversation might be something like this:

User:  How many people are there?
Agent: What year would you like that for?
User:  2018
Agent: There were 2 people in 2018

Overall approach: Contexts

Fortunately - Dialogflow supports this concept directly with Contexts. This lets you save parameters in between statements from the user, so this can be a good way to store the year for later questions.

You an also make Intents that can only be triggered when specific Contexts are active. This can be useful to make sure which Intents make sense at parts of the conversation.

There are two good approaches to using Contexts for these kinds of questions. Although each has trade-offs, which one you use is largely a matter of personal style. You can also use contexts to support the scenario where you need to ask for the year if we don't already have it.

Approach 1: Single Intent per question

With this scheme, you would have a single Intent that responds to each question from the user. Your fulfillment would see if the year parameter is set and, if so, use that parameter as the year and set it in a Context's parameters. If it isn't set - then use the value from the parameters in the Context.

Таким образом, наше намерение "askPeople" может содержать фразы, о которых мы говорили выше:

  • Сколько человек в [год]?
  • Сколько человек в [год]?
  • Сколько людей там?

И мы определяем "год" как параметр @sys.number-integerнапример. (Возможны другие типы сущностей, но пока этого достаточно.)

Если вы используете библиотеку выполнения диалога, наша функция-обработчик для этого может выглядеть примерно так:

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()функция для возврата обещания, которое разрешает результаты из вашей базы данных. Я также вытащил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" не установлен, но они произнесут эту фразу. Об этом мы поговорим ниже.

Для кода выполнения потребуются две разные функции-обработчики, которые могут выглядеть примерно так

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". Во втором подходе резервное намерение сработает, если мы не добавим что-то еще, и это действительно не поможет пользователю.

В любом случае, что мы должны сделать, это спросить у пользователя год, который он хочет, и создать намерение, чтобы зафиксировать год. Поскольку нам может потребоваться сделать это для разных типов вопросов, мы также должны сохранить, какую функцию выполнять, в... как вы уже догадались... Контекст. Итак, давайте предположим, что мы установили контекст "NeedsYear" с параметром с именем "functionName", чтобы отслеживать, какую функцию нам нужно вызвать.

Это намерение (назовем его "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' ){
    // ...
  }
}

Резюме

Используйте контексты.

Контексты служат хорошими привратниками, для которых могут запускаться намерения, а также хорошим способом хранения значений между поворотами разговора.

Контексты сильны.

Другие вопросы по тегам