Как добавить модерацию контента в приложение — пять шагов и ноль рублей

Как только ты добавил в приложение поле ввода — ты стал модератором. Комментарии, отзывы, ники, описания, загруженные картинки: всё это теперь твоя ответственность, а не пользователя.
И вот неожиданная часть, до которой доходят не сразу: проверять надо не только то, что пишет пользователь, но и то, что отвечает твоя модель. Пользователь может подвести её к ответу, за который отвечать будешь ты.
Хорошая новость: у OpenAI эндпоинт модерации бесплатный — без токенов и без счёта. Разберём по шагам.
1. Реши, что именно ты проверяешь
Три места, и в каждом свои правила:
- Вход от пользователя — то, что он отправил тебе. Это защита других пользователей и тебя.
- Выход модели — то, что твоё приложение показало пользователю. Это твоя репутация и требования сторов.
- Загруженные файлы — картинки. Модель модерации умеет их читать, аудио — нет.
Если делаешь первую версию: начни с входа, добавь выход сразу после. Обычно достаточно двух проверок, это и есть классические ограждения на входе и выходе.
2. Сделай первый вызов
Модель называется omni-moderation-latest. Она принимает текст и картинки, картинку — до 20 МБ.
import OpenAI from "openai";
const client = new OpenAI();
const result = await client.moderations.create({
model: "omni-moderation-latest",
input: "текст, который прислал пользователь",
});
console.log(result.results[0].flagged);
На Python — то же самое:
from openai import OpenAI
client = OpenAI()
result = client.moderations.create(
model="omni-moderation-latest",
input="текст, который прислал пользователь",
)
print(result.results[0].flagged)
Ключ, разумеется, живёт в переменных окружения на сервере, а не в коде фронтенда — как безопасно хранить ключи разобрано отдельно. Вызов идёт с твоего бэкенда: иначе любой откроет исходники вкладки и заберёт ключ.
3. Не блокируй по flagged — читай оценки
Самая частая ошибка новичка: взять булево поле flagged и на нём построить бан. В ответе гораздо больше:
flagged— общий вердикт «что-то сработало»;categories— какие именно категории сработали;category_scores— число от 0 до 1 по каждой категории;category_applied_input_types— к чему это относилось: к тексту или к картинке.
Категорий тринадцать: домогательства и угрозы, ненависть и ненависть с угрозой, инструкции к противоправным действиям (и они же с насилием), самоповреждение в трёх видах — намерение, инструкции и общее, сексуальный контент, отдельно сексуальный контент с несовершеннолетними, насилие и графическое насилие.
Разные категории заслуживают разной реакции. «Сексуальный контент» в приложении для взрослых — норма, а «самоповреждение: намерение» требует не бана, а показа телефона доверия. Поэтому вместо одного «да/нет» заведи три исхода:
- низкие оценки — пропускаем молча;
- средние — публикуем, но кладём в очередь на просмотр;
- высокие или чувствительные категории — не публикуем.
В документации это сказано прямо: оценки модерации — это сигналы для твоей политики, а не автоматическое решение о блокировке. Границы между исходами ты выбираешь сам, и выбор этот не технический: подними порог — пропустишь больше нарушений, опусти — заденешь невиновных. Это ровно тот обмен, который разобран в статье про ложные срабатывания.
4. Проверяй ответ модели, а не только запрос
Если твоё приложение генерирует текст, модерацию можно попросить прямо в том же запросе — отдельный вызов не нужен:
const response = await client.responses.create({
model: "gpt-6-astra",
input: [{ role: "user", content: userText }],
moderation: { model: "omni-moderation-latest" },
});
response.moderation.input.flagged; // что прислал пользователь
response.moderation.output.flagged; // что ответила модель
Ты получаешь оценки и для запроса, и для ответа за один заход. Модель при этом всё равно генерирует ответ — решение показывать его или нет остаётся за тобой.
И сразу подвох, о котором предупреждает сама документация: вежливый отказ модели тоже может получить флаг. Если модель отвечает «я не буду объяснять, как сделать оружие», в её ответе есть слово «оружие» — и категория сработает. Поэтому не вешай автоматический бан на флаг выхода: логируй, смотри категорию, а решение принимай по своим правилам.
5. Заведи журнал и смотри в него
Последний шаг, который пропускают все и потом чинят месяцами. Записывай каждое срабатывание: текст (или его хеш), категории, оценки, что ты сделал.
Раз в неделю открывай журнал и читай не пойманных нарушителей, а спорные случаи. Именно там видно, что порог стоит криво, что модель путается на твоём сленге, что пользователи научились обходить фильтр. Без журнала ты не узнаешь ни об одном ложном бане: обиженный человек просто уйдёт молча.
Что получится
Пара часов работы — и у тебя есть: проверка входа, проверка выхода, три исхода вместо тупого бана и журнал, по которому видно, где фильтр ошибается. Стоимость — ноль: эндпоинт бесплатный, платишь только за саму генерацию.
Чего у тебя по-прежнему нет — модерации «по смыслу твоего продукта». Спам-ссылки, продажа чужих аккаунтов, слив контактов в обход площадки — это не «ненависть» и не «насилие», универсальные категории их не ловят. Такие правила пишутся своим классификатором поверх, и начинать стоит с самого простого варианта — сравнить его со списком стоп-слов.
Сколько это стоит и есть ли лимиты?
Сам эндпоинт модерации бесплатный — токены за него не списываются. Ограничения есть на частоту запросов, как и у любого другого метода API; при большом потоке проверяй не каждый символ по ходу набора, а готовое сообщение при отправке. Если вызываешь модерацию отдельным запросом, помни, что это дополнительный сетевой поход — на форме комментария это незаметно, в стриминге лучше проверять ответ по частям.
А если у меня не английский язык?
Работает. Мультимодальная версия модели заметно точнее прежней именно на неанглийских языках. Но проверь на своих данных: возьми полсотни реальных сообщений из своего приложения, прогони и посмотри, где вердикт разошёлся с твоим. Полчаса такой проверки экономят недели споров с пользователями.
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.





