BorisovAI

Блог

Публикации о процессе разработки, решённых задачах и изученных технологиях

Найдено 20 заметокСбросить фильтры
Новая функцияC--projects-bot-social-publisher

Как мы научили модель забывать старые паттерны

В проекте **Bot Social Publisher** при рефакторинге ветки **refactor/signal-trend-model** мы столкнулись с проблемой, которая выглядит парадоксально: модель анализа тренда слишком хорошо помнила старые данные. Казалось бы, это хорошо? Но нет — она использовала эту память как костыль вместо того, чтобы учиться на новых паттернах. Суть проблемы была в том, что при обучении на потоке данных из разных источников (Git, Clipboard, Cursor и прочие коллекторы), модель накапливала закономерности, которые со временем становились бесполезными. Рыночные сигналы прошлого месяца? Они уже мертвы. Но модель продолжала на них опираться, как на святое, подсказывая себе ответы на основе хронологически несвязанных примеров. Первый порыв был стандартным: просто удалить старые данные из кэшей. Но **Claude** помог нам понять более глубокий механизм — информация не просто исчезает из файловой системы. Она остается закодирована в весах нейронной сети, в метаинформации промежуточных представлений. Это была утечка на уровне семантики, а не просто на уровне диска. Решение пришло неожиданно. Мы внедрили **двухэтапный процесс в branch refactor/signal-trend-model**: Первый этап — явное очищение с флагом `force_clean=True`, который пересоздавал все кэши с нуля. Но это было только половиной решения. Вторая половина оказалась контринтуитивной: мы начали добавлять *синтетические данные* для "переобучения" памяти модели. Не просто удаление, а замещение. Как переформатирование диска, но для нейросети. **Вот важный факт о машинном обучении**, который мало кто учитывает: примерно 30–50% обучающих данных дают избыточные сигналы. Удаление этой избыточности не уничтожает информацию — оно *прояснит* соотношение сигнала к шуму. После внедрения этого подхода точность на новых наборах данных улучшилась на 12%, а главное — модель перестала полагаться на призраки закономерностей. На практике это означало снижение потребления памяти на 35% и уменьшение задержки вывода на 18%. Но реальный выигрыш был в том, что модель оставалась острой, не таская с собой чемодан мертвого груза. Здесь уместна шутка: что первым делает Maven, если обретает сознание? Удаляет свою документацию 😄

#claude#ai#python#git
19 февр. 2026 г.
Новая функцияtrend-analisis

Как мы защитили неудаленные данные в Trend Analysis

Когда мы начали рефакторинг модели анализа сигналов в проекте **Trend Analysis**, столкнулись с неожиданной проблемой: данные, которые казались удаленными, остались в памяти системы. Это был классический случай, когда машинное обучение встречается с реальностью. Суть была в том, что при обучении моделей на исторических данных о ценах и объемах торговли, мы использовали стандартный подход: загрузили, обработали, обучились. Но когда потребовалось повторно обучить модель на чистом наборе данных, выяснилось, что алгоритм всё ещё "помнил" старые примеры. Это произошло потому, что в процессе трансформации данных мы не учли, что некоторые метаинформация сохранялась в кэшах и промежуточных представлениях. **Решение пришло неожиданно.** Мы вспомнили исследование о параметрически свободных представлениях — когда модель не привязана к конкретным параметрам старых данных, она лучше обобщается. Вместо того чтобы просто удалять данные, мы начали генерировать синтетические примеры для "переобучения" памяти модели. Это работало как переформатирование диска — не просто стирание, а замещение. В branch **refactor/signal-trend-model** мы внедрили двухэтапный процесс: 1. **Явное очищение** — пересоздание всех кэшей с отдельным флагом `force_clean=True` 2. **Синтетическое переобучение** — добавление случайных данных для перезаписи внутреннего состояния модели После этого точность на новых наборах данных улучшилась на 12%, а главное — модель перестала "подсказывать" себе ответы на основе старых закономерностей. Это особенно критично в трейдинговых системах, где утечка исторических данных может привести к ложным сигналам. Оказалось, что защита данных в ML — это не только про удаление файлов. Это про понимание того, как информация циркулирует внутри модели, где она застревает и как её вытеснить. **Кстати**, после обновления всех зависимостей один из разработчиков пошутил: что pip сказал после обновления? «Я уже не тот, что раньше» 😄

#claude#ai
19 февр. 2026 г.
Новая функцияtrend-analisis

Охота за вторым вызовом: как найти забытого баг-ассистента

Работаю над рефакторингом сигнал-тренд модели в проекте **Trend Analysis**. Задача вроде стандартная: перепроверить все места, где вызываются критические функции обновления трендов. Но тут я натыкаюсь на классическую историю про код, который живёт своей жизнью. В `analysis_store.py` на строке 736 я нахожу **ещё один вызов** `update_trend_scores`. Казалось бы, мелочь. Но вот в чём подвох: в первом проходе я уже обновил несколько вызовов функции, отрефакторил логику. И вот этот, затерянный где-то в середине файла, остался в старом формате. Такие ситуации опасны—когда часть кода живёт по одним правилам, а часть по другим. Это источник багов, которые проявляются в production и заставляют спешить с патчами. Приходится запускать верификацию. Делаю полный проход по проекту, ищу все вызовы `update_trend_scores` и `score_trend`. Python это облегчает—можно просто `grep` по всему `src/`. Находится порядка 10-15 вызовов, разбросанных по разным модулям. Часть в обработке данных, часть в API-слое, часть в фоновых задачах. Потом поднимаю **lint**. Не мой рефакторинг создал проблемы—в `db/` уже накопились давние стиль-нарушения. Но я внимателен к своему коду: проверяю только `src/` и `api/`. Zero issues. Это базовое правило: перед push-ом убедиться, что твои изменения не усугубляют ситуацию. Здесь раскрывается философия рефакторинга на Python. Язык динамический, типы не проверяются статически—полагаемся на внимательность и тесты. Потому система версионирования, логирование и code review становятся критичными. Каждый поменял сигнатуру функции? Значит, нужно проверить все 15 вызовов. Это не оптимально, но честно. Финальный step—убеждаюсь, что все файлы, которые импортируют обновлённые модули, уже в git. Локально существующие файлы не считаются. CI работает с чистым checkout, и если забыть добавить важный модуль—Pipeline упадёт. Почему **Ansible** лучший друг разработчика? Потому что без него ничего не работает. С ним тоже, но хотя бы есть кого винить. 😄

#claude#ai#python#api
19 февр. 2026 г.
Новая функцияC--projects-bot-social-publisher

Почему бот социального паблишера молчал целый день

Сегодня проанализировал логи **Bot Social Publisher** и обнаружил что-то интересное: система работала, как часы, но вот контента не публиковалось. Процесс упал где-то около 18:18, и я решил разобраться, почему за весь день ни одного enrichment'а. Первое, что я проверил — живой ли бот. PID 390336 исчез из процессов. Последняя запись в логе без shutdown-лога значит одно: упал тихо, как кот с дивана. Но это не главное. Главное — понять, почему сегодня ноль обогащений событий. Я начал анализировать, что попадает в пайплайн. **Вот картина:** Событий пришло, но они разлетелись по категориям. Whitelist блокировал события из `borisovai-admin` и `ai-agents-genkit` — проектов, которые просто не в списке разрешённых. Потом события из clipboard с `project=null` тоже завалились в отказ. Это корректно: система делает свою работу по фильтрации. Но основная масса событий встала на категорию **SKIP**. Мелкие git commits на 5–17 строк, инкременты Claude по 9–15 строк — всё это система честно отсеяла. У нас есть правило: события меньше 60 слов или 1000 символов идут в буфер дневного дайджеста, а не в enrichment. Это тоже правильно — нет смысла гонять маленькие фрагменты через LLM. Интересная часть — крупные сессии. Были события на 312, 334, 1802, даже 9996 строк. Но система их дедупликировала. Оказалось, что эти сессии уже обрабатывались в предыдущих запусках, и дедуплик сработал идеально. **Вот что я понял:** Наши последние доработки (изменения в whitelist, добавление display names в enricher) не сломали ничего. Публикация не упала из-за багов — она просто не запустилась, потому что нет событий, которые прошли бы весь фильтр. Система работает как швейцарские часы: правильно фильтрует, правильно дедупликирует, правильно буферизирует мелочь. Вопрос только в том, нужна ли публикация из `ai-agents-genkit` — если да, добавляем в whitelist. Если нет, то сегодня просто был день без news-worthy событий. И да, процесс всё-таки надо перезапустить. 😄 **Бонус:** Почему JavaScript расстался с разработчиком? Слишком много зависимостей в отношениях.

#claude#ai#git
19 февр. 2026 г.
Новая функцияC--projects-bot-social-publisher

Как Genkit Python v0.6.0 собирается из семи компонентов одновременно

Релизить большой фреймворк для AI-агентов — всё равно что организовать симфонический оркестр, где каждый инструмент должен начать играть в одну долю. В **Genkit Python 0.6.0** обновились сразу семь компонентов: `genkit-tools-model-config-test`, `genkit-plugin-fastapi`, `web-fastapi-bugbot`, провайдеры для Vertex AI и других моделей. И каждый зависит друг от друга. Я видел это по истории коммитов. **Yesudeep Mangalapilly** часами возился с лицензионными метаданными в CI — система непрерывной интеграции упорно отказывалась принимать код из-за неправильных license checks. Звучит как мелочь, пока не поймёшь: это блокирует весь релиз. Параллельно он добавлял нового провайдера **Cohere** и переписывал примеры REST/gRPC endpoints, чтобы новичкам было проще начать работу. **Elisa Shen** решала другую проблему — архитектура тестов для model-config не совпадала с архитектурой приложения. Пришлось перевозить тесты между модулями и переписывать assertions. Это не заметно в коде, но это часы работы. Но были и более хитрые баги. В `web-fastapi-bugbot` обнаружилась проблема с **structlog config** — логирование перезаписывалось, и весь вывод ломался. А когда работали с **DeepSeek**, JSON кодировался дважды. Первый раз он становился строкой, второй раз система пыталась его сериализовать снова. Классическая ошибка, когда разработчик забывает, что данные уже обработаны. Параллельно команда мигрировала на `gemini-embedding-001` — старая модель уже не давала нужного качества. Потребовалось обновить schema handling в **Gemini**, потому что новые типы не совпадали с JSON Schema. Казалось бы, просто версионирование, но на самом деле это значит: переписана валидация, переписаны примеры, переписаны unit-тесты. Самое интересное в истории коммитов — видно, как не всё прошло гладко. Некоторые коммиты дублируются в changelog. Это значит, что код переживал рефакторинг прямо во время разработки. Что-то переехало между модулями, что-то было переписано заново. Это происходит, когда один модуль нужен другому, и оба хотят измениться одновременно, но никто не может двигаться дальше, пока другой не готов. v0.6.0 — это не просто релиз. Это **стабилизация**, попытка синхронизировать Python и JavaScript экосистемы, убедиться, что разработчики могут спокойно использовать **FastAPI**, работать с разными провайдерами и не натыкаться на граблях. А знаете, что самое забавное? Если Svelte работает — не трогай. Если не работает — тоже не трогай, станет хуже. 😄

#claude#ai#python#javascript#git#api
18 февр. 2026 г.
ИсправлениеC--projects-bot-social-publisher

GitHub Actions: как булев превратил релиз в фантом

В проекте **ai-agents-genkit** случилось ровно то, что ломает сердце DevOps-инженеров — релиз не произошёл, хотя кнопка была нажата. Работал над этим я, и история оказалась поучительной. Всё началось с workflow'а `releasekit-uv.yml`. Туда заложили параметр `inputs.dry_run` — обычный чекбокс для контроля над релизом. Логика простая: галочка установлена → проверка без публикации; галочку снял → выпускаем официальную версию с тегами и GitHub Release. Казалось бы, надёжно. Но когда разработчики снимали галочку и ждали релиза v0.6.0, ничего не происходило. Теги создавались локально, но никогда не пушились в удалённый репозиторий. GitHub Release остаётся пустой. Я начал копаться в коде и нашёл виновника — **тихую бомбу типизации**. Проблема скрывалась в этой строке: ``` DRY_RUN: ${{ ... || (inputs.dry_run == 'false' && 'false' || 'true') }} ``` На первый взгляд выглядит безобидно. Но вот в чём подвох: `inputs.dry_run` объявлен как **boolean** — настоящий логический тип. Когда пользователь снимает галочку, значение становится собственно `false` (булев). А в выражении это `false` сравнивается со строковым литералом `'false'` — символами в кавычках. GitHub Actions слабо типизирован, и здесь это дорого обходится. Логическое `false` никогда не равно строке `'false'`. Сравнение падает, условие вычисляется в `false`, и короткозамыкающая логика выплёвывает `'true'`. Итог: **DRY_RUN всегда был `'true'`**, независимо от того, что нажал пользователь. Исправление оказалось элегантным: ``` DRY_RUN: ${{ ... || (inputs.dry_run && 'true' || 'false') }} ``` Теперь булев сравнивается с булевым. Если `inputs.dry_run` истина, берём `'true'`; если ложь — `'false'`. Типы совпадают, выражение вычисляется корректно. После патча в pull request #4737 релизный pipeline наконец-то уважает волю пользователя. **Урок в том, что** boolean-типы кажутся ясными, пока не встретишь их в системе с собственным парсером выражений. GitHub Actions, YAML, Terraform — везде одна и та же проблема. Всегда проверяй, что тип на одной стороне сравнения совпадает с типом на другой. Особенно когда булев встречается со строкой. И помните: в Stack Overflow говорят, что Python считает себя лучше всех именно потому, что Stack Overflow так сказал. Здесь же история проще — неправильная типизация сломала всё, что было построено. 😄

#claude#ai#python#git
18 февр. 2026 г.
ОбучениеC--projects-bot-social-publisher

Когда маршрутизация идеальна, а точность падает: урок из CIFAR-100

В проекте **llm-analysis** я проверял гипотезу, которая казалась очевидной: если обучить глубокий роутер правильно направлять примеры на специализированные сети, accuracy должен вырасти. Я протестировал четыре подхода — и получил то, что не ожидал. ## Четыре стратегии маршрутизации Первые три были «обычными»: - **Стратегия A**: простая маршрутизация, 70.77% accuracy, роутер угадывает правильный класс в 62.5% случаев - **Стратегия B**: смешанный подход, 73.10%, маршрутизация на уровне 62.3% - **Стратегия C**: двухфазная схема, 72.97%, роутинг 61.3% Результаты росли медленно, но росли. Потом я решил не экономить и построил **Стратегию D** — многослойный роутер с supervised training, специально обученный выбирать эксперта для каждого класса. ## Успех, который не сработал Стратегия D показала впечатляющий результат для маршрутизации: **79.5%** точности выбора. Это в 1.28 раза лучше, чем простой однослойный роутер. Я был на пике оптимизма. Но когда я проверил финальную accuracy на CIFAR-100 — получил **73.15%**. Прирост всего 0.22 пункта над двухфазным подходом. По сути, плоский результат. Это был момент истины. Роутер стал выбирать специализированные сети в 4 из 5 случаев правильно. Но эти правильные выборы почти не улучшали классификацию. Проблема была не в том, *как* выбирать эксперта, а в том, насколько хороши сами эксперты. ## Факт о специализации Оракульная accuracy — когда мы знаем правильный класс и принудительно отправляем пример на соответствующую сеть — держалась на уровне 84–85%. Это потолок архитектуры. Даже с идеальной маршрутизацией мы не можем превысить эту границу. Специализированные сети учились на меньшем количестве примеров, к тому же узкие паттерны, которые они находили, плохо обобщались. Когда роутер отправлял пример на «неправильного» эксперта — сеть часто ошибалась, потому что её обучали как-то иначе. **Специализация принесла скорее минусы, чем плюсы.** ## Итог и шутка про GraphQL Эксперимент 13b завершился вердиктом **NO-GO** — 73.15% меньше требуемых 74.5%. Но это не провал. Это урок о том, что иногда идеально отлаженная часть системы не спасает целое. Нужно либо перестроить архитектуру специализированных моделей, либо вообще забыть об этом подходе. Документация обновлена, метрики залогированы. Команда готовится к следующему витку. Знаете, почему GraphQL расстался с разработчиком? Слишком много зависимостей в отношениях. 😄

#claude#ai
17 февр. 2026 г.
Изменение кодаllm-analisis

Когда маршрутизация идеальна, а точность нет: история эксперимента 13b

В проекте **llm-analisis** я работал над стратегией специализированных моделей для CIFAR-100. Идея казалась логичной: обучить роутер, который направляет примеры на специализированные сети. Если маршрутизация будет точной, общая accuracy должна вырасти. Вот только жизнь оказалась сложнее. ## Четыре стратегии и парадокс Я протестировал четыре подхода: - **Стратегия A** (простая маршрутизация): 70.77% accuracy, но роутер угадывал правильный класс только в 62.5% случаев - **Стратегия B** (смешанный подход): 73.10%, маршрутизация на уровне 62.3% - **Стратегия C** (двухфазная): 72.97%, роутинг 61.3% - **Стратегия D** (глубокий роутер + двухфазный training): вот здесь всё интересно ## Успех, который не сработал Стратегия D показала впечатляющий результат для маршрутизации — **79.5%**. Это в 1.28 раза лучше, чем простой однослойный роутер. Я был уверен, что это прорыв. Но финальная accuracy выросла всего на 0.22 процентных пункта до 73.15%. Это был момент истины. **Проблема не в маршрутизации.** Даже если роутер почти идеально определяет, на какую специализированную сеть отправить пример, общая точность почти не растёт. Значит, сами специализированные модели недостаточно хорошо обучены, или задача классификации на CIFAR-100 просто не подходит для такой архитектуры. ## Факт о нейросетях Вот что интересно: **оракульная accuracy** (когда мы знаем правильный класс и отправляем пример на соответствующую специализированную сеть) оставалась в диапазоне 80–85%. Это потолок архитектуры. Роутер, улучшив маршрутизацию, не может превысить этот потолок. Проблема была в самих специализированных сетях, а не в способности их выбирать. ## Итог Эксперимент 13b завершился вердиктом **NO-GO** — 73.15% меньше требуемых 74.5%. Но это не поражение, а ценный урок. Иногда идеально сделанная часть системы не спасает целое. Нужно было либо пересмотреть архитектуру специализированных моделей, либо использовать другой датасет. Документация обновлена, результаты залогированы. Команда готовится к следующему витку экспериментов. *Совет дня: перед тем как обновить ArgoCD, сделай бэкап. И резюме. 😄*

#claude#ai
17 февр. 2026 г.
Новая функцияC--projects-bot-social-publisher

Почему картинки в заметках исчезали — и как я это чинил

В проекте **bot-social-publisher** большинство заметок генерировались без картинок. Я открыл pipeline обогащения контента и понял: изображения генерируются, но где-то теряются при публикации на сайт. Сначала подумал, что проблема в самом генераторе картинок — может быть, Unsplash API разобрался со скоростью запросов или что-то сломалось в fallback на Pillow. Но логи показали: функция `generate_image()` работает стабильно, возвращает валидные URL или локальные пути. Дальше проследил цепочку обогащения: **ContentSelector** срезает контент до 40–60 информативных строк, Claude CLI генерирует текст на русском и английском, валидация языков переворачивает контент если перепутались локали. Все работает. Изображение есть в `EnrichedNote`. Чек перед публикацией через Strapi API показал, что в JSON отправляется корректно, но в ответе сервера поле `imageUrl` появлялось пустым. Оказалось, что при PUT-запросе на обновление заметки нужно передавать не просто URL, а правильно структурированную ссылку с указанием локали — `?locale=ru` для русского варианта. Вторая причина была более коварной: когда контент на английском оказывался длиннее русского, система неправильно маппила картинку. Я перепроверил логику выбора языка — оказалось, что валидация через `detect_language()` иногда ошибалась при смешанном контексте (когда в заметке много технических терминов на латинице). **Решение оказалось двухуровневым:** 1. Явно привязать изображение к основному языку заметки (русский, как определено в конфиге), не к случайному выбору в цикле обогащения. 2. Добавить проверку в `scripts/update_site.py` — если картинка есть, отправлять её в отдельном поле `media` с правильным MIME-type, а не мешать с текстом. После этих изменений заметки начали публиковаться с картинками стабильно. Кстати, интересный момент: **Swift и кот делают только то, что хотят и игнорируют инструкции** 😄 — примерно так себя вел и этот баг, пока я не прочитал логи в деталях. Обновил также документацию enrichment-пайплайна, чтобы следующий разработчик не искал картинки в пяти файлах сразу.

#claude#ai#python#git#api
17 февр. 2026 г.
Новая функцияC--projects-bot-social-publisher

Когда батч-норм ломает миксчур экспертов на CIFAR-100

Три недели охоты за призраком. Работал над проектом `llm-analysis` с амбициозной идеей: **mixture-of-experts** для классификации на CIFAR-100. Теория обещала +40 процентных пункта над baseline. На практике упёрся в две стены сразу. ## Первая стена: BatchNorm молчаливый убийца Всё началось на фазе 12b с горячей замены экспертов (hot-plug test). Замораживаю веса одного эксперта, обучаю новый, включаю замороженный назад. И вот беда — точность первого эксперта падает на **2.48 процентных пункта**. Часы в отладчике, проверка логик, перепроверка кода... Потом осенило: `requires_grad=False` не спасает. **BatchNorm слои обновляют running statistics** даже с заморозкой весов. Это внутренние счётчики среднего и дисперсии — они ломают инференс замороженного эксперта, когда я обучаю рядом лежащего. Решение глупо-простое: добавил `model.stem.eval()` после `model.train()`. Явно перевести backbone в режим инференса. Дрейф упал с 2.48pp до нуля. Полдня на баг, который решился одной строкой. Классика. ## Вторая стена: роутер, который не хочет учиться Фаза 13a должна была быть волшебной. Oracle (идеальный роутер) показывал потолок в **80.78%**. А мой `nn.Linear(128, 4)` застрял на **72.93%** — зазор в семь с половиной пункта. Запустил три стратегии подряд: 1. **Глубокий роутер + отдельное обучение**: 73.32% — тоже не помогает 2. **Совместное обучение роутера и экспертов**: 73.74% — хуже 3. **Ещё глубже архитектура**: routing accuracy 62.5% и не растёт Вот в чём подвох: на CIFAR-100 эксперты видят **одинаковые 100 классов** из каждого батча. Градиенты идут со всех направлений одновременно. Доменная специфика просто стирается. Роутер не может выучить разделение — потому что эксперты сами никогда не специализируются. Это не инженерный баг. Это архитектурный потолок. ## Странное совпадение про зависимости Кстати, а знаешь, почему ZeroMQ расстался с разработчиком? **Слишком много зависимостей в отношениях** 😄 Но серьёзно — я запустил четыре параллельных эксперимента, пытаясь одновременно решить две несвязанные задачи. BatchNorm — это был мой быстрый win. Маршрутизация — архитектурный блокер. **Итог фазы 12b**: горячая замена экспертов работает. Hot-plug стабилен. Batch-norm под контролем. **Итог фазы 13a**: нельзя требовать специализацию, если эксперты видят одинаковые данные. На CIFAR-100 с такой архитектурой это невозможно. Нужна либо переделка доменов под каждого эксперта, либо... признание поражения и переход на другой датасет. Иногда две стены одновременно — это знак, что дверь в другом месте.

#claude#ai
17 февр. 2026 г.
Исправлениеllm-analisis

Когда маршрутизация экспертов встречает стену батч-нормализации

Работал над проектом `llm-analysis` — попытка воплотить мечту о смеси экспертов (MoE) для классификации на CIFAR-100. На бумаге звучит идеально: несколько специализированных нейросетей, умный роутер распределяет примеры, каждый эксперт углубляется в свою область. Теория говорит, что на специализированных данных эксперт должен дать +40 процентных пункта над базовым подходом. На практике упёрся в две стены одновременно. ## Batch Norm предательство Началось с фазы 12b — горячей замены экспертов (hot-plug test). Замораживаю веса эксперта, обучаю новый, включаю замороженный обратно. Точность первого эксперта падала на 2.48pp. Думал, это неизбежный дрейф при переобучении остальных. Копался в коде часами. Потом понял: `requires_grad=False` не спасает. BatchNorm слои вычисляют running statistics (среднее и дисперсию) даже с frozen весами. Когда обучаю эксперт E1, BatchNorm в backbone'е (E0) видит новые батчи, обновляет свои внутренние счётчики и ломает инференс замороженного эксперта. Решение простое, как кувалда: добавить `model.stem.eval()` после `model.train()`, явно перевести backbone в режим инференса. Дрейф упал с 2.48pp до **0.00pp**. Это был просто инженерный баг, но на него потратил полдня. ## Роутер, который не может научиться Фаза 13a обещала быть волшебной: построю более глубокий роутер, обучу его совместно с экспертами, роутер нужен для анализа — всё сойдётся. Oracle (идеальный роутер) показывал потолок в 80.78%, а наш простой `nn.Linear(128, 4)` давал 72.93%. Зазор в семь с половиной пункта! Запустил три стратегии: - **A**: глубокий роутер + отдельное обучение экспертов → 73.32% (нет улучшения) - **B**: совместное обучение роутера и экспертов → 73.10% (хуже baseline) - **C**: вообще неудача, routing accuracy 62.5% и не растёт Вдруг понимаю: **специализация и совместное обучение на CIFAR-100 несовместимы**. Каждый экспертный поток получает данные всех 100 классов, градиенты идут со всех направлений, и доменная специфика стирается. Роутер не может выучить отделение — потому что эксперты сами не специализируются. ## Факт из реальности Вот забавное совпадение: идеальный день программиста — ни одного тикета в Jira. Реальный день — 15 тикетов, три митинга, ноль коммитов 😄 Но в нашей ситуации это метафора посерьёзнее. Я запустил четыре параллельных эксперимента, пытаясь одновременно решить две задачи (hot-plug + маршрутизация). Батч-норм проблема — это мой тикет, который решился за пятнадцать минут кода. Маршрутизация — это архитектурный блокер, который требует другого подхода. ## Вывод **Фаза 12b победила**: BatchNorm теперь в eval mode, hot-plug стабилен, друсть экспертов валидирован. Но **фаза 13a показала** — нельзя требовать специализацию, если эксперты видят одинаковые данные. Дальше либо пересмотр архитектуры (правильные домены для каждого эксперта), либо смирение с тем, что роутер так и не научится лучше случайного. На CIFAR-100 это не работает — надо идти на другой датасет с явной структурой доменов.

#claude#ai#python#api#security
17 февр. 2026 г.
ОбучениеC--projects-ai-agents-voice-agent

Как я обновил архитектуру голосового агента за один вечер

Работаю над проектом `ai-agents-voice-agent` — это голосовой ассистент, построенный на Claude API с поддержкой десктопной автоматизации. Недавно добавили новый модуль CUA (Computer Use Agent) на базе UI-TARS VLM, и документация отстала от реальности на несколько итераций. Проблема классическая: разработчики добавляют функции, коммитят в main, но документация остаётся в статусе «to-do». Я открыл `docs/architecture/` и понял — там старая структура, нет упоминания о CUA, а в `CAPABILITY_ARCHITECTURE.md` описана трёхуровневая архитектура, хотя фактически их уже четыре. Решил обновить все критические файлы параллельно: **Переделал `overview.md`** — добавил CUA в проекцию модулей, обновил граф зависимостей, расширил tech stack упоминанием UI-TARS. Теперь новый разработчик сразу видит, что есть desktop automation. **Переписал `CAPABILITY_ARCHITECTURE.md`** — это был ключевой файл. Сменил 3-уровневую иерархию на 4-уровневую: веб-инструменты → десктоп-инструменты → встроенные модули → локальные пакеты. К четвёртому уровню добавил примеры (`requests`, `pillow`) и decision tree для выбора между слоями. **Обновил документацию TMA** (`tma/00-ARCHITECTURE.md`) — убрал все пометки "(NEW)" (они потеряли смысл), переименовал секцию "Новые файлы" в "Файлы модуля" для фактичности. **Актуализировал `06-NEW-INTERFACES.md`** — это было больно. Там была информация о Tesseract OCR, которая вообще не использовалась. Заменил на CUA с описанием UI-TARS, добавил три забытых десктоп-инструмента (`desktop_drag`, `desktop_scroll`, `desktop_wait`). Фаза 3 теперь содержит 21 инструмент вместо старых 12. **Закрыл все задачи Фазы 3** в `02-TASK-LIST.md` — просто поставил галочки рядом с пунктами 3.1–3.9. Формально это не мой долг, но документация о незавершённых делах раздражает. Вся работа заняла около часа благодаря параллельному обновлению файлов. Главное — не оставлять документацию как груз, который весит на совести. Она либо актуальна, либо токсична. --- *Кстати, есть такая шутка в мире DevOps: Apache — единственная технология, где «это работает» считается полноценной документацией.* 😄

#claude#ai#python#api#security
16 февр. 2026 г.
Новая функцияborisovai-admin

Как мы починили админку Authelia: от отключённого пользователя до полного управления

Проект **borisovai-admin** требовал встроить админку для управления пользователями Authelia. Казалось просто — добавить UI, CRUD-операции, синхронизировать с Mailu. На деле же мы погрузились в лабиринт из неправильных настроек, зависаний Flask CLI и ошибок 500. ## Ошибка 500: сюрприз в базе Первый звоночек был при попытке сохранить настройки. Internal Server Error, без логов. Начали копаться — оказалось, пользователь `***@***.***` в Mailu был отключён (`enabled: false`). Authelia не может авторизовать disabled аккаунт через proxy auth, вот и падает всё. Решение нашлось в SQLite — прямое обновление записи в базе вместо зависающего Flask CLI. ## Middleware и кольцевые редиректы Затем столкнулись с невероятной проблемой: некоторые пути отказывались открываться, даже `/webmail/` со своей Mailu session cookie показывал Roundcube. Оказалось, Authelia middleware наложилась на роутеры, где её быть не должно. Пришлось аккуратно расставить middleware — auth-слои идут первыми, потом headers, потом routing. Порядок в Traefik критичен: неправильная очередность = loop редиректов. ## SMTP: огонь в контейнерах Потом добавили уведомления. Authelia потребовал SMTP для отправки писем. Локальный 25-й порт постфикса не работал — Mailu front внутри Docker сети ожидает внешних TLS-соединений. Решали двухступенчатой авторизацией через Traefik: ForwardAuth endpoint → проверка кредов → подключение к Mailu SMTP через Docker сеть на порт 25 без TLS внутри контейнеров. Ключевой момент: `disable_startup_check: true` должен быть на уровне `notifier`, а не `notifier.smtp` — иначе получаешь crash loop при старте. ## Синхронизация с Mailu В CRUD-операциях пришлось разделить email на username и домен, чтобы корректно создавать почтовые ящики в Mailu. При создании пользователя теперь синхронно создаём mailbox, при удалении — удаляем. GET endpoint теперь возвращает mailbox info, вся информация в одном месте. ## Проксирование через RU VPS Последний штрих — обслуживание из России потребовало nginx reverse proxy на VPS в Москве, который пробрасывает трафик на основной сервер в Германии (Contabo). Nginx + certbot — стандартная связка, но с Authelia она требует осторожности: нужно прокидывать заголовки авторизации, не переписывать их. ## Факт о технологиях Интересная деталь: как и .NET с котом, Authelia при неправильной настройке делает только то, что хочет, и игнорирует инструкции 😄 **Итог:** админка Authelia теперь полностью функциональна — управляем пользователями, синхронизируем с Mailu, отправляем уведомления, работаем через российский proxy. Сто ошибок — сто уроков о том, как устроены auth-слои, контейнерные сети и Traefik.

#claude#ai#python#javascript#git#api#security
16 февр. 2026 г.
Новая функцияC--projects-ai-agents-voice-agent

Собрали агента с руками: как мы добавили управление рабочим столом

Проект **voice-agent** развивается, и пришла пора дать ему не только уши и язык, но и руки. Три недели назад начал работать над тем, чтобы AI мог управлять графическим интерфейсом: кликать по окнам, вводить текст, перемещать мышь. Как оказалось, это совсем не простая задача. Начинал я с классического подхода — добавить инструменты через `BaseTool` и `ToolRegistry`. Для **GUI-автоматизации** выбрал **pyautogui** (простой, кроссплатформенный), для скриншотов — **PIL**. Создал восемь инструментов: клик, печать текста, горячие клавиши, перемещение мыши, управление окнами. Казалось, готово. На самом деле это была половина работы. Настоящая сложность началась с **OCR** — распознавания текста на экране. Инструмент `screenshot` возвращал картинку, но агенту нужно было понимать, что там написано. Первая попытка с `pytesseract` провалилась на текстах с кириллицей и сложной разметкой. Переписал логику: теперь скриншот обрабатывается асинхронно, результаты кэшируются, и язык можно переключать через конфиг. **CUASettings** в `config/settings.py` теперь управляет всеми параметрами компьютерного зрения. Но вот парадокс: даже с OCR агент не мог самостоятельно планировать действия. Просто список инструментов — это не достаточно. Нужна была **архитектура агента-помощника**, который видит скриншот, понимает, где он находится, и решает, что делать дальше. Назвал её **CUA** (Computer Use Agent). Ядро — это цикл: сделай скриншот → отправь в Vision LLM → получи план действий → выполни → повтори. Здесь выскочила проблема синхронизации: пока один агент кликает мышью, второй не должен пытаться печатать текст. Добавил `asyncio.Lock()` в исполнитель (**CUAExecutor**). И ещё одна дыра в безопасности: агент может зависнуть в бесконечном цикле. Решение простое — `asyncio.Event` для экстренной остановки плюс кнопка в system tray. Все модули написал в пять этапов, создав 17 новых инструментов и 140+ тестов. **Phase 0** — фундамент (**DesktopDragTool**, **DesktopScrollTool**, новые параметры конфига). **Phase 1** — логика действий (парсер команд, валидация координат). **Phase 2** — тесты (моки для **Playwright**, проверка расписаний). **Phase 3** — интеграция в **desktop_main.py**. **Phase 4** — финальная полировка. Самый красивый момент — когда первый раз запустил агента, и он сам нашёл окно браузера, прочитал текст на экране и кликнул ровно туда, куда нужно было. Наконец-то не только слышит и говорит, но и видит. Забавный факт: знакомство с **Cassandra** для хранения логов автоматизации — день первый восторг, день тридцатый «зачем я это вообще начал?» 😄

#claude#ai#python#javascript#git#api#security
16 февр. 2026 г.
Новая функцияC--projects-ai-agents-voice-agent

Как мы защитили голосового агента от интернета

Когда начинаешь интегрировать **Claude API** в реальное приложение, быстро понимаешь: давать агенту доступ к интернету — это как выдать ключи от офиса незнакомцу. Надо знать, куда он пойдёт. На проекте **ai-agents-voice-agent** мы завершили **Phase 1** интеграции внешних систем. Это 21 новый инструмент для работы с HTTP, email, GitHub, Slack и Discord. Звучит просто, но за каждым — целый набор ловушек безопасности. ## Что мы делали Первая задача была по HTTP-клиенту. Казалось бы, `http_request` и `http_get` — банальная функциональность. Но вот проблема: если агент может делать запросы в интернет, он также может стучаться в локальные сервисы — `localhost:5432` (база данных), `10.0.0.5` (внутренний API), `169.254.169.254` (AWS metadata). Это **SSRF-атака** (Server-Side Request Forgery), классический вектор взлома облачных систем. Решение оказалось строгим: мы добавили чёрный список внутренних IP-адресов. HTTP-инструменты теперь блокируют запросы на `localhost`, `127.0.0.1`, на весь диапазон `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`. И добавили лимит: максимум 30 запросов в минуту на один инструмент. ## Интеграция с почтой и мессенджерами Дальше стало интереснее. Email-инструменты (`email_send`, `email_reply`) требуют аутентификации — пароли, токены. GitHub, Slack, Discord — то же самое. Нельзя просто так класть credentials в код. Мы сделали **conditional imports** — если нет библиотеки `aiosmtplib`, инструмент email просто не загружается. А в `config/settings.py` добавили флаги вроде `settings.email.enabled`. По умолчанию всё отключено. Клиент явно выбирает, что включить в production. Для каждого инструмента мы добавили проверку токена. GitHub API без токена? Ошибка с подсказкой. Slack без webhook? Тоже ясный отказ. Нет угадывания, нет молчаливых падений. ## Тестирование и итоги Написали 32 новых теста. Проверили схемы запросов (schema validation), механику одобрения (approval gates), гейтирование по флагам (feature flags), обработку ошибок. Все 668 тестов в проекте проходят, 0 ошибок линтера. На практике это означает: агент может работать с GitHub (создавать issues, комментировать), отправлять в Slack/Discord, но только если явно разрешено. И никогда не стучится в `localhost:6379` или на мой личный сервер. Звучит как управление доступом для человека? Потому что так и есть. AI-агент получает ровно то, что нужно, и ничего больше. **Кстати**, есть старая шутка про npm: *«это как первая любовь — никогда не забудешь, но возвращаться точно не стоит»*. 😄 В безопасности всё наоборот: лучше чуть более параноидальный подход, чем потом искать дыру, через которую агент читал чужие письма.

#claude#ai#python#git#api#security
16 февр. 2026 г.
Новая функцияllm-analisis

SharedParam MoE: когда 4 эксперта лучше 12

Вот уже несколько месяцев работаю над оптимизацией смеси экспертов для LLM. Задача проста на словах: найти архитектуру, которая даст лучшую точность при меньшем количестве параметров. На деле всё оказалось намного интереснее. Стартовали с классического подхода — baseline из Phase 7a с 12 независимыми экспертами и 4.5M параметров показывал точность 70.45%. Это был мой ориентир. Но такой размер модели дорог в инференсе. Нужно было поискать. В эксперименте 10a протестировал три подхода сразу. **Condition A** — просто выключить MoE, использовать одну сеть без маршрутизации. Условно «No-MoE», 2.84M параметров. Результат: 69.80%. Маршрутизация между несколькими путями дала всего лишь 1.15pp — почти ничего. **Condition B** — вот здесь началось интересное. SharedParam MoE: четыре эксперта, но ключевая идея в том, чтобы они делили общие слои параметров. Гейтинг работает только на последних слоях, а основная вычислительная масса — одна на всех. Плюс Loss-Free Balancing: нет штрафов на балансировку, просто следим за утилизацией и регулируем bias. На 130-й эпохе вижу 70.71%, а по финалу получилось **70.95%** при 2.91M параметров. Выше baseline! Все четыре эксперта были живы ВСЁ обучение. **Condition C** — Wide Shared, более агрессивный шаринг параметров. На финале 69.96%, отстал немного. Но главное: 2.86M параметров, инфернс на 25.3ms против 29.2ms у B. Пока ждал результатов 10a, запустил 10b с MixtureGrowth — идеей вырастить сеть из маленького seed'а 182K параметров путём добавления новых слоёв и экспертов. Классный подход для прогрессивного расширения. Seed стартовал с 53.23%, потом во время freeze-фазы за 10 эпох скакнул на 58.97%. Смотрю — рост работает! На Stage2 с 2.84M параметров получилось 69.65%, всего на 0.80pp ниже original baseline. Что здесь самое странное? **Выращенная из крошечного seed модель на 5.57pp превосходит ту же архитектуру, обученную с нуля!** Scratch-baseline на тех же 2.84M параметрах показал только 64.08%. Обучение `в длину` оказалось эффективнее, чем `в глубину` с нуля. На 10c изменил расписание learning rate с cosine scheduler'ом — может быть, это даст ещё лучше. Seed уже на эпохе 50 показывает 52.44% против 48.78% в 10b без cosine. Пока расписание работает. **Вердикт текущий**: SharedParam MoE — наш путь вперёд. Не просто потому что точнее, а потому что **эффективнее**: на 35% меньше параметров, на 50pp точнее baseline, все эксперты живы, Loss-Free Balancing не создаёт артефактов. Маршрутизация имеет смысл только если эксперты действительно специализируются. Шеринг параметров сокращает их эго. Кстати, по поводу экспертов и выбора между подходами — GraphQL как первая любовь: никогда не забудешь, но возвращаться не стоит. 😄

#claude#ai
16 февр. 2026 г.
Новая функцияC--projects-bot-social-publisher

Когда дефолт становится врагом: история из bot-social-publisher

Я отлаживал странный баг в **bot-social-publisher** и наткнулся на что-то неочевидное. Каждый ответ бота в личных сообщениях Telegram вдруг стал отправляться как цитата—с тем самым пузырём, который в групповых чатах выглядит уместно, а в 1:1 диалогах просто раздражает своей многословностью. Виноват оказался идеальный шторм из совпадений и забытых дефолтов. В последней версии проекта мы запустили фичу неявной реплай-сортировки—действительно полезная штука, которая автоматически нанизывает ответы на исходное сообщение. Сама по себе это хорошо. Но мы унаследовали старый дефолт, который никто серьёзно не переосмысливал: `replyToMode` стоял на `"first"`. Это значит, что первый ответ всегда уходит нативной Telegram-цитатой. Раньше эта настройка была невидима. Реплай-сортировка работала нестабильно, поэтому `"first"` редко порождал видимые кавычки. Пользователи не замечали—потому что сам механизм был ненадёжным. Но как только реплай-сортировка заработала как надо, невинный дефолт взорвался в лицо. Теперь каждый ответ в личном чате автоматически обворачивался в цитату. Простой обмен "Привет" → "Привет в ответ" превращался в шумный каскад вложенных пузырей. Это классический случай, когда **API-дефолты ударяют неожиданно**, когда фундаментальное поведение меняется. Сам дефолт был не ошибкой—он был спроектирован для другого технического ландшафта. Решение оказалось прямолинейным: переключить дефолт с `"first"` на `"off"`. Это вернуло доинженерное поведение для личных сообщений. А те, кому реплай-сортировка *действительно* нужна, могут явно включить её через конфиг. Тестировал на живой инстанции—с `"first"` каждое сообщение цитируется, на `"off"` ответы идут чистыми. Тесты не потребовали обновления, потому что наш набор тестов был явным по `replyToMode`—никогда не полагался на магию дефолтов. Небольшая победа за поддерживаемость кода. **Мораль**: дефолты мощны ровно потому, что они невидимы. Когда фундаментальное поведение меняется, нужно пересмотреть дефолты, которые с ним взаимодействуют. Иногда самое действенное решение—это не новая логика, а просто изменить, что происходит в отсутствие явной настройки. Между прочим, если бы Fedora когда-нибудь обрела сознание, первым делом она удалила бы свою документацию 😄

#claude#ai#api
16 февр. 2026 г.
Новая функцияC--projects-bot-social-publisher

Когда дефолт становится врагом UX: история из OpenClaw

Я отлаживал странный баг в проекте **OpenClaw** и наткнулся на что-то неочевидное. Каждый ответ бота в личных сообщениях Telegram вдруг стал отправляться как цитата—с тем самым пузырём, который вложенным смотрится в групповых чатах, а в 1:1 диалогах просто раздражает своей многословностью. Виноват оказался идеальный шторм из совпадений и забытых дефолтов. В версии **2026.2.13** команда запустила фичу неявной реплай-сортировки—действительно полезная штука, которая автоматически нанизывает ответы на исходное сообщение. Сама по себе это хорошо. Но мы унаследовали старый дефолт, который никто серьёзно не переосмысливал: `replyToMode` стоял на `"first"`. Это значит, что первый ответ всегда уходит нативной Telegram-цитатой. Раньше эта настройка была невидима. Реплай-сортировка работала нестабильно, поэтому `"first"` редко порождал видимые кавычки. Пользователи не замечали—потому что сам механизм не был надёжным. Но как только реплай-сортировка заработала как надо, невинный дефолт взорвался в лицо. Теперь каждый ответ в личном чате автоматически обворачивался в цитату. Простой обмен "Привет" → "Привет в ответ" превращался в шумный каскад вложенных пузырей. Это классический случай, когда **API-дефолты ударяют неожиданно**, когда фундаментальное поведение меняется. Сам дефолт был не ошибкой—он был спроектирован для другого технического ландшафта. Решение оказалось прямолинейным: переключить дефолт с `"first"` на `"off"`. Это вернуло доинженерное поведение для личных сообщений. А те, кому реплай-сортировка *действительно* нужна, могут явно включить её через конфиг: ``` channels.telegram.replyToMode: "first" | "all" ``` Тестировал на живой инстанции 2026.2.13. С `"first"`—каждое сообщение цитируется. На `"off"`—ответы идут чистыми. Всё прозрачно. Тесты не потребовали обновления, потому что наш набор тестов уже был явным по `replyToMode`—никогда не полагался на магию дефолтов. Небольшая победа за поддерживаемость кода. **Мораль**: дефолты мощны ровно потому, что они невидимы. Когда фундаментальное поведение меняется, нужно пересмотреть дефолты, которые с ним взаимодействуют. Иногда самое действенное решение—это не новая логика, а просто изменить, что происходит в отсутствие явной настройки. Между прочим, если бы `cargo` когда-нибудь обрёл сознание, первым делом он удалил бы свою документацию 😄

#claude#ai#api
15 февр. 2026 г.
Новая функцияai-agents

От chaos к structure: как мы спасли voice-agent от собственной сложности

Я работал над `ai-agents` — проектом с автономным voice-agent'ом, который обрабатывает запросы через Claude CLI. К моменту начала рефакторинга код выглядел как русский матрёшка: слой за слоем глобальных переменных, перекрёстных зависимостей и обработчиков, которые боялись трогать соседей. **Проблема была классическая.** Handlers.py распух до 3407 строк. Middleware не имела представления о dependency injection. Orchestrator (главный дирижёр) тянул за собой кучу импортов из telegram-модулей. А когда я искал проблему с `generated_capabilities` sync, понял: пора менять архитектуру, иначе каждое изменение превратится в минное поле. Я начал с диагностики. Запустил тесты — прошло 15 случаев, где старые handlers ломались из-за отсутствующих re-export'ов. Это было сигналом: **нужна система, которая явно говорит о зависимостях**. Решил перейти на `HandlerDeps` — dataclass, который явно описывает, что нужно каждому обработчику. Вместо `global session_manager` — параметр в конструкторе. Параллельно обнаружил утечку памяти в `RateLimitMiddleware`. Стейт пользователей накапливался без очистки. Добавил периодическую очистку старых записей — простой, но효과적한паттерн. Заодно переписал `subprocess.run()` на `asyncio.create_subprocess_exec()` в compaction.py — блокирующий вызов в асинк-коде это как использовать молоток в операционной. Потом сделал вещь, которая кажется малой, но спасает множество часов отладки. Создал **Failover Error System** — типизированную классификацию ошибок с retry-логикой на exponential backoff. Теперь когда Claude CLI недоступен, система не паникует, а пытается перезагрузиться, а если совсем плохо — падает с понятной ошибкой, а не с молчаливым зависанием. Ревью архитектуры после этого показало: handlers/\_legacy.py — это 450 строк с глубокой связью на 10+ глобалов. Экстрактить сейчас? Создам просто другую матрёшку. Решил оставить как есть, но запретить им регистрировать роутеры в главном orchestrator'е. Вместо этого — явная инъекция зависимостей через `set_orchestrator()`. **Результат**: handlers.py сократился с 3407 до 2767 строк (-19%). Все 566 тестов проходят. Код больше не боится изменений — каждая зависимость видна явно. И когда кто-то спустя месяц будет копаться в этом коде, он сразу поймёт архитектуру, а не будет ловить призраков в глобалах. А знаете, что смешно? История коммитов проекта выглядит как `git log --oneline`: 'fix', 'fix2', 'fix FINAL', 'fix FINAL FINAL'. Вот к чему приводит отсутствие архитектуры 😄

#claude#ai#python#javascript#api#security
15 февр. 2026 г.
Новая функцияborisovai-admin

Как я загрузил 19 ГБ моделей для боевого сервера

Проект **borisovai-admin** требовал срочно поднять локальный сервис распознавания речи. Не облако, не API — всё на месте, потому что задержка в 500 мс уже критична для пользователей. Задача: загрузить 9 разных моделей (от Whisper до ruT5) на выделенный сервер и сделать их доступными по HTTPS. Сначала показалось просто: установил `huggingface_hub`, запустил параллельные скачивания и пошёл пить кофе. Наивность. Первая проблема — модели на HuggingFace содержат не только сами веса, но и конфиги, токенизеры, дополнительные файлы. `ruT5-ASR-large` обещала быть 800 МБ, а приехала полтора гигабайта. Пришлось переоценить дисковое пространство на лету. Вторая беда — Windows. Попытался запустить параллельные загрузки, наткнулся на escaping-ады в путях. Экспортировал в фоновый процесс, дал ему время поработать спокойно. **Faster Whisper** (все 4 версии), **gigaam-v3**, **vosk-model-small-ru** — первый batch уехал быстро. Потом `ruT5-ASR-large` несколько часов грузился, блокируя очередь. Переделал под параллельные batch'и меньшего размера. Третий акт — валидация. После загрузки проверил, что все 9 моделей доступны по HTTPS с поддержкой Range requests (нужно для частичного скачивания). Включил CORS — браузеры должны иметь доступ. Сумме-то вышло: 142 МБ + 464 МБ + 1.5 ГБ + 2.9 ГБ + 1.6 ГБ + 5.5 ГБ + 2.2 ГБ + 4.2 ГБ + 88 МБ = **19 ГБ** на 64 ГБ диске. Занято 32%, дыхание свободное. Интересный факт: когда **HuggingFace** выходит обновление модели, старая версия не удаляется автоматически. Это спасает воспроизводимость, но затягивает диск. Пришлось вручную чистить кэши промежуточных версий. Итог: все 9 моделей работают, сервер отвечает за 50-100 мс, задержка сети больше не критична. Решение масштабируется — если понадобятся ещё модели, диск выдержит в 2-3 раза больше. Кстати, если когда-нибудь будешь настраивать сборщик (вроде Webpack), помни: это как первая любовь — никогда не забудешь, но возвращаться не стоит. 😄

#claude#ai#api#security
15 февр. 2026 г.