Боремся с токсичными комментариями с помощью ИИ, FastAPI и React. ai.. ai. architecture.. ai. architecture. fastapi.. ai. architecture. fastapi. grpc.. ai. architecture. fastapi. grpc. microservices.. ai. architecture. fastapi. grpc. microservices. pytest.. ai. architecture. fastapi. grpc. microservices. pytest. python.. ai. architecture. fastapi. grpc. microservices. pytest. python. react.. ai. architecture. fastapi. grpc. microservices. pytest. python. react. ReactJS.. ai. architecture. fastapi. grpc. microservices. pytest. python. react. ReactJS. rest.. ai. architecture. fastapi. grpc. microservices. pytest. python. react. ReactJS. rest. SQL.. ai. architecture. fastapi. grpc. microservices. pytest. python. react. ReactJS. rest. SQL. Веб-разработка.. ai. architecture. fastapi. grpc. microservices. pytest. python. react. ReactJS. rest. SQL. Веб-разработка. ии помощник.. ai. architecture. fastapi. grpc. microservices. pytest. python. react. ReactJS. rest. SQL. Веб-разработка. ии помощник. Микросервисы.. ai. architecture. fastapi. grpc. microservices. pytest. python. react. ReactJS. rest. SQL. Веб-разработка. ии помощник. Микросервисы. нейросети.
Типичный день на Хабре

Типичный день на Хабре

В последнее время я перестал читать комментарии к статьям на Хабре. Причина — токсичность и ненависть друг к другу. Абсолютно безобидные технические статьи подчас вызывают бурю агрессии у отдельных лиц. Всех банить тоже нельзя — свобода слова закреплена в Конституции. Но есть решение: давайте используем искусственный интеллект, который будет анализировать комментарий и переписывать его, меняя токсичность на вежливость, сохраняя основную мысль комментария.

Мы воспользуемся FastAPI для бэкенда, React для фронтенда, заставим их между собой общаться через RESTful API, а бизнес-логику реализуем путём обращения к ИИ через gRPC.

Содержание:

  1. Причины токсичности

  2. Последствия токсичности

  3. Бизнес-логика решения проблемы токсичности

  4. Архитектура приложения

  5. Проектирование базы данных

  6. Пишем бэкенд на FastAPI

  7. Пишем фронтенд на React

  8. Интегрируем ИИ

  9. Тестируем приложение

  10. Собираем приложение

  11. Как этим пользоваться

  12. Полный код проекта

  13. Выводы

1. Причины токсичности

Не так давно на Хабре была статья, где автор отмечает, что фраза «код г…о» в западных компаниях считается жёсткой и грубой. То есть такое поведение считается токсичным. Если вы когда-нибудь отдыхали в западной Европе, то никогда не встречались с хамством и грубостью, чего, к сожалению, нельзя сказать про нашу страну.

Какова причина? Лично я считаю, что причина исключительно — экономическая. Общество (именно общество, а не отдельный индивид), которое выросло в условиях достатка, проявляет больше эмпатии и меньше токсичности. Почему так? Когда существует дефицит, члены общества вынуждены сражаться за ресурсы, а неотъемлемым элементом борьбы является агрессия по отношению к другим. Одним из проявлений агрессии является токсичность.

И это не зависит ни от страны, ни от расы, ни от национальности. Сравните культурный уровень белой субурбии и чёрного гетто в США, даже в пределах одного города, например, Лос Анжелеса. Если взять песни — сколько агрессии и негатива (сколько там встречается слово «бич» – это не «пляж») в музыке чёрного населения из гетто. Я уже молчу о преступности.

Даже в поведении животных тоже это присутствует. Посмотрите как кушают домашние куры, которые выросли и содержатся в условиях достатка — все спокойно кушают, никто ни с кем не дерётся. А теперь посмотрите как кушают уличные сизые голуби — они дерутся за еду, так как еда в дефиците.

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

Я не претендую на академическую точность, это просто моё субъективное мнение. Истина может отличаться.

2. Последствия токсичности

Все люди разные. У кого-то слабая нервная система, у кого-то — сильная. Кто-то более впечатлителен, кто-то менее. Даже на менее восприимчивого человека с сильной нервной системой токсичность действует отрицательно. У человека падает мотивация писать статьи и общаться дальше. Или же он перестаёт читать комментарии, не получая обратную связь.

На многих сайтах, в том числе на Хабре, существуют правила пользования сайтом. Допустим, пользователь нарушил правила сайта и был забанен. В итоге, количество комментариев уменьшается — пользователь больше не сможет делиться своим мнением. Пусть даже и токсичным. Уменьшение числа комментарий напрямую влияет на один из бизнес-показателей — уровень вовлечённости (engagement), а также косвенно на SEO.

Поэтому токсичность несёт под собой как ущерб для психического здоровья пользователей, так и для бизнес-показателей самого сайта.

Тем не менее, вы можете мне возразить: «Иногда приятно почитать токсичные комментарии, слить свою злобу, посмеяться!». Для этого существуют такие ресурсы как «4chan». А для информационного портала, посвящённому технической тематике, такое поведение недопустимо.

3. Бизнес-логика решения проблемы токсичности

Для решения проблемы токсичности я предлагаю следующую блок-схему:

Блок-схема бизнес-логики приложения "неТоксичные комментарии"

Блок-схема бизнес-логики приложения “неТоксичные комментарии”

4. Архитектура приложения

Распределённая архитектура приложения "неТоксичные комментарии"

Распределённая архитектура приложения “неТоксичные комментарии”

Как видно из рисунка выше, мы имеем дело с распределённой архитектурой.

Согласно книге Марка Ричардса и Нила Форда «Fundamentals of Software Architecture: An

Engineering Approach» существует архитектурный стиль на основе сервисов и архитектурный стиль на основе микросервисов. Чем они отличаются? Каждый сервис архитектуры на основе сервисов пользуется общей базой данных, а у каждого микросервиса в миросервисной архитектуре — своя база данных или она вообще может отсутствовать. Это сделано для изоляции данных.

В нашем случае, имеются все признаки микросервисной архитектуры в виде двух микросервисов: пользовательского интерфейса на React с отсутствием своей базы данных, и микросервиса «comments», который имеет свою базу данных SQLite.

Пару слов скажем о микросервисе пользовательского интерфейса на React. В микросервисах пользовательских интерфейсов используется один из двух подходов: монолитные пользовательские интерфейсы или микрофронтенды. Я реализовал микрофронтенд, который работает только с комментариями, реализуя свой собственный UI-компонент.

Микросервис бэкенда «comments» представляет собой элемент предметной области и был разработан с использованием подхода, основанного на концепциях DDD (Domain-Driven Design). Он реализован на фреймворке FastAPI для Python и осуществляет взаимодействие с другими микросервисами посредством удалённых вызовов процедур (RPC): через RESTful API с микросервисом пользовательского интерфейса и через gRPC с API YandexGPT.

Один из принципов микросервисной архитектуры — возможность независимого развертывания. Клиентская часть на React может быть развернута и обновлена независимо от серверной части или других компонентов приложения.

Можно ли было сделать монолитную архитектуру, например, на каком-нибудь Django 5? Конечно, можно. Причём приложение было бы чуть более отзывчивым, так как фронтенд общался бы с бэкендом через view-функции, а не через RPC по API. Почему мы так не сделали? Когда приложение начинает разрастаться, вы можете попасть в так называемый «монолитный ад», выходом из которого будет разбиение на микросервисы, а это процесс очень долгий и сложный.

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

5. Проектирование базы данных

В нашем случае база данных будет состоять всего из одной таблицу comments:

Физическая модель базы данных микросервиса бэкенда

Физическая модель базы данных микросервиса бэкенда

Обратите внимание, что отдельные поля имеют индексацию. Почему одни поля имеют индексацию, а другие — нет? Дело в том, что поля is_toxic и was_moderated в будущем будут подвергнуты фильтрации (оператор WHERE в SQL), а по полю date будет производиться сортировка (оператор ORDER BY в SQL). Это было сделано, чтобы повысить общую производительность и эффективность работы с базой данных. Но не надо забывать, что индексы — это избыточная структура данных, и они иногда могут иметь отрицательный эффект.

Теперь представьте ситуацию, что у нас много-много пользователи одновременно пишут комментарии. В этом случае, для улучшения производительности приложения необходимо позаботиться об асинхронном взаимодействии с СУБД путём выполнения асинхронных запросов в базу данных. Для этого необходимо воспользоваться асинхронным драйвером соответствующей СУБД.

Так как мы используем SQLite в нашем веб-приложении в целях демонстрации, мы воспользуемся драйвером aiosqlite. Драйвер aiosqlite, позволяет выполнять запросы в базу данных без блокировки основного потока.

В рамках реального проекта мы воспользовались бы одной из систем управления базами данных, таких как PostgreSQL или MySQL, в сочетании с соответствующим асинхронным драйвером для этих СУБД.

6. Пишем бэкенд на FastAPI

Спецификация OpenAPI для микросервиса бэкенда

Спецификация OpenAPI для микросервиса бэкенда

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

.backend
│--- .env             # Переменные окружения
│--- Dockerfile       # Файл создания образа Docker        
│--- main.py          # Точка входа в приложение
│--- pyproject.toml   # Конфигурации тестирования pytest
│--- requirements.txt # Зависимости
│--- settings.py      # Настройки приложения
│   
├── crud                 # Каталог для базовых операций с запросами к БД
│    |------ comments.py  # Файл с операциями над комментариями в БД
│    |------ __init__.py  # Инициализация пакета Python
│       
├── data                 # Каталог для хранения данных БД
│    |----- database.sql  # База данных
│    |----- README.md     # Описание каталога
│       
├── database                 # Каталог для подключения к БД
│    |----- db_connection.py  # Файл для подключения к БД
│    |------ __init__.py      # Инициализация пакета Python
│       
├── logs                    # Каталог для хранения логов
│    |---- README.md        # Описание каталога
│       
├── models                # Каталог для хранения моделей БД
│    |------ comments.py   # Файл с классом моделей БД
│    |------ __init__.py   # Инициализация пакета Python
│       
├── routes                # Каталог для эндпоинтов приложения
│    |-----comments.py     # Файл с эндпоинтами приложения
│    |-----__init__.py     # Инициализация пакета Python
│       
├── schemas               # Каталог для хранения схем Pydantic
│    |----- comments.py    # Файл с моделями Pydantic для комментариев
│    |----- envs.py        # Файл с моделью Pydantic с переменными окружения
│    |----- __init__.py    # Инициализация пакета Python
│       
├── services                      # Каталог для бизнес-логики приложения
│    |----- log_handlers.py        # Файл с параметрами логирования системы
│    |----- utils.py               # Файл со вспомогательными инструментами
│    |----- yandexgpt_moderator.py # Файл для взаимодействия с YandexGPT
│    |----- __init__.py            # Инициализация пакета Python
│       
└── tests                                  # Каталог с автотестами
     |----- conftest.py                     # Файл с фикстурами для тестирования
     |----- test_crud_comments.py           # Файл тестирования /crud/comments.py
     |----- test_database_db_connection.py  # Файл тестирования /database/db_connection.py
     |----- test_routes_comments.py         # Файл тестирования /routes/comments.py
     |----- test_services_utils.py          # Файл тестирования /services/utils.py.py
     |----- __init__.py                     # Инициализация пакета Python

Далее надо эти все файлы последовательно заполнить и на каждом этапе протестировать. Чтобы не нагромождать статью я просто дам ссылку на GitHub как выглядит бэкенд.

7. Пишем фронтенд на React

Основная задача создания фронтенда – это построение интерактивного пользовательского интерфейса веб-приложений, который заключается в “фоновом” обмене данными между фронтендом и бэкендом. Грубо говоря, мы должны добиться того, чтобы после каждого действия страница не перезагружалась, а всё происходило плавно и динамически.

Несмотря на то, что у меня основной фронтенд-технологией является Vue.js, здесь в целях эксперимента я решил «поиграться» с последним React 19. Код выглядит не очень красиво, т. к. у React существуют свои паттерны и лучшие практики, которые я обязательно изучу в будущем. Тем не менее получилось следующее:

Демонстрация работы микросервиса фронтенда

Демонстрация работы микросервиса фронтенда

8. Интегрируем ИИ

Интеграция сторонней ИИ-модели — дело финансово хлопотное. Для использования API YandexGPT в своём приложении приходится создавать платёжный аккаунт. Поэтому после генерации API-ключа с вас сразу начнут брать плату за использование.

Яндекс.Облако предоставляет систему грантов для тех, кто никогда не пользовался услугами этого сервиса. То есть вы можете бесплатно в учебных целях протестировать API ИИ, получив грант. Я работал ранее с Яндекс.Облако, поэтому грант мне не дали.

API YandexGPT предоставляет 2 способа взаимодействия: через RESTful API и через gRPC.

В нашем случае, нам надо минимизировать время ответа. gRPC, благодаря своему бинарному формату данных (Protocol Buffers) и поддержке мультиплексирования запросов, имеет значительно меньшие задержки по сравнению с RESTful API. gRPC использует HTTP/2. Это быстрее и эффективнее.

Почему мы пишем бэкенд на Python? По моим наблюдениям, с какой проблемой я бы не столкнулся — для Python всегда есть соответствующая библиотека (либо сторонняя, либо стандартная). YandexGPT тоже имеет Python-библиотеку, которая называется Yandex Cloud ML SDK.

Эта библиотека использует как раз gRPC для доступа к моделям ИИ.

9. Тестируем приложение

В реальных приложениях тестировать необходимо и бэкенд, и фронтенд. В этом проекте я выполнил тестирование только бэкенда — написал автоматические асинхронные тесты, используя библиотеку pytest.

Для начала в настройках приложения надо сделать разделение на базу данных для приложения (она находится в виде файла локально) и базы данных для тестов — она будет находиться в оперативной памяти во время выполнения тестов, а по их окончанию она будет удалена из оперативной памяти. В файле backend/settings.py это выглядит так:

SQLAlCHEMY_DATABASE_URI = "sqlite+aiosqlite:///data/database.sql"
SQLALCHEMY_TEST_DATABASE_URI = "sqlite+aiosqlite:///:memory:"

Это две разные базы данных с одинаковой схемой. Они изолированы друг от друга.

У pytest гибко настраивается стратегия тестирования: мы можем «дропать» базу данных после каждого выполненного отдельного теста, а можно и сохранять её состояние в процессе выполнения всех тестов. Еще pytest позволяет пропускать тесты, если определено условие пропуска теста.

Тестирование микросервиса бэкенда

Тестирование микросервиса бэкенда

Обратите внимание на библиотеку mimesis – на мой взгляд, это самая лучшая библиотека для генерации тестовых данных.

С автотестами можно ознакомиться здесь.

10. Собираем приложение

Первое, что надо сделать — это собрать фронтенд. Мы устанавливали React вместе со сборщиком Vite. Сборка помогает уменьшить размер конечного пакета приложения за счет минимизации и оптимизации кода. Это улучшает время загрузки и производительность приложения в браузере.

Далее надо создать отдельные Dockerfile для фронтенда и бэкенда. Здесь надо учитывать несколько моментов:

  • Создавайте каталог приложения в контейнере Docker – /app. Не копируйте бездумно файлы в корневой каталог /. При таком подходе могут возникнуть конфликты внутри Docker контейнера.

WORKDIR /app
  • Из-за многослойной природы образа Docker (image) вначале копируем файл с зависимостями в каталог приложения /app (в бэкенде (Python) это requirements.txt, в фронтенде (Node.js) это package.json), и только потом запускаем установку зависимостей.

FROM node:23-alpine

WORKDIR /app

COPY package.json .

RUN npm install

Теперь надо запустить одновременно контейнеры бэкенда и фронтенда с помощью Docker Compose, открыв необходимые порты. Еще важнейшим моментом является использование подключаемых томов Docker (volumes). Тома нужны для файла логов и для базы данных в нашем случае. У нас могут останавливаться контейнеры, удаляться, но тома со всей необходимой информацией останутся. Благодаря томам мы не потеряем ни логи, ни базу данных.

Конечно, в реальном продакшене будет использоваться более продвинутые Dockerfile и docker-compose.yaml. Для продакшена используется более тонкая настройка с определением переменных окружения и других параметров. Там даже можно задать параметры используемых ресурсов — объем оперативной памяти и число ядер CPU. В демонстративных целях я немного упростил создание, чтобы не перегружать материал.

11. Как этим пользоваться

Скачав на свой локальный компьютер репозиторий необходимо получить folder_id и секретный ключ Яндекс.Облака (Yandex.Cloud сокращённо YC) и положить их в переменные окружения backend/.env

YC_FOLDER_ID=идентификатор каталога - получить здесь https://yandex.cloud/ru/docs/resource-manager/operations/folder/get-id
YC_SECRET_KEY=Секретный ключ в Yandex Cloud ML SDK - получить здесь https://yandex.cloud/ru/docs/foundation-models/sdk/

Далее в настройках приложения backend/settings.py надо включить ИИ-модерацию

IS_AI_MODERATION_ENABLED = True

Там же в настройках посмотрим запрос к нейросети. Если нас всё устраивает, то ничего не меняем.

AI_MODERATOR_REQUEST = ('Проанализируй комментарий, и если он токсичный, '
                        'то перепиши его полностью, оставив суть, '
                        'убрав всю токсичность и добавив вежливость, '
                        'а если комментарий не токсичный, '
                        'то верни комментарий '
                        'точь-в-точь таким как он был прислан, без изменений.')

Теперь запускаем наше приложение из корневой папки, с помощью команды

docker compose up -d

Ключ -d означает запуск контейнеров в фоновом режиме.

Найдём крайне токсичный комментарий на Хабре:

Что-то подобное я частенько вижу на Хабре

Что-то подобное я частенько вижу на Хабре

Далее перекопируем этот комментарий в наше приложение:

Пытаемся опубликовать токсичный комментарий

Пытаемся опубликовать токсичный комментарий

Нажимаем «Опубликовать» и получаем результат

Комментарий был опубликован, пройдя модерацию ИИ

Комментарий был опубликован, пройдя модерацию ИИ

Обратите внимание, что YandexGPT убрал всю токсичность из комментария, но сохранил смысл.

Тем самым мы добились того, что и комментатор высказал свою позицию, а благодаря ИИ-модерации ещё и никого не оскорбил.

12. Полный код проекта

Полностью протестированный и рабочий код с инструкцией по установке лежит в свободном доступе в репозитории GitHub на моём аккаунте.

Добавлю, что если отключить ИИ-модерацию, приложение также исправно будет работать, публикуя комментарии в “сыром” виде.

13. Выводы

Нейросети и искусственный интеллект — это очередной этап развития человечества. Когда то люди работали на мануфактурах, а потом появилась автоматизация. Всю эту неквалифицированную работу сейчас выполняют машины. Эра ручного труда закончилась. Появилась ли массовая безработица? Нет. Смогли ли люди адаптироваться к новым условиям труда? Да. Появились новые профессии, например, оператор автоматизированных линий. Самое главное — смотреть, что происходит в мире и подстраиваться под новые условия.

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

Таким образом, ИИ представляет собой полезный инструмент, один из множества имеющихся на сегодняшний день. На момент публикации данной статьи он находится на пике популярности. Однако стоит вспомнить, как ранее про асинхронное программирование также трубили из каждого утюга. Знаете ли вы, что в некоторых случаях синхронный код может демонстрировать большую эффективность, чем асинхронный? Асинхронный код может вызвать множество проблем: состояние гонки, сложность отладки, проблемы синхронизации данных. Аналогично, простые приложения, созданные на основе чистого HTML, CSS и JavaScript, могут работать быстрее, чем те, что разработаны с использованием фреймворков Vue.js и React, благодаря отсутствию дополнительных накладных расходов. С искусственным интеллектом ситуация схожа: не следует бездумно внедрять его повсеместно.

Напоследок обратимся к первому закону архитектуры: «Всё в архитектуре программного обеспечения — это компромисс». Это высказывание подразумевает, что каждое архитектурное решение предполагает тщательное взвешивание разнообразных факторов. При этом следует помнить, что улучшение в одной области зачастую может обернуться ухудшением в другой.

Автор: IvanZaycev0717

Источник

Рейтинг@Mail.ru
Rambler's Top100