Розробка

Архітектура складних чат-ботів

Користувачі, поспілкувавшись з розумними голосовими асистентами, чекають від чат-ботів інтелектуальності. Якщо ви розробляєте бота для бізнесу, очікування ще вище: замовник хоче, щоб юзер пройшов за потрібною, заздалегідь прописаним сценарієм, а юзер — щоб робот толково і бажано людською мовою відповів на поставлені питання, допоміг вирішити проблеми, а іноді просто підтримав світську бесіду.


Ми в Elafris робимо англомовні чат-боти, які спілкуються з користувачами по різних каналах — Facebook Messenger, SMS, Amazon Alexa і веб. Наші клієнти — великі страхові компанії, які представляють white label нашого продукту своїм клієнтам. Бот повинен замінити службу підтримки, страхового агента, і вміти просто побалакати. Кожна з цих завдань вимагає свого підходу в розробці.

У цій статті ми розповімо, з яких модулів складається наш сервіс, як зроблений кожен з них, який підхід ми вибрали і чому. Поділимося нашим досвідом аналізу різних інструментів: коли генеративні нейронні мережі — не кращий вибір, чому замість Doc2vec ми користуємося Word2vec, у чому краса і жах ChatScript і так далі.

На перший погляд може здатися, що проблеми, які ми вирішуємо, досить тривіальні. Однак в області Natural Language Processing існує ряд труднощів, пов’язаних як з технічною реалізацією, так і з людським фактором.

  1. Англійською мовою володіє мільярд людей, і кожен носій використовує його по-своєму: існують різні діалекти, індивідуальні особливості мовлення.
  2. Багато слова, фрази і вирази неоднозначні: характерний приклад — на цій картинці.
  3. Для правильної інтерпретації сенсу слів необхідний контекст. Однак бот, який задає клієнту уточнюючі питання, виглядає не так круто, як той, який може переключитися на будь-яку тему за бажанням користувача і відповісти на будь-яке питання.
  4. Часто в живій мові і листуванні люди або нехтують правилами граматики, або відповідають настільки коротко, що відновити структуру пропозиції практично неможливо.
  5. Іноді для того, щоб відповісти на питання користувача, необхідно звірити його запит з текстами FAQ. При цьому потрібно переконатися, що знайдений в FAQ текст дійсно є відповіддю, а не просто містить кілька збігаються з запитом слів.


Це лише кілька найбільш очевидних аспектів, а ще є сленг, жаргон, гумор, сарказм, помилки правопису і вимови, абревіатури та інші моменти, що ускладнюють роботу у цій сфері.

Для вирішення цих проблем ми розробили бота, який використовує комплекс підходів. AI-частина нашої системи складається з менеджера діалогів, сервісу розпізнавання складних і важливих микросервисов, які вирішують конкретні завдання: Intent Classifier, FAQ-сервіс, Small Talk.

Завести розмову. Dialog Manager

Завдання Dialog Manager в боте Elafris — програмна симуляція спілкування з живим агентом: він повинен провести користувача за сценарієм розмови до якоїсь корисної мети.
Для цього потрібно, по-перше, дізнатися, чого хоче користувач (наприклад, розрахувати вартість страховки на авто), по-друге, з’ясувати необхідну інформацію (адреса та інші дані користувача, дані про водіїв і машинах). Після цього сервіс повинен дати корисний відповідь заповнити форму і видати клієнту результат за цими даними. При цьому ми не повинні запитувати користувача про те, що він вже вказав раніше.

Dialog Manager дозволяє створити такий сценарій: описати програмно, побудувати з маленьких цеглинок — конкретних питань або дій, які повинні відбутися в певний момент. Фактично сценарій являє собою орієнтований граф, де кожен вузол — це повідомлення, питання, дія, а ребро визначає порядок та умови переходу між цими вузлами, якщо є множинний вибір переходу із одного вузла в інші.
Основні типи вузлів

  • Вузли, які чекають, поки до них дійде черга, і вони відображаються в повідомленнях.
  • Вузли, які чекають, поки користувач виявить певну намір (наприклад, напише: «Я хочу отримати страховку»).
  • Вузли, які очікують дані від користувача, щоб провалидировать їх і зберегти.
  • Вузли для реалізації різних алгоритмічних конструкцій (циклів, розгалужень і т. д.).

Якщо вузол закритий, управління йому більше не передасться, і користувач не побачить питання, який був уже заданий. Таким чином, якщо зробити пошук в глибину з такого графу до першого відкритого сайту, ми отримаємо питання, яке необхідно задати користувачеві в даний момент часу. По черзі відповідаючи на запитання, які генерує Dialog Manager, користувач поступово закриє всі вузли в графі, і буде вважатися, що запропонований сценарій він виконав. Тоді ми, наприклад, видаємо користувачеві опис варіантів страховки, які ми можемо запропонувати.

«Я вже все сказав!»

Припустимо, ми запитаємо в ім’я користувача, а він в одному повідомленні також видасть свою дату народження, ім’я, стать, сімейний стан, адреса, або надішле фотографію своїх водійських прав. Система витягне всі значущі дані і закриє відповідні вузли, тобто питання про дату народження та стать задаватися вже не будуть.

«А до речі…»

Також в Dialog Manager реалізована можливість одночасного спілкування на кілька тем. Наприклад, користувач каже: «Я хочу отримати страховку». Потім, не закінчивши цей діалог, додає: «Я хочу зробити оплату за раніше прикріпленому полісом». У таких випадках Dialog Manager зберігає контекст першої теми, і після завершення другого сценарію пропонує відновити попередній діалог з того місця, де він був перерваний.

Є можливість повертатися до питань, на які користувач вже відповів раніше. Для цього система зберігає снепшот графа при отриманні кожного повідомлення від клієнта.

Які варіанти?

Крім нашого, ми розглядали ще один AI-підхід до реалізації менеджера діалогів: на вхід нейронної мережі подається намір користувача та параметри, а система сама генерує відповідні стану, наступне питання, яке потрібно задати. Однак на практиці такий спосіб вимагає додавання rule based підходу. Можливо, такий варіант реалізації підійде для тривіальних сценаріїв — наприклад, для замовлення страв, де треба отримати лише три параметри: що користувач хоче замовити, коли він хоче отримати замовлення і куди його привезти. Але у випадку складних сценаріїв, як у нашої предметної області, це поки недосяжно. На даний момент технології machine learning не здатні якісно провести користувача до мети по складному сценарієм.

Dialog Manager написаний на Python, Tornado framework, оскільки спочатку наша AI-частина писалася як єдиний сервіс. Був обраний мову, на якому все це можна реалізувати, не витрачаючи ресурси на комунікацію.

«Давай визначимося». Recognition Service

Наш продукт вміє комунікувати по різних каналах, але AI-частина є повністю клієнт незалежній: ця комунікація доходить тільки у вигляді проксированного тексту. Менеджер діалогів передає контекст, відповідь користувача та зібрані дані Recognition Service, який відповідає за розпізнавання наміри користувача та вилучення потрібних даних.
Сьогодні Recognition Service складається з двох логічних частин: Recognition Manager, який управляє пайплайном розпізнавання, і екстракторів.

Recognition Manager

Recognition Manager відповідає за всі базові етапи розпізнавання сенсу мови: токенизацию, лемматизацию і т. д. Також він визначає порядок екстракторів (об’єктів, які розпізнають сутність і ознаки в текстах), за якими буде пропущено повідомлення, і вирішує, коли варто зупинити розпізнавання і повернути готовий результат. Це дозволяє запускати тільки необхідні екстрактори в найбільш очікуваному порядку.

Якщо ми запитали, як звуть юзера, то логічно першочергово перевірити, чи прийшло у відповіді ім’я. Ім’я прийшло, і більше корисного тексту немає — значить, розпізнавання можна завершити на цьому кроці. Прийшли ще якісь корисні сутності — значить, розпізнавання потрібно продовжити. Швидше за все, чоловік дописав ще якісь персональні дані — відповідно, потрібно запускати екстрактор обробки персональних даних.

Залежно від контексту порядок запуску екстракторів може змінюватися. Такий підхід дозволяє нам істотно знизити навантаження на весь сервіс.

Екстрактори

Як вже згадувалося вище, екстрактори вміють розпізнавати певні сутність і ознаки в текстах. Наприклад, один екстрактор розпізнає номери телефонів; інший визначає, позитивно чи негативно чоловік відповів на запитання; третій — розпізнає і веріфіцірует адресу в повідомленні; четвертий — дані про транспортний засіб користувача. Проходження повідомлення через набір екстракторів — це і є процес розпізнавання наших вхідних повідомлень.

Для оптимальної роботи будь-якої складної системи необхідно комбінувати підходи. Цього принципу ми дотримувалися при роботі над екстрактора. Виділю деякі принципи роботи, які ми використовували в екстракторах.

Використання наших же микросервисов з Machine Learning всередині (екстрактори відправляють повідомлення на цей сервіс, іноді доповнюють його наявною у них інформацією і повертають результат).

  • Використання POS tagging, Syntactic parsing, Semantic parsing (наприклад, визначення наміри користувача по дієслову)
  • Використання повнотекстового пошуку (може застосовуватися, щоб знайти марку і модель машини в повідомленнях)
  • Використання регулярних виразів і патернів відповідей
  • Використання сторонніх API (таких як Google Maps API, SmartyStreets, і т. д.)
  • Дослівний пошук пропозицій (якщо людина відповів коротко “yep”, то пропускати через це ML-алгоритми для пошуку наміри немає сенсу).
  • Ми також використовуємо в екстракторах вже готові рішення по обробці природної мови.

 

Які варіанти?

Ми розглядали бібліотеки NLTK, Stanford CoreNLP і SpaCy. NLTK першої випадає у видачі Google, коли починаєш ресерч за NLP. Вона дуже крута для прототипування рішень, володіє великою функціональністю і досить проста. Але її продуктивність залишає бажати кращого.

У Stanford CoreNLP є серйозний мінус: вона тягне за собою віртуальну машину Java з дуже великими модулями, вбудованими бібліотеками, і споживає багато ресурсів. Крім того, висновок з цієї бібліотеки складно кастомизировать.

У результаті ми зупинилися на SpaCy, тому що вона має у своєму розпорядженні достатньої для нас функціональністю і володіє оптимальним співвідношенням легковажності і швидкодії. Бібліотека SpaCy працює в десятки разів швидше, ніж NLTK, і пропонує набагато більш якісні словники. При цьому вона набагато легше, ніж Stanford CoreNLP.

На даний момент ми використовуємо spaCy для токенизации, векторизації повідомлення (з допомогою вбудованої навченої нейромережі), первинного розпізнавання параметрів тексту. Оскільки бібліотека покриває лише 5% наших потреб в області розпізнавання, нам довелося дописати безліч функцій.

«Раніше як було…»

Recognition Service не завжди був двухсоставной структурою. Перша версія була тривіальною: ми по черзі задіяли різні екстрактори і намагалися зрозуміти, чи є в тексті ті чи інші параметри, наміри. AI там навіть не пахло — це був повністю rule based підхід. Складність полягала в тому, що одне й те ж можна виразити намір масою способів, кожен з яких потрібно описати в правилах. При цьому необхідно враховувати контекст, оскільки одна і та ж фраза користувача в залежності від поставленого питання може вимагати різних дій. Наприклад, з діалогу: «Ти одружений?» – «Вже два роки» можна зрозуміти, що юзер одружений (boolean значення). А з діалогу «Скільки ти їздиш на цій машині?» – «Вже два роки» потрібно отримати значення «2 роки».

Ми з самого початку розуміли, що підтримка rule based рішення потребуватиме великих зусиль, і з зростанням числа підтримуваних намірів кількість правил збільшуватиметься значно швидше, ніж у випадку з системою на базі ML. Однак точки зору бізнесу. нам потрібно було запустити MVP, a rule based підхід дозволяв зробити це швидко. Тому ми використовували його, а паралельно працювали над ML-моделлю розпізнавання интентов. Як тільки вона з’явилася і почала давати задовільні результати, від rule-based підходу потихеньку почали відходити.

Для більшості випадків екстракції інформації ми використовували ChatScript. Ця технологія надає власний декларативний мову, що дозволяє писати шаблони для екстракції даних з природної мови. Завдяки WordNet під капотом це рішення дуже потужний (наприклад, можна вказати шаблон розпізнавання «колір», і WordNet розпізнає будь звужує поняття, таке як «червоний»). Аналогів ми на той момент не бачили. Але написаний ChatScript дуже криво і глючно, з його використанням практично неможливо реалізувати складну логіку.

У підсумку недоліки переважили, і ми відмовилися від ChatScript на користь NLP-бібліотек Python.
У першій версії Recognition Service ми вперлися в стелю гнучкості. Впровадження кожної нової фічі сильно сповільнювало всю систему в цілому.

Так ми прийняли рішення повністю переписати Recognition Service, розділивши його на дві логічні частини: маленькі, легкі екстрактори і Recognition Manager, який буде керувати процесом.

«Чого ти хочеш?». Intent Classifier

Щоб бот міг адекватно спілкуватися — видавати потрібну інформацію за запитом і фіксувати дані юзера – необхідно визначити намір (интент) користувача на основі відправленого ним тексту. Список интентов, за яким ми можемо взаємодіяти з користувачами, обмежений бізнес-завданнями клієнта: це може бути намір дізнатися умови страховки, заповнити дані про себе, отримати відповідь на часто задається питання і так далі.

До класифікації интентов існує безліч підходів, заснованих на нейронних мережах, зокрема на рекурентних LSTM/GRU. Вони відмінно зарекомендували себе в останніх дослідженнях, однак у них є спільний недолік: для коректної роботи необхідна дуже велика вибірка. На невеликому обсязі даних такі нейромережі небудь складно навчити, або вони видають незадовільні результати. Те ж стосується фреймворку Fast Text від Facebook (ми розглядали його, оскільки це state-of-the-art рішення для обробки коротких і середніх фраз).

Навчальні вибірки у нас дуже якісні: датасеты становить штатна команда лінгвістів, які професійно володіють англійською мовою та знають специфіку страхової галузі. Однак наші вибірки відносно невеликі. Ми намагалися розбавляти їх загальнодоступними датасетами, але ті, за рідкісним винятком, не відповідали нашої специфіки. Також ми пробували залучати фрілансерів c Amazon Mechanical Turk, однак цей спосіб теж виявився неробочим: дані, які вони надсилали, були частково низькоякісними, вибірки доводилося повністю перевіряти.

Тому ми шукали рішення, яке буде працювати на невеликій вибірці. Хороша якість обробки даних продемонстрував класифікатор Random Forest, навчений на даних, які були перетворені в вектора нашої bag-of-words моделі. За допомогою крос-валідації ми підібрали оптимальні параметри. Серед переваг нашої моделі — швидкодія і розмір, а також відносна легкість розгортання і довчання.

У процесі роботи над Intent Classifier стало зрозуміло, що для деяких завдань його використання неоптимально. Припустимо, користувач хоче змінити ім’я, вказане в страховці, або номер автомобіля. Щоб класифікатор правильно визначав цей намір, довелося б вручну додавати в датасет всі шаблонні фрази, які використовуються в такому випадку. Ми знайшли інший вихід: зробити маленький екстрактор для Recognition Service, який визначає намір за ключовими словами і NLP-методів, а Intent Classifier використовувати для нешаблонных фраз, в яких метод з ключовими словами не працює.

«Про це завжди запитують». FAQ

У багатьох наших клієнтів є розділи з FAQ. Щоб користувач міг отримати такі відповіді прямо з чат-бота, необхідно було надати рішення, яке б а) распознавало запит FAQ; б) знаходило б найбільш релевантний відповідь в нашій базі і видавало його.

Є ряд моделей, навчених на датасете SQUAD від Стенфорда. Вони добре працюють в разі, коли текст відповіді з FAQ містить у собі слова з питання користувача. Припустимо, в FAQ написано: «Фродо сказав, що віднесе Кільце в Мордор, хоч і не знає дороги туди». Якщо користувач запитає: «Куди Фродо віднесе Кільце?», система відповість: «Мордор».

У нас сценарій, як правило, був іншим. Наприклад, на два схожих запиту — «Can I pay?» і «Can I pay online?» бот повинен реагувати по-різному: у першому випадку пропонувати людині форму платежу, у другому відповідати — так, заплатити онлайн можна, ось адреса сторінки.

Ще один клас рішень для оцінки схожості документів орієнтований на довгі відповіді — як мінімум кілька пропозицій, серед яких міститься інформація, яка цікавить користувача. На жаль, у випадках з короткими питаннями і відповідями («Як мені сплатити онлайн?» — «Ви можете оплатити за допомогою PayPal») вони працюють дуже нестабільно.

Інше рішення —підхід Doc2vec: великий текст переганяють у векторне подання, яке потім порівнюють з іншими документами в тому ж вигляді і виявляють коефіцієнт схожості. Цей підхід теж довелося відкинути: він орієнтований на довгі тексти, ми в основному маємо справу питаннями і відповідями з одного-двох речень.

Наше рішення ґрунтувалося на двох кроках. Перший: ми, використовуючи вкладень, переводили в вектори кожне слово в реченні, використовуючи модель Word2vec від Google. Після цього ми вважали середній вектор за всіма словами, представляючи одну пропозицію у вигляді одного вектора. Другим кроком ми брали вектор питання і знаходили в базі FAQ, що зберігається у такому ж векторному вигляді, найближчий відповідь по певній мірі, в нашому випадку за косинусної.

До переваг можна віднести легкість імплементації, дуже легку розширюваність і досить просту інтерпретованість. До недоліків — слабку можливість оптимізації: цю модель складно допрацьовувати — вона або працює добре у більшості ваших юзкейсов, або доведеться від неї відмовитися.

«А поговорити?». Small Talk

Іноді користувач пише щось абсолютно нерелевантне, наприклад: «Погода сьогодні хороша». Це не входить в список вакансій, що нас интентов, але ми все одно хочемо відповісти осмислено, продемонструвавши інтелектуальність нашої системи.

Для таких рішень використовується комбінація підходів, описаних вище: в їх основі або дуже прості rule based рішення, або генеративні нейронні мережі. Ми хотіли отримати прототип раніше, тому взяли публічний датасет з інтернету і використовували підхід, дуже схожий на той, що застосовували для FAQ. Наприклад, користувач написав щось про погоду — і ми з допомогою алгоритму, що порівнює векторні представлення двох пропозицій по якійсь косинусної мірою, шукаємо в публічному датасете пропозицію, яка буде якомога ближче до тематики погоди.

Навчання

Зараз у нас немає мети створити бота, який би навчався на кожному повідомленні, прийнятому від клієнтів: по-перше, як показує досвід, це шлях до смерті бота (згадаймо, як IBM Watson довелося прати базу, тому що він став ставити діагнози з матом, а Twitter-бот Microsoft встиг стати расистом всього за добу). По-друге, ми прагнемо якомога якісніше закривати завдання страхових компаній; самонавчальний бот — не наша бізнес-завдання. Ми написали ряд інструментів для лінгвістів і QA-команди, за допомогою яких вони можуть вручну донавчати ботів, досліджуючи діалоги та листування з користувачами ході постмодерації.

Тим не менш, наш бот вже, здається, готовий пройти тест Тьюринга. Деякі користувачі заводять з ним серйозну розмову, вважаючи, що спілкуються зі страховим агентом, а один навіть почав погрожувати скаргою начальнику, коли бот його неправильно зрозумів.

Плани

Зараз ми працюємо над візуальною частиною: відображенням усього графа сценарію і можливістю складати його за допомогою GUI.

З боку Recognition Service ми впроваджуємо лінгвістичний аналіз для розпізнавання і розуміння значення кожного слова в повідомленні. Це дозволить підвищити точність реакції і витягувати додаткові дані. Наприклад, якщо людина заповнює страховку на авто і згадує, що у нього є незастрахований будинок, бот зможе запам’ятати це повідомлення і передати оператору, аби той зв’язався з клієнтом і запропонував страховку житла.

Ще одна фіча в роботі — обробка фідбек. Після завершення діалогу з ботом ми просимо користувача, чи йому сподобалося обслуговування. Якщо Sentiment Analysis розпізнав відгук користувача як позитивний, ми пропонуємо юзеру поділитися думкою в соцмережах. Якщо аналіз показує, що юзер відреагував негативно, бот уточнює, що було не так, фіксує відповідь, говорить: «Окей, ми виправимося», — і не пропонує ділиться відкликанням в стрічці.

Один з ключів до того, щоб спілкування з ботом було максимально природними — зробити бота модульним, розширити набір доступних йому реакцій. Ми працюємо над цим. Може, завдяки цьому користувач готовий був щиро прийняти нашого бота за страхового агента. Наступний крок-зробити так, щоб чоловік намагався оголосити боту подяку.

Related Articles

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Close