Последние пару недель я экспериментировал с интеграцией отличного API обработки естественного языка (NLP) Wit.ai в стек приложений на основе Node.js. Вот как мы получили рабочий прототип чат-бота (Робата) для Амстердамской публичной библиотеки.

Я предполагаю, что у вас есть базовые знания о том, как Wit.ai обрабатывает предложения и истории. Если вы понятия не имеете, как все это сочетается друг с другом, вам лучше всего прочитать Wit.ai и как его использовать от Дэнни де Фриза от Джулии Building Демистификация диалогового интерфейса с искусственным интеллектом .

Мы решили использовать бета-версию функции Истории Wit.ai вместо того, чтобы использовать собственную бизнес-логику для создания историй.



Начиная

Мы начали с создания относительно простого интерфейса в стиле окна чата для внешнего интерфейса. У этого интерфейса было только две задачи: отправлять сообщения от <input> через соединение WebSocket на сервер и отображать каждое сообщение, приходящее с сервера.

Как только у нас установилось работающее (и стабильное) соединение между клиентом и сервером. Пришло время реально реализовать подключение к Вит.ай.

HTTP API против библиотеки Node.js

Люди, стоящие за Wit.ai, на самом деле предоставляют относительно полнофункциональную библиотеку Node.js (node-wit), чтобы легко начать работу.

Мы шаг за шагом следовали файлам примеров и в итоге получили рабочее общение с Wit.ai. Все было хорошо, по крайней мере, мы так думали.

Вскоре мы столкнулись с множеством ошибок. Наш чат-бот реагировал на начальные вопросы так же, как мы настроили его в интерфейсе Wit.ai, но все последующие сообщения как-то перепутались. Типичное взаимодействие с нашим ботом выглядело следующим образом:

«Привет, я Райк, как дела?»
- «Привет, Райк! Я Робат, чем я могу вам помочь? "

«Я ищу книгу о самолетах»
- «Подписка стоит 40 евро в год».

or

«Я ищу компакт-диск»
- «Компакт-диск! Какого исполнителя вы ищете? »

«Белые полосы»
- «Привет, белые полосы! Я Робат, чем я могу вам помочь? "

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

Контекст - это ключ

Мы решили отказаться от node-wit в пользу простого HTTP-API, чтобы иметь возможность понять, что происходит. Документация HTTP-API очень проста и понятна, в отличие от документации node-wit.

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

Сессии

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

Мы решили добавить настраиваемое stop действие в конце каждой истории, чтобы иметь возможность очищать текущий сеанс после каждой содержащейся в ней истории. После внедрения этого мы перестали беспокоиться о том, чтобы Робат говорил что-то о разговорах, которые уже были проведены.

Контекст

Каждый раз, когда в истории вызывается действие, Wit.ai предоставляет контекст текущего разговора и массив сущностей из текущего сообщения. Сначала мы были очень сбиты с толку тем, что на самом деле должно было быть в этом контексте. Он каждый раз возвращал пустой объект. После некоторого тестирования мы обнаружили две важные вещи:

  • Контекст содержит только то, что мы помещаем в объект и отправляем обратно в Wit.ai.
  • Сущности не хранятся в нескольких сообщениях.

Позвольте мне прояснить последний пункт на примере.
Допустим, у нас есть простая история, которая позволяет пользователю получить текущие погодные условия для своего местоположения. Если бы мы ничего не делали с контекстом, разговор выглядел бы примерно так:

User: "What's the weather like in Amsterdam?"
context: {}
entities: [
  { type: 'intent', value: 'weather' }
  { type: 'location', value: 'Amsterdam' }
Robat: "Let me see.."
context: {}
entities: []
Action: fetchWeather()
context: {} // PROBLEM
entities: []
Robat: "For what location do you want to know the weather?"
context: {
  missingLocation: true
}
entities: []

Проблема здесь в том, что мы потеряли сущности первого сообщения пользователя во время разговора. На данном этапе мы не можем искать погоду, потому что потеряли местоположение. Wit.ai по умолчанию нигде не сохраняет эти объекты.

Мы исправили это, добавив действие merge, которое копирует все сущности в контекст, чтобы иметь возможность использовать эти сущности после того, как Робат что-то сказал. Новый разговор выглядит примерно так:

User: "What's the weather like in Amsterdam?"
context: {}
entities: [
  { type: 'intent', value: 'weather' }
  { type: 'location', value: 'Amsterdam' }
Action: merge()
context: {}
entities: [
  { type: 'intent', value: 'weather' }
  { type: 'location', value: 'Amsterdam' }
Robat: "Let me see.."
context: { location: 'Amsterdam' }
Action: fetchWeather()
context: { location: 'Amsterdam' }
Robat: 'It\'s 16ºC in Amsterdam'
context: {
  location: 'Amsterdam',
  result: '16ºC'
}

Заканчивать

Теперь, когда мы устранили эти две основные проблемы, мы были готовы приступить к реализации других историй в интерфейсе Wit.ai. Мы решили вернуться к node-wit, чтобы реализовать все истории, потому что он обрабатывает двусторонние обращения к HTTP-API за вас (он сильно очищает кодовую базу).

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

Я очень доволен конечным результатом нашего прототипа и надеюсь, что другие тоже создадут классные вещи с Wit.ai.

Вы тоже сталкивались с теми же проблемами, что и мы? Если да, то как вы их решили? Я бы с удовольствием поговорил об этом в комментариях 😊

Https://upscri.be/3e59ef