Создайте бота технической поддержки за 20 минут с помощью Tanuki + GPT4

Создайте бота технической поддержки в течение 20 минут с использованием Tanuki + GPT4

Программист Тануки - созданный с помощью DALL·E 3

TLDR: Этот рабочий процесс реагирует на сообщения обратной связи от клиентов и преобразует их в приоритетные технические заявки, используя GPT4 + Тануки (открытый исходный код).

Кому это полезно? Любому, кто заинтересован в создании приложений, интегрирующих возможности GPT4 в сложные рабочие процессы с минимальными усилиями.

На сколько продвинута эта публикация? Требуется базовое знание Python, но это все. Тануки позаботится о всем остальном.

Что эта публикация не делает. Мы не получаем Твиты (см. их API) и не публикуем их в вашу службу поддержки клиентов (сильно зависит от технологического стека).

Введение

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

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

Кроме того, языковые модели в нулевом режиме часто не совпадают (языковые модели могут по-разному понимать задачу) и порождают галлюцинации. Чтобы противостоять этому, мы создали Тануки – простой способ создания приложений, оснащенных выровненными языковыми моделями и гарантирующих структурированный и типизированный вывод.

Здесь я продемонстрирую, как использовать Тануки за 20 минут для создания:

  1. Чат-бота (службы поддержки клиентов)
  2. Классификатора (службы поддержки клиентов)
  3. Надежных структурированных объектов для внесения записей в базу данных.

Цель использования в двух словах

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

Тануки будет использован в этом примере для создания ответа, классификации твита и создания технической заявки при необходимости. Мы смогли создать это Python-приложение за 20 минут. Теперь мы хотим показать вам, как сделать то же самое. Если вы нетерпеливы, вы можете найти репозиторий и пример использования по следующим ссылкам:

  • Репозиторий Тануки Github
  • Код для этого конкретного примера здесь или в Google Colab ноутбуке

Что такое Тануки и почему это полезно для этого примера использования?

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

  • Учет типов: Выводы всегда следуют структуре намеков на тип, заданной пользователем (кошмарная задача в случае использования LLM, так как выводы представляют собой свободный текст). Выводы могут быть указаны как базовые типы Python (целые числа, списки, словари), так и более сложные типы, такие как классы Pydantic или Литералы.
  • Поведение модели языка легко выравнивается с помощью демонстрационных образцов. Добавление этих инструкций необязательно, но обычно значительно улучшает производительность модели. Добавление образцов-заглушек так просто, как запись их на листке бумаги.
  • Тануки осуществляет автоматическую дистилляцию модели, то есть учительская модель (GPT-4) автоматически сокращается до более маленькой дообученной модели с уменьшением стоимости и латентности до 15 раз без потери производительности.

Эти функции имеют отношение к данному случаю использования:

  1. Выходные данные LLM будут использоваться в дальнейшем другими системами. Для обеспечения стабильности и отсутствия ошибок времени выполнения, необходимо всегда типизировать выходные данные. Это критически важно, поскольку рабочий процесс создает структурированные объекты заявок, которые должны быть зарегистрированы в базах данных, ожидая данных в определенной схеме.
  2. Субъективное и неочевидное, что должно (и не должно) быть осуществлено. Более того, как языковая модель реагирует на отзывы клиентов имеет огромное значение (тон и сообщение должны быть правильными, чтобы не раздражать клиентов, которые уже испытывают проблемы). Поэтому важна согласованность, так как вы хотите, чтобы LLM понимало, что является адекватным ответом, и чтобы любые дальнейшие действия (вы не хотите упустить запрос клиента) соответствовали тому, как сотрудник будет решать эти проблемы. Согласование языковой модели с этими проблемами – единственный способ обеспечить возможность использования в производстве.
  3. Количество потенциальных заявок на поддержку и отзывов в производственной среде колоссально. Таким образом, снижение затрат в 15 раз может представлять собой огромную экономию и мотивацию для долгосрочного использования, особенно поскольку производительность будет оставаться постоянной, и рабочий процесс не будет затрагиваться возможными изменениями в будущих версиях GPT4.

Для определения проекта мы используем следующие требования

Сначала мы предполагаем следующий общий рабочий процесс:

  • Пользователь отправляет сообщение обратной связи о продукте или услуге на учетную запись в Twitter.
  • LLM анализирует обратную связь и отвечает эмпатично (или как лучше может), то есть аспект чат-бота.
  • Исходя из обратной связи клиента, LLM классифицирует обратную связь как “требующую действий” или нет, то есть аспект классификатора.
  • Если обратная связь требует действий, тогда чат-бот создаст объект заявки клиента для последующего использования в других приложениях.

Мы будем использовать GPT4 от OpenAI для этого случая использования. Для начала нам нужно настроить некоторые переменные окружения с вашим ключом API OpenAI. Для этого мы должны создать файл .env и добавить его в директорию. Позже файл .env будет прочитан, и переменные окружения будут корректно обработаны.

OPENAI_API_KEY=sk-XXX

Это все, что вам нужно сделать, чтобы начать использовать Tanuki! Затем давайте посмотрим, как создать этот случай использования.

Построение рабочего процесса

Как уже упоминалось ранее, вы могли бы использовать GPT4 с помощью подсказок, если стоимость не является фактором, хотя получение типизированных выходных данных потребует дополнительной работы. Если у вас были бы несколько недель, вы могли бы даже настроить предварительно обученную LLM с открытым исходным кодом для выполнения этой задачи. Вместо этого мы воспользуемся Tanuki, чтобы сделать это за 20 минут.

Первая строка работы – нам нужно установить Tanuki

pip install tanuki.py

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

from pydantic import BaseModelclass Tweet(BaseModel):    """    Объект твита    Имя - это учетная запись пользователя    Текст - это твит, который они отправили    Идентификатор - это уникальный классификатор    """    name: str    text: str    id: str

Затем мы создадим response Pydantic объект, который будет присвоен этому твиту для отправки обратно пользователю, и если человеку необходимо действовать, исходя из входного сообщения, также необходимо создать объект SupportTicket, который будет сохранен в базе данных

from typing import Literal, Optionalclass Response(BaseModel):    """    Объект ответа, где атрибут response - ответ, отправляемый клиенту    requires_ticket - логическое значение, указывающее, является ли входящий твит вопросом или прямой проблемой    требующей вмешательства и действия людей    """    requires_ticket: bool     response: str class SupportTicket(BaseModel):    """    Объект заявки в поддержке, где    issue - краткое описание проблемы клиента    urgency - показывает, насколько срочно команда должна ответить на проблему    """    issue: str    urgency: Literal["low", "VoAGI", "high"]

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

Сначала мы создаем функцию, которая создает объект Response на основе входящего tweet. Мы указываем tweet в качестве входа и Response в качестве выходного типа данных. Указание подсказок типов является важным, поскольку это скажет модели языка, выполняющей функцию, что создать в качестве окончательного вывода. Tanuki также всегда гарантирует, что выводы соответствуют подсказкам типов, поэтому мы можем смело сказать, что из-за неправильных объектов или ненадежных результатов ничего не сломается.

Затем мы добавляем краткое описание в документацию функции о том, что должен сделать LLM, и добавляем декоратор @tanuki.patch. Это гарантирует, что выводы из classify_and_respond имеют правильные типы для дальнейшего анализа.

import tanuki@tanuki.patchdef classify_and_respond(tweet: Tweet) -> Response:    """    Отвечайте на текстовые сообщения клиентов службы поддержки с сочувствием и вежливостью.     Передайте, что вам не безразлична проблема и если проблема является прямой для команды поддержки или вопросом, команда ответит на нее.     """

Чтобы обеспечить надежную работу и добавить операторы выравнивания для направления производительности LLM к выводу для пользователя, мы создадим еще одну функцию с названием align_respond и декоратором @tanuki.align.

В функции align_respond мы выровняем выводы LLM, показывая примеры допустимых входных и выходных данных. Это выравнивание будет:

  1. Показать, как ответить на запрос клиента
  2. Показать, какие запросы от клиента необходимо зарегистрировать (и создать внутренний тикет).

Эти выравнивания будут использоваться в качестве входных данных и желаемых выходных данных с использованием операторов assert в Python. Ниже приведены несколько примеров выравнивающих операторов для объектов вывода чат-бота:

@tanuki.aligndef align_respond():    input_tweet_1 = Tweet(name = "Laia Johnson",                          text = "Мне действительно нравится новая лопата, но ручка сломалась через 2 дня использования. Можно ли получить замену?",                          id = "123")    assert classify_and_respond(input_tweet_1) == Response(                                                            requires_ticket=True,                                                             response="Привет, нам очень жаль это слышать. Мы свяжемся с вами и предоставим замену как можно скорее. Можете ли вы сообщить нам номер вашего заказа?"                                                            )    input_tweet_2 = Tweet(name = "Keira Townsend",                          text = "Ненавижу новый дизайн iPhone. Он такой уродливый. Перехожу на Samsung",                          id = "10pa")    assert classify_and_respond(input_tweet_2) == Response(                                                            requires_ticket=False,                                                             response="Привет, нам очень жаль это слышать. Мы примем это во внимание и сообщим команде разработки о вашем отзыве"                                                            )    input_tweet_3 = Tweet(name = "Thomas Bell",                          text = "@Amazonsupport. У меня вопрос о заказе, вы доставляете в Финляндию?",                          id = "test")    assert classify_and_respond(input_tweet_3) == Response(                                                            requires_ticket=True,                                                             response="Привет, спасибо, что обратились. Вопрос будет отправлен в нашу службу поддержки, и они свяжутся с вами как можно скорее"                                                            )    input_tweet_4 = Tweet(name = "Jillian Murphy",                          text = "Только что купил новый набор и пока что я его обожаю!",                          id = "009")    assert classify_and_respond(input_tweet_4) == Response(                                                            requires_ticket=False,                                                             response="Привет, спасибо, что обратились. Мы рады, что вам нравится продукт"                                                            )

Такие операторы assert существенно снижают вероятность галлюцинаций и непредвиденных сбоев, выравнивая LLM с задуманной функциональностью. Мне нравится думать об этом как о “выравнивании, руководимом тестами” (разработка, ориентированная на тестирование LLM).

Точно также нужно сделать и для второй части, т.е. создания тикета поддержки для регистрации. Следуя той же структуре patch и align-функций, у нас есть следующее:

@tanuki.patchdef create_support_ticket(tweet_text: str) -> SupportTicket:    """    Используя текст твита, создайте тикет поддержки для сохранения во внутренней базе данных    Создайте краткое описание действий, которые необходимо предпринять, и срочности проблемы    """@tanuki.aligndef align_supportticket():    input_tweet_1 = "Мне действительно нравится новая лопата, но ручка сломалась через 2 дня использования. Можно ли получить замену?"    assert create_support_ticket(input_tweet_1) == SupportTicket(                                                                issue="Нужен заменительный продукт, потому что ручка сломалась",                                                                 urgency="высокая"                                                                )    input_tweet_2 = "@Amazonsupport. У меня вопрос о заказе, вы доставляете в Финляндию?"    assert create_support_ticket(input_tweet_2) == SupportTicket(                                                                issue="Выяснить и ответить, доставляем ли мы в настоящее время в Финляндию",                                                                urgency="низкая"                                                                )    input_tweet_3 = "Только что купил новый набор и пока что я его обожаю! Упаковка крема немного повреждена, но мне нужна замена"    assert create_support_ticket(input_tweet_3) == SupportTicket(                                                                issue="Нужно новое кремовое средство, так как упаковка немного повреждена",                                                                 urgency="VoAGI"                                                                )

Чтобы все объединить вместе, мы создаем функцию analyse_and_respond() для создания ответа и поддержки запроса, если это необходимо, и мы закончили!

def analyse_and_respond(tweet: Tweet) -> tuple[Optional[SupportTicket], Response]:    # получить ответ    response = classify_and_respond(tweet)    # если для ответа требуется запрос, создать запрос    if response.requires_ticket:        support_ticket = create_support_ticket(tweet.text)        return response, support_ticket    return response, None

Полный и окончательный код для всего этого должен выглядеть так:

from dotenv import load_dotenvload_dotenv()from pydantic import BaseModelfrom typing import Literal, Optionalimport tanukiclass Tweet(BaseModel):    """    Объект твита    Имя - имя пользователя    Текст - сообщение, которое они отправили    """    name: str    text: str    id: strclass Response(BaseModel):    """    Объект ответа, где атрибут response - это ответ, отправленный клиенту    requires_ticket - логическое значение, указывающее, был ли входящий твит вопросом или прямой проблемой,    требующей вмешательства и действий человека    """    requires_ticket: bool     response: str class SupportTicket(BaseModel):    """    Объект тикета поддержки, где     проблема - краткое описание проблемы клиента    срочность - указывает, насколько срочно команда должна ответить на проблему    """    issue: str    urgency: Literal["низкий", "VoAGI", "высокий"]# создание ответа@tanuki.patchdef classify_and_respond(tweet: Tweet) -> Response:    """    Отвечайте на текст твита пользователю с сочувствием и мягкостью.     Дайте понять, что вы заботитесь о проблеме и если проблема оказалась прямой, то команда поддержки исправит ее или если это был вопрос, команда ответит на него.     """@tanuki.aligndef align_respond():    input_tweet_1 = Tweet(name = "Лайа Джонсон",                          text = "Мне очень нравится новая лопата, но ручка сломалась через 2 дня использования. Могу ли я получить замену?",                          id = "123")    assert classify_and_respond(input_tweet_1) == Response(                                                            requires_ticket=True,                                                             response="Привет, нам очень жаль это слышать. Мы вернемся к вам с заменой как можно скорее. Можете ли вы отправить нам номер вашего заказа?"                                                            )    input_tweet_2 = Tweet(name = "Кейра Таунсенд",                          text = "Ненавижу новый дизайн iphone. Он такой ужасный. Перехожу на Samsung",                          id = "10pa")    assert classify_and_respond(input_tweet_2) == Response(                                                            requires_ticket=False,                                                             response="Привет, нам очень жаль это слышать. Мы примем это во внимание и сообщим команде по продукту о вашем мнении"                                                            )    input_tweet_3 = Tweet(name = "Томас Белл",                          text = "@Amazonsupport, у меня есть вопрос о заказе, вы доставляете в Финляндию?",                          id = "test")    assert classify_and_respond(input_tweet_3) == Response(                                                            requires_ticket=True,                                                             response="Привет, спасибо, что обратились. Вопрос будет отправлен нашей службе поддержки, и они свяжутся с вами как можно скорее"                                                            )    input_tweet_4 = Tweet(name = "Джиллиан Мерфи",                          text = "Только что купил новый goodybox и пока что я его обожаю!",                          id = "009")    assert classify_and_respond(input_tweet_4) == Response(                                                            requires_ticket=False,                                                             response="Привет, спасибо, что обратились. Мы рады слышать, что вам нравится продукт"                                                                        )# создание тикета поддержки@tanuki.patchdef create_support_ticket(tweet_text: str) -> SupportTicket:    """    Используя текст твита, создайте тикет поддержки для сохранения во внутреннюю базу данных    Создайте краткое описание действий, которые необходимо выполнить, и срочность проблемы    """@tanuki.aligndef align_supportticket():    input_tweet_1 = "Мне очень нравится новая лопата, но ручка сломалась через 2 дня использования. Могу ли я получить замену?"    assert create_support_ticket(input_tweet_1) == SupportTicket(                                                                issue="Требуется замена продукта из-за поломки ручки",                                                                 urgency = "высокий"                                                                )    input_tweet_2 = "@Amazonsupport, у меня есть вопрос о заказе, вы доставляете в Финляндию?"    assert create_support_ticket(input_tweet_2) == SupportTicket(                                                                issue="Узнайте и ответьте, доставляем ли мы в данный момент в Финляндию",                                                                urgency="низкий"                                                                )    input_tweet_3 = "Только что купил новый goodybox и пока что я его обожаю! Упаковка с кремом была немного повреждена, нам нужно, чтобы ее заменили"    assert create_support_ticket(input_tweet_3) == SupportTicket(                                                                issue="Требуется новый крем, так как упаковка была немного повреждена",                                                                 urgency="VoAGI"                                                                )    # конечная функция для рабочего процессаdef analyse_and_respond(tweet: Tweet) -> tuple[Optional[SupportTicket], Response]:    # получить ответ    response = classify_and_respond(tweet)    # если для ответа требуется запрос, создать запрос    if response.requires_ticket:        support_ticket = create_support_ticket(tweet.text)        return response, support_ticket    return response, None

И вот, все готово! Теперь мы можем протестировать рабочий процесс с несколькими примерами.

def main():    """    Эта функция анализирует входящий твит и возвращает ответ и, при необходимости, тикет    """    # начнем с вызова команды aligns для регистрации выравнивающих операторов    align_respond()    align_supportticket()    input_tweet_1 = Tweet(name = "Jack Bell",                          text = "Бро @Argos почему мой заказ не пришел? Я заказал его две недели назад. Ужасное обслуживание",                          id = "1")    response, ticket = analyse_and_respond(input_tweet_1)        print(response)    # requires_ticket=True     # response="Привет Jack, мы очень сожалеем об этом. Мы сразу же разберемся и свяжемся с вами в ближайшее время."        print(ticket)    # issue="Заказ клиента не пришел через 2 недели"    # urgency='высокая'    input_tweet_2 = Tweet(name = "Casey Montgomery",                          text = "@Argos Время доставки было 3 недели, но обещали 1. Не в восторге.",                          id = "12")    response, ticket = analyse_and_respond(input_tweet_2)        print(response)    # requires_ticket=True     # response="Привет Casey, мы очень сожалеем о задержке в доставке. Мы разберемся с этой проблемой и свяжемся с вами в ближайшее время."        print(ticket)    # issue='Время доставки превысило обещанное'    # urgency='VoAGI'    input_tweet_3 = Tweet(name = "Jacks Parrow",                          text = "@Argos Новый логотип выглядит довольно уродливо, интересно, почему его поменяли",                          id = "1123")    response, ticket = analyse_and_respond(input_tweet_3)    print(response)    # requires_ticket=False     # response="Привет Jacks Parrow, мы сожалеем, что вам не нравится новый логотип. Мы передадим ваш отзыв соответствующей команде. Спасибо, что дали нам знать."        print(ticket)    # None

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

И все это заняло менее полутора часа.

Что дальше

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

Если это звучит интересно и вы хотите узнать больше (или, что еще лучше, принять участие), присоединитесь к нашему Discord. Это был только один пример использования, вы можете найти другие здесь (например, создание структурированных данных через Web-скрапинг, создание приложения-списка дел на основе естественного текста, блокирование грубого языка и многое другое).

Если у вас есть вопросы, дайте мне знать здесь, в комментариях, или в нашем Discord. До скорого!