AI-агент для динамического ценообразования на маркетплейсе: следим за конкурентами и обновляем цены программно

Зачем вообще автоматизировать репрайсинг в 2026 году

Wildberries и Ozon перевели API на модель pay-as-you-go с января 2026. Теперь каждый запрос к ценам конкурентов стоит денег. Это изменило логику мониторинга: брать данные каждые пять минут по всему каталогу уже нельзя без внятного ROI. Автоматизация из "приятной фичи" превратилась в единственный способ уложиться в экономику.

В марте логистика на WB подорожала. Комиссии подросли. Себестоимость продажи одного SKU изменилась у тысяч продавцов одновременно. Тот, кто пересчитал цены вручную за три дня, эти три дня либо торговал в минус, либо терял позиции из-за завышенной цены.

В топовых категориях конкуренты могут менять цены многократно в течение суток. По наблюдениям за несколькими нишами (электроника, косметика, детские товары) там ценообразование живёт своей жизнью: утренняя просадка, дневной откат, вечерний демпинг под трафиковый пик. Человек с таблицей Excel здесь просто не в той лиге.

И ещё один момент: разница в 3-5% нередко определяет, кто получает Buy Box и кто стоит первым в выдаче. Алгоритмы маркетплейсов давно не работают по принципу "самый дешёвый выигрывает". Учитывается рейтинг, скорость доставки, процент выкупа. Но цена в пределах узкого коридора от конкурента остаётся одним из жёстких фильтров. Промахнуться на 200 рублей на товаре за 4000 рублей, и позиция падает.

Ручной репрайсинг больше не ломается из-за лени. Он ломается математически.

График с хаотичными скачками цен конкурентов на маркетплейсе за 24 часа

Цены конкурентов на популярные SKU могут меняться десятки раз в сутки, и ручное отслеживание этого потока нереально.

Как устроен AI-агент для динамического ценообразования: архитектура

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

Сборщик данных читает конкурентов. Здесь сразу развилка: официальное API маркетплейса или парсер. У WB в 2026 году API для внешних продавцов закрыто для получения цен конкурентов, Ozon даёт кое-что через Seller API, но полноты там нет. Парсер даёт больше данных, но это серая зона по условиям использования, плюс Ozon и WB активно режут нетипичный трафик. Реальная цена вопроса: официальный путь дешевле в поддержке, парсер дешевле в лицензиях, но дороже в эксплуатации, когда структура страницы меняется после очередного редизайна.

Движок принятия решений бывает трёх видов, и это принципиально влияет на поведение агента.

Rule-based: пишешь правила вроде "если конкурент дешевле на 5%, снизь цену на 3%, но не ниже floor". Быстро, предсказуемо, легко объяснить бизнесу. Ломается на нестандартных ситуациях: акции конкурентов, сезонные флуктуации, выход нового игрока с демпингом.

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

LLM-агент с инструментами: самый интересный вариант прямо сейчас. Модель получает текущий контекст, вызывает инструменты (запрос конкурентов, чтение остатков, чтение истории продаж) и возвращает решение с объяснением. По latency заметно медленнее rule-based подхода, стоит денег за каждый вызов, зато рассуждает о нестандартных ситуациях и оставляет читаемый trail, почему цена была изменена.

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

И здесь жёсткое ограничение, которое задаётся руками: floor-price. Агент физически не может записать цену ниже этой границы. Это не мягкое правило в промпте и не рекомендация в конфиге. Это проверка в коде исполнителя, до вызова API. Потому что LLM-агент с плохим промптом или сломавшийся парсер, прочитавший чужой товар как конкурента, вполне способен предложить цену в 1 рубль. И API маркетплейса такую цену примет без возражений.

Архитектура AI-агента репрайсинга: три модуля с потоками данных между ними

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

Сбор данных о конкурентах: парсинг vs API в условиях 2026 года

Начну с арифметики, которую обычно пропускают.

Официальный Seller API Wildberries стал платным: 0.001 рубля за запрос. Звучит смешно, пока не считаешь. У вас 10 000 SKU, мониторинг раз в час, 24 часа в сутки. Это 10 000 × 24 = 240 000 запросов в день, 7.2 млн в месяц. По 0.001 руб. получается 7 200 рублей в месяц только на опрос цен. Добавьте остатки, рейтинг, участие в акциях, и бюджет легко утраивается. Для небольшого селлера это уже статья расходов, которую надо планировать.

Парсинг публичных страниц WB и Ozon технически работает. Но оферта 2026 года прямо запрещает автоматизированный сбор данных с публичного фронтенда. Юридически вы в серой зоне, практически: аккаунт блокируют без предупреждения. Несколько крупных агентств в начале года словили баны именно так. Я не рекомендую.

B2B-агрегаторы как рабочий компромисс

Сервисы MPStats, Moneyplace, Price Control уже собрали данные и перепродают доступ. Их инфраструктура работает по договорам с площадками или через легитимные каналы, их риски. Актуальные цены на тарифы лучше уточнять напрямую у этих сервисов: диапазон зависит от глубины данных и количества SKU, и ценники регулярно обновляются. Для большинства магазинов с каталогом до 5 000 SKU покупка готовых данных дешевле и надёжнее, чем собственная интеграция с API.

Если всё же идёте через Seller API, вот минимальный запрос на Python:

# Пример запроса к WB Seller API для получения цен конкурентов по артикулу
import requests

ARTICUL = 12345678
TOKEN = "your_wb_api_token"

url = f"https://card.wb.ru/cards/v1/detail?appType=1&curr=rub&dest=-1257786&nm={ARTICUL}"
response = requests.get(url, headers={"Authorization": TOKEN})
data = response.json()

if data.get("data") and data["data"].get("products"):
    product = data["data"]["products"][0]
    price = product["salePriceU"] / 100  # цена в копейках
    print(f"Артикул {ARTICUL}: текущая цена {price} руб.")

Обратите внимание на salePriceU: WB хранит цену в копейках, делите на 100. Поле dest=-1257786 задаёт регион доставки (Москва), цена может отличаться для других регионов. Подробнее о форматах передачи данных между площадками читайте в шпаргалке по форматам цен, остатков и SKU при синхронизации Ozon, WB и Яндекс.Маркета.

Что именно собирать

По каждому конкуренту нужны пять параметров: цена, рейтинг карточки, количество отзывов, наличие товара на складе (поле quantity), участие в акциях WB (поле promotions или флаг isNew). Рейтинг и отзывы не меняются каждый час, их достаточно опрашивать раз в сутки. Цена и наличие, особенно в период распродаж, меняются агрессивно.

Частота опроса по категориям

Электроника и гаджеты: каждые 2 часа. Цены там двигаются быстро, и отставание на 4-6 часов означает потерю Buy Box. Одежда и обувь: раз в 6 часов хватает, ценовая динамика медленнее, зато важнее отслеживать наличие размеров. Товары повседневного спроса (бытовая химия, продукты): мониторинг раз в 3-4 часа оправдан в акционные периоды и избыточен в обычное время.

Один практический момент: не мониторьте всех конкурентов подряд. Выберите 5-7 прямых конкурентов с близким ценовым сегментом и объёмом продаж. Остальные создают шум в данных и съедают квоту API.

Расчёт минимальной цены: себестоимость, комиссии и логистика 2026

Прежде чем агент вообще смотрит на конкурентов, он должен знать одно число: floor-price. Это цена, ниже которой опускаться нельзя физически, иначе продажа уйдёт в минус. Всё остальное ценообразование строится вокруг этой границы.

Формула простая:

floor = (себестоимость + логистика + хранение) / (1 - комиссия - целевая_маржа)

Деление, а не сложение, потому что комиссия WB считается от цены продажи, а не от затрат. Это классическая ошибка: продавец складывает расходы, прибавляет 15% сверху и получает цену, при которой реально зарабатывает меньше, чем рассчитывал.

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

Логистика. В марте 2026 WB обновил тарифы: теперь стоимость доставки зависит от объёмного веса и склада отгрузки. Коробка 30x20x15 см с Электростали и та же коробка с Краснодара дадут разные цифры. Агент должен получать актуальный тариф через API или из обновляемого справочника, а не из константы в коде.

Хранение. Оно бесплатно, пока оборачиваемость выше 60 дней. Если товар лежит медленнее, плата начисляется за каждый день. При длинных акциях, скажем на 30-45 дней, склад может съесть несколько процентов маржи, которые на первый взгляд вообще не видны.

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

def calculate_floor_price(
    cost: float,          # себестоимость
    wb_commission: float, # доля комиссии, например 0.15
    logistics: float,     # стоимость логистики за единицу
    storage: float,       # стоимость хранения за единицу
    target_margin: float  # целевая маржа, например 0.20
) -> float:
    """
    Минимальная цена, ниже которой агент не опустится.
    """
    total_costs = cost + logistics + storage
    # цена = расходы / (1 - комиссия - целевая_маржа)
    floor = total_costs / (1 - wb_commission - target_margin)
    return round(floor, 2)


# Пример: товар себестоимостью 800 руб., категория с комиссией 15%,
# логистика 85 руб. по мартовскому тарифу, хранение 12 руб. за единицу,
# целевая маржа 18%
print(calculate_floor_price(
    cost=800,
    wb_commission=0.15,
    logistics=85,
    storage=12,
    target_margin=0.18
))
# → 1177.44 руб.

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

Один практический момент: если у вас несколько SKU на одном складе с разной оборачиваемостью, считайте floor-price для каждого отдельно. Усреднение здесь работает плохо, быстрый товар маскирует убытки медленного, и агент начинает снижать цену там, где этого делать нельзя. Как автоматизировать пересчёт цен без привлечения разработчика описано в статье о динамическом ценообразовании в e-commerce через n8n.

Визуализация формулы нижней границы цены с разбивкой по слоям себестоимости и марже

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

Стратегии репрайсинга: какую логику заложить в агента

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

Рабочих стратегий несколько, и они решают разные задачи.

Beat by N%, самая прямолинейная. Агент смотрит топ-5 конкурентов в категории, берёт минимальную цену и держит свою на N% ниже. Хорошо работает, когда вы торгуете типовым товаром и хотите просто не вылетать из видимости. Параметр N обычно ставят в диапазоне 2-5%: достаточно, чтобы алгоритм WB тебя заметил, и не настолько много, чтобы убить маржу. Главный минус: если конкурент роняет цену из-за стока или акции, агент тянется за ним и может завести вас в убыток. Обязательно добавляйте нижнюю границу по себестоимости как хард-стоп.

Follow leader работает иначе. Вы определяете лидера категории, мониторите его цену и держитесь рядом: обычно минус 1-3% или вообще вровень. Стратегия осмысленна для товаров с низкой дифференциацией, когда покупатель выбирает по цене и отзывам, а не по бренду. Лидер, как правило, уже нашёл ценовой потолок рынка. Зачем изобретать велосипед?

Maximize margin, агрессивная стратегия в другую сторону. Агент не снижает цену, пока позиция в поисковой выдаче выше заданного порога, скажем, топ-20. Если позиция держится, агент методично тестирует потолок: поднимает цену на 1-2% каждые 24-48 часов и смотрит, не проседает ли выдача. Как только позиция падает ниже порога, откатывается на шаг назад. Это требует аккуратной реализации с памятью состояний, но на товарах с хорошим рейтингом и стабильным спросом может дать заметный прирост маржи без потери оборота.

Акционный режим, отдельная ветка логики. Во время распродаж WB (Чёрная пятница, "11.11", летние акции) правила игры меняются: трафик кратно выше, алгоритм продвигает участников акции, конкуренты демпингуют. Агент должен уметь определять, что сейчас акционный период, временно переключаться на агрессивную стратегию beat by N%, а после окончания акции возвращаться к базовой. Дата акций WB обычно известна заранее, так что это можно хардкодить в календарь или тянуть через API событий маркетплейса.

Теперь про ограничение, которое я бы поставил в любой из этих стратегий. Агенту нельзя давать опускать цену больше чем на 15% за сутки. Резкое снижение вызывает подозрение у алгоритмов WB: карточку могут временно понизить в выдаче или отправить на проверку. Кроме того, если вы вдруг поставили неправильную нижнюю границу или в данных конкурента оказался мусор, агент за одну ночь может уничтожить маржу по всей категории. Ограничение в 15% в сутки, это предохранитель, не потолок эффективности.

На практике я видел, как комбинируют maximize margin в будни и beat by N% в пятницу-воскресенье, когда конкуренция выше. Это уже более сложная логика с расписанием, но агент справляется, если правильно описать условия переключения.

Матрица сравнения четырёх стратегий репрайсинга по осям агрессивность и скорость реакции

Выбор стратегии зависит от двух параметров: насколько быстро нужно реагировать на рынок и насколько агрессивно конкурировать по цене.

LLM как движок принятия решений: где это оправдано

Rule-based репрайсер работает отлично, пока мир простой. Два-три конкурента, фиксированная маржа, стабильная категория. Написал набор условий на Python, запустил крон, забыл. GPT-4o здесь будет как микроскоп для забивания гвоздей: дорого, медленно, и главное, не нужно.

Сложность начинается, когда решение перестаёт быть детерминированным. Пятница перед 8 марта. Новый конкурент с демпинговыми ценами вышел два дня назад. Рейтинг вашего товара вырос с 4.2 до 4.7. Каждый из этих факторов должен влиять на цену, но правило вида if competitor_price < our_price: set_price(competitor_price * 0.99) об этом ничего не знает.

LLM-агент нужен именно в этой точке. Когда контекст важнее формулы.

Схема выглядит так: агент получает структурированный промпт с данными по конкурентам, текущей позицией, историей цен и набором правил. Отвечает JSON с новой ценой и объяснением. Поле reason здесь не просто красота для отчёта. Когда агент написал "конкурент А снизил цену на 15% за ночь, вероятно распродажа остатков, поднимаю нашу цену до его минус 1% вместо следования вниз", вы можете это проверить, оспорить и поправить правила. Чистый black-box без reasoning превращает аудит в гадание.

По деньгам: GPT-4o mini стоит около $0.15 за миллион входящих токенов и $0.60 за миллион исходящих (цены актуальны на май 2026, проверяйте текущий прайс на странице OpenAI). Компактный промпт для одного SKU тратит несколько сотен токенов суммарно. На объёме 1000 SKU с десятком пересчётов в сутки расходы на LLM остаются в пределах нескольких десятков долларов в месяц, но точная цифра зависит от длины промптов и частоты вызовов. Похожий подход с GPT-4o как движком рассуждений подробно разобран в туториале про AI-агента-нутрициолога на n8n с интеграцией RetailCRM: там видно, как устроен цикл вызова инструментов и логирования решений.

Вот рабочая заготовка:

import openai
import json

def llm_reprice(current_price: float, competitors: list, floor_price: float) -> dict:
    prompt = f"""
Ты — агент репрайсинга. Данные по конкурентам: {competitors}.
Текущая цена: {current_price} руб. Минимально допустимая цена: {floor_price} руб.
Правила:
1. Не опускаться ниже floor_price.
2. Не снижать цену более чем на 10% за один шаг.
3. Если все конкуренты выше нас — поднять цену до их минимума минус 1%.
Верни JSON: {{"new_price": float, "reason": str}}
"""
    response = openai.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"}
    )
    return json.loads(response.choices[0].message.content)

Несколько вещей, которые стоит добавить перед продом. Во-первых, валидация: модель может вернуть new_price ниже floor_price даже при явном запрете в промпте. Проверяйте ответ перед применением, не доверяйте LLM как терминальному арбитру. Во-вторых, response_format={"type": "json_object"} убирает большую часть мусора вокруг JSON, но не гарантирует нужные ключи. Добавьте try/except KeyError. В-третьих, передавайте в competitors не сырой список словарей, а только то, что агент реально должен видеть: цену, рейтинг, статус наличия. Лишние поля раздувают токены и шумят в reasoning.

Граница между "нужен LLM" и "хватит правил" проходит по одному вопросу: принимает ли человек на вашем месте решение на основе контекста, который нельзя записать формулой? Если да, агент оправдан.

Интеграция с API Wildberries и Ozon: обновление цен программно

С января 2026 года WB закрыл бесплатный доступ к price API. Токен теперь привязан к платной подписке и выдаётся в личном кабинете продавца в разделе "Настройки" → "Доступ к API". Если у вас старый токен, выпущенный до этой даты, он уже не работает на ценовых эндпоинтах.

Эндпоинт для обновления цен: POST https://discounts-prices-api.wildberries.ru/api/v2/upload/task. В феврале 2026 WB добавил поддержку bulk-операций, и теперь в одном запросе можно передать до 500 объектов. До этого обновления приходилось либо разбивать на пачки по 100, либо делать отдельный запрос на каждый SKU.

Базовая реализация выглядит так:

import requests

def update_price_wb(nm_id: int, new_price: int, token: str) -> bool:
    """
    Обновляет цену товара на Wildberries.
    new_price передаётся в рублях (целое число).
    """
    url = "https://discounts-prices-api.wildberries.ru/api/v2/upload/task"
    headers = {"Authorization": token, "Content-Type": "application/json"}
    payload = {
        "data": [
            {"nmID": nm_id, "price": new_price}
        ]
    }
    resp = requests.post(url, json=payload, headers=headers)
    if resp.status_code == 200:
        print(f"SKU {nm_id}: цена обновлена до {new_price} руб.")
        return True
    print(f"Ошибка {resp.status_code}: {resp.text}")
    return