BI-аналитик и Python-разработчик: данные, кубы, ИИ-агенты

Считаю метрики и строю систему, которая считает их сама: ETL → витрины → кубы → дашборд, а где готового инструмента не хватает — backend и сервис под него. 30 лет в телекоме и связи, 13 лет в веб-разработке, 12 лет ИТ-практики, 20+ сервисов в продакшене. Python — основной язык, второй — PHP. Беру задачу целиком: от разбора данных заказчика до nginx, systemd и страницы, которой пользуются.

Python FastAPI LLM / RAG SQL · PostgreSQL Redis ETL Кубы данных · OLAP Backend pandas Playwright Docker Оркестрация GPU Linux · nginx · systemd Git
info@v35.ru +7 926 211-11-97 Москва · гибрид или удалённо

Коротко в цифрах

20+сервисов выведено в продакшен за 2025–2026
59 121фрагмент в самом большом RAG-индексе
1 104организации из парсера вместо 49 по лимиту выдачи
~200 мсобработка фото товара нейросетью на GPU
1запрос в минуту на вкладку вместо 12–32 после перехода на Redis Streams + SSE
5GPU-сервисов делят одну видеокарту через свой арбитр
222 792документа отдаёт свой API к торговой системе без API
2,9–6,9 сот начала фразы до ответа голосового бота в телефоне
13 летприкладной веб-разработки и SEO
17сервисов-ассистентов в продакшене на двух серверах
2 серверасвоя инфраструктура: Linux, nginx, GPU

Стек

Уровни — самооценка; то, что подтверждено тестами на hh.ru, отмечено отдельно.

Языки и backend

Python (основной), PHP, JavaScript, SQL, Bash. FastAPI, uvicorn, REST, webhooks, asyncio, pydantic, WebSockets, потоковая выдача ответов.

Данные, кубы, аналитика

SQL: PostgreSQL, MySQL, SQLite, ClickHouse — сложные выборки, оконные функции, оптимизация, витрины. Кубы данных и OLAP-модели: агрегаты по разрезам (территория, канал, бренд, период) под отчётность и дашборды. pandas, numpy, Excel сводные. ETL-пайплайны «источник → очистка → витрина → отчёт». Парсинг и сбор данных: Playwright, Selenium, requests, PDF/Excel/DOCX, OCR.

Парсинг и сбор данных

Playwright (Firefox/Chromium), Selenium, requests, asyncio. Обход лимитов выдачи (сетка ячеек вместо «топ-50»), индексированная выдача как обход капчи, SOCKS5-прокси с авторизацией и ротацией, чекпойнты и возобновление прогона, дедупликация из нескольких неполных источников, верификация каждой записи. Разбор PDF/Excel/DOCX, OCR сканов, скриншот как доказательство.

MCP и агентные протоколы

FastMCP: свой MCP-сервер с тулом поиска в Яндексе, транспорт Streamable HTTP, рукопожатие initialize → сессия → tools/list → tools/call, ответ и JSON, и через SSE. Клиент на чистом requests с разбором SSE-кадров. Telegram-боты, MAX Bot API.

Телефония и голос

SIP/RTP изнутри (UAC, PCMU 8 кГц, VAD, полудуплекс), GSM/VoIP-шлюз Yeastar TG100 — маршруты в обе стороны и голосовой бот-консультант на живом звонке. Синтез (Silero, XTTS) и распознавание (faster-whisper) в реальном времени.

LLM и RAG

DeepSeek, OpenAI GPT, Gemini, YandexGPT, Ollama локально. FAISS, эмбеддинги intfloat/multilingual-e5-large, чанкинг, поиск с порогом релевантности, ответы со ссылками на источники, защита от промпт-инъекций.

Боты и интеграции

aiogram, python-telegram-bot, MAX Bot API (webhook), сценарии и роли, админ-права. Интеграции: Честный ЗНАК (True API / СУЗ, ЭЦП), Wildberries, Ozon, Яндекс.Маркет, СДЭК, Finam API, ЕГРИП ФНС.

Медиа и ML-инференс

faster-whisper (таймкоды на каждое слово), XTTS v2, BiRefNet и BEN2 (удаление фона), DeepSeek-OCR, ComfyUI + LTX (генерация видео), ffmpeg/ffprobe, ASS-субтитры.

Redis: очереди, события, координация

Два инстанса Redis 7 в проде: шина событий (noeviction, AOF everysec) и кэш (allkeys-lru) — политика вытеснения задаётся на инстансе, поэтому «очередь без потерь + кэш с LRU» в одном процессе несовместимы. Redis Streams (XADD с MAXLEN, consumer groups) вместо pub/sub — чтобы клиент дочитывал события с последнего ID после реконнекта. ACL с отдельным пользователем на сервис (~parser:* &parser:*), распределённые локи, rate limit, идемпотентность.

Оркестрация GPU

Свой арбитр видеопамяти (gpu-arbiter): приоритеты сервисов, выгрузка «дешёвых» моделей перед загрузкой тяжёлой, always_on для тех, кто нужен всегда, TTL простоя и reaper по таймеру. Координация — HTTP /admin/unload + POSIX flock, без WebSocket и без дёрганья systemd-юнитов (нельзя ловить транзитные состояния).

Инфраструктура

Linux (Ubuntu/Debian), VPS, systemd, nginx как reverse proxy и TLS, cron, logrotate, rsync, Docker, ufw, WireGuard/Shadowsocks/SOCKS5, резервное копирование с проверкой восстановлением.

Веб и SEO

Webasyst/Shop-Script (темы, шаблоны, плагины), WordPress, Joomla, UMI.CMS. Lighthouse/PageSpeed, Core Web Vitals, технический SEO, Яндекс.Метрика, собственные счётчики посещаемости.

Подтверждено тестами hh.ru

Python — средний · SQL — базовый · PostgreSQL — продвинутый · API — продвинутый · Docker — продвинутый · Английский — C1 · Математическая статистика — продвинутый · Linux, Git, C#, PHP, C++, Java, JavaScript, HTML, CSS, Go, алгоритмы.

Проекты

Каждый кейс — реальный сервис. Цифры взяты из рабочих журналов и логов, готов разобрать на интервью любой из них, включая допущенные ошибки.

ИИ-консультант по каталогу интернет-магазина (RAG)

продакшен · веб-виджет и Telegram на одном API

Задача: отвечать покупателям строго по официальному каталогу и FAQ, не выдумывая свойства товаров.

  • База знаний: каталог на 251 товар и 212 записей FAQ → смысловой чанкинг по разделам (описание / применение / состав) → 689 фрагментов.
  • Эмбеддинги мультиязычной модели, индекс FAISS; на запрос — поиск top-12 фрагментов, контекст уходит в промпт модели.
  • Один FastAPI-сервис обслуживает и виджет на сайте, и Telegram-бота: логика и база знаний не дублируются.
  • Защита: rate-limit по IP и по сессии, авто-бан при флуде, ограничение длины сообщения, фильтр промпт-инъекций на русском и английском, история диалога с обрезкой контекста.
Ответы строго по базе и со ссылкой на источник; поиск — десятки миллисекунд на CPU, без GPU.
Стек: Python, FastAPI, FAISS, sentence-transformers, DeepSeek API, aiogram, nginx, systemd.

RAG по нормативной документации: 59 121 фрагмент

большой корпус · проверяемые ответы

Задача: система, которая отвечает на вопросы по объёмной нормативной базе и показывает, из какого документа взят ответ.

  • Индексация смешанного корпуса: текстовые главы и PDF (извлечение текста через PyMuPDF).
  • Чанкинг 500 символов с перекрытием 50 → 59 121 фрагмент (14 185 из текстов, 44 936 из PDF).
  • Поиск top-15 с порогом похожести: отсекает нерелевантный контекст вместо того, чтобы подмешивать его в ответ.
  • Пересборка индекса одной командой — база обновляется без правки кода.
Ответ всегда сопровождается списком файлов-источников: результат можно проверить, а не принимать на веру.
Стек: Python, FastAPI, FAISS, multilingual-e5-large, PyMuPDF, nginx.

AI-studio: генерация видео с приёмом предоплаты

деньги · очередь · воркер · GPU-оркестрация

Задача: публичный сервис, где клиент платит за генерацию видео/фото и получает результат; деньги не должны списываться за неудачную задачу.

  • Платежи: платёжный шлюз, схема «баланс с холдами»: клиент пополняет счёт, стоимость фиксируется при постановке в очередь, списание — только после успешной генерации. Проверка подписи вебхука, идемпотентность, ledger как единственный источник правды по деньгам.
  • Очередь: PostgreSQL, FOR UPDATE SKIP LOCKED, полосы local/cloud, приоритеты, ограничение параллельности по полосе — локальная карта не должна захлёбываться.
  • Генерация: локально ComfyUI + LTX на своей GPU, облачные провайдеры как вторая полоса — маршрутизация по типу задачи и по загрузке.
  • Услуги в проде, каждая проверена сквозным прогоном: звук → видео (25/25), фото → видео (30/30), сжатие видео (47/47 + 17 краевых; вход до 5 ГБ), караоке с титрами (28/28; 40 с трека → 1080p за 18 с), доступ к закрытому разделу бота по оплаченному коду (67/67).
  • Файлы крупные: приём до 5 ГБ через nginx без буферизации на диск (proxy_request_buffering off) — тяжёлое тело сразу идёт в приложение, которое обрывает чтение на своём лимите и объясняет причину раньше, чем клиент увидит сухой 413.
Страница услуг AI-studio: баланс 3 480 ₽, доступно 2 230 ₽ с учётом холда 1 250 ₽, шесть услуг с ценами за секунду, минуту и операцию
Рабочий кабинет: баланс и доступная сумма с учётом заморозки под выполняющуюся задачу, услуги с ценой за секунду, минуту и операцию.

Оркестратор GPU — отдельная технология проекта. Пять GPU-сервисов (удаление фона, транскрибация, OCR, синтез речи, музыка) живут на одной видеокарте и не влезают в память одновременно. Написан арбитр: у каждого сервиса есть приоритет, пиковая и простойная память, TTL и флаг always_on; перед загрузкой тяжёлой модели арбитр выгружает более «дешёвые», а выгрузка — это HTTP-запрос к самому сервису, а не убийство процесса.

  • Координация через POSIX-лок: арбитр вызывается каждым сервисом перед загрузкой модели, поэтому гонки исключены — и никакого WebSocket и дёрганья systemd (транзитные состояния ловить нельзя).
  • Свободная память считается с поправкой на неизбежный резерв драйвера: «свободно по nvidia-smi» ≠ «доступно сервису».
  • Реестр сервисов в JSON + отдельный таймер-reaper, который снимает зависшие отметки о занятости.
В итоге: пять моделей на одной карте работают по очереди, а не падают с OOM; клиент платит только за полученный результат.
Стек: Python, FastAPI, PostgreSQL (ledger, очередь, холды), ComfyUI/LTX, Redis, nginx, systemd, собственный gpu-arbiter (flock + HTTP).

Сервис удаления фона для карточек товаров

GPU-инференс · ~200 мс на кадр

Задача: из фотографии товара на любом фоне получать вырезанное изображение для карточки интернет-магазина — быстро и без Photoshop.

  • Две модели сегментации (BiRefNet и BEN2) с прогревом при старте, чтобы первый запрос пользователя не ждал загрузки весов.
  • Замеренное время обработки — 190–200 мс на фото 900×1200.
  • Отдача в PNG с прозрачным фоном и JPEG на белом; второй формат досчитывается по требованию, а не сразу.
  • Найден и устранён баг скачивания (рекурсивный вызов обработчика клика), скачивание переведено на временный элемент, добавлена защита от двойного клика и освобождение памяти. Проверено автоматизированным тестом в реальном браузере.
  • Переезд на другой сервер без простоя: подняли новый инстанс, прогрели, переключили прокси, погасили старый.
Сервис работает на живом сайте бренда; доступ к инференсу закрыт фаерволом, наружу смотрит только nginx.
Стек: Python, FastAPI, PyTorch, BiRefNet/BEN2, Playwright, nginx, systemd, ufw.

Видео-сервис: четыре инструмента на одном API

транскрибация · клипы 9:16 · перевод · дубляж

Задача: превращать длинные видео в готовые вертикальные ролики с субтитрами, переводом и очисткой речи.

  • Транскрибация: faster-whisper с таймкодами на каждое слово (на них строятся караоке-субтитры и вырезание слов-паразитов), VAD-фильтр отсекает музыку и тишину, экспорт в TXT / SRT / JSON.
  • Нарезка 9:16: размытый увеличенный фон и кадр по центру (ffmpeg), субтитры ASS с подсветкой активного слова; до 12 фрагментов за раз по точным меткам.
  • Перевод фраз батчами через LLM и чистка речи с группировкой соседних слов в один вырез.
Один API вместо четырёх разрозненных скриптов; таймкоды слов дают точную подсветку субтитров.
Стек: Python, FastAPI, faster-whisper (CTranslate2), ffmpeg, ASS, DeepSeek, nginx.

Сбор данных с гео-сервиса: 1 104 организации вместо 49

инженерная задача про лимиты выдачи

Задача: собрать все организации города, а не «топ-50», который сервис отдаёт на один поисковый запрос.

  • Диагностика: один центр города на общем зуме → 49 организаций. Это не «все», а лимит выдачи.
  • Решение — сетка: область разбивается на ячейки 1–2 км с перекрытием 30 %, поиск идёт по каждой ячейке на детальном зуме, где лимит не срабатывает.
  • Геометрия Web Mercator (метры на пиксель, длина градуса долготы по широте) вынесена в отдельный модуль и тестируется без запросов к сервису.
  • Дедупликация по ID, отброс служебных вкладок, чекпойнт после каждой ячейки и запись результата сразу на диск — обрыв не теряет собранное, прогон возобновляется.
  • Устойчивость к блокировкам: внешний SOCKS5 с авторизацией и локальный мост-прокси, ротация адресов.
Вместо 49 организаций — 1 104+ уникальных с адресами и телефонами.
Стек: Python, Playwright (Firefox), asyncio, SOCKS5, GeoJSON/CSV, чекпойнты.

maps.v35.ru — панель управления парсером и шина событий на Redis

интерфейс · задачи · живой лог · SSE

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

  • Задачи и прогресс: постановка в очередь, отмена, статусы по каждой ячейке сетки (у большого прогона — тысячи ячеек), история и возобновление после обрыва.
  • Выгрузки: XLSX с результатом; длинные запросы не режутся таймаутом прокси (300 с на чтение и запись).
  • Вход по коду на почту, с жёстким лимитом на ручки авторизации: каждый запрос — это письмо, поэтому флуд давится отдельной зоной лимитов. Лимиты вынесены в conf.d, потому что зоны limit_req живут только в http-контексте и внутри server роняют nginx.
  • Живой лог — через SSE, а не опросом: раньше браузер каждые 3 секунды тянул лог целиком и каждые 5 секунд — список задач. С ростом числа вкладок нагрузка росла линейно.
Карта города в панели: сетка из 120 ячеек, цвет показывает число найденных объектов, красные — прижимные на кромке, пунктир — пустые
Карта покрытия: 120 ячеек сетки, каждая раскрашена по числу найденных объектов. Красная рамка — «прижимная» ячейка на кромке города (без неё объекты у границы не находятся), пунктир — пустая.
Очередь задач с прогрессом по ячейкам и живой лог задачи: строки по каждой ячейке, чекпойнты, дедупликация
Очередь задач и живой лог: прогресс «ячеек 92/120 (76,7 %)» рядом со статусом, а лог идёт построчно по каждой ячейке — видно и чекпойнты, и дедупликацию карточек.

Что сделано с нагрузкой (проект v35-bus, этап 1): поднят Redis 7 с AOF и noeviction, воркер публикует события (job_started, job_log_line, job_progress, cell_done, busy_changed) в Stream через XADD … MAXLEN, фронт читает их одним долгоживущим соединением /api/events. Выбраны именно Streams, а не pub/sub: pub/sub не хранит историю, и вкладка, открытая на секунду позже, теряет прогресс.

  • Порядок подключения: курсор (Last-Event-ID) → снимок текущего состояния → чтение строго после курсора. Поэтому перезагрузка страницы не теряет прогресс.
  • Публикация событий никогда не бросает исключение и после сбоя уходит на паузу: упавший Redis не должен добавлять ожидание в каждую строку лога.
  • Лог отправляется пачками через pipeline: один сетевой round-trip на десятки строк.
  • Резервный путь сохранён: при недоступном Redis клиент получает событие degraded и возвращается к редкому опросу, а не к пустому экрану.
Запросов в минуту на вкладку: 12–32 → 1. На трёх вкладках с открытыми логами трафик упал с 2,8 МБ/мин до 283 КБ/мин, транзакции БД — с 331 до 33/мин. Собственный вклад панели в базу стал практически нулевым.

Кубы данных поверх сырых таблиц. Сырые данные в панели — это нормализованные таблицы: город, ячейка сетки, организация, связь «организация ↔ ячейка», прогон. Считать по ним «сколько объектов в квадрате» каждый раз заново нельзя: на большом городе это тысячи ячеек и сотни тысяч связей. Поэтому поверх них — агрегированные таблицы-кубы с готовыми разрезами:

  • геометрия: город × ячейка (строка, колонка, координаты, статус, признак кромки) — из этого рисуется карта без единого JOIN-а;
  • покрытие: счётчики по статусам ячейки (done, empty, partial, banned, pending) — прогресс прогона читается одним запросом;
  • плотность: число найденных объектов и ссылок на ячейку — именно эти числа задают цвет квадрата и легенду 0 / 1–3 / 4–10 / 11–25 / 26–50 / 50+;
  • разрез по прогонам: таблица city_run хранит счётчики по каждому поисковому запросу (задач всего / выполнено / с ошибкой, ячеек пройдено / пустых / частичных, ссылок) — из неё видно, какой именно прогон закрыл квадрат.

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

Стек: Python, FastAPI, Redis Streams + SSE, PostgreSQL, nginx (SSE без буферизации), systemd, Docker.

MCP-сервер «Yandex Search Gateway»

Model Context Protocol · Streamable HTTP · 82.38.69.191

Задача: дать LLM-клиентам (Claude, Cursor и любым другим агентам) штатный способ искать в Яндексе, а не подсовывать им "сам напиши запрос и попробуй угадать".

  • Сервер на FastMCP, поднят на американском VPS (82.38.69.191), версия 1.29.0. Транспорт — Streamable HTTP по адресу /mcp, авторизация Bearer-токеном.
  • Полный рукопожатие MCP: initialize → сессия Mcp-Session-Id → notifications/initialized → tools/list → tools/call. Сервер умеет отдавать ответ и чистым JSON, и через SSE (text/event-stream) — именно последнее ждёт большинство клиентов.
  • Тул yandex_search с параметрами query и max_results, внутри — Yandex Search API v2; в ответ идёт готовый текст с заголовками, ссылками и сниппетами.
  • Написан и клиент (mcp_yandex.py): разбор SSE-кадров, ручное управление сессией, вызов тула из командной строки — чтобы проверять шлюз без чужого фреймворка.
Проверено живым вызовом: запрос «keune tinta купить» вернул 3 результата с URL и сниппетами за один round-trip. Тот же шлюз используется в SEO-задачах (позиции, выдача, конкуренты) без браузера и капчи.
Стек: Python, FastMCP 1.29, MCP Streamable HTTP, Yandex Search API v2, SSE, Bearer-токены, VPS (Ubuntu).

API к торговой системе «ТрейдТН» (своя СУБД, не 1С)

reverse engineering · 15 эндпоинтов · 222 792 документа

Задача: у торговой программы своя СУБД «FC» на файлах .trd и никакого API. Нужно было читать данные снаружи и строить обмен с 1С.

  • Разобран формат файлов без документации: таблицы, смещения полей, типы чисел (BCD/double) — и поверх них написан read-only HTTP API FenixTradeAPI/2.0 на Python 3.14.
  • 15 эндпоинтов: /api/meta, /api/documents (222 792), /api/items (1,25 млн позиций), /api/products (89 798), /api/customers (16 720), /api/payments, остатки, склады, счета, связи.
  • Честность про качество данных: шапки документов читаются верно (суммы, авторы, контрагенты), а текстовые и числовые поля товаров, позиций и клиентов пока читаются со смещением — это зафиксировано в статусе проекта, а не спрятано.
  • Обмен с 1С: разобран готовый контракт выгрузки (ExportTradeTN_to1C.csv: артикул, наименование, цена, партия, НДС, ГТД) и конфиги батч-выгрузки trade50.exe @cfg.
Система, у которой «нет API», теперь отдаёт документы, товары и остатки по HTTP — а не только через Windows-клиент в локальной сети.
Стек: Python, HTTP API, разбор бинарных форматов, SMB/CIFS, батч-выгрузка, обмен с 1С (CSV-контракт).

Панель холодных звонков (coldcols.v35.ru) и голосовой контур через GSM

LLM-симулятор диалога · SIP/GSM-шлюз · голос из конца в конец

Часть 1 — панель для отладки скрипта обзвона. Разговор идёт письменно: оператор отвечает по скрипту, а модель играет собственника — с возражениями, сомнениями и торгом. Так скрипт проверяется на десятках разговоров без единого реального звонка.

  • Две роли и 9 типажей собственника; в шапке видно, по какому промпту идёт каждая сторона, и можно сравнивать два набора промптов.
  • Подстановки собираются разбором документов, а не генерацией — модель, придумывающая реплики, придумает и цифры; здесь 43 фразы из скрипта и готовые отработки возражений, плюс отдельная группа «пробелы в базе».
  • Версионность промптов и базы знаний с откатом, разбор прогона, вход по коду на e-mail с allowlist администратора.
Публичная страница панели холодных звонков: назначение, состав стека FastAPI + PostgreSQL, вход по одноразовому коду, журнал выпусков
Страница «О программе» — открыта до входа: видно назначение, стек и журнал выпусков.

Часть 2 — голосовой контур через GSM-шлюз. Железный шлюз Yeastar/NeoGate TG100 (GSM/3G ↔ VoIP, одна SIM) заведён в сеть и оживлён с нуля:

  • Восстановление доступа: сброс кнопкой только при подаче питания, вход через CGI. Главная ловушка — поле secret: устройство считает его само как base64(md5(пароль)), а затем сдвигает каждый символ на +2 по ASCII. Обычный base64 не подходит и даёт &error:1;.
  • Маршруты проверены в обе стороны: SIP → GSM (абонент ответил) и GSM → SIP (входящий принят, номер звонящего передан).
  • Голосовой бот целиком: звонок → SIP INVITE → RTP (PCMU 8 кГц) → VAD с адаптивным порогом → STT faster-whisper → LLM-консультант с RAG → TTS Silero → RTP обратно. В живом разговоре 3 реплики подряд: рекомендация шампуня, уточнение про окрашивание, вопрос «где купить».
  • Замеры на живом звонке: распознавание 0,1–0,2 с, ответ консультанта 1,6–3,2 с, синтез короткой версии 1,1–1,5 с — до начала ответа 2,9–6,9 с.
Три бага, которые видны только на живом звонке и уже исправлены: эхо возвращало бота к самому себе (лечится полудуплексом), ответ уходил на адрес шлюза вместо адреса абонента, а второй одновременный звонок перехватывал первый — теперь второй получает 486 Busy Here, а зависший вызов снимается по таймауту. Плюс словарь произношения латиницы: ошибок в названиях брендов было 57 %, стало 17 %.
Стек: Python, FastAPI, PostgreSQL, LLM + RAG, faster-whisper, Silero TTS, SIP/RTP (PCMU), Yeastar TG100, nginx, systemd.

Парсинг и сбор данных: промышленный, с чекпойнтами и доказательствами

5 маркетплейсов · ЕГРИП ФНС · скриншоты как доказательство

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

1. Обход лимитов, а не «серфинг по страницам».

  • Гео-сервис отдаёт только топ-50 на запрос — значит разбиваем город на сетку ячеек 1–2 км с перекрытием 30 % и обходим поквадратно; получилось 1 104 организации вместо 49.
  • Маркетплейс закрыт капчей на прямые URL — идём через индексированную поисковую выдачу с операторами site: и поиском по ID карточки.
  • У другого маркетплейса найден параметр API, отдающий карточки конкретного продавца за 1–2 запроса вместо перебора десятков тысяч SKU.
  • ЕГРИП ФНС блокирует серию запросов — работают мобильные прокси с ротацией каналов и пауза при капче.

2. Неполные источники и дедупликация. Один каталог магазина дал 248 карточек, а реально их 3 997. Метод: три независимых источника (пагинация каталога, 20 категорий магазина, поиск по магазину), затем объединение по ID. Воронка по другому маркетплейсу: 1 365 кандидатов → 188 витрин → 50 подтверждённых.

3. Верификация, а не доверие. Каждая карточка открывается отдельно: проверяется продавец, h1, бренд в разметке и жив ли товар. Результат в отчёте: 190 активных карточек Keune и 0 удалённых — то есть цифра проверена, а не найдена регуляркой. Скриншот каждой карточки — 190 снимков — доказательная база, которую не оспорить «страница поменялась».

4. Реквизиты и документы. Выписки ЕГРИП по ОГРНИП скачиваются с подписью УКЭП и разбираются: парсер ищет значения по названию показателя, а не по номеру строки — выписки бывают двух разных вёрсток, и наивный парсер ломается на второй. Дальше — генерация комплекта: заявление в DOCX, сводный PDF для печати, реестр в XLSX на 23 графы.

5. Устойчивость длинных прогонов. Обход идёт часами: чекпойнт после каждой ячейки, запись результата сразу на диск с flush(), возможность возобновления с произвольной точки. Обрыв связи или блокировка не обнуляют два часа работы. Дедупликация по ID, отброс служебных вкладок (/reviews, /gallery, /prices), лог с прогрессом и скриншотом каждой проверки.

Суммарно по проектам: 5 маркетплейсов и гео-сервис, 13 000+ проверенных карточек, 1 104 организации из гео-сетки, официальные выписки ЕГРИП с e-mail из ФНС, готовый комплект документов и реестр в XLSX. Парсинг здесь — часть юридической работы, а не способ «набрать текста».
Стек: Python, Playwright (Firefox/Chromium), Selenium, requests, asyncio, SOCKS5-прокси с авторизацией, PyMuPDF, python-docx, openpyxl, SQLite, XLSX/CSV-отчётность.

Мониторинг продавцов на маркетплейсах + выписки ЕГРИП

сбор данных · документы · отчётность

Задача: правообладателю нужно найти всех продавцов, торгующих его брендом без разрешения, и собрать доказательства по каждому.

  • Яндекс.Маркет: 50 продавцов и 1 506 карточек; воронка от 1 365 кандидатов до 50 подтверждённых витрин.
  • Проверка актуальности карточек: 1 357 из 1 422 актуальны (95,4 %), отчёт с прогресс-файлом и возможностью перезапуска после сбоя.
  • Wildberries: 18 продавцов и 1 555 карточек; найден способ получать карточки конкретного продавца за 1–2 запроса вместо перебора десятков тысяч SKU.
  • Выписки ЕГРИП с сайта ФНС: запрос → статус → скачивание подписанного PDF; парсер ищет значения по названию показателя, а не по номеру строки — иначе ломается на второй вёрстке документа.
  • Генерация комплекта документов: папка на каждого ИП, заявление в DOCX, выписка, карточки, сводный PDF для печати и реестр в XLSX на 23 графы.
Повторяемый конвейер: следующий бренд проверяется одной командой, а не ручной работой на неделю.
Стек: Python, requests, SQLite, Playwright, SOCKS5, PyMuPDF, python-docx, openpyxl, LibreOffice headless.

Парк ассистентов: 17 сервисов на двух серверах (TG и MAX)

эксплуатация · мониторинг · платный доступ · две юрисдикции

Что это: не один бот, а парк из 17 сервисов в продакшене, каждый под своим systemd-юнитом: 10 на американском VPS (нужен для части API и для обхода гео-ограничений) и 7 на домашнем сервере с GPU.

На американском VPS (82.38.69.191):

  • MCP-шлюз поиска (mcp-yandex, порт 8081) — тот самый Yandex Search Gateway из отдельного кейса.
  • Анализатор сайтов — оркестратор в три потока: доступность и скорость (HTTP-проба + PageSpeed Insights), техническое SEO (robots, sitemap, title, meta, H1, SSL) и позиции в Яндексе по ключевым фразам.
  • Мониторинг доступности — проверяет список сайтов по интервалу и присылает в Telegram, когда сайт упал и когда поднялся; состояние в state.json, подписчики отдельным списком.
  • Генератор HR-статей, обработчик изображений, помощник, бот отеля и три юридических бота (включая споры с СНТ).

На домашнем сервере с GPU (7 сервисов):

  • MAX-ассистент (MAX Bot API) — самый большой: 56 функций в одном боте. Умеет: диалог с LLM и историей, разбор присланных изображений, транскрибацию голосовых, генерацию чертежей (рендер математики и геометрии в картинку), раздел тренажёра с вопросами и отдельный раздел тестов.
  • Платный доступ кодом: клиент покупает на сайте код из 8 цифр на 1 час — бот проверяет его отдельным сервисом и пускает в закрытый раздел; админский пароль работает всегда.
  • Платёжный бот СДЭК (webhook, БД, QR и ссылка на оплату, хранение согласий на обработку ПД), школьный бот с админ-правами по Telegram ID, боты-консультанты для сайтов (FastAPI + LLM, антифлуд, фильтр инъекций, уведомления менеджеру).
17 сервисов — это уже не «написал бота», а эксплуатация: единый деплой, ротация ключей, логи и автозапуск. Отдельный урок в копилку: два инстанса одного бота на long polling конфликтуют (409), апдейты уходят случайному — поэтому есть карта «какой сервис на какой машине включён». Две юрисдикции выбраны осознанно: часть внешних API доступна только с американского адреса.
Стек: Python, aiogram, MAX Bot API, FastAPI, SQLite/PostgreSQL, systemd, nginx, два VPS (RU и US), PageSpeed Insights, Yandex Search API.

Инфраструктура: два Linux-сервера, GPU и автоматизация обслуживания

роли сервисов · VRAM · синхронизация · восстановление
  • Два сервера (i9, Ubuntu, NVIDIA): nginx-прокси на одном, инференс на другом; доступ к порту инференса закрыт фаерволом для всех, кроме прокси.
  • Управление видеопамятью как ресурсом: посчитан бюджет, сервисы разнесены по машинам, предусмотрен «предохранитель» — переключение тяжёлого сервиса на CPU, чтобы не было конфликта портов и crash-loop.
  • Автоматическая двусторонняя синхронизация документов между машинами: сначала pull, потом push (исключает «пинг-понг»), разрешение конфликтов по времени файла, проигравшая версия не удаляется, а уходит в отдельную папку.
  • Обслуживание: ротация логов и лимиты (диск перестал переполняться), плановые задачи, диагностика после сбоев питания, клонирование системы на второй диск через rsync.
Инфраструктура, которую можно передать: сервисы описаны, роли разнесены, восстановление проверено.
Стек: Linux, systemd, nginx, ufw, cron, rsync, Bash, Docker, NVIDIA-драйверы.

Локальный ИИ-стек и эксперименты с инференсом

свои модели · генерация видео · OCR
  • Локальный запуск LLM и веб-интерфейса к ним: свои модели, свой интерфейс, обновление без потери данных.
  • Генерация видео локально: ComfyUI + LTX, работа в 16 ГБ видеопамяти, офлоад тяжёлых компонентов, математика кадров.
  • Сравнение стоимости: локально / напрямую у вендора / через посредника — посредники накручивают до ×2–2,9.
  • OCR-сервис: изображение → текст, автовыбор GPU или CPU.
  • Отдельный эксперимент: разблокировка вычислительных блоков у карты, предназначенной для майнинга, — 68,55 TFLOPS FP16 на тензорных ядрах, стабильность под 100 % нагрузкой.
Понимание, где инференс выгоден локально, а где дешевле API — с расчётами, а не на ощущениях.
Стек: Ollama, ComfyUI, LTX, DeepSeek-OCR, Docker, CUDA.

Веб и SEO: 13 лет прикладной работы

CMS · скорость · аналитика
  • Магазины и сайты на Webasyst/Shop-Script: собственная тема, шаблоны, плагины, каталог, миграции с бэкапами.
  • Ускорение: разбор Lighthouse и точные правки — отложенная загрузка скриптов, приоритет для главного изображения, перегенерация логотипа с 7,7 КБ до 2,3 КБ, сброс кэша шаблонов. Каждая правка — с проверкой, что страница отвечает 200 и в логе ошибок пусто.
  • Аналитика: собственный счётчик уникальных посетителей по дням.
  • Баги, которые видны только на живом трафике: растянутые изображения каталога, падение на пустом значении во вспомогательном методе, неправильный ID блога.
Сайты, которые я делал, работают годами и не требуют «переписать всё заново».
Стек: Webasyst/Shop-Script, PHP, WordPress, JavaScript, Lighthouse, Яндекс.Метрика.

Живые демо

То, что можно открыть и потрогать прямо сейчас.

Что ищу

Роль

BI-аналитик / BI-разработчик · аналитик данных · Python-разработчик (backend, данные, интеграции) · инженер данных / ETL-разработчик · LLM/ИИ-инженер · разработчик ИИ-агентов и ботов · инженер автоматизации. Готов к роли ведущего разработчика направления.

Формат

Москва, гибрид — или полностью удалённо. Полная занятость; готов к тестовому заданию и к разбору кода на интервью. Рассматриваю и роли с ответственностью за направление (ведущий разработчик / техлид по данным и автоматизации).

Сильные стороны

Довожу до продакшена, а не до прототипа: 20+ сервисов работают и сейчас. Сам поднимаю сервер, сам пишу ETL и витрины, сам разбираюсь, почему упало в три часа ночи. 20 лет личной ответственности за результат в своём бизнесе — поэтому не нужно проверять, доведёт ли задачу до конца.