Проектирование флота автономных агентов с единым центром управления, изолированными серверами кабинетов и гибридной моделью исполнения для организации «РЫСЬ-ИНСТРУМЕНТ».
Наша схема рабочая и более правильная с точки зрения изоляции, чем «один большой сервер на все кабинеты». Но проектировать её необходимо не как «много независимых агентов, которые просто смотрят в одну базу», а как распределённую систему с центральным управлением и удалёнными исполнителями.
Если коротко:
Правильная архитектура для «РЫСЬ-ИНСТРУМЕНТ» — это центральный «мозг» + удалённые агенты-исполнители на серверах под кабинеты + единая база знаний/состояния/аудита.
То есть не каждый агент сам по себе, а:
Ниже разберём архитектуру подробно с учётом специфики наших магазинов: Техбензо, ЭиБ, Техносад.
В изначальном документе предполагалось примерно следующее:
Теперь применяется другая модель:
Это меняет архитектуру в лучшую сторону по изоляции, но добавляет требования к:
То есть теперь главный вопрос не «как запустить браузер в контейнере», а:
Как управлять флотом удалённых агентов, которые работают с разными кабинетами, но подчиняются одному центру, одной логике и одной базе знаний.
Система строится по схеме центр + исполнители.
Это один главный сервер или группа серверов, где находятся:
То есть центр — это «директор». Он:
Каждый сервер под кабинет — это исполнитель. На нём находятся:
То есть удалённый сервер не должен быть «умным» целиком. Он должен быть скорее руками. Он:
Вот здесь важный момент.
Каждый агент на каждом сервере напрямую подключается к одной базе данных. Например:
worker-tehbenzo -> PostgreSQL
worker-eib -> PostgreSQL
worker-tehnosad -> PostgreSQL
worker-wb-1 -> PostgreSQL
Это просто на старте, но плохо по безопасности и управлению. Минусы:
Агенты не ходят напрямую в базу. Они общаются с центром через:
Например:
User -> Open WebUI -> LangGraph Orchestrator -> NATS -> Worker-Tehbenzo
Worker-Tehbenzo -> NATS/MinIO/API -> Control Plane -> PostgreSQL
То есть база одна, но прямой доступ к ней имеет только доверенная центральная часть. Это намного лучше.
Единая база нужна обязательно. Но нужно разделить типы данных.
Таблица кабинетов:
account_id
marketplace = ozon / wildberries
shop_name
client_id
assigned_worker_id
status
ip_address
browser_profile_id
timezone
locale
api_key_ref
created_at
updated_at
Пример для наших магазинов:
account_id: ozon_tehbenzo
marketplace: ozon
shop_name: Техбензо
assigned_worker: server-ozon-tehbenzo
status: active
browser_profile: profile_ozon_tehbenzo
account_id: ozon_eib
marketplace: ozon
shop_name: ЭиБ
assigned_worker: server-ozon-eib
status: active
browser_profile: profile_ozon_eib
account_id: ozon_tehnosad
marketplace: ozon
shop_name: Техносад
assigned_worker: server-ozon-tehnosad
status: active
browser_profile: profile_ozon_tehnosad
Это нужно, чтобы оркестратор понимал:
worker_id
hostname
ip_address
capacity
assigned_accounts
status
last_heartbeat
version
cpu_usage
ram_usage
browser_count
Пример:
worker_id: worker-ozon-tehbenzo
hostname: ozon-node-tehbenzo
ip: 185.x.x.10
capacity: 1 cabinet
status: online
last_heartbeat: 2026-10-03 14:22:10
version: agent-0.3.1
worker_id: worker-ozon-eib
hostname: ozon-node-eib
ip: 185.x.x.11
capacity: 1 cabinet
status: online
last_heartbeat: 2026-10-03 14:22:12
version: agent-0.3.1
worker_id: worker-ozon-tehnosad
hostname: ozon-node-tehnosad
ip: 185.x.x.12
capacity: 1 cabinet
status: online
last_heartbeat: 2026-10-03 14:22:14
version: agent-0.3.1
Это нужно для мониторинга и маршрутизации.
task_id
user_id
account_id
task_type
payload_json
priority
status
requires_approval
created_at
started_at
finished_at
worker_id
result_json
error_message
Пример задачи:
{
"task_id": "task_12345",
"account_id": "ozon_tehbenzo",
"task_type": "change_prices",
"payload": {
"shop": "Техбензо",
"change_type": "percent",
"value": 5,
"scope": "all_active_products",
"category": "бензопилы"
},
"requires_approval": true,
"priority": "normal"
}
event_id
task_id
worker_id
account_id
step_number
action_type
status
screenshot_url
extracted_data_json
timestamp
Пример:
{
"event_id": "evt_998877",
"task_id": "task_12345",
"account_id": "ozon_tehbenzo",
"step_number": 4,
"action_type": "click_save_button",
"status": "success",
"screenshot_url": "s3://artifacts/ozon_tehbenzo/task_12345/step_4.png"
}
Это очень важно для обучения и разбора ошибок.
Это самая ценная часть для обучения.
procedure_id
name
marketplace
account_scope
description
steps_json
prompt_template
version
status
created_from_task_id
success_count
failure_count
last_used_at
Пример процедуры:
{
"procedure_id": "proc_ozon_change_price_tehbenzo",
"name": "Изменение цены товара в Ozon для Техбензо",
"marketplace": "ozon",
"account_scope": "ozon_tehbenzo",
"version": 12,
"steps": [
{ "step": 1, "action": "open_seller_cabinet", "target": "https://seller.ozon.ru" },
{ "step": 2, "action": "navigate", "target": "Цены и скидки" },
{ "step": 3, "action": "search_product", "target": "SKU бензопилы или название" },
{ "step": 4, "action": "update_price", "field": "price_input" },
{ "step": 5, "action": "click", "target": "Сохранить" },
{ "step": 6, "action": "verify", "condition": "price_saved_success" }
]
}
Это и есть «обучение»: система записывает действия, превращает их в процедуры и потом переиспользует.
Для обучения и понимания команд нужна векторная база. Можно использовать:
pgvector внутри PostgreSQL — лучший бесплатный вариант для старта;Qdrant — если будет очень много данных;Chroma — для локальных экспериментов.Что хранить:
Пример:
Пользователь: "Подними цены в Техбензо на 7% на все бензопилы"
Векторная память находит:
- процедуру изменения цен для Техбензо;
- нужный кабинет ozon_tehbenzo;
- прошлый успешный сценарий;
- ограничения по подтверждению.
Это отдельный критичный блок. Нужно хранить:
Но нельзя хранить их в открытом виде в общей базе.
Минимально правильный вариант:
Из бесплатных/условно бесплатных инструментов:
Лучше всего:
Control Plane -> Vault -> Worker получает секрет только на время выполнения
Это важный архитектурный момент.
Если у нас несколько серверов под магазины «РЫСЬ-ИНСТРУМЕНТ» (Техбензо, ЭиБ, Техносад), и каждый сервер имеет прямой доступ к общей базе, то:
Правильнее:
Агент не знает лишнего.
Агент получает только задачу для своего кабинета.
Агент отправляет только результат.
Всю общую логику хранит центр.
То есть база одна, но доступ к ней централизованный.
Для распределённых агентов нужна нормальная очередь. Не стоит делать так:
агент каждые 5 секунд спрашивает базу: есть задача?
Это будет грязно и неэффективно. Лучше использовать очередь событий.
Плюсы:
Пример каналов:
tasks.ozon.tehbenzo
tasks.ozon.eib
tasks.ozon.tehnosad
tasks.wb.main
events.worker-ozon-tehbenzo
events.worker-ozon-eib
events.worker-ozon-tehnosad
approvals.tasks
worker.heartbeat
Плюсы:
Минусы:
Плюсы:
Минусы:
Для проекта «РЫСЬ-ИНСТРУМЕНТ»:
NATS JetStream — основной транспорт задач и событий.
PostgreSQL — состояние, аудит, процедуры, кабинеты.
Redis — кэш, блокировки, быстрые статусы.
MinIO — файлы, скриншоты, отчёты.
Разберём на примере команды:
«Подними цены в Техбензо на 5% на все бензопилы»
Например:
Подними цены в кабинете Ozon «Техбензо» на 5% для всех активных бензопил.
Оркестратор получает сообщение и начинает разбор. Он определяет:
По базе:
account_name = Техбензо
marketplace = ozon
assigned_worker = worker-ozon-tehbenzo
Тут важно помнить трёхуровневую модель из прошлого документа:
Для изменения цен на Ozon лучше сначала пробовать официальный API. Если есть полный API-доступ, то часто даже не нужно открывать браузер. Пример:
1. Получить список товаров Техбензо.
2. Получить текущие цены.
3. Отфильтровать категорию "бензопилы".
4. Рассчитать новые цены (+5%).
5. Показать пользователю план изменений.
6. Получить подтверждение.
7. Отправить новые цены через API.
8. Проверить результат.
Это быстрее, безопаснее и стабильнее.
Перед любыми изменениями система должна показать план. Например:
Кабинет: Ozon Техбензо
Товаров к изменению: 47 (бензопилы)
Метод: +5% к текущей цене
Способ: Ozon Seller API
Риск: средний
Требуется подтверждение: да
И таблица:
Бензопила Штиль MS-180: 18 900 ₽ -> 19 845 ₽
Бензопила Хускварна 236: 24 500 ₽ -> 25 725 ₽
Бензопила Партнер 350: 9 900 ₽ -> 10 395 ₽
...
В чате появляется кнопка или запрос:
Подтвердить изменение цен в Техбензо?
[Подтвердить] [Отклонить]
Пока подтверждения нет, задача не выполняется. Это и есть Human-in-the-Loop.
Если задача через API:
Control Plane -> NATS -> worker-ozon-tehbenzo -> Ozon API
Если задача через браузер:
Control Plane -> NATS -> worker-ozon-tehbenzo -> Playwright -> Ozon Seller UI
Worker делает:
В базу пишется:
Задача завершена
Кабинет: Техбензо
Изменено товаров: 47
Ошибок: 0
Скриншоты: есть
Время выполнения: 2 минуты
В чат приходит:
Готово.
Цены обновлены в кабинете Ozon «Техбензо».
Изменено: 47 товаров (бензопилы).
Ошибок: 0.
Отчёт: ссылка.
Ниже практичный бесплатный/самодостаточный стек.
Ubuntu Server 24.04 LTS
Для центра лучше именно Server, не Desktop.
Для центра:
Docker Compose
или
Podman Compose
На старте Docker Compose проще.
PostgreSQL 16 + pgvector
Хранит: кабинеты, задачи, процедуры, историю, пользователей, аудит, векторные представления.
NATS JetStream
Для задач, событий, статусов, подтверждений.
MinIO
Хранит: скриншоты, PDF, Excel-выгрузки, отчёты, видео/записи действий, если будут.
Python + FastAPI + LangGraph
или:
Python + FastAPI + Qwen-Agent
Но для сложного управления состояниями, подтверждениями и самокоррекцией LangGraph лучше.
Open WebUI
Плюсы:
Если нужен более кастомный интерфейс с кнопками, таблицами и подтверждениями, можно дополнительно сделать:
Next.js + AG-UI / CopilotKit
Но для старта лучше Open WebUI.
Prometheus
Grafana
Loki
Node Exporter
Опционально:
Uptime Kuma
Alertmanager
Минимально:
Open WebUI auth + API tokens
Лучше:
Authentik
или
Keycloak
Если будет много пользователей и разные права доступа.
Лучше:
Vault
Минимально:
SOPS + age + Ansible
Ansible
Обязательно. Если серверов много, руками их замучаешься настраивать.
На каждом удалённом сервере должен быть минимальный, но одинаковый набор.
Ubuntu Server 24.04 LTS
Desktop нужен только если требуется визуальный браузер с GUI, но чаще можно работать через headless браузер или виртуальный дисплей.
Например:
/opt/agent/agent-daemon
/etc/agent/config.yaml
/var/log/agent/
Он запускается через systemd:
systemctl start marketplace-agent
Для UI-задач:
Chromium
или:
Playwright Chromium
или:
Nstbrowser / антидетект-профиль
Если задача только через API, браузер может вообще не запускаться.
Например:
/opt/agent/profiles/ozon_tehbenzo/
/opt/agent/profiles/ozon_eib/
/opt/agent/profiles/ozon_tehnosad/
Внутри:
browser_profile/
cookies/
local_storage/
settings.json
Это важно: у каждого кабинета должен быть свой постоянный профиль.
На случай обрыва связи:
/var/lib/agent/task_queue/
/var/lib/agent/journal/
/var/log/agent/agent.log
/var/log/agent/browser.log
/var/log/agent/errors.log
Минимум:
node_exporter
Или встроенный /health endpoint агента.
Очень важно: не нужно выставлять базу, очередь и внутренние сервисы наружу.
Лучшая схема:
Все серверы соединены в приватную сеть через WireGuard / Tailscale / NetBird.
Например:
Control Plane: 10.10.0.1
PostgreSQL: 10.10.0.2
NATS: 10.10.0.3
MinIO: 10.10.0.4
Worker-Tehbenzo: 10.10.1.11
Worker-EiB: 10.10.1.12
Worker-Tehnosad: 10.10.1.13
Тогда:
Плюсы:
Минусы:
Плюсы:
Минусы:
Плюсы:
Если нужна максимальная автономность:
WireGuard
Если хочется быстро и удобно:
Tailscale
Если серверов будет много и нужна нормальная сеть:
WireGuard + Ansible
Это один из самых важных разделов.
Каждый агент должен иметь доступ только к своему кабинету.
Неправильно:
worker-ozon-tehbenzo знает ключи от ЭиБ и Техносад.
Правильно:
worker-ozon-tehbenzo знает только ключи кабинета ozon_tehbenzo.
worker-ozon-eib знает только ключи кабинета ozon_eib.
worker-ozon-tehnosad знает только ключи кабинета ozon_tehnosad.
Агенты не должны подключаться к PostgreSQL напрямую. Лучше:
Агент -> Control Plane API / NATS
Все изменения должны требовать подтверждение:
Без подтверждения можно разрешить только:
Каждое действие должно сохранять:
Нужен белый список разрешённых операций. Например:
can_read_prices
can_change_prices
can_change_stock
can_edit_cards
can_manage_ads
can_download_reports
can_delete_products
И по пользователям «РЫСЬ-ИНСТРУМЕНТ»:
Руководитель: полный доступ ко всем кабинетам
Менеджер Техбензо: только кабинет Техбензо, чтение + выгрузки
Менеджер ЭиБ: только кабинет ЭиБ, изменение цен после подтверждения
Менеджер Техносад: только кабинет Техносад, изменение цен после подтверждения
Перед изменением цен система должна делать:
Например:
Если цена меняется больше чем на 30% за раз — требовать повторное подтверждение.
Если цена становится ниже себестоимости — стоп.
Если товаров больше 1000 — показывать сводку и давать частичный запуск.
Очень важно. Если задача «поднять цену» выполнилась, но сеть оборвалась, повторный запуск не должен поднять цену ещё раз.
Правильная логика:
1. Получить текущую цену.
2. Сравнить с целевой.
3. Если уже целевая — пропустить.
4. Если нет — обновить.
5. Записать результат.
Идея обучаться на действиях пользователей очень хорошая, но обучение лучше делать централизованно.
Каждый сервер собирает:
Центр:
Не на каждом сервере. Дообучение лучше делать централизованно:
События со всех серверов -> центральное хранилище -> обучающий пайплайн -> обновлённая модель/промпты/процедуры
Для дообучения можно использовать:
Но на старте лучше не делать ставку на полноценное дообучение. Лучше начать с:
Это быстрее и надёжнее.
Это ключевая часть «обучения». Алгоритмы должны быть не просто текстом, а структурой.
{
"name": "ozon_change_price_percent_tehbenzo",
"type": "api",
"marketplace": "ozon",
"account_scope": "ozon_tehbenzo",
"steps": [
{ "action": "get_products", "filter": "active", "category": "бензопилы" },
{ "action": "get_prices" },
{ "action": "calculate_new_prices", "formula": "current_price * 1.05" },
{ "action": "human_approval" },
{ "action": "update_prices" },
{ "action": "verify_prices" }
]
}
{
"name": "ozon_edit_card_title_eib",
"type": "rpa_plus_vlm",
"marketplace": "ozon",
"account_scope": "ozon_eib",
"steps": [
{ "action": "open_page", "url": "https://seller.ozon.ru/app/products" },
{ "action": "find_product", "method": "search_by_name_or_sku" },
{ "action": "open_card" },
{ "action": "find_field", "field": "Название" },
{ "action": "replace_text" },
{ "action": "human_approval" },
{ "action": "click_save" },
{ "action": "verify_saved" }
]
}
{
"name": "ozon_generic_price_change_vlm_tehnosad",
"type": "vlm",
"marketplace": "ozon",
"account_scope": "ozon_tehnosad",
"goal": "Изменить цену товара (газонокосилка/триммер) на указанное значение",
"strategy": [
"Сделать скриншот страницы",
"Найти товар",
"Найти поле цены",
"Ввести новое значение",
"Нажать сохранить",
"Проверить сообщение об успехе"
],
"fallback": "ask_human"
}
Если серверов много, обновления должны быть автоматическими.
Единый образ агента + Ansible деплой
Например:
/ansible
inventory/
playbooks/
install_worker.yml
update_agent.yml
restart_agent.yml
setup_firewall.yml
setup_vpn.yml
Инвентарь:
[tehbenzo_workers]
ozon-node-tehbenzo ansible_host=1.2.3.10
[eib_workers]
ozon-node-eib ansible_host=1.2.3.11
[tehnosad_workers]
ozon-node-tehnosad ansible_host=1.2.3.12
[wb_workers]
wb-node-main ansible_host=1.2.4.10
Обновление:
ansible-playbook update_agent.yml --limit tehbenzo_workers
ansible-playbook update_agent.yml --limit eib_workers
ansible-playbook update_agent.yml --limit tehnosad_workers
Нужен единый мониторинг.
Prometheus
Grafana
Loki
Node Exporter
Alertmanager
Для простых алертов:
Telegram webhook
Пример алерта:
Worker ozon-node-tehbenzo не выходил на связь 5 минут.
Кабинет: Ozon Техбензо.
Последняя задача: task_99812.
Статус: unknown.
Нужно продумать отказоустойчивость.
Если задача может быть выполнена через API, то падение UI-сервера не критично. Например:
Центр может выполнить изменение цен Техбензо через Ozon API без участия конкретного сервера.
Но нужно учитывать лимиты и безопасность.
Если задача требует браузерной сессии, то сервер важен. Тогда при падении:
Почему не стоит автоматически переносить кабинет на другой сервер? Потому что:
Для важных кабинетов можно иметь резервный сервер, но это сложнее. Нужно заранее понимать:
Поэтому для начала лучше:
Один кабинет (Техбензо, ЭиБ, Техносад) = один активный сервер-исполнитель.
Агенты через центр/очередь.
Минимум:
pg_dump ежедневно
WAL-архивирование
бэкапы в S3/MinIO
проверка восстановления
Лучше:
pgBackRest или WAL-G
Для начала можно без горячей реплики, но желательно иметь:
PostgreSQL primary
PostgreSQL standby
Если проект будет критичным, использовать:
Patroni
etcd
HAProxy
Но это уже высокий уровень сложности.
Если база недоступна, воркер должен:
Под каждый кабинет свой сервер. Это максимальная изоляция. Но есть несколько вариантов.
Плюсы:
Минусы:
Когда подходит:
Плюсы:
Минусы:
Когда подходит:
Рекомендуется гибрид.
API-задачи выполняются централизованно или с любого доверенного узла.
UI-задачи выполняются на выделенном сервере под кабинет.
Например:
Если сервер выделен под один кабинет, то внутри можно не усложнять. Можно запускать:
systemd service -> agent -> browser profile
Но если на сервере несколько кабинетов, тогда нужна изоляция. Варианты:
Из прошлого документа хороший вариант — LXC/LXD, но он сложнее. Для старта проще:
Docker Compose + Playwright container
Если нужна более серьёзная изоляция:
LXC unprivileged containers
Это важно для распределения нагрузки.
После месяца наблюдений система должна иметь примерно такие сущности.
"подними цены в Техбензо на 5% на все бензопилы"
"убери скидку на триммеры в ЭиБ"
"обнови остатки по газонокосилкам в Техносад"
"скачай отчёт по продажам Техбензо за неделю"
"поменяй фото в карточке бензопилы Штиль MS-180"
"включи рекламу на генераторы в ЭиБ"
Каждая команда связывается с: кабинетом, действием, параметрами, процедурой, уровнем риска.
ozon_tehbenzo_price_change_percent
ozon_eib_price_change_fixed
ozon_tehnosad_stock_update_from_excel
ozon_tehbenzo_card_edit_title
ozon_eib_card_edit_photos
ozon_tehnosad_ads_enable_campaign
ozon_tehbenzo_download_sales_report
shop_name (Техбензо / ЭиБ / Техносад)
sku
product_name
percent
fixed_price
min_price
max_price
apply_to_all
apply_to_category
min_price_threshold
max_change_percent
require_approval
allowed_users
allowed_time_window
кто запустил
когда
в каком кабинете (Техбензо / ЭиБ / Техносад)
какие товары
какие цены до
какие цены после
успех/ошибка
Нужно, чтобы можно было зайти с ПК, телефона, браузера и написать агенту. Для этого нужно:
Пользователь заходит и видит:
РЫСЬ-ИНСТРУМЕНТ
Кабинеты:
- Ozon Техбензо
- Ozon ЭиБ
- Ozon Техносад
- WB Основной
Быстрые команды:
- Поднять цены
- Опустить цены
- Обновить остатки
- Скачать отчёт
- Проверить статус задач
Пишет:
Подними цены в Техбензо на 5%
Система отвечает:
Найдено 320 товаров.
После изменения:
- минимальная цена: 590 → 619 ₽
- максимальная цена: 45 990 → 48 289 ₽
Средний рост: 5%
Подтвердить?
Пользователь жмёт:
Подтвердить
Система выполняет и возвращает:
Выполнено.
Изменено 320 товаров.
Ошибок 0.
Скриншоты доступны.
Это критично.
Только чтение:
покажи цены Техбензо
покажи остатки ЭиБ
скачай отчёт Техносад
проверь статус
Одно действие:
поднять цену на один товар в Техбензо
изменить заголовок карточки в ЭиБ
включить кампанию в Техносад
Много объектов:
изменить цены на 1000 товаров в Техбензо
обновить остатки по всему магазину ЭиБ
Здесь лучше показывать:
Опасные операции:
удаление товаров
отключение магазина
снятие с продажи
массовое изменение цен больше чем на 20%
изменение бюджета рекламы выше порога
Для маркетплейсов и бизнеса важно иметь журнал. Пример записи:
{
"change_id": "chg_001",
"timestamp": "2026-10-03T14:25:00Z",
"marketplace": "ozon",
"account": "ozon_tehbenzo",
"object_type": "product_price",
"object_id": "SKU-TB-2024-001",
"product_name": "Бензопила Штиль MS-180",
"old_value": 18900,
"new_value": 19845,
"initiated_by": "manager_tehbenzo",
"approved_by": "director",
"executed_by": "worker-ozon-tehbenzo",
"method": "api",
"status": "success"
}
Это даст возможность:
Для начала можно реализовать не физический откат, а логический. Перед изменением система сохраняет:
старая цена
старый остаток
старое название
старое описание
старая ставка рекламы
Потом при необходимости:
Откатить задачу task_12345
Система предлагает:
Найдено 47 изменений в Техбензо.
Откатить все?
[Да] [Нет]
Для цен это реализуется хорошо. Для карточек сложнее, но тоже возможно, если сохранять старые значения.
Чем больше серверов, тем сложнее: обновления, мониторинг, диагностика, конфигурация.
Решение:
Ansible + единый образ агента + версионирование.
Если один кабинет начали менять два агента одновременно, будет хаос.
Решение:
один активный исполнитель на кабинет;
блокировки по account_id;
очередь задач.
Если база одна и падает, система теряет память.
Решение:
бэкапы;
репликация;
локальные журналы воркеров;
очередь с сохранением событий.
Если сервер взломан, он может отправлять вредные события.
Решение:
минимальные права;
подписи задач;
аудит;
изоляция секретов;
whitelist команд;
ручное подтверждение опасных действий.
LLM может неправильно понять команду.
Решение:
структурированное подтверждение;
показывать план;
не выполнять без явного одобрения;
ограничивать допустимые действия.
Сайты меняются, селекторы ломаются.
Решение:
гибридная модель:
1. API
2. RPA
3. VLM fallback
Ниже практичный бесплатный стек.
Ubuntu Server 24.04
Docker Compose
PostgreSQL 16 + pgvector
NATS JetStream
Redis
MinIO
FastAPI
LangGraph
Open WebUI
Caddy или Traefik
Prometheus
Grafana
Loki
Ansible
Ubuntu Server 24.04
Python 3.12
Playwright
Chromium
systemd
Node Exporter
WireGuard/Tailscale
Vector или Fluent Bit для логов
LangGraph — центральный оркестратор
Qwen-Agent — вспомогательные агенты
DashScope API — LLM/VLM
MCP-серверы — для Ozon и Wildberries
LLaMA-Factory — дообучение, если понадобится
PostgreSQL
pgvector
MinIO
WireGuard
Caddy
Authentik или Keycloak
Vault или SOPS
Ansible Vault
Есть доступ к 250 моделям — это отлично. Но важно распределять задачи.
Использовать дешёвые/быстрые текстовые модели:
классификация команды
определение кабинета (Техбензо / ЭиБ / Техносад)
извлечение параметров
маршрутизация
Использовать более сильные текстовые модели:
планирование задач
анализ рисков
генерация плана
объяснение ошибок
Использовать VLM:
qwen-vl-max
qwen-vl-plus
qwen3-vl версии
Задачи:
Использовать централизованный пайплайн:
данные -> разметка -> LoRA/SFT -> оценка -> деплой
Но на старте лучше обойтись без дообучения, через:
Ниже ориентир.
Если всё в одном месте:
CPU: 8 vCPU
RAM: 16-32 GB
SSD: 500 GB NVMe
Network: 100 Mbps+
Если разделять:
CPU: 4-8 vCPU
RAM: 8-16 GB
SSD: 200 GB
CPU: 4-8 vCPU
RAM: 16-32 GB
SSD: 500 GB - 1 TB NVMe
CPU: 2-4 vCPU
RAM: 4-8 GB
SSD/HDD: 1-5 TB
Если в основном API:
CPU: 1-2 vCPU
RAM: 2 GB
SSD: 20-40 GB
Если браузер:
CPU: 2-4 vCPU
RAM: 4-8 GB
SSD: 40-80 GB
Если тяжёлые страницы и скриншоты:
CPU: 4 vCPU
RAM: 8 GB
SSD: 80 GB
Если серверы уже есть, основная стоимость — администрирование и поддержка. Если арендовать, ориентиры могут быть такими.
1 control plane
1 database
3 worker (Техбензо + ЭиБ + Техносад)
Примерно:
Control: $50-100/мес
DB: $50-120/мес
Workers: 3 x $20-50 = $60-150/мес
Итого: $160-370/мес
Если по серверу на кабинет:
5 workers (Техбензо, ЭиБ, Техносад, WB, резерв)
1 control
1 db
1 storage/monitoring
Примерно:
Workers: 5 x $20-40 = $100-200
Control: $80-150
DB: $80-200
Storage/monitoring: $50-100
Итого: $310-650/мес
Тогда расходы:
электричество;
интернет;
резервные копии;
администрирование;
возможно прокси;
разработка.
В прошлом документе хорошо описаны: гибридная автоматизация, VLM + RPA, LangGraph, HITL, изоляция, чат-интерфейс, обучение. Теперь нужно добавить:
Control Plane + Worker Nodes
Каждый кабинет знает свой сервер.
NATS JetStream или аналог.
Агенты общаются через центр.
На случай обрыва связи.
Чтобы повтор не сделал изменение дважды.
Каждый сервер должен иметь версию.
Ansible.
Prometheus/Grafana.
Что делать, если сервер упал.
Целевая схема для «РЫСЬ-ИНСТРУМЕНТ»:
Пользователь
|
| HTTPS
v
Open WebUI / Web App / PWA
|
v
Control Plane API
|
|-- PostgreSQL + pgvector
|-- Redis
|-- MinIO
|-- NATS JetStream
|-- LangGraph Orchestrator
|-- MCP Ozon Server
|-- MCP Wildberries Server
|-- Monitoring
|-- Auth
|-- Vault/SOPS
|
v
Worker Servers
|
|-- Worker Ozon Техбензо
|-- Worker Ozon ЭиБ
|-- Worker Ozon Техносад
|-- Worker WB Основной
|-- Worker резервный
Каждый Worker:
Agent Daemon
Browser Profile
Local Journal
Playwright
Local Secrets for one cabinet
Screenshot Module
Heartbeat Module
Нужно собрать:
список серверов;
список кабинетов (Техбензо, ЭиБ, Техносад, WB);
список IP;
список API-ключей;
какие задачи нужны по каждому кабинету;
какие операции можно делать через API;
какие только через UI;
кто имеет право подтверждать изменения.
Результат:
реестр кабинетов и серверов
Сделать:
WireGuard/Tailscale
firewall
SSH hardening
запрет внешних портов для БД
отдельная подсеть
бэкап-доступ
Поднять:
PostgreSQL
NATS
Redis
MinIO
FastAPI
Open WebUI
Caddy
Monitoring
Создать таблицы:
workers
accounts
tasks
events
procedures
approvals
audit_log
Написать агент:
регистрация
получение задач
выполнение
скриншоты
журнал
отправка событий
ожидание подтверждения
Начать с безопасных:
выгрузка отчёта
просмотр цен
просмотр остатков
проверка статуса
Потом:
изменение одного товара
массовое изменение цен
редактирование карточек
Включить:
запись действий
сохранение скриншотов
извлечение процедур
сохранение шаблонов команд
Подключать новые кабинеты:
новый сервер -> Ansible -> регистрация -> назначение кабинета -> тест -> запуск
Идея с отдельными серверами под каждый кабинет — хорошая и безопасная, если кабинеты действительно важные и их нужно изолировать.
Но реализовывать нужно так:
Не "много умных агентов с общей базой",
а "один центр управления и много удалённых исполнителей".
То есть:
Чтобы начать без лишнего усложнения, схема такая:
1 сервер:
- PostgreSQL
- NATS
- Redis
- MinIO
- FastAPI
- LangGraph
- Open WebUI
3 сервера под кабинеты РЫСЬ-ИНСТРУМЕНТ:
- Техбензо
- ЭиБ
- Техносад
WireGuard
Ansible
1. Отчёты и чтение
2. Цены через API
3. Остатки через API
4. UI-задачи для одного кабинета
5. Массовые операции с подтверждением
6. Обучение и процедуры
Следует избегать следующего:
Для схемы с множеством серверов под кабинеты оптимальная архитектура такая: