Создай своего ИИ-ассистента с помощью ChatGPT и Streamlit

—

от автора

Привет, Хабр! ? Меня зовут Дима Косаревский, я инженер данных (DE), увлеченный Data Science и всем, что связано с этим направлением. Data Science позволяет извлекать ценную информацию из огромных объемов данных при помощи статистических и вычислительных методов.

В последнее время эти ИИ, вроде ChatGPT, врываются прямо во все сферы. И вот благодаря увлечению Data Science можно использовать этих ботов, чтобы помогать людям, да ещё и пообщаться с ними на разные темы. ChatGPT действительно впечатляет. Он не только общается на разные темы, но еще и стихи сочинять умеет. Здорово, правда? Вот один из примеров, которые я получил недавно:

Покоритель мира — ИИ,

ChatGPT сулит грани новой жизни.

Работу заберёт у многих,

Страх испытавших горечь пламени.

В ответственности вложена сила,

Судьбы людей на блюдечке держа.

ИИ слава без предела,

В бесконечности вразумляя нас.

AI can do my job

AI can do my job

Сайт ChatGPT иногда работает медленно, и для доступа к нему в некоторых странах, например в России, требуется VPN. Некоторые из моих друзей хотят протестировать ИИ без СМС и регистрации. Я решил эту проблему с помощью официального API ChatGPT и Streamlit. Давайте посмотрим как это сделать?

ИИ-ассистент в 5 шагов

Из этого туториала можно узнать:

  1. Как использовать ChatGPT API

  2. Как отобразить диалог с ботом в чате

  3. Как преобразовать текст в речь (TTS)

  4. Как сделать локализацию

  5. Как собрать все вместе в приложении Streamlit

Хотите сразу к делу? Вот рабочее приложение и репозиторий с кодом.

Функции-помощники

И сразу код. Сначала напишем несколько вспомогательных функций:

def clear_chat() -> None:     st.session_state.generated = []     st.session_state.past = []     st.session_state.messages = []     st.session_state.user_text = ""  def show_text_input() -> None:     st.text_area(label=st.session_state.locale.chat_placeholder, value=st.session_state.user_text, key="user_text")  def show_chat_buttons() -> None:     b0, b1, b2 = st.columns(3)     with b0, b1, b2:         b0.button(label=st.session_state.locale.chat_run_btn)         b1.button(label=st.session_state.locale.chat_clear_btn, on_click=clear_chat)         b2.download_button(             label=st.session_state.locale.chat_save_btn,             data="\n".join([str(d) for d in st.session_state.messages[1:]]),             file_name="ai-talks-chat.json",             mime="application/json",         ) 

Эти функции позволяют очистить состояние cессии Streamlit и отобразить область ввода пользовательского текста и кнопки чата.

1. Как использовать ChatGPT API

Взаимодействие с API:

import streamlit as st import openai  from typing import List   def create_gpt_completion(ai_model: str, messages: List[dict]) -> dict:     openai.api_key = st.secrets.api_credentials.api_key     completion = openai.ChatCompletion.create(         model=ai_model,         messages=messages,     )     return completion 

Эта функция принимает на вход два аргумента: ai_model, модель GPT, и messages, список сообщений чата для поддержания контекста диалога. Указываем ключ API, используя функцию секретов Streamlit, и создаём экземпляр класса ChatCompletion, используя метод create, передавая модель и сообщения. Когда API отвечает, функция возвращает результат в виде словаря (json): следующего вида:

{   "choices": [     {       "finish_reason": "stop",       "index": 0,       "message": {         "content": "How can I help you?",         "role": "assistant"       }     }   ],   "created": 1681080142,   "id": "chatcmpl-73Y1mIfmDFWzuHILFQ8PG3bQcvOzU",   "model": "gpt-4-0314",   "object": "chat.completion",   "usage": {     "completion_tokens": 6,     "prompt_tokens": 27,     "total_tokens": 33   } } 

В целом, эта функция обеспечивает простой способ взаимодействия с API OpenAI и создания чат-бота. Параметр messages можно использовать для хранения истории разговоров и обеспечения контекстуальности и согласованности ответов чат-бота.

2. Как отобразить диалог с ботом в чате

В этом шаге можно увидеть код для отображения разговора в чате с использованием модели GPT. Код состоит из трех основных функций: show_chat, show_gpt_conversation и show_conversation.

from streamlit_chat import message   def show_chat(ai_content: str, user_text: str) -> None:     if ai_content not in st.session_state.generated:         # store the ai content         st.session_state.past.append(user_text)         st.session_state.generated.append(ai_content)     if st.session_state.generated:         for i in range(len(st.session_state.generated)):             message(st.session_state.past[i], is_user=True, key=str(i) + "_user", avatar_style="micah")             message("", key=str(i))             st.markdown(st.session_state.generated[i]) 

Тут на помощь приходит библиотека streamlit-chat. Она позволяет отображать чат с ботом в удобном формате.

Функция show_chat отвечает за отображение сообщений разговора между ИИ и пользователем. В качестве аргументов он принимает ai_content (ответ от ИИ) и user_text (введенный пользователем текст).

  1. Сначала функция проверяет, отсутствует ли ai_content в списке st.session_state.generated. Если это не так, пользовательский ввод и созданный ИИ контент добавляются в списки st.session_state.past и st.session_state.generated соответственно.

  2. Если в списке st.session_state.generated есть сообщения, функция будет перебирать список и отображать сообщения пользователя, за которыми следуют ответы, сгенерированные ИИ, с использованием функции message.

def show_gpt_conversation() -> None:     try:         completion = create_gpt_completion(st.session_state.model, st.session_state.messages)         ai_content = completion.get("choices")[0].get("message").get("content")         st.session_state.messages.append({"role": "assistant", "content": ai_content})         if ai_content:             show_chat(ai_content, st.session_state.user_text)             st.divider()             show_audio_player(ai_content)     except InvalidRequestError as err:         if err.code == "context_length_exceeded":             st.session_state.messages.pop(1)             if len(st.session_state.messages) == 1:                 st.session_state.user_text = ""             show_conversation()         else:             st.error(err)     except (OpenAIError, UnboundLocalError) as err:         st.error(err) 

Функция show_gpt_conversation управляет потоком генерации ответа ИИ и отображения разговора пользователю.

  1. Сначала пытаемся вызвать функцию create_gpt_completion, чтобы сгенерировать ответ ИИ с использованием модели GPT и пользовательского ввода.

  2. Затем ответ ИИ (ai_content) добавляется в список st.session_state.messages .

  3. Если контент, сгенерированный ИИ, не пустой, функция вызывает функцию show_chat, чтобы отобразить сообщения разговора и обработать любые ошибки, если таковые имеются, с помощью блоков try-except.

def show_conversation() -> None:     if st.session_state.messages:         st.session_state.messages.append({"role": "user", "content": st.session_state.user_text})     else:         ai_role = f"{st.session_state.locale.ai_role_prefix} {st.session_state.role}. {st.session_state.locale.ai_role_postfix}"  # NOQA: E501         st.session_state.messages = [             {"role": "system", "content": ai_role},             {"role": "user", "content": st.session_state.user_text},         ]     show_gpt_conversation() 

Функция show_conversation отвечает за управление состоянием беседы и обновление списка сообщений.

  1. Если в st.session_state.messages есть сообщения, ввод пользователя (st.session_state.user_text) добавляется к списку.

  2. Если сообщений нет, создается вводное сообщение ИИ с ai_role и добавляется в список, после чего следует ввод данных пользователем.

  3. Функция show_gpt_conversation вызывается для обработки потока разговора и генерации ответов ИИ.

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

3. Как конвертировать текст в речь (TTS)

В функции show_gpt_conversation можно заметить вызов функции show_audio_player, но что там под капотом?
Давайте посмотрим.

from io import BytesIO from gtts import gTTS, gTTSError  def show_audio_player(ai_content: str) -> None:     sound_file = BytesIO()     try:         tts = gTTS(text=ai_content, lang=st.session_state.locale.lang_code)         tts.write_to_fp(sound_file)         st.write(st.session_state.locale.stt_placeholder)         st.audio(sound_file)     except gTTSError as err:         st.error(err) 

Начнем с необходимых импортов:

  • BytesIO из модуля io и предоставляет способ чтения и записи из байтового буфера.

  • gTTS и gTTSError из библиотеки преобразования текста в речь, которая, внезапно, преобразует текст в речь.

Теперь давайте посмотрим на функцию show_audio_player :

Функция show_audio_player принимает параметр ai_content, который представляет собой текст, который будет отображаться и воспроизводиться как звук. При запуске функции, создав объект BytesIO. Этот объект будет хранить аудиоданные в памяти, что упростит воспроизведение аудио позже.

Далее будем использовать блок try для обработки любых возможных ошибок при преобразовании текста в аудио. Внутри блока try создаём экземпляр объекта gTTS с заданным текстом и языком. ДалееgTTS преобразует входной текст на заданном языке в речь. Дальше происходит запись аудиоданных в буфер sound_file.

Наконец, воспроизведится преобразование текста в речь, используя метод st.audio Streamlit. Если во время процесса возникают какие-либо ошибки, легко отлавливаем их и выводим информацию об исключении с помощью метода st.error.

Функция show_audio_player готова. Она принимает строку в качестве входных данных, создает аудиофайл из текста, а затем воспроизводит его в приложении Streamlit.

4. Как сделать локализацию

Если есть необходимость или желание сделать локализацию для нескольких языков — это легко сделать с помощью примера кода ниже:

Код локализации ⤵️
from dataclasses import dataclass from typing import List   @dataclass class Locale:     ai_role_options: List[str]     ai_role_prefix: str     ai_role_postfix: str     title: str     language: str     lang_code: str     chat_placeholder: str     chat_run_btn: str     chat_clear_btn: str     chat_save_btn: str     select_placeholder1: str     select_placeholder2: str     select_placeholder3: str     radio_placeholder: str     radio_text1: str     radio_text2: str     stt_placeholder: str   AI_ROLE_OPTIONS_EN = [     "helpful assistant",     "code assistant",     "code reviewer",     "text improver",     "cinema expert",     "sports expert", ]  AI_ROLE_OPTIONS_RU = [     "ассистент, который готов помочь",     "ассистент программиста",     "рецензент кода программиста",     "эксперт по улучшению текста",     "эксперт по кинематографу",     "эксперт в области спорта", ]  en = Locale(     ai_role_options=AI_ROLE_OPTIONS_EN,     ai_role_prefix="You are a female",     ai_role_postfix="Answer as concisely as possible.",     title="AI Talks",     language="English",     lang_code="en",     chat_placeholder="Start Your Conversation With AI:",     chat_run_btn="Ask",     chat_clear_btn="Clear",     chat_save_btn="Save",     select_placeholder1="Select Model",     select_placeholder2="Select Role",     select_placeholder3="Create Role",     radio_placeholder="Role Interaction",     radio_text1="Select",     radio_text2="Create",     stt_placeholder="To Hear The Voice Of AI Press Play", )  ru = Locale(     ai_role_options=AI_ROLE_OPTIONS_RU,     ai_role_prefix="Вы девушка",     ai_role_postfix="Отвечай максимально лаконично.",     title="Разговорчики с ИИ",     language="Russian",     lang_code="ru",     chat_placeholder="Начните Вашу Беседу с ИИ:",     chat_run_btn="Спросить",     chat_clear_btn="Очистить",     chat_save_btn="Сохранить",     select_placeholder1="Выберите Модель",     select_placeholder2="Выберите Роль",     select_placeholder3="Создайте Роль",     radio_placeholder="Взаимодествие с Ролью",     radio_text1="Выбрать",     radio_text2="Создать",     stt_placeholder="Чтобы Услышать ИИ Нажми Кнопку Проигрывателя", ) 

В этом ⤴️ коде показано, как создать простую систему локализации приложения с двумя вариантами языка — Английским и Русским. Основными компонентами кода являются:

  1. Импортируются необходимые модули: dataclasses используются для создания структур классов данных, а typing используется для тайп-хинтов.

  2. Создаётся родительский класс данных Locale, содержащий общий атрибут ai_role_options для списка возможных ролей ИИ для всех поддерживаемых языков.

  3. Определяется два дочерних класса данных, EnLocale и RuLocale, которые наследуются от Locale и предоставляют фактические переводы для каждого фрагмента статического текста в приложении. Английские переводы предоставляются в EnLocale, а русские переводы — в RuLocale.

  4. Назначаются предопределённые роли ИИ для каждого языка с помощью AI_ROLE_OPTIONS_EN и AI_ROLE_OPTIONS_RU — это нам пригодится позже.

  5. Создаются экземпляры каждого дочернего класса данных, en для английского языка и ru для русского языка, с соответствующими списками ролей ИИ.

При реализации локализации в приложении можно использовать соответствующий экземпляр (en или ru) в зависимости от выбранного языка, чтобы отображались правильные переводы для всех меток, сообщений и другого текста. Используя этот пример, можно легко выполнить локализацию для своего языка или даже для нескольких языков.

5. Как объединить все это в приложении Streamlit

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

Код приложения ⤵️
from streamlit_option_menu import option_menu from src.utils.lang import en, ru from src.utils.conversation import show_chat_buttons, show_text_input, show_conversation import streamlit as st  #  GENERAL SETTINGS --- PAGE_TITLE: str = "AI Talks" PAGE_ICON: str = "?" LANG_EN: str = "En" LANG_RU: str = "Ru" AI_MODEL_OPTIONS: list[str] = [     "gpt-3.5-turbo",     "gpt-4",     "gpt-4-32k", ]  st.set_page_config(page_title=PAGE_TITLE, page_icon=PAGE_ICON)  selected_lang = option_menu(     menu_title=None,     options=[LANG_EN, LANG_RU, ],     icons=["globe2", "translate"],     menu_icon="cast",     default_index=0,     orientation="horizontal", )  # Storing The Context if "locale" not in st.session_state:     st.session_state.locale = en if "generated" not in st.session_state:     st.session_state.generated = [] if "past" not in st.session_state:     st.session_state.past = [] if "messages" not in st.session_state:     st.session_state.messages = [] if "user_text" not in st.session_state:     st.session_state.user_text = ""  def main() -> None:     c1, c2 = st.columns(2)     with c1, c2:         c1.selectbox(label=st.session_state.locale.select_placeholder1, key="model", options=AI_MODEL_OPTIONS)         role_kind = c1.radio(             label=st.session_state.locale.radio_placeholder,             options=(st.session_state.locale.radio_text1, st.session_state.locale.radio_text2),             horizontal=True,         )         match role_kind:             case st.session_state.locale.radio_text1:                 c2.selectbox(label=st.session_state.locale.select_placeholder2, key="role",                              options=st.session_state.locale.ai_role_options)             case st.session_state.locale.radio_text2:                 c2.text_input(label=st.session_state.locale.select_placeholder3, key="role")     if st.session_state.user_text:         show_conversation()         st.session_state.user_text = ""     show_text_input()     show_chat_buttons()  if __name__ == "__main__":     match selected_lang:         case "En":             st.session_state.locale = en         case "Ru":             st.session_state.locale = ru         case _:             st.session_state.locale = en     st.markdown(f"<h1 style='text-align: center;'>{st.session_state.locale.title}</h1>", unsafe_allow_html=True)     main() 

Этот код настраивает наше приложение с интерфейсом чата для взаимодействия с различными моделями ИИ.

  1. Импортирует необходимые библиотеки и модули.

  2. Определяет общие настройки, такие как заголовок страницы, иконку для страницы браузера и параметры языка.

  3. Настраивает конфигурацию страницы Streamlit с указанными параметрами.

  4. Создаёт горизонтальное меню выбора языка (английский или русский).

  5. Инициализирует значения состояния сеанса для хранения контекста разговора и пользовательского ввода.

  6. Определяет функцию main, которая содержит следующие элементы:

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

    • Отображение истории разговоров с show_conversation, если пользователь ввел текст.

    • Отображение поля ввода для пользователя, чтобы ввести свое сообщение с помощью show_text_input.

    • Отображение ряда кнопок чата с помощью show_chat_buttons, чтобы пользователи могли управлять чатом и отправлять сообщения.

  7. Выполняет основную функцию main и отображает выбранный язык и название приложения. Отображение компонентов интерфейса чата на веб-странице.

Демо:

AI Talks

AI Talks

Подведение итогов

Спасибо за уделённое время, надеюсь моя статья была полезна! Теперь Вы можете создать своего ИИ-помощника или использовать AI Talks из любой страны без регистрации и СМС. VPN тоже не нужен.

app-sharing-gallery. Также можно задавать вопросы в Telegram в AI Talks Chat или смотреть за обновлениями приложения на канале AI Talks.

Блог пост на английском можно почитать на веб-сайте Streamlit. Можно посмотреть видео в TikTok. Репозиторий с кодом ожидающий ваших issue, pr и звёзд ?. Успехов! ?


ссылка на оригинал статьи https://habr.com/ru/articles/729662/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *