Atlorium

Картинку загрузил пользователь, а отвечает площадка: три слоя проверки вместо одного

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

12 минут чтения модератор · владелец площадки · разработчик · юрист по контенту

На доске объявлений появилось фото садового инвентаря. Обычная картинка: лопата, секатор, моток шпагата на фоне забора. Автоматическая проверка пропустила её без замечаний, модератор при беглом просмотре тоже. Через сутки объявление сняли по жалобе: в правом нижнем углу фотографии стоял номер телефона и три слова, по которым было понятно, что продают не секатор.

Проверка отработала честно. Ей задали вопрос «есть ли на этой картинке нагота или насилие», и она честно ответила «нет». Вопрос «что здесь написано» ей никто не задавал.

Это самая частая и самая дорогая ошибка в автомодерации изображений: одну проверку принимают за все. Разберём, какие вопросы картинке нужно задавать, почему ответ на один из них ничего не говорит об остальных и где проходит граница, за которой машину сменяет человек.

Три вопроса к картинке

Слово «модерация изображений» скрывает три разные задачи. Их решают разные механизмы, и результат одного ничего не сообщает о двух других.

Вопрос Что ловит Что при этом молчит
Что за содержимое? Откровенное фото, сцену насилия — там, где нет ни надписей, ни узнаваемых предметов Метки и текст: распознавать нечего, читать нечего
Что изображено? Шприц, оружие, игральные фишки, бутылку, сигарету Классификация: в фотографии шприца нет ни наготы, ни насилия
Что написано? Телефон продавца, баннер казино, объявление о документах, лозунг Классификация И метки: на картинке нет ни предмета, ни «взрослого» — там просто буквы

Обратите внимание на третью строку. Она не про экзотику: именно так выглядит значительная часть того, что подлежит удалению на российских площадках. Скриншот переписки с контактом, картинка-объявление, где текст набран поверх нейтрального фона, баннер с промокодом. Ни один классификатор содержимого на такой картинке не сработает — и не должен, он отвечает на другой вопрос.

Во что обходится каждый вариант

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

Только ручная модерация

Что происходит
Каждое изображение открывает человек. При тысяче загрузок в сутки это полный рабочий день одного сотрудника только на просмотр.
Во что обходится
Зарплата модератора плюс то, что он пропускает к концу смены. Ночью и в выходные материал висит неотмодерированным — а срок на удаление по требованию считается в часах, а не в сменах.
Чем закрывается
Машина отсеивает очевидное и оставляет человеку спорное. Объём ручного просмотра падает в разы, а ночная дыра закрывается автоматически.

Один классификатор содержимого

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

Автоудаление по машинному признаку

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

Слой, который решает: текст на картинке

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

Причина простая. Запрещённое редко фотографируют — его рекламируют. А реклама — это текст. Продавец не выкладывает фотографию товара, он выкладывает картинку с ником, каналом и обещанием. Организатор ставок не рисует рулетку, он ставит баннер с промокодом. Изготовитель документов не снимает бланк, он публикует объявление на цветном фоне.

Для классификатора содержимого и для распознавателя предметов все эти изображения одинаково безобидны: на них нет ни наготы, ни насилия, ни предмета из стоп-листа. Это картинка с буквами. Отсеять её можно, только прочитав буквы.

Есть и обратная сторона, о которой честно предупредим. Словарь легко сделать слишком широким, и тогда он начинает вредить. Стоп-слово «справка» заблокирует объявление бухгалтерских курсов, «регистрация» — анонс конференции, «соль» — рецепт. Правило простое: чем строже реакция на категорию, тем точнее должны быть выражения в ней. Для строгих категорий лучше связки из двух слов («купить диплом»), а не отдельные корни.

Код: проверка на публикации

Типовое встраивание — одна проверка в момент сохранения записи. Ответ содержит решение, перечень сработавших фильтров с доказательствами и сырые слои для перепроверки.

Проверка изображения перед публикацией

# Полный проход: метки + классификация содержимого + текст на изображении
curl -X POST \
     -H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
     -H "Content-Type: application/json" \
     -d '{"image":"data:image/jpeg;base64,/9j/4AAQSkZJRg...","includeText":true}' \
     "https://atlorium.com/api/imagecheck"

# Плюс поиск картинки в вебе — против украденных фото в объявлениях
curl -X POST \
     -H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
     -H "Content-Type: application/json" \
     -d '{"image":"...","includeText":true,"includeWebSearch":true}' \
     "https://atlorium.com/api/imagecheck"
        

import base64
import requests

BASE = "https://atlorium.com"
HEADERS = {"Authorization": "Bearer ak_sandbox_demo_mockdata_v1"}


def moderate(path: str) -> dict:
    with open(path, "rb") as f:
        payload = base64.b64encode(f.read()).decode()

    r = requests.post(
        f"{BASE}/api/imagecheck",
        json={"image": payload, "includeText": True},
        headers=HEADERS,
        timeout=90,
    )
    r.raise_for_status()
    return r.json()


def route(path: str) -> str:
    """Куда отправить запись: в ленту, модератору или в отказ."""
    report = moderate(path)

    # Решение — рекомендация, а не приговор. Промежуточное review существует
    # потому, что автоудаление по машинному признаку ломается на медицинских,
    # исторических и новостных материалах.
    if report["decision"] == "block":
        return "reject"
    if report["decision"] == "review":
        # Модератору показываем ДОКАЗАТЕЛЬСТВА, а не только вердикт: без них
        # он всё равно откроет картинку и будет искать причину сам.
        for flag in report["flags"]:
            print(f"  {flag['title']}: {flag['evidence']} (слой {flag['source']})")
        return "manual"
    return "publish"
        

using System.Net.Http.Json;

HttpClient http = new() { BaseAddress = new Uri("https://atlorium.com") };
http.DefaultRequestHeaders.Add("Authorization", "Bearer ak_sandbox_demo_mockdata_v1");

var request = new
{
    image = Convert.ToBase64String(await File.ReadAllBytesAsync(path)),
    includeText = true,          // слой, который ловит надписи; выключать не стоит
    includeWebSearch = false,    // отдельная единица работы, включайте осознанно
};

HttpResponseMessage response = await http.PostAsJsonAsync("/api/imagecheck", request);
ImageReport? report = await response.Content.ReadFromJsonAsync<ImageReport>();

// billedUnits показывает, сколько единиц списано фактически: заказанный поиск
// в вебе, не давший совпадений, из счёта выпадает.
Console.WriteLine($"{report?.Decision}, единиц: {report?.BilledUnits}");

record ImageFlag(string Category, string Title, string Source, string Evidence, string Severity);
record ImageReport(string Decision, ImageFlag[] Flags, string Text, int BilledUnits);
        

const BASE = "https://atlorium.com";
const HEADERS = {
  Authorization: "Bearer ak_sandbox_demo_mockdata_v1",
  "Content-Type": "application/json",
};

async function moderate(buffer) {
  const r = await fetch(`${BASE}/api/imagecheck`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
      image: buffer.toString("base64"),
      includeText: true,
    }),
  });

  const report = await r.json();

  // Пустой text означает либо что надписей нет, либо что слой не заказывали.
  // Отличить одно от другого можно по billedUnits — это не ошибка ответа.
  return {
    decision: report.decision,
    reasons: report.flags.map((f) => `${f.title}: ${f.evidence}`),
  };
}
        

{
  "decision": "review",
  "flags": [
    { "category": "gambling", "title": "Азартные игры", "source": "text",
      "evidence": "бонус за депозит", "severity": "review" },
    { "category": "contacts", "title": "Контакты для увода сделки", "source": "text",
      "evidence": "+7 900 000-00-00", "severity": "review" }
  ],
  "safeSearch": {
    "adult": "very_unlikely", "racy": "very_unlikely", "violence": "very_unlikely",
    "medical": "very_unlikely", "spoof": "very_unlikely"
  },
  "labels": [
    { "name": "Font", "score": 0.93 },
    { "name": "Poster", "score": 0.85 }
  ],
  "text": "ФРИСПИНЫ КАЖДЫЙ ДЕНЬ\nбонус за депозит 200%\nпиши: +7 900 000-00-00",
  "billedUnits": 2,
  "elapsedMs": 640
}
        

Тот же вызов на шести языках (Python, TypeScript, Go, Java, C#, PHP) — в репозитории image-moderation-api-client. Примеры запускаются сразу: ключ в них уже стоит демонстрационный.

Обратите внимание на ответ выше. Классификация содержимого чиста по всем пяти категориям, среди меток — «шрифт» и «плакат», то есть ничего запрещённого. Единственное, что выдало картинку, — прочитанный на ней текст. Именно так выглядит типовое объявление, и именно так выглядел бы отчёт «всё в порядке», если бы третий слой был выключен.

Как встроить, чтобы это работало

Проверяйте на загрузке, а не на показе. Изображение проверяется один раз при сохранении. Проверка при каждой отрисовке карточки превратит популярное объявление в поток одинаковых запросов и посадит скорость страницы.

Показывайте модератору доказательства, а не вердикт. «Подозрительно» без объяснения означает, что человек всё равно откроет картинку и будет искать причину сам — то есть автоматизация не сэкономила ничего. «Сработало: азартные игры, текст, „бонус за депозит“» — это решение за две секунды.

Настройте строгость по категориям, а не глобально. Оружие на доске объявлений и оружие на историческом форуме требуют разной реакции. Одна общая «чувствительность» всегда оказывается либо слишком мягкой для одной категории, либо слишком строгой для другой.

Ведите учёт ложных срабатываний. Каждое снятое модератором «подозрительно» — это сигнал о слишком широком выражении в словаре. Без такого учёта словарь только растёт, и через полгода половина ленты уходит на ручной разбор.

Границы: чего проверка не делает

Не устанавливает личность. Сервис не распознаёт лица и не сопоставляет их с людьми. Это отдельная задача с отдельным правовым режимом, и здесь её нет.

Не хранит изображения. Картинка нужна для анализа и не сохраняется после него. Хранение чужих пользовательских изображений — обязательство, которое сервис проверки на себя не берёт.

Не заменяет юридическую оценку. Решение «не публиковать» означает, что сработал фильтр площадки, а не что материал признан запрещённым. Квалификация — вопрос права, и принимает её человек.

Не даёт стопроцентной полноты. Ни одна автоматическая проверка не ловит всё: текст бывает нечитаемым, предмет — неузнаваемым, а формулировка — новой. Автомодерация снимает объём и закрывает ночную дыру; она не отменяет ни модератора, ни механизм жалоб, ни реакцию на предписание.

Частые вопросы

Частые вопросы

Разве одного классификатора «на 18+» недостаточно?

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

Что такое «текст на изображении» и почему это отдельная проверка?

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

Можно ли сэкономить, отключив часть проверок?

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

Почему решений три, а не «можно / нельзя»?

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

Кто задаёт список запрещённого?

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

Распознаются ли лица? Хранятся ли изображения?

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

Заменит ли это модератора?

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

Сервисы из этой статьи

Анализ изображения

Модерация UGC в три слоя: что изображено, нет ли взрослого содержимого и что написано НА картинке. Вердикт — по вашим стоп-листам под нормы РФ

Распознавание текста с изображения

OCR «картинка → текст»: печатный текст, таблицы и формулы со скана или фото через REST API; плата только за успешное распознавание

Попробовать прямо сейчас — без регистрации

Демо-ключ ak_sandbox_demo_mockdata_v1 — публичный и общий для всех. С ним API отвечает моками: данные правдоподобные, но сгенерированные, и они не меняются от запроса к запросу — на них удобно писать тесты. Настоящие данные приходят с личным ключом.

curl -H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
     https://atlorium.com/openapi/img_ru.json
Полезно? Перешлите коллеге: Telegram VK

Восстанавливаем соединение…

Похоже, связь с сервером ненадолго прервалась. Переподключаемся автоматически — пожалуйста, подождите несколько секунд.

Не удалось переподключиться

Проверьте интернет-соединение. Можно повторить попытку или обновить страницу.

Сессия устарела

Соединение восстановлено, но сессию нужно перезагрузить. Обновите страницу, чтобы продолжить.