Автоматические ответы на отзывы покупателей Wildberries на базе ИИ

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

ОПИСАНИЕ

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

Магазин получает порядка трёхсот отзывов в сутки, и ответ на каждый — ручная работа, которая раньше делалась урывками и с задержкой в день-два. Модуль встроен в общий цикл парсера, который прокручивается каждые 20–30 минут, поэтому отзыв, пришедший утром, получает ответ через полчаса. Контур состоит из трёх скриптов — сбор отзывов из API Wildberries, подготовка ответа, отправка ответа — и общей таблицы в PostgreSQL, через которую они и связаны.

Сбор отзывов

Сбор устроен вокруг ограничений площадки. Всего у магазина порядка полутора миллионов отзывов, при этом выборка Wildberries физически не отдаёт больше двухсот тысяч записей, а перечитывать отвеченные и архивные бессмысленно — они не меняются. Поэтому запрашиваются только неотвеченные и только за последние сутки: один-два запроса за цикл вместо четырёх десятков. Лимитер Wildberries штрафной — каждая попытка достучаться при закрытом доступе удлиняет блокировку (наблюдали рост отката с 540 до 654 секунд), — поэтому на отказ по лимиту скрипт не повторяет запрос и не ждёт, а выходит и пробует в следующем цикле. Текст отзыва склеивается из трёх полей WB: покупатель часто пишет претензию только в «недостатки», оставив основной текст пустым.

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

Статусная модель

Таблица отзывов работает как конечный автомат: new (ждёт ответа) → ready (ответ готов) или check (ответ готов, но его должен прочитать человек) → sent (отправлен), плюс терминальное answered для отзывов, ушедших без нас. Разделение колонок принципиально: скрипт сбора обновляет только то, что приходит от WB, и в его upsert не входят наши колонки — иначе очередной цикл затирал бы готовый ответ и мы платили бы за один и тот же отзыв дважды. Очередь на обработку — это и есть статус new: как только ответ записан, отзыв уходит из очереди навсегда, сколько бы раз ни запускали скрипт.

Два потока подготовки ответов

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

Экономика: Batch API

Экономика решена через Batch API: он режет цену вдвое независимо от размера пачки, поэтому поток можно дробить на мелкие частые пачки — накапливать сутки ради скидки не нужно. Выходит около $0.001 за отзыв, порядка трёх долларов в месяц. Отдельная работа — не заплатить дважды. Ожидание пачки ограничено пятнадцатью минутами вместо суток, которые даёт провайдер: скрипт стоит внутри общего цикла, и часами ждать нельзя. Если пачка не успела, отзывы остаются в очереди, а следующий прогон сначала проверяет, не считается ли ещё пачка с прошлого цикла, и дожидается её вместо отправки новой. Идентификатором запроса в пачке служит сам идентификатор отзыва, а не порядковый номер: позиционный ключ привязал бы ответы к порядку списка, и при сбое разложить уже оплаченные ответы по отзывам было бы нечем, а ошибка в сопоставлении означает чужой ответ под чужим отзывом. Стоимость каждого прогона считается по фактическим токенам и пишется в лог.

Структурированный ответ модели

Модель отвечает не текстом, а структурой по JSON-схеме: готовый ответ, флаг «это для человека» и обоснование флага. Обоснование в базу не пишется, но из схемы не убрано намеренно — с обязательным пояснением модель заметно устойчивее принимает решение. Все тексты для модели вынесены в отдельные markdown-файлы: инструкция по тону и разбор типовых ситуаций (брак, пересорт, не подошёл размер, деньги), правила вывода и список шаблонов. Правит их не программист, и правятся они без изменения кода.

Граница «отвечаем сами / зовём человека»

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

Кеширование инструкции

Разобрано и кеширование инструкции: у выбранной модели кеш включается от 4096 токенов, а инструкция с правилами занимает около 1250 — то есть молча не работает, без всякой ошибки. Скрипт сам считает токены инструкции (подсчёт бесплатный) и предупреждает об этом в логе; переход на более дорогую модель ради кеша не окупился бы, а как только инструкция дорастёт до порога, кеш включится сам. Проверка кеша при этом не имеет права ронять прогон: раньше её падение на первом же запросе к API оставляло весь цикл без ответов, теперь она молча пропускается.

Доступ к модели с закрытого сервера

Отдельно решался доступ: на сервере закрыт api.anthropic.com, тогда как Wildberries, Google и база доступны напрямую. Глобальные переменные окружения здесь не годятся — они увели бы в прокси весь трафик проекта. Прокси навешен на единственный http-клиент, который создаётся для запросов к модели, а весь остальной проект продолжает ходить напрямую; проверено, что изоляция работает в обе стороны. Под прокси подняты и таймауты соединения с числом попыток: с настройками по умолчанию подключение отваливалось по таймауту тринадцать циклов подряд.

Отправка ответов

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

Место запуска

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

ИСПОЛЬЗУЕМЫЕ ИНСТРУМЕНТЫ

  • Python 3
  • Anthropic API
  • Claude Haiku 4.5
  • Batch API
  • structured output (JSON-схема)
  • prompt caching
  • подсчёт токенов
  • REST API Wildberries (feedbacks-api)
  • PostgreSQL
  • psycopg2
  • requests
  • httpx
  • SOCKS5-прокси
  • Markdown-промпты
  • Git

РЕЗУЛЬТАТ

  • Ответ на отзыв готовится за полчаса вместо ручной работы раз в день-два; порядка трёхсот отзывов в сутки обрабатываются без участия человека.
  • Стоимость доведена до ~$0.001 за отзыв (около $3 в месяц) за счёт Batch API и разделения потока: на 4–5 звёзд идут шаблоны, модель вызывается только на претензии.
  • Реализована защита от повторной оплаты: статусная очередь, неперезаписываемые колонки при обновлении из WB, поиск незакрытой пачки прошлого цикла и сопоставление ответов по идентификатору отзыва, а не по позиции в списке.
  • Сбор построен под ограничения площадки: только неотвеченные за сутки (один запрос вместо сорока), корректная работа со штрафным лимитером, автоматическое снятие с обработки отзывов, отвеченных вручную или ушедших в архив.
  • Модель отвечает структурой по JSON-схеме; правила эскалации на человека описаны явным списком признаков (юридическая угроза, вред здоровью, требование документов, контрафакт, денежное требование, мат), а не «уверенностью» модели.
  • Все инструкции, правила и шаблоны вынесены в markdown-файлы и правятся без изменения кода.
  • Разобрана экономика prompt-кеша: порог кеширования проверяется на лету, скрипт предупреждает, если кеш молча не включается.
  • Доступ к модели с закрытого сервера решён точечным прокси на одном http-клиенте без влияния на остальной трафик проекта.
  • Отправка сделана необратимо-безопасной: лимит на прогон, изоляция ошибок по отзывам, остановка прогона при отказе по лимиту, статус «на проверку человеку» для отзывов с юридическим или медицинским риском.

AI АССИСТЕНТ

Задать вопрос по этой работе