Разбор вопроса Главная задача при разборе пользовательского запроса — распознать в нем все необходимые для его интерпретации сущности.
Вопрос
«Какая погода сейчас в Токио?» — понятен, так как он содержит и тему вопроса и все необходимые для понимания данной темы параметры: место и время. Вопрос «сейчас в Токио?» непонятен, так как неясна тема вопроса. Вопрос — «какая погода?» тоже не до конца понятен, так как не определены необходимые для данной темы параметры — место и время. Иногда
мы можем использовать значения по умолчанию такие как «дата» — прямо сейчас и «место» — какое-то географическое местоположение по умолчанию для задавшего вопрос. Иногда это работает, а иногда ответ будет абсолютно неверен — особенно когда существует контекст беседы.
Когда
мы первый раз спрашиваем что-то у нашего собеседника, будь то незнакомый человек, наш старый друг, или даже чатбот общего профиля — мы
формулируем наш вопрос максимально правильно и полно, не пропуская ни одного значимого слова.
Иными
словами наш вопрос должен содержать всю необходимую информацию, позволяющую на него ответить, не переспрашивая и не задавая уточняющих вопросов.
«Ты знаешь сейчас какая погода в Токио»? — полный и однозначный вопрос.
Примечание.
Первый вопрос — не обязательно тот с которого мы начинаем беседу. Это может быть первый вопрос в рамках новой темы текущей беседы.
Небольшое
отступление. Очевидно, что мы рассматриваем немного неестественный вопрос при общении между двумя людьми, проще поискать в интернете и узнать все самому, но, предположим Ваш друг, в силу каких-либо причин, обладает эксклюзивной информацией на этот счет. Примем данное допущение в
нашем примере, так как для обсуждения работы диалоговой памяти удобен именно такой простой пример.
Сущности Ниже перечислены сущности, выделенные из вопроса и необходимые для его понимания
«погода»
— предмет разговора, мы спрашиваем именно о погоде. В данном случае слово «погода» — индикатор тематики вопроса, а не параметр с различными допустимыми вариантами значений. «сейчас»
— мы хотим получить информацию на сегодняшний день. В данном случае мы опираемся на значение «сегодня» параметра «дата». «Токио» — нас интересует погода в этом городе, то есть мы используем параметр «город», со значением «Токио». Обратите внимание на два различающихся типа сущностей в вопросе.
Сущности,
идентифицирующие некую тематику, и не имеющие конкретных значений. В нашем примере — «погода» — это сущность, указывающая на тему вопроса и не имеющая списка значений. Сущности,
имеющие значения. В нашем примере «сейчас» — значение неявно присутствующей в вопросе сущности «дата» и «Токио» — значение неявно присутствующей в вопросе сущности «город». Дополнительно стоит отметить,
что значения подобных сущностей дискретны. Неполные вопросы Все последующие диалоговые вопросы на данную тему могут быть сформулированы более кратко:
«А что в Киото?» Опущен предмет разговора и дата. Получив такой вопрос, мы предполагаем, что по прежнему говорим о погоде и о сегодняшнем дне.
«A завтра какая?» Предполагаем, что мы по прежнему говорим о погоде, но уже на завтрашний день. Город мы подразумеваем последний упомянутый.
Мы
будем называть такие вопросы «неполными». Неполные вопросы не могут быть распознаны вне контекста, то есть если они были заданы первыми в беседе. Сами по себе они не содержат необходимого и достаточного набора сущностей для их понимания.
В
контексте беседы мы можем ограничиться неполными вопросами, переопределяя лишь одну или две сущности из трех необходимых в данном примере. Остальные же сущности, при анализе вопроса, берутся из памяти, то есть из контекста беседы или так называемой, диалоговой памяти.
Подробнее о кратковременной памяти в NLPCraft — документация , раздел «Conversation & STM»
Кратковременность памяти Какое основное свойство подобного рода диалоговой памяти? Она относительно кратковременна.
Если
Вы позвоните другу через 2 дня после последнего разговора и спросите: «А послезавтра какая?» он даже не поймет вас. Он утратил контекст беседы. Вам придется терпеливо переспрашивать его заново, используя все те три сущности, про которые мы говорили выше, и лишь после этого вы снова сможете снова какое-то время задавать новые вопросы в сокращенной форме.
В
NLPCraft мы считаем, что если во время беседы была сделана пауза более чем 5 мин — любой контекст разговора должен быть сброшен.
Смена контекста беседы Если
Вы резко меняете контекст беседы и при этом задаете четко сформулированный полный вопрос со всеми необходимыми и достаточными данными для его понимания — вы сбрасываете контекст беседы.
Мы продолжаем разговор о погоде и вдруг:
«Сколько стоит кофе в Старбакс?» Все дальнейшие неполные вопросы уже имеют отношение или к кофе или к заведению Старбакс.
«А в Макдональдс?» Вероятно, вы хотели узнать цену кофе в Макдональдс.
«А почем там донатсы?» Если этот вопрос задан вторым — вам ответят про стоимость донатсов в Старбакс.
Если
третьим — то в Макдональдс, так как значение параметра, идентифицирующего заведение, второго вопроса в данном случае вытеснит соответствующее значение первого. То есть мы забыли о Старбакс и говорим
теперь о Макдональдс.
Заменяемые сущности Из
примеров видно, что во время диалога, сущности иногда вытесняют друг друга из памяти. Так «Макдональдс» в примере вытеснил «Старбакс», но сущность «цена кофе» никуда не ушла.
Тип
сущности (в терминологии NlpCraft группа) определяет будет ли элемент вытеснен новой сущностью при следующем вопросе или нет.
Для того чтобы элементы были организованы максимально гибко, каждый из них может быть отнесен к целому набору групп .
Подробный пример
Пусть модель содержит пользовательских 3 элемента
«sale» (всего один синоним — «продажа») «buy» (один синоним — «закупка») «best_employee» (синонимы — словосочетание «лучший сотрудник» и слово «лучший») Наша задача — поддержать следующий диалог
«Дай мне данные о продажах» Выдаем информацию о продажах, мы нашли в тексте элемент «sell».
«А кто был лучшим?» Возвращаем отчет по лучшим продавцам. Мы распознали элемент «best_employee», а элемент «sell» хотим выбрать из памяти.
«Дай мне данные о закупках» Выдаем
общую информацию о закупках, (так как обнаружен элемент «buy»), но не отчет по лучшим закупщикам, так как нас, очевидно, об этом не просили. То есть элемент «best_employee» должен быть заранее удален из памяти.
«А кто был лучшим?» Ответом должен служить отчет по лучшим закупщикам. Мы распознали «best_employee» , а элемент «buy» следует выбрать из памяти.
«Еще раз дай мне отчет по продажам» Снова выдаем просто информацию о продажах, то есть сущность «best_employee» должна быть уже вытеснена из памяти.
Мы хотим добиться контроля над тем, чтобы в некоторых случаях элементы оставались в памяти, а в некоторых — исчезали.
ПРАВИЛО ВЫТЕСНЕНИЯ
Элемент,
принадлежащий к какому-то набору групп, вытесняет из памяти все элементы относящиеся к такому же набору групп, или набору, являющимся надмножеством данного набора.
То есть, элемент относящийся к меньшему числу групп, вытесняет более общие элементы.
Пример. Элемент, принадлежащий группам («G1», «G2», «G3»)
вытеснит элемент, принадлежащий группам («G1», «G2», «G3») вытеснит элемент, принадлежащий группам («G1», «G2», «G3», «G4») не вытеснит элемент, принадлежащий группам («G1», «G2») (подмножество) не вытеснит элемент, принадлежащий группам («G10», «G11») (другой набор) Еще один пример. Элемент, принадлежащий группе «G1» (частный случай набора групп, состоящего из одного элемента)
вытеснит элемент, принадлежащий группе «G1» вытеснит элемент, принадлежащий группам («G1», «G2») не вытеснит элемент, принадлежащий группам («G10», «G11») (другой набор) Вернемся к нашему сценарию
Итого нам нужно поддержать 4 intents для наших 3 элементов .
«id=sale term={id==’sale’} «id=sale term={id==’sale’} term={id==best_employee}» «id=buy term={id==’buy’} «id=buy_best_person term={id==’buy’} term={id==best_employee}» Для этого просто настроим группы
Элемент «sell» — группа A. Элемент «buy» — группа A. Элемент «best_employee» — входит сразу в 2 группы A и B. Вернемся к диалогу
Дай мне данные о продажах. На входе сущность группы «A».
Смотрим в память — там пусто.
Выдаем информацию о продажах.
В память заносим элемент группы «A».
А кто был лучшим? На входе сущность групп «A, B».
Смотрим в память — там элемент группы «A», складываем.
Выдаем отчет по лучшим продавцам.
В память заносим элемент группы «A» и элемент групп «A, B»
Дай мне данные о закупках. На входе сущность группы «A».
Смотрим в память — там элементы групп «A», «A, B», все удаляем.
Выдаем общую информацию о закупках.
В память заносим элемент группы «A»
А кто был лучшим? На входе сущность групп «A, B».
Смотрим в память — там элемент группы «A», складываем.
Выдаем отчет по лучшим закупщикам.
В память заносим элемент группы"A" и элемент групп «A, B».
Еще раз дай мне отчет по продажам. На входе сущность группы «A».
Смотрим в память — там элементы групп «A», «A, B», все удаляем.
Снова выдаем информацию о продажах.
В память заносим элемент группы «A».
Все просто :)
Предположим, что у нас поменялись планы, и мы хотим поддержать следующий сценарий
«Дай мне данные о продажах» — Выдаем информацию о продажах. «А кто был лучшим?» — Возвращаем отчет по лучшим продавцам. «Дай мне данные о закупках» — Выдаем отчет о лучших закупщиках (в данной версии мы хотим поступить именно так) Для этого перенастроим группы
Элемент «sell» — группа A. Элемент «buy» — группа A. Элемент «best_employee» — группа B. Все
работает, можете проверить. Лишь обратите внимание на то, что элемент «best_employee» группы B в данном случае будет вытеснен из памяти лишь по таймауту или если значительное количество последних пользовательских вопросов будет связано с совершенно другими элементами.
Явная смена контекста Иногда
тему разговора лучше сбросить принудительно, не дожидаясь пока собеседник сам о ней забудет. Так следует поступить, если вы не хотите или не можете сформулировать строгий вопрос, однозначно идентифицирующий
новую тему.
Пример
Ваш последний вопрос собеседнику был «Какая погода в Токио?»
Вам ответили.
Если
Вы тут же выдадите восклицание «Лучше в Нью-Йорк!» — Вам, вероятно, вернут информацию о погоде в Нью-Йорке в настоящее время, ведь в памяти еще жив индикатор беседы «погода» и «дата» по умолчанию «сегодня». Если
же Вы предварительно сбросите тему — "Все! хватит о погоде! Забудем про
это«` — то Вас как минимум переспросят — что значит «Лучше в Нью-Йорк!»? Едем туда? Заказываем билеты? Бронируем отпуск? То есть попробуют уточнить ваш запрос, а не станут пытаться неловко поддержать уже законченную вами тему. В NLPCraft такая явная смена контекста поддерживается просто путем замены актуальной модели при работе с REST API.
Подведем итоги Соберем воедино и сформулируем еще раз все то, что мы уже обсуждали до этого.
Если в середине беседы задаются «неполные» вопросы — значения недостающих сущностей могут быть получены из диалоговой памяти. Появление в вопросе новой сущности, не имеющей значений, свидетельствует, скорее всего, о смене темы беседы Диалоговая память кратковременна и сама очистится через какое-то время или же может быть сброшена принудительно. Принудительная очистка памяти может помочь при определении темы последующего вопроса и упросить поиск соответствующего ему intent . Заключение Следуя
логике приведенных простых правил, мы можем проектировать наши диалоги в
стиле максимально приближенном к естественному человеческому общению, но и не усложняя чрезмерно систему без излишней на то надобности.
Излишнее
усложнение подобных систем приносит как правило совсем немного совершенно неочевидных плюсов, а также значительно усложняет систему, затрудняет отладку и формирует совершенно непредсказуемую логику ответов.
Старайтесь
быть проще — пользуйтесь контекстом беседы, когда это удобно и нужно, и
наоборот, очищайте память, если хотите форсировать смену темы разговора.