Перейти к основному контенту

Интеграция с ChatGPT

Сейчас во многие сферы нашей жизни все больше проникает искусственный интеллект. Так, ChatGPT, разработанный OpenAI, является универсальным инструментом, который приносит пользу во многих областях и широко распространен.

Чтобы не отставать от всего мира, мы приняли решение о внедрении возможности работы с ChatGPT на нашей платформе! Это позволит нам расширить ее функциональность и предоставить пользователям доступ к бесконечным знаниям и ресурсам, которые дает этот искусственный интеллект.

На нашей платформе работа с ChatGPT осуществляется при помощи разработанных командой Metabot плагинов.

Плагин для GPT можно найти в Общих плагинах на платформе. Он содержит три скрипта:

  • JS скрипт GPT (Common.GPT.GPT) — вся работа с GPT выполняется через данный скрипт;
  • Session (PHP обертка для V8) — скрипт Common.GPT.GPT сам использует класс сессии, но если необходимо не стандартное поведение, то класс сессии можно использовать напрямую;
  • JS скрипт Session (Common.GPT.Session) — подключает PHP обертку.

Рекомендуем ознакомиться с документацией Плагины

Как использовать GPT плагин

Есть два способа взаимодействия с GPT в боте:

  • Взаимодействие с GPT в команде "Выполнить JavaScript Callback";
  • Взаимодействие с GPT в системном скрипте с типом "Fallback".

Первый вариант подходит для случаев, когда GPT используется в боте точечно в определённых скриптах. Этот вариант является предпочтительным и с более управляемой логикой под каждый скрипт. Логика такого скрипта является более понятной и "прямой".

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

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

Рассмотрим использование обоих вариантов на примере.

Скрипт "Общаться только с GPT"

В скрипте размещаем две команды:

  • "Выполнить JavaScript";
  • "Выполнить JavaScript Callback".

Далее создаем меню, которое будет автоматически добавляться к GPT ответам.

В первой команде перед запуском Callback подключаем и инициализируем GPT (1-2 строка), чистим текущую сессию (4 строка) и запоминаем контекст с кодом текущего скрипта (gpt.setContext(bot.getCurrentScriptCode()) / gpt.setContext(null) / gpt.disableByContext()). Контекст можно использовать, например, для поиска инструкции Prompt для GPT, или для выключения GPT в Fallback с помощью gpt.disableByContext().

const Gpt = require('Common.GPT.GPT')
const gpt = new Gpt()
gpt.setContext(null)
gpt.clearSession()

В JavaScript Callback получаем меню из текущего скрипта (2 строка), и добавляем команды для его вывода (5-11 строки). Если пользователь нажал на кнопку, происходит выход из Callback, с помощью планировщика.

Есть два варианта запуска другого скрипта при нажатии на кнопку выхода из меню пользователем:

bot.scheduleScript(runScriptId, leadId)
throw new Error('stop') // ИЛИ ДОБАВИТЬ КОМАНДУ СТОП, ПОСЛЕ ДАННОЙ КОМАНДЫ JS-CALLBACK
bot.run({
    "script_id": runScriptId,
    //"script_code": "your_script_code",
    //"skip_till_command_id": 111,
    //"stop_current_flow": false,
  })
  
// ДАННАЯ ТОЧКА КОДА ДОЛЖНЫ ВЫПОЛНЯТЬСЯ, ТК СКРИПТ ПРЕРВАН ВЫШЕ С ПОМОЩЬЮ МОМЕНТАЛЬНОГО ВЫПОЛНЕНИЯ СКРИПТА (bot.runJob)
bot.sendMessage('ОШИБКА 1! Данная точка кода никогда не должна выполняться!') 
  
return true

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

// Получаем меню из текущего скрипта
let menuButtons = bot.getButtonsForCurrentScript();

// Запустится сразу при выполнении данной команды
if (isFirstImmediateCall) {
  bot.sendMessage(
    'Для выхода из данного режима нажмите кнопку "Назад" или напишите "меню" или "бот", подробнее смотрите в инструкции - Как пользоваться ботом', 
    menuButtons
  )
  return false
}

let runScriptId = bot.getScriptIdFromButtonsByIncomingMessage(menuButtons)
// Если нажали на кнопку, выходим из колбэка, с помощью планировщика

if (runScriptId !== null) { 
  // Запуск другого скрипта из текущей точки
  bot.run({
    "script_id": runScriptId
  })
  
  // ДАННАЯ ТОЧКА КОДА ДОЛЖНА ВЫПОЛНЯТЬСЯ, ТАК КАК СКРИПТ ПРЕРВАН ВЫШЕ С ПОМОЩЬЮ МОМЕНТАЛЬНОГО ВЫПОЛНЕНИЯ СКРИПТА (bot.runJob)
  bot.sendMessage('ОШИБКА 1! Данная точка кода никогда не должна выполняться!') 
  
  return true
}
// Код ниже запустится, когда лид ответит находясь в текущем скрипте

// Подключение и инициализация GPT
const Gpt = require('Common.GPT.GPT')
const gpt = new Gpt()

// Взять входящее сообщение из вебхука, отправить в GPT и вывести ответ
gpt.runFromWebhook(null, menuButtons)

return false // не выходим из JS Callback

Если же пользователь ответил оставшись в текущем скрипте, то выполняется подключение и инициализация GPT (31-32 строки), а затем входящее сообщение отправляется GPT из вебхука и его ответ выводится пользователю (35 строка).

Для Telegram: ответное сообщение будет обновляться по мере получения ответов от GPT. Если длина ответа от GPT превышает установленный maxMessageLength лимит, то ответ будет отправлен несколькими сообщениями.

После отправки сообщения GPT прикрепит к нему кнопки.

Системный скрипт "Fallback"

В данном скрипте взаимодействие с GPT выполняется при помощи команды Выполнить JavaScript. JS скрипт проверит контекст и не будет запускать GPT, если он выключен для данного раздела. Если GPT включен, то скрипт запустит его и выведет ответ с прикрепленным к нему последним меню.

Также в Fallback происходит проверка на существования ответа от GPT, и если GPT ответа нет или GPT не запускался в данном разделе, то будет выведен обычный Fallback текст. Для этого в Fallback после команды Выполнить JavaScript следует добавить команду Отправить текст с условием: return !(memory.getAttr('has_gpt_answer') * 1).

Первым делом в скрипте выполняется подключение и инициализация GPT (2-3 строки). Затем выполняется проверка на включение GPT в разделе из которого был совершен переход в скрипт Fallback (6 строка).

// Подключение и инициализация GPT
const Gpt = require('Common.GPT.GPT')
const gpt = new Gpt()

// Если GPT выключен в данном разделе
if (gpt.isDisabledByContext()) {
  return
}

// Поиск шаблона промта в кастомной таблице по текущему контексту (коду скрипта)
let gptContext = gpt.getContext()
if (gptContext !== null && typeof gptContext === 'string' && gptContext.length > 0) {
  let items = table.find('gpt_prompts', ['prompt'], [['context', '=', gptContext]])  
  if (items.length > 0) {
    let item = items[0]
    if (item.prompt !== null && item.prompt.length > 0) {
      let startPrompt = 
      `GPT, сейчас ты в роли чат-бота и являешься консультантом в ИТ-компании, отвечай так, будто ты работаешь в этой компании.
      Если ответа в этом тексте нет, то ответь Клиенту: "Извините, но у меня не хватает знаний для ответа на вопрос".  
      Можешь дополнять ответ смайликами (emoji).
      `  
      
      if (gptContext === 'our_products_and_services') {
        startPrompt += ` Ответ клиенту обязательно дополни следующей фразой:
         "Чтобы я смог лучше понять, что именно вы хотите, перейдите, пожалуйста, в соответствующий раздел продуктов или услуг на нашем сайте."
        ` 
      }
      
      if (gptContext === 'our_products' || gptContext === 'our_services') {
        startPrompt += `Обязательно строй ответы так, чтобы пытаться продать товар.`
      }
      
      gpt.gptSession.addMessage('system_start', startPrompt)
      gpt.gptSession.addMessage('system', item.prompt)
    }
  }
}

// Взять входящее сообщение из вебхука, отправить в GPT и вывести ответ
gpt.runFromWebhook()

Затем происходит поиск шаблона prompt в кастомной таблице по текущему коду скрипта. Если проверка прошла успешно, то GPT отправляются указания для построения ответа в зависимости от контекста (17-31 строки). Последним этапом входящее сообщение отправляется GPT из вебхука и его ответ выводится пользователю (40 строка).

База знаний и GPT

Существует еще один вариант использования GPT в боте. Для него понадобится создать свою базу знаний, с которой будет работать GPT.

Логика работы: за prompt в данном случае мы берем текст статей. Так как невозможно вместить всю статью в prompt запрос, текст дробится на части и вычисляются эмбеддинги-векторы, эти векторы сохраняются в БД бота. При получении вопроса от клиента, вычисляется его эмбеддинг и ищется его соответствие по статьям. Из текста найденной статьи формируется prompt и полученный ответ отправляется пользователю.

Эмбеддинг — результат процесса преобразования языковой сущности – слова, предложения, параграфа или целого текста в числовой вектор.

Для реализации базы знаний потребуется создать таблицу gpt_knowledge_base и скрипт, состоящий из двух команд: Выполнить JavaScript и Выполнить JavaScript Callback и пунктов меню, которые крепятся к ответу.

Таблица должна содержать следующие поля:

Поле Тип Описание
id AUTOINCREMENT ID статьи

context

TEXT Контекст статьи

content

TEXTAREA Содержимое статьи

embeddings

VECTOR Эмбеддинг-вектор текста статьи

Далее необходимо добавить в таблицу необходимые "статьи" и заполнить поле embeddings, например, при помощи следующего кода:

let tmp = kb.getRows(null, 1)
let article = tmp.length > 0 ? tmp[0] : null
if (article !== null) {
  let embeddings = gpt.gptDriver.getEmbeddings(article['content'])
  kb.saveEmbeddings(article['id'], embeddings)
}
return false

В команде Выполнить JavaScript в скрипте "База знаний" подключаем и инициализируем GPT (1-2 строка), чистим текущую сессию (4 строка) и запоминаем контекст с кодом текущего скрипта (gpt.setContext(bot.getCurrentScriptCode()) / gpt.setContext(null) / gpt.disableByContext()).

const Gpt = require('Common.GPT.GPT')
const gpt = new Gpt()
gpt.setContext(null)
gpt.clearSession()

В команде Выполнить JavaScript Callback повторно подключаем и инициализируем GPT. 

// Подключенире и инициализация GPT
const Gpt = require('Common.GPT.GPT')
const gpt = new Gpt()

const callbackStartMessage = 'Для выхода из данного режима нажмите кнопку "Назад" или напишите "меню" или "бот", подробнее смотрите в инструкции — Как пользоваться ботом'
const kbContext = null // контекст поиска статей
const kbLimit = 1 // количество статей передаваемых в prompt
const kbMaxDistance = 0.17 // для отсечки найденных статей по косинусному расстоянию

let prompt = `GPT, сейчас ты в роли чат-бота и являешься консультантом в ИТ-компании, отвечай так, будто ты работаешь в этой компании.
Ответ на вопрос ищи ТОЛЬКО среди информации, которая тебе отправлена, ничего не придумывая и не ищя ответ в своих общих знаниях.
Добавляй emoji в предложения с ответом, там где это уместно.
Если ответ не возможно сформировать на основе предоставленной информации, то ответь Клиенту: "Извините, но у меня не хватает знаний для ответа на вопрос"`

return gpt.runForKnowledgeBase(callbackStartMessage, prompt, kbContext, kbLimit, kbMaxDistance)

Затем отправляем запрос к GPT с сформированным заранее prompt (15 строка).