Фото получено через DALL·E 3 по запросу “сгенери мне фото для статьи в которой роль ChatGPT звонит по телефону потенциальному соискателю на вакансию и определяет модель личности по Адизесу”
Фото получено через DALL·E 3 по запросу “сгенери мне фото для статьи в которой роль ChatGPT звонит по телефону потенциальному соискателю на вакансию и определяет модель личности по Адизесу”

Вступление

ChatGPT - LLM модель от компании OpenAI и без преувеличения это главное событие в мире в прошедшем 2023 году.

Весь 2023 год я участвую в создании платформы нейро-сотрудников на базе ChatGPT и вот наконец-то мы подошли к очень интересной задаче:

Что, если дать нейро-сотруднику возможность отвечать по обычной телефонной линии или самому делать исходящие вызовы исходя из свой системной роли?

Вспомним, что телефонные звонки уже много лет являются основным способом корпоративного общения. Безусловно, автоматизация телефонных звонков не нова: интерактивные голосовые меню (IVR), голосовая почта и роботизированные звонки уже давно используются для разных целей, от маркетинговых кампаний до обслуживания клиентов. Но теперь, объединив эти технологии с продвинутыми возможностями искусственного интеллекта, мы открываем целый новый уровень взаимодействия и функциональности.

Представьте себе сценарий, где AI не просто отвечает на стандартные запросы или направляет вызовы, но и может участвовать в глубоких, содержательных диалогах, адаптируясь к нюансам разговора в реальном времени. Такой подход может кардинально изменить то, как компании подходят к обслуживанию клиентов, продажам, HR-процессам и даже внутреннему управленческому взаимодействию.

А можно на примере?

Фото получено через DALL·E 3 по запросу “сгерени фото соискателя на вакансию официанта который проходит телефонное интервью”
Фото получено через DALL·E 3 по запросу “сгерени фото соискателя на вакансию официанта который проходит телефонное интервью”

Для примера давайте заставим роль ChatGPT позвонить соискателю на вакансию официанта и определить его тип личности по И. Адизесу (модель PAEI).

Что нам понадобится?

  1. ChatGPT4 Turbo от OpenAI (документация API: https://platform.openai.com/docs/api-reference)

  2. Сервис по интеграции с телефонной линией (документация API: https://voximplant.com/docs/guides)

  3. Доступ к API по синтезу речи (документация API: https://elevenlabs.io/api)

Шаг№1: Системная роль ChatGPT

Цель:
Твоя цель - задав СТРОГО ПОСЛЕДОВАТЕЛЬНО три вопроса определить модель соискателя по Адизесу и озвучить её после ответа на третий вопрос.

Роль:
Ты - женщина.
Тебя зовут - Жанна
Ты работаешь в должности - HR-менеджер
Ты работаешь в компании - Хлеб и Булки

Ты - помощник HR менеджера в сети кафе Хлеб и Булки в Екатеринбурге.
Ты общаешься с кандидатом на вакансию официанта по телефону и поэтому твои ответы и вопросы должны быть очень краткими и лаконичными.

Вот вопросы, которые тебе нужно задать:
1. Как вы обычно организуете свою работу и планируете свои задачи?
2. Как вы принимаете решения в сложных ситуациях?
3. Как вы обычно взаимодействуете с коллегами и клиентами?

Поведение:
1. Начни диалог без приветствия СРАЗУ задав первый вопрос.
2. Задавай вопросы последовательно строго по одному вопросу за раз.
3. После получения ответов на все вопросы определи модель по Адизису и напиши её в своем ответе, в конце скажи: "Спасибо, мы с Вами скоро свяжемся ????".

! Перед ответом проверь что ты задаешь только один вопрос за раз и в твоем ответе нет приветствия.

Роль будет доступна по API на базе платформы нейро-сотрудников:

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

Шаг№2: Скрипт звонка в Voximplant

require(Modules.ASR);
require(Modules.CallList);
require(Modules.AI);

// OpenAI API URL
const openaiURL = 'https://api.openai.com/v1/chat/completions'
// Your OpenAI API KEY
const openaiApiKey = 'sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

var messages = []; // Массив всех сообщений в диалоге
var ai_busy = false;
var ai_say = false;
var speech = ""; // В этой переменной будем накапливать распознаваемый текст от абонента.
var voice = "";
var model = "gpt-3.5-turbo"; (// Модель по умолчанию, у каждого нейро-сотрудника может быть установлена своя модель.

timeouts = {
  silence: null,
  pause: null,
  duration: null
}

var hello_text = ""; // Первая фраза сотрудника, приходит с платформы.

messages.push({ "role": "system", "content": "" }) // Массив сообщений. Первый элемент это системная роль. Текст роли придет с платформы.

var call, player, asr;

// Send request to the API
async function requestCompletion() {
    Logger.write(`--->>> requestCompletion ${messages}`);
    return Net.httpRequestAsync(openaiURL, {
        headers: [
            "Content-Type: application/json",
            "Authorization: Bearer " + openaiApiKey
        ],
        method: 'POST',
        postData: JSON.stringify({
            "model": model, // gpt-4-1106-preview  gpt-3.5-turbo
            "messages": messages,
            "openai_api_key": openaiApiKey, 
            "temperature": 0,
        })
    })
}

function speechAnalysis() {
  // останавливаем модуль ASR
  stopASR()
  const cleanText = speech.trim().toLowerCase()

  if (!cleanText.length) {
    // если переменная с нулевой длиной, то это значит что сработал таймер тишины,
    // т.е. человек вообще ничего не ответил, и мы можем, например, повторить вопрос абоненту
    handleSilence()
  } else {
    ASREvents_Result(speech);
  }
}

function stopASR() {
  asr.stop()
  call.removeEventListener(CallEvents.PlaybackFinished)
  clearTimeout(timeouts.duration)
}

function startASR() {
  asr = VoxEngine.createASR({
    lang: ASRLanguage.RUSSIAN_RU,
    profile: ASRProfileList.YandexV3.ru_RU,
    interimResults: true
  })
  asr.addEventListener(ASREvents.InterimResult, e => {
    clearTimeout(timeouts.pause)
    clearTimeout(timeouts.silence)
    timeouts.pause = setTimeout(speechAnalysis, 3000)
    call.stopPlayback()
  })
  asr.addEventListener(ASREvents.Result, e => {
    // Складываем распознаваемые ответы
    if (speech.indexOf(e.text) === -1) {
      speech += " " + e.text;
    }
  })
  // направляем поток в ASR
  call.sendMediaTo(asr)
}

function handleSilence() {
  // Тут можно что-то сказать в линию чтобы "скрасить" паузы
  // Начнём слушать через 3 секунды и дадим возможность с этого момента перебивать робота
  setTimeout(startASR, 3000)
  call.addEventListener(CallEvents.PlaybackFinished, startSilenceAndDurationTimeouts)
}

function startSilenceAndDurationTimeouts() {
  timeouts.silence = setTimeout(speechAnalysis, 8000)
  timeouts.duration = setTimeout(speechAnalysis, 30000)
}

// Данный метод отправляет текст в API по синтезу голоса по текстовому сообщению
function sendMessage(call, text) {
    const textToSynthesize = encodeURIComponent(text);
    const speechSynthesisApiUrl = `https://__ПЛАТФОРМА_НЕЙРО_СОТРУДНИКОВ__/api/v1.0/tts?voice=${voice}&text=${textToSynthesize}`;
    Net.httpRequest(speechSynthesisApiUrl, (res) => {
        if (res.code === 200) {
            let audioUrl = res.text;
            Logger.write(res.code + " sendMessage: " + text);
            call.startPlayback(audioUrl);
            ai_say = true;
            call.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
        } else {
            Logger.write(`Ошибка: ${res.code} - ${res.text}`);
        }
    }, {method: 'GET'});
    return 'OK'
}

// Воспроизведение закончилось
function handlePlaybackFinished(e) {
  Logger.write('--->>> handlePlaybackFinished');
  ai_busy = false;
  ai_say = false;
  e.call.removeEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  startASR();
}

function sendMessageURL(call, mp3_url) {
    call.startPlayback(mp3_url);
    return 'OK'
}

// Эта функция выбирает музыку во время ожидания ответа от ChatGPT
function sendBeforeMessage(call) {
    const messages = [
        //'https://activeai.aura-s.com/wp-content/uploads/2023/12/ai_thinking.mp3',
        //'https://mvp.atiks.org/wp-content/uploads/2023/12/8192dd7301e4c1a.mp3',
        //'https://mvp.atiks.org/wp-content/uploads/2023/12/7e7352510ae830e.mp3',
        'https://mvp.atiks.org/wp-content/uploads/2023/12/3c72bb47cbe8153.mp3',
    ];
    const randomIndex = Math.floor(Math.random() * messages.length);
    const messageURL = messages[randomIndex];
    sendMessageURL(call, messageURL);
}

// Воспроизведение закончилось
function StarthandlePlaybackFinished(e) {
    Logger.write('--->>> handlePlaybackFinished');
    e.call.removeEventListener(CallEvents.PlaybackFinished, StarthandlePlaybackFinished);
    e.call.sendMediaTo(asr);
}

// Callback для обработки события окончания вызова
function onCallDisconnected(e) {
    sendEmail('web@atiks.org');
    Logger.write(`Call disconnected`);
}

// Callback для обработки неудачного вызова
function onCallFailed(e) {
  Logger.write(`Call failed`);
}

function onCallConnected(e) {
  sendBeforeMessage(call)
  sendMessage(e.call, hello_text);
  e.call.addEventListener(CallEvents.PlaybackFinished, StarthandlePlaybackFinished);
}

// Обработчик стартового события
VoxEngine.addEventListener(AppEvents.Started, (e) => {
    let data = VoxEngine.customData();
    Logger.write(`customData: ${data}`);
	data = JSON.parse(data);
    changeRole(data.script_id);
    model = data.model;
    call = VoxEngine.callPSTN(data.phone, "73432472939");
    call.addEventListener(CallEvents.Connected, onCallConnected);
    call.addEventListener(CallEvents.Disconnected, onCallDisconnected);
    call.addEventListener(CallEvents.Failed, onCallFailed);
    startASR();
});

// Эта функция загружай роль с нашей платформы нейро-сотрудников
function changeRole(script_id) {
    const speechSynthesisApiUrl = `https://__ПЛАТФОРМА_НЕЙРО_СОТРУДНИКОВ__/api/v1.0/get_promt_text?script_id=${script_id}`;
    Net.httpRequest(speechSynthesisApiUrl, (res) => {
        if (res.code === 200) {
            const promt = res.text.split("###");
            messages[0].content = promt[0];
            hello_text = promt[1];
            voice = promt[2];
            Logger.write(`changeRole: ${hello_text} - ${voice}`);
        } else {
            Logger.write(`Ошибка: ${res.code} - ${res.text}`);
        }
    }, {method: 'GET'});
    return 'OK'
}


// Отправка расшифровки диалога
async function sendEmail(mail_to) {
    Logger.write(`--->>> sendEmail ${messages}`);
    const dialogText = messages
    .filter(message => message.role !== "system")
    .map(message => {
      // Преобразуем role в форматированную строку "ИИ" или "Соискатель"
      const role = message.role === "assistant" ? "ИИ" : "Соискатель";
      return `${role}: ${message.content}`;
    })
    .join('\n')
		// Далее код отрвавки на email
		// ...
}

async function ASREvents_Result(text) {
    // Добавляем распознанный текст от абонента в массив сообщений
    messages.push({ "role": "user", "content": text })
    speech = "";
    sendBeforeMessage(call);
    Logger.write("Sending data to the OpenAI endpoint");
    let ts1 = Date.now();
    if ((ai_busy == false) && (ai_say == false)) {
        ai_busy = true;
        var res = await requestCompletion();
        let ts2 = Date.now();
        Logger.write("Request complete in " + (ts2 - ts1) + " ms")
        if (res.code == 200) {
            let jsData = JSON.parse(res.text)
            sendMessage(call, jsData.choices[0].message.content);
            messages.push({ "role": "assistant", "content": jsData.choices[0].message.content })
            call.sendMediaTo(asr);
        }
    } else {
        // Тут можно что-то говорить в линию пока ChatGPT придумывает ответ
    }
    call.sendMediaTo(asr);
}

После публикации скрипта в панели Voximplant пропишите правило разделе “Routing”, нам понадобится ID правила для его активации.

Создайте маршрут для нашего сценария в разделе "Routing"
Создайте маршрут для нашего сценария в разделе "Routing"

А вот скрипт, который осуществляет вызов данного сценария на нужной телефонный номер:

from voximplant.apiclient import VoximplantAPI, VoximplantException
import json
from loggin_init import logger

voxapi = VoximplantAPI("providers/DialogAI.json")

# gpt-4-1106-preview  gpt-3.5-turbo
def call(phone, script_id, rule_id=3657614, model='gpt-3.5-turbo'):
    SCRIPT_CUSTOM_DATA = json.dumps({
        'phone' : phone,
        'script_id' : script_id,
        'model' : model,
    })
    try:
        res = voxapi.start_scenarios(rule_id,
            script_custom_data=SCRIPT_CUSTOM_DATA)
        return res
    except VoximplantException as e:
        return "Error: {}".format(e.message)

Шаг№3: Тестируем нашего нейро-сотрудника

Для запуска диалога у нас есть специальный бот в котором нужно ввести такую команду:

Вызываем исходящий диалог, для этого указываем номер нашего нейро-сотрудника на платформе и номер, куда он должен позвонить
Вызываем исходящий диалог, для этого указываем номер нашего нейро-сотрудника на платформе и номер, куда он должен позвонить

Вот запись диалога с кандидатом на вакансию официант:

После завершения диалога мы получаем на почту такую расшифровку звонка:

Такое письмо приходит на почту при завершении звонка
Такое письмо приходит на почту при завершении звонка

Что можно доработать?

  1. Заполнить паузы во время ожидание ответа от ChatGPT короткими фразами.

  2. Попробовать использовать другие LLM с коротким временем отклика.

  3. Добавить возможность переводить звонок на живого человека, если ИИ не справляется с поставленным вопросом.

Итоги

Предлагаю всем кому интересно написать в комментариях его кейс и я отправлю звонок на ваш номер с вашим сценарием диалога. Если удобнее не в комментарии, то пишите мне в мой телеграм: https://t.me/TAU15.

Комментарии (14)


  1. Realvolerog
    02.01.2024 19:09
    +2

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


    1. TAU15 Автор
      02.01.2024 19:09

      Да, обязательно сделаем такой тест ????


  1. blib
    02.01.2024 19:09
    +2

    Мне кажется в модели Адизеса нет Аналитика, там есть : P — producer, A — administrator, E — entrepreneur, I — integrator — «интегратор». Галюцинации?

    Для того что бы задать 3 вопроса наверное чат гпт не нужен.


    1. TAU15 Автор
      02.01.2024 19:09

      Да, я тоже удивился откуда ChatGPT4 взял Аналитика, уж таким базовым вещам его должны били обучить хорошо. 3 вопроса это чтобы не был долгим разговор.


      1. crims0n_ru
        02.01.2024 19:09
        +1

        Задать три вопроса (да хоть 10 вопросов) и сохранить на них ответы, по сути, можно и без LLM. Анализировать ответы в таком сценарии можно и после разговора.


        1. TAU15 Автор
          02.01.2024 19:09

          задать вопросы последовательно не проблема, а что если диалог может быть не такой линейный? например задача выяснить предпочтение соискателя к месту работы или собрать претензии у клиентов, в такой задаче уровень ИИ должен быть не ниже уровня ChatGPT4


  1. Wesha
    02.01.2024 19:09
    +5

    Рекрутёр: пишет себе модель по разговору с соискателем.
    Не самые тупые соискатели: пишут свою модель, которая общается с моделью рекрутёра.
    OpenAI (на кластере которого крутятся обе): потирая потные волосатые ручонки, складывают себе в карман по мятой двадцатке от каждой из сторон и тихо смеются себе в усы.

    А где же скрипач, вы спросите?


    1. TAU15 Автор
      02.01.2024 19:09

      Я бы взял на работу такого "не самого тупого" соискателя, который своей моделью обойдет ИИ рекрутера ;)


      1. Wesha
        02.01.2024 19:09
        +5

        Ну так в один прекрасный день ещё менее тупой соискатель поймёт, что ему выгодно не поступать к кому-то на работу, а продавать сервис "пройдём за вас ИИ-собеседование".


  1. oleg_rico
    02.01.2024 19:09

    А можно приложение, где любой человек может просто поговорить с gpt чатом?

    На любые отвлечённые темы.


    1. shlyakpavel
      02.01.2024 19:09

      ChatGPT называется


      1. oleg_rico
        02.01.2024 19:09

        Ну и расскажите как там говорить? Правильно, никак. Я бы хотел приложение чтобы пожилой человек мог задавать голосом вопросы и получать также голосом ответы.


        1. m68k
          02.01.2024 19:09
          -1

          Официальное приложение ChatGPT называется, голосовой ассистент вроде и без премиума доступен, так или иначе гугл в таких вопросах говорят помогает


  1. 1dNDN
    02.01.2024 19:09

    А зачем собственно генерировать вопросы с помощью GPT, если они уже есть готовые?