- Создание чатбота с вопросами и ответами
- Введение
- Разговорный поток
- Создание
- Объявление нашего чатбота
- Организация наших диалогов
- Написание диалогов
- Корневой диалог (/dialogs/root-trailing.js):
- Intent dialog (/dialogs/intent-incoming.js):
- Диалог FAQ (/dialogs/faq-trailing.js):
- Диалог «Пока» (/dialogs/bye-trailing.js):
- Шлюз сообщений
- Получение сообщений
- Отправка сообщений
- Шлюз:
- Тестирование
- Заключение
Создание чатбота с вопросами и ответами
Введение
Чатбот FAQ призван отвечать на самые распространенные вопросы, задаваемые пользователями. Мы создадим такой чатбот, используя Bard и некоторые его функции, которые помогут нам создать более умный чатбот.
Bard — это фреймворк для создания чатботов, написанный на NodeJS/Typescript, но вы можете использовать его и в своих NodeJS/Javascript проектах.
Разговорный поток
Мы хотим разработать интуитивно понятный чатбот, который не зависит от стандартного потока диалогов. Разговор должен быть интуитивно понятным. Пользователь должен иметь возможность задать любой вопрос в любой момент разговора, а чатбот должен на него ответить. Мы можем достичь этого, написав диалог, который ожидает вопросов и отправляет соответствующие ответы, используя входящий слой.
Основная часть разговора будет написана с помощью слоя trailing, где мы можем манипулировать и перенаправлять взаимодействие между диалогами.
Выше мы видим основной поток. Чатбот спрашивает и ждет вопроса пользователя. При взаимодействии с пользователем он пытается получить ответ на основе его ввода, затем показывает ответ, если он получен, в противном случае повторяет попытку (максимум 3 попытки). Затем чатбот говорит «пока» и завершает разговор.
Создание
Объявление нашего чатбота
Прежде всего, нам нужно настроить наш проект:
npm init
npm i --save bard-builder@1.6.4 express
Теперь мы должны импортировать Bard и объявить наш чатбот.
Давайте создадим файл main.js
:
const {Bot} = require("bard-builder");
const main = function() {
/* declare the chatbot instance with the default state */
const bot = new Bot({state: {answer_max_tries: 0}});
/* here we declare the dialogs */
/* here we start the chatbot */
/* here we setup and start the message gateway */
}
main();
Организация наших диалогов
Чтобы начать писать диалоги, нам нужно поместить каждый диалог в отдельный файл в папке с именем dialogs
. Это поможет нам построить и визуализировать диалог.
└── dialogs
├── root-trailing.js
├── faq-trailing.js
├── bye-trailing.js
└── faq-incoming.js
Теперь нам нужно связать все эти диалоги в нашем объявленном экземпляре Bot
. Для этого нам потребуется создать еще один файл с именем flow.js
. Структура папки будет выглядеть следующим образом:
└── main.js // where we declare and setup our chatbot
└── flow.js // link and setup the dialogs
└── dialogs
├── root-trailing.js
├── faq-trailing.js
├── bye-trailing.js
└── intent-incoming.js
const root_trailing = require("./dialogs/root-trailing");
const faq_trailing = require("./dialogs/faq-trailing");
const bye_trailing = require("./dialogs/bye-trailing");
const intent_incoming = require("./dialogs/intent-incoming");
/*
export a function that receives the chatbot as a parameter, then link the dialogs to it
*/
module.exports = function(bot) {
/* this can be used to pass dependencies to dialogs */
const deps = {};
/* link dialogs into our chatbot */
bot.trailing("root", root_trailing(deps));
bot.trailing("faq", faq_trailing(deps));
bot.trailing("bye", bye_trailing(deps));
bot.incoming("intent", intent_incoming(deps));
}
И нам нужно изменить нашу функцию main function
внутри файла main.js
, чтобы настроить поток:
const {Bot} = require("bard-builder");
const main = function() {
/* declare and setup the chatbot instance */
const bot = new Bot({state: {answer_max_tries: 0}});
setup_flow(bot);
/* here we start the chatbot */
/* here we setup and start the message gateway */
}
main();
Написание диалогов
Корневой диалог (/dialogs/root-trailing.js
):
Теперь мы можем начать писать эти пустые диалоги. Диалог root trailing dialog
будет отвечать за приветствие пользователя и перенаправление к диалогу faq trailing dialog
:
/* export a function that returns the dialog (array of functions) */
module.exports = function(deps) {
return [
(session, course) => {
/* get known data */
const is_known = session.state.known_greeting;
/* if user already interacted, then send a different message to him */
let greeting_message = "Hello! I am FAQ Chatbot!";
if (is_known) greeting_message = "Hello again!";
session.send(greeting_message);
/* set known to true */
session.state.known_greeting = true;
/* redirect interation to the faq trailing dialog */
return course.replace("faq");
}
];
}
Intent dialog (/dialogs/intent-incoming.js
):
Теперь мы должны написать наш intent incoming dialog
, который будет отвечать за понимание вводимых пользователем данных и проверку того, является ли это правильным вопросом.
Нам нужно будет создать таблицу «ответ-вопрос» для валидации пользовательского ввода. Вы можете использовать конфигурационный файл JSON, но мы просто напишем его внутри файла .dialogs/intent-incoming.js
.
Если пользователь вводит правильный вопрос, то он сохранит ответ в session.state.answer
.
Поскольку это диалог входящего уровня, взаимодействие не прекратится после достижения конца. Он будет продолжаться до тех пор, пока не достигнет предыдущего слоя, если вы не остановите его (вручную, опустив course.next()
на последнем шаге).
Вам следует заменить валидацию вопроса на какой-нибудь когнитивный движок. Их существует множество, в том числе и бесплатные.
const questions_list = {
"who are you?": "I am a just a chatbot, that's sad because I even have a name :/",
"what is a chatbot?": "Chatbot is a applicati0n th47 coNDuc7 4 c0nv3rS47i0 i7h um4n",
"what is your purpose?": "Not to pass butter, sadly."
};
/* export a function that returns the dialog (array of functions) */
module.exports = function(deps) {
return [
(session, course) => {
/* get the user input */
const user_input = session.getMessage().data;
if (!(user_input && user_input.length)) {
return course.next();
}
/* check if user input is a valid question, if so save it in session and redirect it to the faq dialog */
const answer = questions_list[user_input.toLowerCase()];
if (answer) {
session.state.answer = answer;
return course.replace("faq");
}
/* ensure interation to keep going through and reach the trailing layer */
return course.next();
}
];
}
Диалог FAQ (/dialogs/faq-trailing.js
):
Здесь мы можем проверить наличие предыдущего установленного значения для сессии answer
. Если оно существует, отправьте ответ. В противном случае отправьте ответ в начало faq trailing dialog
, если повторных попыток будет более 2 раз, скажите «пока» и завершите сессию.
/* export a function that returns the dialog (array of functions) */
module.exports = function(deps) {
return [
(session, course) => {
/* if have an answer, jump to the next step */
const have_answer = session.state.answer;
if (have_answer) return course.next();
session.send("Can I help you?");
return course.wait();
},
(session, course) => {
const have_answer = session.state.answer;
if (!have_answer) {
/* if retries reaches more than 2 times, say bye and end the session */
let max_tries = session.state.answer_max_tries || 0;
if (max_tries >= 2) {
session.send("I can't help you if I can't understand you.");
/* reset tries counter */
session.state.answer_max_tries = 0;
return course.replace("bye");
}
session.send("Sorry, I don't have an answer to that.");
session.state.answer_max_tries = ++max_tries;
return course.replace("faq");
}
/* reset tries counter */
session.state.answer_max_tries = 0;
/* send answer and set its session value to null */
session.send(have_answer);
session.state.answer = null;
return course.next();
},
(session, course) => {
/* ask if want to ask another question */
session.send("Want to ask it again?");
return course.wait();
},
(session, course) => {
/* if response is yes, redirect to the faq dialog again, if not say bye */
const response = session.getMessage().data;
if (response != "yes" && response != "y") {
session.send("Alright!");
return course.replace("bye");
}
return course.replace("faq");
}
];
}
Диалог «Пока» (/dialogs/bye-trailing.js
):
Здесь мы прощаемся с нашим пользователем.
/* export a function that returns the dialog (array of functions) */
module.exports = function(deps) {
return [
(session, course) => {
session.send("Goodbye! I hope I've been helpful!");
return session.end()
}
];
}
Шлюз сообщений
Теперь, когда мы написали все диалоги, мы можем начать писать шлюз сообщений. Вы можете использовать bot.push(message)
для вставки исходящего сообщения или bot.pull()
для получения входящего сообщения.
Для этого создайте файл с именем gateway.js
в папке нашего проекта:
└── main.js // where we declare and setup our chatbot
└── flow.js // link and setup the dialogs
└── gateway.js // create the message gateway (receiving and sending messages)
└── dialogs
├── root-trailing.js
├── faq-trailing.js
├── bye-trailing.js
└── intent-incoming.js
Получение сообщений
Вы, вероятно, получаете сообщения от брокера сообщений через webhook, поэтому нам нужно будет создать его (вы можете использовать другие фреймворки, но для упрощения мы будем использовать "express"
, это отличный надежный фреймворк).
Отправка сообщений
Чтобы отправить ответ на сообщения, отправленные потоком беседы, в ответ на полученные, мы можем использовать функцию bot.pull()
. Она вытащит исходящее сообщение из потока беседы. Мы можем сделать это, создав систему вытягивания и отправив все исходящие сообщения нашему брокеру сообщений.
Шлюз:
Итак, мы создаем webhook для получения сообщений и pull-систему для отправки сообщений брокеру сообщений (вашему брокеру) — вы можете заменить его. Нам нужно поместить приведенный выше код внутрь ранее созданного файла gateway.js
:
const {Message, MessageTypes} = require("bard-builder");
const express = require("express");
module.exports = class Gateway {
constructor(port, bot) {
this.port = port;
this.bot = bot;
/* declare message broker (mock) */
this.message_broker = {
sendMessage: (message) => console.log("Simulating sending message:", message.data)
};
/* declare webhook server */
this.server = express();
/* to parse JSON body */
this.server.use(express.json());
/* declare endpoit for receiving messages */
this.server.post("/receive/message", (request, response) => {
const body = request.body;
const message = new Message(
body.contact, body.session, body.origin, body.data
);
/* use bot.push(message_object) to send a message to the conversation flow */
this.bot.push(message);
return response.status(200).send("OK - Message received!");
});
this.server.listen(this.port);
}
pullProcess() {
/* get message from chatbot */
const message = this.bot.pull();
/* if it is an Error instance, re-run this with delay (probably empty) */
if (message instanceof Error) {
return setTimeout(() => this.pullProcess(), 500);
}
/* send message to message broker */
this.message_broker.sendMessage(message);
/* re-run this */
return setImmediate(() => this.pullProcess());
}
}
Выше мы получаем входящее сообщение от webhook и создаем/вставляем экземпляр Message
в поток взаимодействия с помощью bot.push(message)
.
Каждый раз, когда это происходит, в потоке разговора выполняется новое взаимодействие.
Вы можете создать переключатель для обработки всех типов входящих сообщений и установить соответствующий тип в экземпляр
Message
.
Мы объявляем наш брокер сообщений и создаем функцию, которая вызывает себя несколько раз для получения сообщений из потока взаимодействия. Функция извлечения пытается получить сообщение, и в случае неудачи будет ждать некоторое время для повторного запуска (возможно, очередь пуста). В случае успеха она отправит сообщение нашему брокеру сообщений и снова вызовет функцию. Используя этот механизм, мы можем гарантировать, что не заблокируем поток, только вытаскивая сообщения. Мы перепланируем эти вызовы так, чтобы они подходили везде, где это возможно (используя setImmediate()
и позволяя другим частям кода дышать и работать гладко.
И чтобы добавить это в систему чатбота, мы должны снова изменить наш файл main.js
:
const {Bot} = require("bard-builder");
const setup_flow = require("./flow.js");
const Gateway = require("./gateway.js");
const main = function() {
/* declare and setup the chatbot instance */
const bot = new Bot({name: "my-faq-bot"});
setup_flow(bot);
/* here we start the chatbot */
bot.start();
/*
declare gateway (webhook and pulling system) and
start pulling messages from bot and sending it to the message broker
*/
const gateway = new Gateway(8888, bot);
gateway.pullProcess();
}
main();
Вот и получился простой, но умный чатбот FAQ.
Тестирование
Вы можете сделать HTTP-запрос к созданному нами вебхуку с телом сообщения:
POST > http://localhost:8888/receive/message
{
"contact": "11445917045",
"session": "dkioas32902",
"origin": "insomnia",
"data": "hello!"
}
Затем вы можете отправлять сообщения вашему чатботу, и вывод чатбота, вероятно, будет в вашей консоли. Вот вопросы, которые нужно задать с ответом:
"who are you?"
"what is a chatbot?"
"what is your purpose?"
Вы можете добавить больше вопросов в переменную questions-list
в intent incoming dialog
.
Заключение
Теперь мы закончили наш FAQ чатбот. Я рекомендую вам изменить question-table
в intent incoming dialog
для любого предпочитаемого вами движка познания. И для брокера сообщений тоже. Есть несколько хороших движков познания и брокеров сообщений, некоторые из них бесплатны.
Вы можете развить этот чатбот до чего-то большего. Нет границ тому, что вы можете здесь сделать.
Пример этого руководства вы можете найти здесь: FAQ чатбот