SW-catalog — B2B SaaS-платформа для оптимизации закупочной деятельности в малом и среднем бизнесе. Платформа агрегирует внутренние закупочные данные компании, структурирует проекты, позиции и поставщиков и поднимает ИИ-агента, который ищет альтернативных поставщиков из открытых источников и сравнивает их с уже существующими условиями. Базовый референс — fashion-бренды (закупка тканей, фурнитуры, упаковки, услуг отшива), но модель данных универсальная: можно использовать для электроники, мебели, любых составных изделий.
- Каталог проектов и изделий. У каждого проекта есть свой состав (компоненты, позиции для закупки), бюджет, статус, тираж, валюта.
- Загрузка данных сверху: вставьте текст, BOM, прайс-лист или таблицу — ИИ-агент-парсер разнесёт это в проекты, позиции и поставщиков.
- Карточка проекта с тремя колонками: компоненты слева, детали выбранной позиции по центру, поставщики по этой позиции справа. У каждого поставщика — переключатель мониторинга.
- ИИ-агент discovery запускается «именно на пункт внутри проекта»: ищет альтернативных поставщиков через веб-поиск и читалку страниц, проверяет каждого и сохраняет карточку поставщика с ценой, сроком, страной, контактом, ссылкой.
- ИИ-агент мониторинга по расписанию обходит «галочками отмеченных» поставщиков и пишет в журнал изменения цены/сроков. Изменения видны в таблице на странице проекта и на главной.
- Поиск изображения товара через XMLStock Yandex Live: ИИ берёт название и спецификацию позиции, скачивает первую подходящую картинку и привязывает её к позиции.
- Сводный отчёт об экономии: каждой позиции выбирается лучший по цене вариант, базовая стоимость считается по «текущим» поставщикам, экономия = базовая − лучшая. Отображается в дашборде и карточке проекта.
- Глобальные настройки: профиль бренда, регионы, категории, валюта, периодичность мониторинга, список постоянных площадок-источников (B2B-маркетплейсы, дистрибьюторы и т. п.).
agent_server/FastAPI-бэкенд, scheduler, агентный runtime, парсер загрузок, тулы (reader, search, image search), слой хранения.frontend/React + TypeScript SPA. Sidebar-навигация: Главная / Проекты / Поставщики / Отчёты / Настройки.data/Локальное файловое хранилище (монтируется в backend-контейнер).references/Референсы интерфейса.docker-compose-api.ymlОсновной compose-файл для локального запуска.
Бэкенд хранит данные в ./data:
data/config.json— глобальные настройки и список постоянных площадок-источников.data/projects.json— проекты с составом (позиции для закупки).data/suppliers.json— карточки поставщиков, привязанные к (project_id, item_id).data/uploads.json+data/uploads/<id>.txt— сырые загрузки и история парсинга.data/changes.json— журнал изменений цен/условий по отслеживаемым поставщикам.data/runs/<run_id>.json+data/events/<run_id>.json— журнал запусков ИИ-агента.data/notes/item-<item_id>.txt— переиспользуемые заметки агента по конкретной позиции.data/images/<file>— скачанные изображения позиций, отдаются по/static/images/....
Запускается «на позицию внутри проекта». В контекст получает профиль бренда, проект, позицию, её спецификацию, целевую цену, уже известных поставщиков и список постоянных площадок.
Tools:
search_supplier_web(query, ...)— поиск через XMLSearch / XMLStock.read_supplier_page(url)— reader-цепочка: Jina → direct httpx + Trafilatura → Playwright + Trafilatura.add_supplier(name, price_text, currency, lead_time, country, url, ...)— сохраняет или обновляет карточку поставщика для текущей позиции. Дедупликация по нормализованному URL, иначе по имени.write_item_notes(content)— переписывает короткое reusable-резюме по позиции (видно в карточке позиции и используется на следующем запуске).
В режиме monitor агент проходит по уже отслеживаемым поставщикам и обновляет цены/сроки. Если
цена изменилась, пишется запись в data/changes.json.
Когда пользователь загружает текст/таблицу через дашборд, бэкенд кладёт файл в data/uploads/,
запускает LLM (chat completions с response_format=json_object) с системным промптом из
prompts.UPLOAD_PARSER_PROMPT и получает JSON {projects: [...], summary: ...}. Структура
напрямую превращается в проекты, позиции и упомянутые «текущие» поставщики (флаг is_existing=true).
Если OPENAI_API_KEY не задан, бэкенд использует простой fallback: каждая непустая строка становится
отдельной позицией одного проекта.
/api/projects/{id}/items/{id}/image запускает фоновый таск, который:
- Идёт в XMLStock Yandex Live (порт парсера
tools/xml_images.pyизC:/Users/JGSnapp/Desktop/Stroki). - Берёт первую URL-картинку из ответа, скачивает её через httpx в
data/images/<item_id>.<ext>. - Привязывает локальный URL (
/static/images/...) к позиции.
GET /healthGET /api/state— агрегированное состояние для фронта.GET/PUT /api/config- Projects:
GET /api/projects,POST /api/projects,GET/PUT/DELETE /api/projects/{id} - Items:
POST/PUT/DELETE /api/projects/{id}/items[...]POST /api/projects/{id}/items/{item_id}/discover— запуск discovery-агентаPOST /api/projects/{id}/items/{item_id}/image— поиск изображенияGET /api/projects/{id}/items/{item_id}/notes— текущие заметки агента
- Suppliers:
GET /api/suppliers,GET /api/items/{id}/suppliers,POST /api/projects/{id}/items/{id}/suppliers,PUT /api/suppliers/{id},PUT /api/suppliers/{id}/monitor,PUT /api/suppliers/{id}/status,DELETE /api/suppliers/{id} - Sites:
POST /api/sites,PUT/DELETE /api/sites/{id} - Uploads:
GET /api/uploads,POST /api/uploads,GET /api/uploads/{id}/raw - Runs:
GET /api/runs,GET /api/runs/{id}/events - Changes:
GET /api/changes - Image debug:
GET /api/images/search?query=...
Скопируйте пример:
cp env.example .envОсновные переменные:
OPENAI_API_KEY,OPENAI_BASE_URL,AGENT_MODEL— прямой OpenAI-compatible endpoint.PROXY_API_KEY,PROXY_BASE_URL,PROXY_MODEL,PROXY_FALLBACK_MODEL— ProxyAPI endpoint. Для OpenRouter используйтеPROXY_BASE_URL=https://api.proxyapi.ru/openrouter/v1и модель видаqwen/qwen3-235b-a22b-2507; код автоматически переключается на Chat Completions. ЕслиPROXY_FALLBACK_MODELзадана, backend повторит текстовый запрос на ней при ошибке основной модели.JINA_READER_BASE_URL,DIRECT_READER_ENABLED,PLAYWRIGHT_READER_ENABLED— настройки reader-цепочки для парсинга страниц поставщиков.XMLSTOCK_GOOGLE_URL— endpoint поиска по веб-страницам (для discovery-агента).XMLSTOCK_YANDEXLIVE_URL— endpoint поиска по картинкам (для image-агента, порт парсера из Stroki/XMLSearch).DATA_DIR— путь до файлового хранилища внутри backend-контейнера. По умолчанию/data.
docker compose -f docker-compose-api.yml up --buildСервисы:
- Frontend: http://localhost:3000
- Backend API: http://localhost:8000
- Health check: http://localhost:8000/health
Остановка:
docker compose -f docker-compose-api.yml downSidebar-навигация: Главная / Проекты / Поставщики / Отчёты / Настройки.
- Главная: сверху форма загрузки данных (текст, таблица или файл), три карточки KPI (Оценка закупок · Поставщиков · Активных проектов), таблица проектов, под ней свежие изменения по отслеживаемым поставщикам.
- Проекты: тот же каталог проектов в полноэкранной таблице.
- Карточка проекта: breadcrumb, заголовок со статусом, 6 mini-KPI (компоненты, поставщики,
на мониторинге, оценка закупки, экономия, бюджет). Ниже — three-column grid:
- слева Компоненты товара: позиции с миниатюрой, нижней меткой цены и ссылкой «фото» для запуска поиска изображения;
- в центре детали выбранной позиции: спецификация, диапазон цен по найденным поставщикам, кнопки «Найти поставщиков» и «Найти картинку», блок с заметками ИИ-агента;
- справа Поставщики для позиции: карточки, отсортированные по цене; toggle мониторинга; плашка «текущий / ИИ / мониторинг».
- Поставщики: глобальный список со всех проектов и фильтром.
- Отчёты: история запусков ИИ-агента и загрузок.
- Настройки: глобальные параметры (профиль, регионы, валюта, периодичность) и список постоянных площадок-источников.
Для каждой позиции, по которой есть хотя бы два поставщика с распознанной ценой:
best_price = min(price_i)baseline_price = max(price_i по текущим/preferred-поставщикам или по всем, если таких нет)savings += max(0, (baseline_price - best_price) * quantity)
baseline_estimate и spent_estimate агрегируются по всем проектам и показываются в дашборде
вместе с savings_pct = savings / baseline * 100.
Алгоритм намеренно консервативный: если у позиции пока один поставщик, экономия по ней нулевая. Когда ИИ-агент находит более дешёвый вариант, экономия начинает расти.
- Scheduler работает внутри backend-процесса (
apscheduler) и каждые 5 минут проверяет позиции, у которых пора снова обновить отслеживаемых поставщиков. - Один и тот же item не запускается параллельно — повторный запуск возвращает
409. - Состояние «running» хранится в памяти, без распределённой блокировки.
- Все длинные тексты (notes, описания, summary) пишутся на русском — это зафиксировано в
prompts.ITEM_DISCOVERY_PROMPT.
python3 -m compileall agent_server
docker compose -f docker-compose-api.yml up --build- Файловое хранилище, без БД. Подходит для одного бренда / небольшой команды.
- Image search и веб-поиск требуют действующих XMLStock endpoints.
- Парсер загрузок берёт первые 12 000 символов входа; для больших BOM-таблиц это можно расширить.
- Нет пользовательской авторизации — это single-tenant дашборд под одну команду.