У меня на столе стоит небольшая золотистая коробочка размером чуть больше Mac mini. Внутри — приватный AI-сервер: чат с локальной 26B-моделью, поисковая индексация моих документов с GPU-парсингом, конструктор агентов в Dify, RAGFlow для тяжёлого парсинга PDF, мониторинг, бэкапы, опциональный кластер из двух машин по QSFP 200G. Тридцать контейнеров, пять минут на установку через sudo bash install.sh, ноль обращений к внешним API.

Я делал это не как pet-project, а под себя — мне нужна была машина для работы с корпоративными документами, договорами и регламентами, которые ни при каких условиях нельзя отдавать в облачные ассистенты. Сборка получилась самостоятельным дистрибутивом — назвал его AGmind, выложил на GitHub под Apache 2.0.

В статье разберу:

— из чего собран стек и зачем там каждый компонент; — почему RAGFlow пришлось пересобрать с нуля и что я туда добавил; — как устроен кластер из двух Spark'ов; — пять конкретных грабель GB10, которые я ловил вечерами; — почему Claude Code за месяц превратил один из этих компонентов в работающий продукт, но при этом не заменил собственно программиста.

Зачем мне понадобился свой AI-сервер

Облачные ассистенты — OpenAI, Anthropic, Gemini — прекрасно работают с тем, что не страшно отдавать наружу. Как только речь заходит про корпоративные документы, юридические договоры, медкарты, внутренние регламенты — каждый второй compliance-офицер заворачивает SaaS, и совершенно справедливо. Это не вопрос того, насколько хорош TLS у вендора. Это вопрос того, что данные физически уходят на чужие серверы, и дальше доверять можно только бумажкам.

Self-hosted-альтернатив я перебрал руками. Open WebUI — нормальный чат, но с документами работает плохо. Dify — мощный workflow-конструктор и приличный фронт, но встроенный парсер PDF слабый: таблицы плывут, картинки пропадают, многоколоночные тексты разваливаются. RAGFlow — наоборот, сильный парсер, но UI и оркестрация уступают Dify. AnythingLLM, FlowiseAI, LangFlow — у каждого своя специализация и свой набор пропусков.

Мне нужно было одно: чтобы я мог положить в папку 200 PDF-документов, задавать по ним вопросы, ходить в это через нормальный чат, собирать агентов через drag-n-drop, и чтобы всё это поднималось одной командой и так же одной командой бэкапилось. Кубики для этого в open-source есть, дистрибутива — нет. Так появился AGmind.

Железо: что такое DGX Spark в двух словах

DGX Spark — это компактный AI-десктоп от NVIDIA на чипе GB10 Grace Blackwell. Анонсировали в марте 2025 как Project DIGITS, продавать начали 15 октября 2025. Стартовая цена $3999, в феврале 2026 из-за дефицита LPDDR5x подняли до $4699.

Внутри одного SoC сидят:

— 20-ядерный ARM-процессор (10 высокопроизводительных Cortex-X925 + 10 энергоэффективных Cortex-A725, спроектированы NVIDIA совместно с MediaTek); — Blackwell GPU c compute capability sm_121: 48 SM, 6144 CUDA-ядра, 192 тензорных ядра пятого поколения; — 128 ГБ памяти LPDDR5x с пропускной способностью 273 ГБ/с — единый когерентный пул, который видят и CPU, и GPU без всякого PCIe-копирования; — два QSFP-порта на 200 Гбит каждый (NVIDIA ConnectX-7), через которые два Spark'а склеиваются в кластер.

По чистой GPU-производительности это что-то между RTX 5070 и 5070 Ti. Сенсация не в FLOPs, а именно в 128 ГБ unified memory: на одной коробке за $4 тысячи я могу инферить 70B-модель в FP4 без всякого шардирования, чего на потребительском железе обычно не сделать.

Что у этой коробки болит — драйверы. Spark официально живёт на ветке 580.x; на 590+ NVIDIA словила три независимые регрессии, специфичные для unified memory GB10:

— vLLM зависает на CUDAGraph capture при первом инференсе. Воспроизведено независимо в репозитории eugr/spark-vllm-docker, там в README прямо стоит требование «driver 580.x, на 590.x deadlock»; — утечка памяти в драйвере 590.48.01: после нормального завершения CUDA-процесса в системе пропадает порядка 80 ГБ, которых не видно ни в AnonPages, ни в Slab, ни в PageTables; лечится только перезагрузкой; — TMA-баг в драйвере 595.58.03 валит NVFP4-квантизацию с illegal memory access.

В моём install.sh поэтому стоит apt-mark hold nvidia-driver-580-open — это не паранойя, а защита от unattended-upgrades, которые рано или поздно подтянут 590, и человек, поставивший систему вечером, утром получит мёртвую vLLM.

Архитектура: что входит в стек и зачем

Стек у меня живёт в докере и состоит примерно из тридцати контейнеров.

Dify — фронтенд и оркестратор workflow

Dify (langgenius/dify-api:1.13.3) — основной интерфейс, через который я живу. Это open-source-аналог LangChain Studio плюс ChatGPT-like чат, плюс drag-n-drop конструктор агентов. У них есть workflow-редактор, в котором можно нодами собрать что-нибудь типа «получи документ → распарси через RAGFlow → суммаризируй gemma-4 → отправь в Telegram», и это собирается за пять минут без кода.

Я использую Dify как primary frontend (agmind-dify.local), а внутри он умеет ходить в локальную vLLM, в RAGFlow через плагин из marketplace и в любой OpenAI-compatible endpoint. Сам Dify — это пять контейнеров: api, worker, web, sandbox, plugin_daemon.

RAGFlow — пересобранный с нуля под Spark

Здесь надо отдельно. Upstream-RAGFlow в коробке не работает на DGX Spark — там и x86-only зависимости, и устаревшие ONNX-рантаймы, и куча мелочей. Готового образа под arm64 + sm_121 + CUDA 13 в природе нет.

Поэтому я взял базу из патчей HendrikSchoettle/ragflow-dgx-spark (там сделана работа по переезду на ARM), накатил поверх свои cherry-pick'и из upstream main и собрал собственный образ ar2r223/ragflow-spark:v0.24.1-spark, запиннил по SHA256, выложил в DockerHub.

В сборке:

Базовая ветка v0.24.0 + патчи Hendrik: cascade-OCR на Latin / Cyrillic / Chinese (важно — в upstream был только английский), file metadata в ES-чанках, поддержка AVIF, фиксы IMAP и WebDAV mtime, ONNX Runtime GPU 1.25.0 под aarch64 + CUDA 13 + sm_121; — Cherry-pick из upstream main (от 27 апреля): TokenChunker (атомарные таблицы + image_context), TitleChunker (5 уровней regex-иерархии для книг, законов, мануалов), pdf_chunk_metadata.py, extract_pdf_outlines, 7 ingestion-шаблонов (book / general / laws / manual / one / paper / resume); — Мои runtime-патчи: русский prompt для image describe в cv_model.py (когда у KB language=Russian, vision-модель получает русскоязычный prompt вместо захардкоженного английского), русские заголовки и описания в 10 ingestion-шаблонах, плюс лечение бага Pipeline.globals в Hendrik fork v0.24, где Pipeline не наследовал от Canvas и валился с AttributeError на первом же чанке.

В результате RAGFlow умеет на моём железе разбирать русскоязычные PDF с таблицами, схемами и иллюстрациями, описывать картинки русским текстом через vision-модель и индексировать всё это в Elasticsearch 9.x. Образ ~13.3 ГБ, тянется один раз.

Apropos — тэг :latest в моём versions.env запрещён, все образы пинятся либо по версии, либо по digest. Это правило в CI: галлюцинирующие LLM любят предлагать «правдоподобные» теги, которых не существует в registry. Об этом подробнее в разделе про Claude Code.

vLLM — три инстанса под разные задачи

На GB10 у меня крутится три инстанса vLLM:

vLLM (LLM)vllm/vllm-openai:gemma4-cu130, запускает google/gemma-4-26B-A4B-it. Это NVIDIA playbook-сборка под arm64 и sm_121, единственная, на которой gemma-4 нормально живёт на Spark. По умолчанию gpu_memory_utilization=0.60 — оставляю запас для docling-serve, у которого пиковое потребление до 16 ГБ; — vLLM (embeddings)nvcr.io/nvidia/vllm:26.02-py3, поднимает deepvk/USER-bge-m3. Это, кстати, не стандартный bge-m3, а файнтюненная под русский вариант от deepvk, на наших корпоративных текстах работает заметно лучше; — vLLM (reranker) — то же базовое NGC-изображение, поднимает BAAI/bge-reranker-v2-m3 для финальной сортировки кандидатов.

Почему 26.02-py3, а не 26.03? Потому что 26.03 требует драйвер 595.45+, а у нас 580.142, и поднимать драйвер мы не можем (см. выше). Маленький пример того, как один gotcha тянет за собой выбор всех остальных версий.

Атеншн-бэкенд я фиксирую VLLM_ATTENTION_BACKEND=TRITON_ATTN, потому что FlashInfer FP8 на sm_121 валится с kernel only supports sm120 — это известный баг под новый compute capability.

Docling — парсер документов с GPU OCR

Docling-serve (docling-serve-cu130:v1.16.1) — это GPU-ускоренный конвертер документов от IBM. Принимает PDF / DOCX / PPTX / XLSX, отдаёт Markdown с сохранённой структурой таблиц, заголовками и описаниями картинок.

В стеке у меня три preset'а под разные типы документов:

FAST — для текстовых PDF с готовым text layer (Word/LaTeX export). Отключает OCR и table-structure recognition, на 5-страничной arxiv-статье даёт 4.05 секунды против 6.01 в default; — BALANCED (default) — полный pipeline, OCR включается автоматически если text layer неполный, TableFormer работает в accurate-режиме; — SCAN — для отсканированных страниц без text layer. Принудительный easyocr с lang=[ru, en] и picture_description через vLLM gemma-4 vision с concurrency=8, по моим замерам у каждого значимого изображения получается осмысленное русскоязычное описание, которое попадает в индекс.

Docling делит GPU с vLLM, отсюда и gpu_memory_utilization=0.60 у LLM-инстанса.

Базы и хранение

Тут без сюрпризов:

PostgreSQL 16-alpine — метаданные Dify, состояние плагинов; — Redis 7.4 — task queue, кэш плагинов, pub/sub для celery; — Weaviate 1.37 — векторное хранилище под эмбеддинги (есть toggle на Qdrant, но дефолт Weaviate); — MinIO — S3-совместимое объектное хранилище под загруженные документы (agmind-storage.local); — Elasticsearch 9.x — нужен RAGFlow, в нём чанки + полнотекстовый индекс. RAGFlow жёстко завязан на ES, Postgres он не умеет; — MySQL 8.0 — тоже под RAGFlow, у них в схеме MySQL захардкожен.

Мониторинг и операционка

Без мониторинга стек из тридцати контейнеров — это слепая лошадь. У меня:

Prometheus + Grafana 3001 с десятью собственными дашбордами (общий обзор, контейнеры, GPU master, GPU worker, peer-worker, логи, алерты, аудит, RAG-метрики, RAGFlow). Отдельная боль: на GB10 unified memory dcgm-exporter не работает совсем, а NVML возвращает N/A на половину запросов. Пришлось писать свой textfile collector, который парсит nvidia-smi и отдаёт agmind_gpu_* метрики через node-exporter; — Loki + Grafana Alloy — логи всех контейнеров, в апреле перевёл с Promtail на Alloy (это уже официально рекомендуемый агент); — Alertmanager — каналы в Telegram и webhook; — Portainer 2.39 — визуальное управление контейнерами на master + автодеплой агента на peer Spark с одним и тем же PORTAINER_AGENT_SECRET (чтобы переустановки не ломали привязку); — fail2ban + UFW — firewall LAN-only по умолчанию, защита от bruteforce.

Безопасность

Из коробки:

— все секреты лежат в /opt/agmind/credentials.txt с правами 600, root-only; — у каждого контейнера в compose сдропано 30+ Linux-capabilities; — Dify Sandbox изолирован в отдельной Docker-сети ssrf-network, наружу ходит через Squid-прокси (защита от SSRF в плагинах); — опциональная Authelia с TOTP/WebAuthn на Grafana и Portainer; — agmind rotate-secrets перегенерирует все пароли и ключи одной командой.

Кластер из двух Spark'ов

Если у вас две Spark-машины, AGmind их склеивает в кластер по QSFP 200G DAC. Это не просто «увеличить память» — это разнесение нагрузок: vLLM получает выделенный GPU без оглядки на парсер.

  ┌─────────────────────┐                        ┌─────────────────────┐
  │  spark-master       │   QSFP 200G DAC        │  spark-peer         │
  │  (frontend + БД +   │ ◄──── direct link ────►│  (только vLLM,      │
  │  Dify + RAGFlow +   │   192.168.100.0/24     │  выделенный GPU,    │
  │  monitoring)        │                        │  128K context)      │
  │  WAN: ethernet      │                        │  WAN: NAT через     │
  │  iptables MASQUERADE│ ─────── default gw ───►│  master по QSFP     │
  └─────────────────────┘                        └─────────────────────┘

Что важно понимать:

На single-Spark vLLM делит GPU с docling, поэтому в визарде задаётся вопрос про контекст (32K / 64K / 128K) — на 128K при активном парсинге уже впритык; — На dual-Spark этот вопрос пропускается: peer крутит только vLLM, GPU выделен полностью, 128K контекст по умолчанию без компромиссов; — NAT-on-demand — peer выходит в WAN через QSFP master'а только когда нужно (тянет образы или модели), потом командой agmind nat off WAN отрезается, и intent air-gap сохраняется. Полезно, если железо стоит в защищённой сети, а интернет нужен раз в неделю на апдейт; — Симметричная установкаsudo bash install.sh на обеих нодах, визард сам определяет роль через LLDP, при отсутствии — fallback на ping. Для CI можно задать --mode=master или --mode=worker явно; — Passwordless SSH между нодами визард настраивает сам, мониторинг показывает обе машины в Grafana отдельными дашбордами gpu-master и gpu-worker.

Что я с этим всем делаю в реальной жизни

Открываю agmind-dify.local в браузере — и:

Чат с локальной 26B-моделью. TTFT 183 миллисекунды, генерация 23–24 токена в секунду на одиночном запросе, до 50 токенов агрегированно на трёх параллельных. Контекст 65K с FP8 KV-кэшем, до 45 параллельных запросов на одной коробке. Никаких лимитов, ничего не уходит наружу.

Загружаю папку с PDF — задаю по ним вопросы. Под капотом RAGFlow парсит каждый документ, выдирает таблицы и картинки, прогоняет картинки через vision-модель (gemma-4 vision описывает каждое изображение русским текстом), нарезает на чанки, эмбеддинги в Weaviate. Когда я спрашиваю — bge-m3 ищет похожие чанки, bge-reranker сортирует, gemma-26B пишет ответ со ссылками на источник.

Конструктор агентов. Нужен агент, который раз в день забирает определённые письма из Gmail, классифицирует их, важные складывает в Notion, остальные суммаризирует в дайджест и отправляет в Telegram, — собирается в Dify за десять минут на drag-n-drop, без единой строчки кода.

Бэкап. sudo agmind backup — снимок Postgres, Redis, всех volume'ов и конфигов. Опционально шифрование age, опционально upload по SSH на peer-машину.

Производительность на честных бенчмарках

Без чисел разговор про self-hosted AI бессмысленный.

Метрика

gemma-4-26B-A4B-it (MoE)

TTFT (streaming)

183 мс

TPS (1 запрос)

23–24 ток/с

TPS (3 параллельных)

~50 ток/с агрегированно

Контекст

65K (FP8 KV-cache)

Max concurrency @ 65K

45 запросов

Память: веса

48.5 GiB (bfloat16)

Память: KV-cache

41.7 GiB (FP8)

Total footprint

~95 GiB

Docling (5-page arxiv PDF)

Время

Cold start

6.04 с

Warm

1.6 с

Per-page (warm)

0.32 с

284-page PDF, без VLM

88 с

Тот же PDF, с VLM concurrency=8

191 с

Прочее

Полный install.sh на чистой системе

~30 минут (включая 52 ГБ весов)

Установка

bash

git clone https://github.com/botAGI/AGmind.git
cd AGmind
sudo bash install.sh

Визард задаёт 10–15 вопросов: single-Spark или dual-Spark, какую LLM (по умолчанию gemma-4, либо curated-список Qwen / Llama / Mistral / phi-4, либо custom HuggingFace), какой контекст, какие опциональные сервисы (RAGFlow, Open WebUI, LiteLLM, SearXNG, DB-GPT, Crawl4AI, Authelia 2FA), куда бэкапиться. Через ~25 минут стек живой, все credentials лежат в /opt/agmind/credentials.txt с правами 600.

Day-2-операции:

bash

agmind status              # сервисы, GPU, модели, эндпоинты
agmind doctor              # системная диагностика
agmind logs <service>      # тейл логов
agmind ragflow status      # три ragflow-контейнера + ES health
agmind docling bench <pdf> # cold/warm/per-page тайминг
agmind upgrade --diff      # что устарело относительно pinned-версий
agmind backup              # Postgres + Redis + volumes
agmind rotate-secrets      # перегенерировать пароли и ключи

Пять грабель GB10, на которые я наступил

Часы потерь честные. Каждая история — это вечер, который вместо «допилить фичу» превратился в «понять, почему это вообще не работает».

1. 502 на каждый запрос после force-recreate (потерял два дня)

Я писал nginx-конфиг по привычке нулевых: upstream agmind_api { server api:5001; } плюс proxy_pass http://agmind_api. Через неделю мне понадобилось рестартнуть Dify api с обновлённым .env через docker compose up -d --force-recreate api. Получил 502 на каждый запрос. Логи api здоровы, сам api отвечает на curl по новому IP, nginx — 502.

Оказалось, новый api получил новый IP (был .5, стал .7), а nginx upstream-блок закешировал старый IP при старте. Добавил resolver 127.0.0.11 valid=10s; (Docker embedded DNS) — не помогло. Ещё час дебага: resolver влияет только на proxy_pass $variable, а на upstream-блоки и на статический proxy_pass http://name — нет. Это в документации nginx написано, но мелким шрифтом.

Решение — переписать всё через variable form:

nginx

location /api {
    set $u_dify_api http://api:5001;
    proxy_pass $u_dify_api;
}

И полностью убрать upstream {}-блоки. Сейчас в templates/nginx.conf.template действует жёсткое правило при ревью: ни одного proxy_pass http://<name> без $. Регрессионный тест проверяет, что после force-recreate api сервис отвечает 200 без ручного рестарта nginx.

2. Зомби-задачи в Redis после force-recreate (потерял два часа)

Запустил большой PDF на 280 страниц через docling, висит в processing десять минут. Подумал — worker завис, сделал docker compose up -d --force-recreate worker. Вернулся через час — задача всё ещё processing. Перезапустил весь стек. Та же история. Любая новая загрузка стабильно висит в waiting, GPU idle.

Реальная причина оказалась в моих же force-recreate'ах, которые оставили в Redis stale-state: ключи generate_task_belong:<task_id> (TTL 1800), pub/sub-каналы /1.celeryev привязаны к hostname celery@OLDID, новый worker их не читает. Recreate = новый контейнер с новым hostname. Redis ничего об этом не знает.

Лечится так:

bash

redis-cli -a $PW -n 0 --scan --pattern 'generate_task_belong:*' | \
  xargs redis-cli -a $PW -n 0 DEL
redis-cli -a $PW -n 1 --scan --pattern 'celery-task-meta-*' | \
  xargs redis-cli -a $PW -n 1 DEL

FLUSHDB блокируется ACL даже для default user — это security hardening, разрешён только DEL by key. Правильный путь дальше — менять env через docker restart api worker, а не через recreate.

3. mDNS self-collision (потерял час)

Хотел, чтобы agmind-dify.local, agmind-rag.local, agmind-storage.local резолвились в одну машину. Положил три алиаса в /etc/avahi/hosts на один и тот же IP. Avahi пишет в журнал: Local name collision. С другого хоста алиасы не резолвятся.

Час дебага. Оказалось, это self-collision внутри avahi: демон при старте регистрирует $(hostname).local → 192.168.1.42 как primary address record, и когда я добавляю alias на тот же IP, avahi видит конфликт между двумя record'ами для одного IP — и даже не отправляет probe в сеть.

Фикс — использовать avahi-publish-address -R name.local IP через systemd-обёртку, по одному юниту на алиас. /etc/avahi/hosts оставить только для имён с другим IP.

Бонус-баг к этой истории: второй mDNS-respond'ер на UDP/5353 ломает avahi молча. NoMachine с EnableLocalNetworkBroadcast 1, iTunes/Bonjour и прочие занимают порт. Проверяется через sudo ss -ulnp | grep 5353 — там должен быть только avahi.

4. Distroless-контейнеры без shell (потерял полчаса)

Loki, redis_exporter, nginx-prometheus-exporter — distroless-образы. У них нет /bin/sh. Healthcheck через CMD-SHELL wget ... выдаёт stat /bin/sh: no such file or directory, и контейнер навечно висит unhealthy, хотя метрики отдаются нормально. Если у вас в compose depends_on: { redis_exporter: { condition: service_healthy } } — стек никогда не поднимется.

Правило простое: для distroless-образов здоровье проверяется через Prometheus up{job=...}, а не через docker healthcheck.

5. APT и truncated downloads (потерял вечер)

Это уже не про AGmind, а про инфраструктуру. Но без этой истории картина неполная.

apt-get install -y linux-firmware (это 603-мегабайтный deb для arm64) — dpkg -i падает с «неожиданный конец файла или потока». SHA256 не совпадает. --reinstall не помогает, apt-get clean не помогает. stat показывает 632 МБ, curl -sI возвращает Content-Length 634 МБ. Файл обрезается на ~2 МБ до конца.

Curl по HTTP принимает truncated body как успех — HTTP 200, Connection closed до конца Content-Length, для curl это валидный финал. Где-то между моим хостом и upstream-зеркалом сидит прозрачный прокси или CDN, который режет соединение.

Решение — везде HTTPS. TLS требует close_notify alert при завершении, и если он не пришёл, клиент знает, что body неполный. После замены http://ports.ubuntu.com на https:// всё стало качаться чисто.

Я узнал это случайно, когда уже хотел переустанавливать систему. Сидел и думал — это что, я железо при распаковке покалечил?

Месяц с Claude Code: что это на самом деле

Самое интересное в этой истории — не какой стек я собрал, а как я его собирал. Я не писал код руками. Точнее, писал, но в объёме одной-двух строк, чтобы что-то быстро поправить. Все 88 КБ install.sh, все 16 lib-модулей, все nginx-шаблоны, все Python-скрипты для генерации манифестов сгенерировал Claude Code.

Это не «ChatGPT, дай сниппет». Claude Code — это CLI-агент, который читает файлы в репозитории, делает правки, гоняет тесты, открывает PR. Он сам видит мой проект, сам помнит, что в каком модуле. Я формулирую задачу человеческим языком, он её делает.

О себе для контекста: учился на программиста, лет 12–15 назад писал в продакшене, потом ушёл в бизнес и десять лет руки до кода почти не доходили. Архитектурное мышление осталось, диффы я читать не разучился, но сесть и накатать тысячу строк bash с идемпотентным wizard'ом — давно не садился. Тем интереснее было собрать что-то целиком, имея AI-агента в роли младшего разработчика на рутине.

Что у меня работало хорошо: bash-скрипты с edge-case'ами (в install.sh сейчас покрыто около тридцати возможных состояний хоста — частичная установка после прерывания, существующие credentials, сломанный peer, занятый порт 5353, битый apt-cache); регрессионные тесты под mock'и для cluster-mode; документация (этот пост — я диктую содержание, Claude формулирует, я вычитываю).

Что работало плохо. LLM врут, и это не лечится. Самый болезненный случай — Claude как-то предложил minio/minio:RELEASE.2026-02-01T00-00-00Z как стабильный пин MinIO. Тэг выглядит правдоподобно: timestamp в правильном формате, паттерн совпадает с реальными релизами. docker compose config принимает его как валидный. При docker compose up падает с manifest unknown, и установка валится на 9-й фазе из 11.

После этого случая я в CLAUDE.md (это файл с правилами проекта, его Claude автоматически читает в начале каждой сессии) написал жирно: «LLM не знает, какие тэги существуют в registry. Никаких "выглядит правдоподобно". Только docker manifest inspect перед commit». И добавил автотест tests/compose/test_image_tags_exist.sh, который проверяет каждый image:tag в registry. Теперь галлюцинации ловятся в CI.

Контекст забывается между сессиями. Каждая новая сессия — чистый лист. Если я не зафиксирую правило «не делай force-recreate во время RAG-индексации, это ломает Redis-state, я уже терял два часа», он через неделю это снова предложит. Без CLAUDE.md ничего не работает. Сейчас у меня там 200 с лишним строк правил, и каждое — выстраданное факапом. Раздел называется «Learned the hard way».

Иногда не понимает приоритеты. Говоришь «поправь баг в OCR» — попутно отрефакторит три соседних модуля, потому что заметил code smell. Иногда полезно, иногда я просто хочу патч, а не PR на восемьсот строк. Лечится через тот же CLAUDE.md: «не правь то, о чём тебя не просили».

Что я вынес про AI-разработку

Главное наблюдение, которое стоит написать прямым текстом: AI-агент не делает тебя умнее. Он делает тебя быстрее в том, в чём ты уже хоть как-то разбираешься.

Если я не понимаю, почему proxy_pass http://name ломается на force-recreate, а proxy_pass $variable — нет, никакой Claude мне это сам не объяснит. Он сгенерирует код, я его поставлю, оно сломается, и я не пойму, почему. Если же я понимаю — Claude напишет nginx-конфиг с правильным паттерном за минуту вместо часа.

То же самое со всем остальным. Без общего понимания, как устроен Docker, что такое systemd-юнит, как работает HTTP, я бы не отличил рабочий код от рабочего-с-виду. Claude — не замена программиста. Это сильно ускоренный младший разработчик, на которого можно скинуть рутину, при условии, что архитектор у тебя есть. И если этого архитектора нет — никакая модель его не заменит, хоть она и пишет код в десять раз быстрее тебя.

Где это реально меняет уравнение: в задачах, где у тебя есть видение, опыт читать чужой код, понимание предметной области — но нет команды и нет физического времени. Раньше один человек с такими исходными мог сделать MVP за пару месяцев. Сейчас — production-grade продукт за 30 дней. У меня в репозитории 86 коммитов, 32 тысячи строк, 30 контейнеров, и это не маркетинговая фраза, а git log. Дисциплина простая:

— чёткое видение продукта (не «дай мне умную систему», а «wizard на 10 вопросов, идемпотентность, откат при ошибке, конкретные SLO»); — CLAUDE.md с правилами проекта, который пополняется после каждого факапа; — железо под рукой для проверки на живом стенде — эмуляция врёт, форумы устаревают, документация по умолчанию отстаёт от реальности.

Если у вас есть DGX Spark — забирайте install.sh, гоняйте на чистой системе, в issue-tracker'е расскажите, что сломалось. Если Spark'а нет, но интересно, как устроен private RAG end-to-end — lib/ и templates/docker-compose.yml читаются как самодокументация: я писал их так, чтобы через год самому помнить, почему именно gpu_memory_utilization=0.60, а не 0.70.

Во второй части расскажу про мониторинг unified memory без NVML — там пришлось довольно изобретательно костылить — и про supply-chain hardening для Dify-плагинов в air-gap-сценариях. Подписывайтесь, если интересно.

github.com/botAGI/AGmind

Комментарии (10)


  1. MEGA_Nexus
    03.05.2026 09:50

    Статья классная, хотя лично я не понял и половину из написанного.

    По чистой GPU-производительности это что-то между RTX 5070 и 5070 Ti. Сенсация не в FLOPs, а именно в 128 ГБ unified memory: на одной коробке за $4 тысячи я могу инферить 70B-модель в FP4 без всякого шардирования, чего на потребительском железе обычно не сделать.

    Только это 128 ГБ обычной оперативной памяти LPDDR5x, не HBM (High Bandwidth Memory), поэтому то, что она unified memory, вряд ли сделает подсистему памяти быстрее, чем можно получить в потребительском сегменте.

    Иными словами, возможно было бы проще взять потребительское железо, набить его ОЗУ нужного объёма и поставить туда RTX 5080 или RTX 3090\4090\5090 и получить больше производительности за меньшие деньги.

    Учитывая количество найденных вами в Spark проблем, вариант собрать что-то своё уже не кажется таким уж плохим. Как минимум, в самостоятельной сборке больше свободы и нет вендор лока, а также можно легко поменять стек технологий на другой. Здесь же в Spark будет работать только то, что совместимо с этим Spark и его arm ядрами.


    1. Pcturl
      03.05.2026 09:50

      проще взять потребительское железо, набить его ОЗУ нужного объёма и поставить туда RTX 5080 или RTX 3090\4090\5090 и получить больше производительности за меньшие деньги

      И это будет всё ещё "небольшая золотистая коробочка размером чуть больше Mac mini"? Потому что автор акцент на форм-факторе ставит буквально в первом же предложении.


  1. MEGA_Nexus
    03.05.2026 09:50

    Дополнительно отмечу, что на PRO Hi-Tech относительно недавно было тестирование Nvidia DGX Spark и какого-то феноменального преимущества его перед другим железом в работе с ИИ я не увидел. https://www.youtube.com/watch?v=lLG-xN25v20

    Но сама железка DGX Spark хороша, как и концепция иметь полноценный локальный ИИ в маленькой коробочке. Жаль только что DGX Spark - это очень нишевое решение, поэтому цена на его неадекватно высокая.


  1. jojozuka
    03.05.2026 09:50

    google/gemma-4-26B-A4B-it - для этого с избытком хватит rtx 3090 с 24 Gb VRAM. С вашими 128 Gb вы могли позволить себе гораздо больше. Сложно сказать, но имхо, всё, что вы развернули, работало бы на значительно более сромном компе. Выглядит как недоиспользование возможностей. А ещё не расширяемое решение.

    но спасибо за то, что поделились опытом

    Он делает тебя быстрее в том, в чём ты уже хоть как-то разбираешься

    не совсем так. Я как не разбирался в асинхронном программировании, так и не разбираюсь и уже не буду. Но ИИ пишет мне асинхронный код, и всё прекрасно работает. Достаточно просто сказать "а теперь перепиши все вызовы АПИ на асинхронное выполнение"


    1. jshapen
      03.05.2026 09:50

      Эти 128 гигов совсем не те, что на видеокарте. Модели на 70b будут там еле ворочаться. 3090 на этой разряженной гемме покажет не меньше 100 токенов. Да и лидер сейчас это qwen3.6-27b (40 токенов на 3090)


  1. redfox0
    03.05.2026 09:50

    накатать тысячу строк bash с идемпотентным wizard’ом

    Может, я чего-то не понимаю, но например… ansible.


  1. akirsanov
    03.05.2026 09:50

    Не самое адекватное решение для инференса.
    За эти деньги надо покупать мак студию м3 ультра с 800+Гб/сек скоростью памяти, и получить в 3-4 раза большую скорость инференса.
    Спарк вообще странное нишевое устройство из за медленной памяти.
    Это хороший "ардуино" для богатых поиграться в обучение маленьких моделек, но как девайс для инференса ну такое.
    Мак м3 ультра дает существенный прирост и потолок до 512 Гб? но дороже, а м4 макс с 128 Гб рам на 500 Гб/сек (а у спарка 200) обойдется в 3500$ в штатах и уже в 2+ раза уделывает спарк. Дешевле и быстрее, минус - без куды, но оно вам и не надо если вам только ллм гонять.


  1. jshapen
    03.05.2026 09:50

    Даже м5 макс 128 gb даст на qwen3.6-27b всего около 20 токенов, что на грани юзабельности


  1. zizop
    03.05.2026 09:50

    Спасибо за статью! Вы проделали огромную работу.


  1. Druzd
    03.05.2026 09:50

    DGX Spark не заточены на инференс, там приемлемо работают модели с квантизацией Q4, т. к. NPU модули хорошо обрабатывают float4. В паре два спарка работают хорошо, когда на одном веса, на другом Kv-cashe. На реддите есть обзоры где запускают на паре спарков модели по 130Млрд.параметров Q4.

    Спарки заточены на обучение моделей до 8 млрд. параметров. Вот тут они очень хороши! Когда быстро надо проверить гипотезу при обучении, собрать мини модель, обкатать А/В тестами, и потом можно транспонировать на большую модель код.

    Или же фантюнинг открытых моделей, когда прикручиваем свой уникальный слой в веса, например QLora.

    У меня аналог Asus Ascent GX10. Я использую под обучение своих моделей. К примеру модель на 4млрд. параметров с 10-тью MoE экспертами обучается за 20 часов. Все дело в размере VRAM, на обычной Rtx 5090 с 32 Gb формально возможно обучение, но это заняло бы месяц, а то и два.