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


Ограниченная LLaMA
Хотя LLaMA 7B и казалась удачным выбором, её способность анализировать контекст оказалась ограниченной. Типичные проблемы:- Зависимость от контекста — один и тот же текст может быть как нейтральным, так и провокационным.
- Субъективность LLМ — сегодня запрос допустимый, а в следующую проверку система его блокирует.
- Ложные метафоры — например, «взорвать мозг красивой картинкой» может попасть под фильтр насилия.
- Пуританская этика — нейтральные запросы с намёками на романтику блокируются как эротические.
- Сложные контексты — запросы в духе «нарисуй сцену из фильма Х» могут содержать NSFW-элементы, но не всегда должны блокироваться.
Новые модели, новые возможности
С повсеместным внедрением structured output мы, конечно же, применили его в фильтре. Это не дало значительного прироста качества, зато повысило стабильность работы системы, что не могло не радовать. Мы чётко задавали ожидаемую структуру ответа, и LLМ бесперебойно следовала этим правилам. Благодаря схемам данных стало проще добавлять новые фичи — например, фильтр начал возвращать категорию пользовательских запросов, список которых мы заранее декларировали в JSON-схеме. Мы продолжили эксперименты с различными моделями, среди них особенно выделились:- Qwen30B показал впечатляющие результаты в задачах контент-фильтрации с хорошим балансом точности и производительности.
- Сюрпризом стала GPT-4o mini — модель отлично справлялась с нашей задачей.
from enum import Enum
from typing import Optional
from openai import OpenAI
from pydantic import BaseModel
# Initialize client with custom base URL and <Aitip>API</Aitip> token
client = OpenAI(
base_url="", # Replace with your actual base URL
api_key="" # Replace with your actual <Aitip>API</Aitip> 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 для быстрой и расширенной классификации и умный динамический трешхолд — и уже потом получать финальное решение на основе LLМ. Так, за последние четыре дня новый тестовый фильтр небезопасного контента обработал около 10 тыс. запросов, отклонил почти 2 тыс. — ошибки составили всего 8%. Наша цель — снизить ложные срабатывания до 2-3%, сохранив быструю скорость работы и не повысив затраты. Что продолжим делать:- Собирать отзывы, чтобы доучивать промпт на основе жалоб на блокировки;
- Тестировать цепочки агентов, например, сначала общий фильтр — потом уточняющий вопрос для пограничных случаев;
- Добавим больше контекстных правил в промпт;
- Введём whitelist для безопасных, но подозрительных слов.
Оригинал статьи опубликован на Хабре