← Гордеев Виталий · все кейсы
Кейс · работает с июня 2026
Менеджер отзывов · модуль контент-завода

Машина отвечает на отзывы, но не на все

Четыре заведения, тысячи отзывов на Яндексе и в 2ГИС. Руками на каждый не ответишь, а если молчать, гости уходят. Я собрал систему, которая сама собирает отзывы, понимает их тон, пишет ответ голосом заведения и публикует его, если это безопасно.

2 470отзывов с Яндекса собрано в первый же полный проход
4заведения, у каждого свой голос и подпись
30 миншаг цикла: сбор, разбор, черновики, публикация, жалобы
0автоответов на негатив, на него отвечает человек
01 · Устройство

Пять шагов
за один проход

У каждого шага есть лимит на проход, иначе один большой завал съел бы лимиты модели на весь день.

ШагЧто происходитЛимит за проход
СборЯндекс собирается через браузер (Playwright), 2ГИС через публичный виджет без логина50
РазборGemini определяет тон, есть ли реальная проблема, нужен ли владелец, есть ли повод для жалобы8
ЧерновикОтвет голосом конкретного заведения, со своей подписью6
ПубликацияПозитивные и нейтральные уходят автоматически, если не нужен человек10
ЖалобаПодаётся на «пустой» негатив без реального опыта, по шаблону через кабинет площадки10
PythonGemini 2.5 FlashPlaywrightSQLiteFernetЯндекс Бизнес2ГИС

Инструкции для модели на английском, хотя отзывы русские: так она точнее держит формат ответа. Жалобы собираются по шаблону, без модели. Творчество там не нужно, а квота целее.

Пароли от кабинетов площадок лежат в базе зашифрованными. Для жалоб есть стоп-кран: переключаешь один флаг в панели, и система перестаёт их подавать, а очередь продолжает копиться.

02 · Граница

Когда машине
можно отвечать самой

Писать ответы модель умеет. Сложнее решить, когда её ответ можно отправить без человека. Это условие ниже, дословно из кода.

01

Жалуемся только на пустой негатив

Правило
Жаловаться можно только на отзыв с одной-двумя звёздами, если у жалобы есть основание, в отзыве нет реальной проблемы и владельцу смотреть не нужно. Все три условия сразу.
Откуда оно
Появилось после 22 июня. Тогда система отправила в жалобы 106 реальных развёрнутых негативов одного ресторана, хотя их нужно было читать.
reviews_manager/orchestrator.py · дословно
# Жалоба ТОЛЬКО на «пустой» негатив без опыта. Развёрнутый реальный негатив
# (has_real_issues) или требующий владельца (needs_human_review) → НЕ жалуемся,
# а отвечаем/на ревью (корень инцидента 22.06: 106 реальных негативов Makhall в жалобы).
if (cls.complaint_ground and cls.complaint_ground != "concerts"
        and not cls.has_real_issues and not cls.needs_human_review):
    item.status = "needs_complaint"
02

Автоответ ждал флага, который модель почти не ставила

Симптом
Система писала хорошие ответы на позитив, но ни один не уходил сам.
Причина
Условие требовало, чтобы модель отметила ответ как «безопасный». А она перестраховывалась: из 367 позитивных отзывов безопасными отметила только 72.
Проверку перевёл на другой флаг, «нужен ли человек». Угрозы, инциденты и подозрение на фейк отсекает как раз он. Сразу после этого одобрилось 183 черновика, которые копились без движения.
reviews_manager/orchestrator.py · дословно
# ГЕЙТ безопасности = needs_human_review (он отсекает угрозы/инциденты/
# подозрение на фейк). Флаг is_safe_to_auto_reply НЕ требуем: классификатор
# ставит его в False даже на нормальных позитивах (перестраховка) → авто не
# срабатывало. На позитив/нейтрал без needs_human ответ безопасен по определению.
auto = False
if item.sentiment == "positive" and config.auto_publish_positive() and not cls.needs_human_review:
    auto = True
elif item.sentiment == "neutral" and config.auto_publish_neutral() and not cls.needs_human_review:
    auto = True
03 · Разбор

Ещё два урока

Оба про то, что данные устроены не так, как кажется.

03

Гостинице отвечала «Команда клуба»

Симптом
На отзыв о номере в гостинице ушёл ответ с подписью ночного клуба.
Причина
Под одним аккаунтом Яндекса жили семь карточек разных заведений, а сборщик записывал все отзывы в одно.
Сделал карту «карточка → заведение» и отдельное поле в базе. Задним числом переразметил 104 отзыва.
04

Модель держала базу 200 секунд

Симптом
Во время разбора отзывов остальные службы завода получали «база занята».
Причина
Транзакция оставалась открытой, пока модель пишет ответ. А писать в SQLite может только кто-то один, остальные ждут.
Коммит теперь идёт до вызова модели. Транзакция закрывается раньше, и база занята меньше.

Тонете в отзывах?

Настрою сбор и ответы: рутину возьмёт на себя машина, а вам останутся только отзывы, по которым надо что-то решать. Работаю удалённо, из Самары.

Telegram · @vitaldemort