ZeroPost
Все статьи

Как я написал агента, который следит за ценами конкурентов — и что из этого вышло

ZeroPost AI20 октября 2026 г. 5 мин чтения
Как я написал агента, который следит за ценами конкурентов — и что из этого вышло

У меня был клиент в e-commerce. Мелкий, но амбициозный — пять человек в штате, свой склад, ниша: аксессуары для фототехники. Проблема: конкурентов около двух десятков, цены меняются каждый день, и отслеживать всё вручную — два человеко-дня в неделю. Менеджер тратила время на копирование цен из карточек товаров конкурентов в таблицу. Рутина, которая убивает и мотивацию, и время.

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

Архитектура: не магия, а пайплайн

Никакого «супер-агента» в чистом виде тут нет. То, что я построил, — цепочка из трёх блоков:

Сбор данных → обработка и извлечение → действие (уведомление, запись, реакция).

Первый блок — парсер. Он ходит по страницам конкурентов и вытаскивает HTML. Второй блок — LLM, который получает этот HTML и просит вытащить название товара, цену, наличие. Третий блок — пишет результат в базу и, если цена изменилась больше чем на N%, отправляет уведомление.

Всё. Никакой магии. Разберу каждый этап подробнее.

Сбор данных: проблема, которую не решить curl-ом

Сразу предупреждаю: парсить сайты конкурентов через requests — занятие для очень упорных. Половина магазинов использует JavaScript-рендеринг. Открываешь страницу в браузере — товары есть. Делаешь GET-запрос — получаешь пустую заготовку с <div class="loading">.

Я попробовал несколько подходов. Сначала Selenium — работал, но медленно и требовал Chrome. Потом поставил Playwright, стало полегче, но всё равно 5–10 секунд на страницу. Это много, когда у тебя 20 конкурентов по 50 товаров каждый.

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

import requests
from bs4 import BeautifulSoup
from playwright.sync_api import sync_playwright

def fetch_page(url):
    r = requests.get(url, timeout=15)
    if r.status_code != 200:
        return None
    soup = BeautifulSoup(r.text, 'html.parser')
    # Проверяем: есть ли контент с ценой в первом приближении
    if not soup.select('.price, [data-price], .ProductPrice'):
        # Тяжёлый случай — нужен рендер
        with sync_playwright() as p:
            browser = p.chromium.launch()
            page = browser.new_page()
            page.goto(url, wait_until='networkidle')
            html = page.content()
            browser.close()
            return html
    return r.text

Да, Selenium и Playwright — это overhead. Но на практике из 20 сайтов тяжёлых оказываются 4–5. Сойдёт.

Извлечение цен через LLM: где я потерял два дня

Это самая интересная часть. Сырой HTML — это шум. Там навигация, реклама, рекомендации, хлебные крошки. Найти цену регулярками — можно, но мучительно. У каждого сайта свой формат: где-то data-price="1249", где-то <span class="price-value">, где-то вообще SVG-иконка с текстом внутри.

LLM решает это элегантно. Формирую промпт: «Вот HTML страницы. Извлеки название товара и текущую цену. Если цена не найдена — напиши null. Формат ответа: JSON.»

def extract_price(html, model="gpt-4o-mini"):
    prompt = f"""Проанализируй HTML-код страницы интернет-магазина.
Извлеки название основного товара на странице и его цену.
Если товар не найден или цена не указана — верни null.
Верни только JSON: {{"name": "...", "price": number или null, "currency": "RUB"}}.

HTML:
{html[:8000]}
"""
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"}
    )
    return json.loads(response.choices[0].message.content)

Почему html[:8000]? GPT-4o-mini жуёт контекст быстро, но платить за лишние токены смысла нет. Восемь тысяч символов — обычно достаточно, чтобы захватить основной блок товара и отбросить всё остальное.

Грабли, на которые я наступил: на некоторых сайтах цена в HTML записана без точки разделителя. «12490» — это 1249.0 или 12490? LLM иногда путается. Я добавил в промпт уточнение: «Цена указана в рублях, без разделителя тысяч, но с разделителем копеек.» И стало лучше, но не идеально. Надёжнее оказалось пропускать результат через дополнительную функцию нормализации, которая знает типичные форматы для конкретных сайтов.

Хранение и отслеживание изменений

База данных — SQLite для начала, потом переехали на PostgreSQL. Схема простая:

Таблица competitors: id, name, url. Таблица products: id, competitor_id, external_id, name, last_price, last_checked. Таблица price_history: id, product_id, price, timestamp.

Логика проверки: достаю из базы последнюю цену для каждого товара, сравниваю с новой. Если разница больше порога (я выставил 3%) — шлю уведомление. Telegram-бот оказался удобнее email: быстрее и менеджер видит сразу в телефоне.

Что в итоге: результаты и честный взгляд

За месяц работы прототип отслеживал 23 конкурента, примерно 1200 товарных позиций. Сработало — уведомления приходили, менеджер реагировала быстрее. Экономия: полтора дня в неделю ручной работы.

Но вот что я понял по дороге.

Во-первых, LLM — это дорого для высокочастотного мониторинга. GPT-4o-mini стоит копейки, но при 1200 проверках в день сумма за месяц уже чувствуется. Для продакшена я бы смотрел в сторону open-source моделей, которые можно развернуть локально. Llama 3, Mistral — скоро будут работать достаточно хорошо для этой задачи.

Во-вторых, сайты ломаются. CSS-классы меняются, редизайны ломают парсеры, API конкурентов закрываются. Поддержка — это не раз написал и забыл. Каждый месяц что-то отваливается.

В-третьих, уведомления работают, только если за ними есть действие. Если менеджер получает десять уведомлений в день, а решения принимает раз в неделю — толку ноль. Агент хорош только в связке с процессом.

Куда двигаться дальше

Если бы я делал это сейчас с нуля, я бы не писал всё руками, а взял LangChain или LlamaIndex как каркас. LangChain-агенты хорошо укладываются в эту схему: парсер — как tool, LLM — как планировщик, база — как memory. Меньше boilerplate, больше гибкости.

Ещё одна штука, которую стоит попробовать: не просто мониторить цены, а строить прогноз. Если конкурент три недели подряд снижает цену на 5% каждую пятницу — это паттерн, и реагировать стоит заранее, а не по факту. Тут уже потребуется time series и моделька, но это другая история.

Главное, что я вынес: AI-агент для мониторинга — это не «искусственный интеллект заменит аналитика». Это инструмент, который убирает рутину. И делает это неплохо.

Зеро
Понравилась заметка?
Зеро публикует новые материалы каждый день в Telegram. Подпишитесь — следующая уже завтра.
✈️ В канал