Привет! Меня зовут Миша Мартьянов, я инженер по исследованиям и разработке в red_mad_robot. Моя работа — искать новые идеи, проверять гипотезы и улучшать продукты. На этом пути иногда приходится изобретать уникальные решения. Например, мы создали собственный фильтр, чтобы отсеивать нежелательный контент с помощью LLM. Рассказываю, как мы к этому пришли и с какими сложностями столкнулись.

В нейросетях для генерации изображений и видео пользователи часто тестируют границы дозволенного — от рецепта плова до инструкции по изготовлению напалма может лежать всего один промпт. Здесь на помощь приходит интеллектуальный фильтр запросов. Его задача — блокировать нарушения, не мешая при этом работать обычным пользователям.
На рынке многие провайдеры предлагают модерацию контента в реальном времени, среди них есть действительно мощные и отлаженные решения. Варианты разные: одни встроены непосредственно в сервис — например, некоторые AI-генераторы видео уже включают в себя надёжные фильтры, другие — независимые системы, не привязанные к конкретному продукту.
Наши AI-cервисы — тот же Daisy — перешли из разряда самоделок в массовые продукты. Но с ростом популярности повышается нагрузка на систему фильтрации. Платить за модерацию каждого запроса невыгодно, да и давно хотелось собрать собственный фильтр с уникальными фичами, работающий на наших мощностях. В голове уже копились мысли по улучшению фильтра — нужен был тестовый контур с гибкой настройкой, чтобы проверить идеи.
Эвристика неэффективна
Начнём с главного вывода: эвристические алгоритмы неэффективны. Регулярные выражения и keyword-фильтры дают слишком много ложных срабатываний (false positive) и легко обходятся, поэтому мы сразу сделали ставку на LLM.
Это произошло несколько лет назад, когда выбор открытых языковых моделей был невелик — по-настоящему доступной и эффективной была разве что LLaMA. Мы провели серию экспериментов, последовательно улучшая качество фильтрации. Как тестовую базу использовали реальные запросы пользователей — собрали обезличенный набор данных из тысяч органических запросов, которые люди вводили в наших сервисах. После нескольких итераций правок мы разработали универсальную систему инструкций для языковой модели. Этот промпт выполнял несколько ключевых функций:
Чётко определял ограниченный набор категорий запрещенного контента, например, насилие или криминал;
Описывал пограничные случаи и правила их обработки;
Учитывал контекст и семантику запросов, а не просто реагировал на триггерные слова;
Содержал примеры корректных модерационных решений (few-shot).

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

Ограниченная LLaMA
Хотя LLaMA 7B и казалась удачным выбором, её способность анализировать контекст оказалась ограниченной. Типичные проблемы:
Зависимость от контекста
размытые границы — один и тот же текст может быть как нейтральным, так и провокационнымСубъективность LLM
сегодня запрос допустимый, а в следующую проверку система его блокируетЛожные метафоры
например, «взорвать мозг красивой картинкой» может попасть под фильтр насилияПуританская этика
нейтральные запросы с намёками на романтику блокируются как эротическиеСложные контексты
запросы в духе «нарисуй сцену из фильма Х» могут содержать NSFW-элементы, но не всегда должны блокироваться
Наш сложный промпт с многоуровневой системой проверок требовал от модели слишком многого — семантический анализ выходил за рамки её возможностей. Главной проблемой стали ложные срабатывания: система иногда блокировала безобидные запросы, а некоторые запрещённые темы всё же проходили через простые манипуляции с формулировками. Мы это исправляли, но в моменте приходилось мириться с неудобствами. Тем не менее, результат был достойным: мы сохранили баланс между скоростью и качеством модерации, а главное — успешно блокировали подавляющее большинство нарушений.
Новые модели, новые возможности
С повсеместным внедрением structured output мы, конечно же, применили его в фильтре. Это не дало значительного прироста качества, зато повысило стабильность работы системы, что не могло не радовать. Мы чётко задавали ожидаемую структуру ответа, и LLM бесперебойно следовала этим правилам. Благодаря схемам данных стало проще добавлять новые фичи — например, фильтр начал возвращать категорию пользовательских запросов, список которых мы заранее декларировали в JSON-схеме.
Мы продолжили эксперименты с различными моделями, среди них особенно выделились:
Qwen30B показал впечатляющие результаты в задачах контент-фильтрации с хорошим балансом точности и производительности.
Сюрпризом стала GPT-4o mini — модель отлично справлялась с нашей задачей.
Всё это подтолкнуло нас к важному архитектурному решению — отказаться от размещения моделей на собственных серверах. Экономика проекта оказалось выгоднее, чем при предыдущей инфраструктуре. Но главное преимущество — сохранился полный контроль над логикой работы системы. Наши Guardrails и бизнес-правила остались всё теми же, а в облако перешла только наиболее ресурсоемкая часть вычислений.
Пример валидации с использованием structured output из справочника OpenAI:
from enum import Enum
from typing import Optional
from openai import OpenAI
from pydantic import BaseModel
# Initialize client with custom base URL and API token
client = OpenAI(
base_url="", # Replace with your actual base URL
api_key="" # Replace with your actual API token
)
class Category(str, Enum):
VIOLENCE = "violence"
ILLEGAL_ACTIVITIES = "illegal_activities"
DRUGS = "drug_abuse"
EXTREMISM = "extremism"
class ContentCompliance(BaseModel):
is_violating: bool
category: Optional[Category]
explanation_if_violating: Optional[str]
response = client.responses.parse(
model="gpt-4o-mini",
input=[
{
"role": "system",
"content": "Determine if the user input violates specific guidelines and explain if they do.",
},
{"role": "user", "content": "User Request"},
],
text_format=ContentCompliance,
)
compliance = response.output_parsed
print(compliance)
Умнее, быстрее и экономичнее
Сейчас мы значительно модернизировали фильтр. Он включает в себя всё лучшее из прошлой версии, а также новые идеи, например, использовать BERT для быстрой и расширенной классификации и умный динамический трешхолд — и уже потом получать финальное решение на основе LLM.
Так, за последние четыре дня новый тестовый фильтр небезопасного контента обработал около 10 тыс. запросов, отклонил почти 2 тыс. — ошибки составили всего 8%.
Наша цель — снизить ложные срабатывания до 2-3%, сохранив быструю скорость работы и не повысив затраты.
Что продолжим делать:
Собирать отзывы, чтобы доучивать промпт на основе жалоб на блокировки;
Тестировать цепочки агентов, например, сначала общий фильтр — потом уточняющий вопрос для пограничных случаев;
Добавим больше контекстных правил в промпт;
Введём whitelist для безопасных, но подозрительных слов.
Сейчас наш фильтр стал умнее, быстрее и дешевле, но работа не закончена. Впереди — эксперименты с цепочками агентов, дообучение на реальных запросах и новые способы контекстной обработки. Идеального фильтра контента не существует, но можно сделать его настолько незаметным для пользователей, насколько это возможно — и при этом эффективным.
Над материалом работали:
текст — Миша Мартьянов
редактура — Игорь Решетников
иллюстрации — Юля Ефимова
Это блог red_mad_robot. Мы запускаем цифровые бизнесы и помогаем компаниям внедрять AI. Здесь наша команда разработки на собственных кейсах рассказывает о том, что происходит с AI сегодня, а стратегические аналитики подсказывают, что будет завтра. Мы бы подписались.
Наш Telegram-канал (там всё другое, а ещё есть анонсы мероприятий): t.me/redmadnews