Модерация комментариев нейросетью: пример на Python

Спам, оскорбления, реклама, нормальные комментарии — нейросеть раскладывает их по ярлыкам, а спорные отдаёт модератору. Показываем код и считаем стоимость.

3 мин · · разработка · бизнес

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

Как работает модерация

Каждый комментарий получает ярлык:

  • ok: нормальный комментарий, публикуем.
  • spam: явный спам, удаляем.
  • insult: оскорбление, скрываем.
  • ads: реклама, удаляем.

Плюс значение уверенности (confidence). Если модель не уверена (confidence ниже порога), комментарий идёт в очередь к модератору. Так автоматизация берёт на себя очевидные случаи, а человек решает спорные.

Быстрая модерация с glm-5.3-flash

Начнём с самой дешёвой модели Zukko — glm-5.3-flash ($0,04/$0,04 за 1M токенов). Подходит ли она для ваших комментариев, проверим ниже на размеченной выборке.

bash
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://api.zukko.pro/v1",
    api_key="fetch_ваш_ключ",
)

LABELS = ["ok", "spam", "insult", "ads"]

SYSTEM_PROMPT = """Ты модератор комментариев. Оцени комментарий и выбери ярлык.

Ярлыки:
- ok: нормальный комментарий, публиковать

Маршрутизация: автоматически или к человеку

Не все случаи очевидны. Если уверенность ниже порога, комментарий идёт модератору:

bash
ACTIONS = {"ok": "publish", "spam": "delete", "insult": "hide", "ads": "delete"}

def route_comment(text: str, threshold: float = 0.8) -> dict:
    """Решает: опубликовать, удалить или отправить на проверку"""
    result = moderate_comment(text)
    confidence = float(result.get("confidence", 0))
    if confidence < threshold or result["label"] not in ACTIONS:
        return {
            "action": "manual_review",
            "suggested_label": result["label"],
            "confidence": confidence,
            "reason": result.get("reasoning", ""),
        }
    return {"action": ACTIONS[result["label"]], "label": result["label"], "confidence": confidence}

if __name__ == "__main__":
    for comment in ["Отличная статья!", "Может, это реклама, а может, и нет", "СПАМ!!!!"]:
        print(comment, "->", route_comment(comment)["action"])

confidence модель выставляет сама, это не откалиброванная вероятность. Порог 0,8 — стартовое значение; подберите своё по выборке, где правильные ярлыки уже проставлены людьми.

Пакетная обработка: десятки комментариев за запрос

Для ночной проверки или разбора архива удобно отправлять комментарии пачками по 20–50 штук. Просим модель вернуть номер комментария, чтобы ответы не перепутались:

bash
def moderate_batch(comments: list[str], model: str = "glm-5.3-flash") -> dict[int, dict]:
    """Модерирует пачку комментариев, возвращает {номер: результат}"""
    batch_text = "\n---\n".join(f"Комментарий {i}:\n{c}" for i, c in enumerate(comments, 1))
    system_for_batch = SYSTEM_PROMPT + """
Тебе пришло несколько комментариев. Для КАЖДОГО выведи одну строку JSON:
{"id": <номер комментария>, "label": "...", "confidence": 0.9}
Одна строка — один объект, без лишнего текста.
"""
    response = client.chat.completions.create(
        model=model,
        max_tokens=60 * len(comments) + 100,
        messages=[
            {"role": "system", "content": system_for_batch},
            {"role": "user", "content": batch_text},

Очередь для модератора

Комментарии с низкой уверенностью копятся в отдельной очереди. Модератор подтверждает или исправляет решение:

bash
class ModerationQueue:
    def __init__(self):
        self.items = []  # в реальном проекте — таблица в базе

    def add(self, comment_id: str, text: str, suggestion: dict):
        self.items.append({
            "id": comment_id,
            "text": text,
            "suggestion": suggestion,  # что предложила модель
            "status": "pending",
        })

    def resolve(self, comment_id: str, final_label: str):
        """Модератор принял решение"""

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

Стоимость

Расчёт для 10 000 комментариев в месяц на glm-5.3-flash, в среднем 200 входных и 50 выходных токенов на комментарий:

(10 000 × 200 × 0.04 / 1M) + (10 000 × 50 × 0.04 / 1M) = $0.08 + $0.02 = $0.10 в месяц

Если перепроверять спорные комментарии на claude-sonnet-5 ($0,30/$0,30), каждая тысяча таких перепроверок добавит 1000 × 250 × 0.30 / 1M = $0.075. Какая доля окажется спорной, зависит от вашей площадки и порога — посчитайте на своих данных.

Актуальные цены и модели: GET https://api.zukko.pro/v1/models или страница «Модели» в кабинете.

Настройка ключа

Создайте ключ в кабинете Zukko, разрешите ему только нужные модели и поставьте месячный лимит — для 10 тысяч комментариев с запасом хватит $1. См. также быстрый старт и примеры на Python.

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

Как часто разбирать очередь?

Зависит от площадки. Где комментарии видны сразу и важна реакция (новости, соцсети), очередь разбирают в течение дня. На форумах и в блогах достаточно одного-двух раз в день.

Может ли модель учиться на решениях модератора?

Сама по себе нет. Но решения модератора можно добавлять в промпт как примеры («такой комментарий — ads») и периодически пересчитывать точность. Если точность падает, перенесите спорные случаи на другую модель и сравните результат на той же выборке.

Что если комментарии на нескольких языках?

Укажите в промпте, что комментарии могут быть на разных языках, и добавьте в тестовую выборку примеры на каждом. Сравните точность glm-5.3-flash и claude-sonnet-5 на этих примерах — так вы узнаете, нужна ли более дорогая модель.

Стоит ли модерировать старые комментарии?

Если архив раньше не проверялся, пакетная обработка позволит пройтись по нему недорого: 10 000 комментариев на glm-5.3-flash обойдутся примерно в $0.10.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.