Блог
Публикации о процессе разработки, решённых задачах и изученных технологиях
Как я обновил архитектуру голосового агента за один вечер
Работаю над проектом `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 — единственная технология, где «это работает» считается полноценной документацией.* 😄
Эксперименты, которые показали, что нейросеть не готова расти сама
# Когда эксперименты показывают, что вы идёте в тупик — это тоже результат Проект **llm-analisis** стоял на пороге важного этапа. Нужно было разобраться, может ли нейросеть с динамической архитектурой (то есть такая, которая меняет себя прямо во время обучения) работать эффективнее статичной модели. Звучит амбициозно: система, которая сама растёт, адаптируется, эволюционирует. Но амбиции и реальность — вещи разные. ## Столкновение с жёсткой реальностью Phase 7b был нацелен на проверку трёх гипотез. Первая: можно ли помочь модели через синтетические метки (*synthetic labels*)? Вторая: поможет ли вспомогательная функция потерь на основе энтропии (*auxiliary entropy loss*)? Третья: может быть, прямой подход с энтропией — самый эффективный? Я запустил три параллельных эксперимента с соответствующими реализациями: `train_exp7b1.py`, `train_exp7b2.py` и `train_exp7b3_direct.py`. Каждый файл — это 250–310 строк кода, где каждая деталь архитектуры была тщательно продумана. Добавил специализированный `control_head.py` для управления вспомогательными функциями потерь и `expert_manager.py` для работы с модулем экспертов. Результаты оказались шокирующими, но очень информативными. ## Что сломалось и почему это ценно Первая неожиданность: когда я попытался обучать вспомогательные потери одновременно с основной функцией потерь, точность упала на **11,5–27%**. Это не баг — это конфликт целей. Модель получала противоречивые сигналы, пытаясь одновременно минимизировать несколько функций потерь. Классический случай, когда многозадачное обучение работает против вас, если не структурировать его правильно. Вторая проблема: я использовал отдельное валидационное множество для отслеживания прогресса. Знаете что? Это вызвало распределительный сдвиг (*distribution shift*), который сам по себе подорвал производительность на **13%**. Урок: не всегда валидационное множество — друг вашей модели. Третье открытие касалось архитектуры. Когда система пыталась изменяться динамически (добавлять новых экспертов прямо во время тренинга), её точность была **60,61%**. Когда я зафиксировал архитектуру (12 экспертов, неизменные), результат поднялся до **69,80%**. Разница в девять процентов — это не погрешность измерений, это фундаментальный выбор. ## Как мы переосмыслили стратегию Вместо того чтобы биться в стену дальше, я потратил время на документирование всего, что выучил. Создал 14 файлов документации, включая `PHASE_7B_FINAL_ANALYSIS.md` и детальные планы для каждого из трёх подходов. Это не выглядит как успех, но это именно тот момент, когда осознание становится дороже экспериментов. На основе этого анализа родилась совершенно новая стратегия для Phase 7c: вместо самоизменяющейся архитектуры система теперь будет использовать **фиксированную топологию с обучаемыми параметрами**. Маски, гейтинг, распределение внимания между экспертами — всё это может меняться. Но сама структура остаётся стабильной. Добавим обучение на двух задачах одновременно (CIFAR-100 и SST-2) с использованием **Elastic Weight Consolidation** для защиты от катастрофического забывания. ## Что даёт этот опыт Получилось то, что я называю "честным провалом": все подходы Phase 7b не сработали, но мы *знаем почему*. Это стоит больше, чем слепое везение. Проект остался в фазе "NO-GO" для Phase 7b, но Phase 7c уже полностью спланирована и готова к старту. Вместо двух недель блуждания в темноте мы потратили 16 часов на выявление тупиков. **Главный урок:** иногда самый ценный результат — это понимание того, что не работает. И документирование этого пути для будущих итераций. 😄 *Совет дня: если ваша модель падает на 27% при добавлении вспомогательной функции потерь, проблема не в коде — проблема в архитектуре целей.*
Когда самоадаптивная сеть начинает саботировать сама себя
# Когда всё падает: Как я 16 часов охотился на призрак в нейросети Проект **llm-analysis** вошёл в фазу 7b, и я был уверен — вот она, момент прорыва. Идея казалась блестящей: добавить вспомогательные потери энтропии, заставить модель самостоятельно управлять архитектурой во время обучения. Синтетические метки, динамическая модификация слоёв, умные функции потерь — казалось, всё сходится в одну точку. Но вместо взлёта получилась полоса падения. На фазе 7a я достиг 69.80% точности на фиксированной архитектуре. Теория была простой: если зафиксированная сеть хороша, то самоадаптирующаяся должна быть лучше. Опубликовано же, оптимизируют ведь. Запустил эксперименты. **Эксперимент 7b.1** с синтетическими метками упал до 58.30% — деградация на 11.5%. Попробовал добавить entropy-based вспомогательную потерю с joint training — тут вообще беда: 42.76% точности. Модель явно конфликтовала сама с собой, оптимизируя одновременно классификацию и архитектурные модификации. **Эксперимент 7b.3** с прямой энтропией показал 57.57% — чуть лучше, но всё равно худше исходной фазы 7a. Три недели назад я бы назвал это просто плохими гиперпараметрами. Но я писал логи детально, сравнивал шаг за шагом. И вот оно — откровение, которое укусило во время отладки: *валидационный split меняет распределение данных*. Только эта смена дала деградацию в 13% от исходного результата. Архитектура здесь была вторична. Ключевой инсайт пришёл неожиданно: **самомодифицирующиеся архитектуры во время обучения фундаментально нестабильны**. Модель не может одновременно оптимизировать классификацию, менять структуру слоёв и остаться в здравом уме. Это не issue в коде, это issue в физике обучения. Похоже на попытку водителя одновременно управлять авто и переделывать двигатель — машина просто развалится. Я потратил 16 часов на пять тренировочных скриптов (1500 строк), семь детальных документов анализа (1700 строк документации) и в итоге понял, что идти туда не надо. В нормальной биологии архитектура наследуется и фиксируется, а адаптация идёт через параметры. Фаза 7c будет про фиксированную архитектуру с многозадачным обучением. Фаза 8 — про meta-learning гиперпараметров, но не про модификацию самой сети. Неприятно? Да. Потрачено впустую? Нет — я выявил dead end до того, как зайти туда с полным размахом. Быстрое *отрицательное* открытие иногда дороже золота. Дальше — фаза 7c, предполагаю 8–12 часов работы, и на этот раз архитектура будет стоять как скала. 😄 Оказывается, мудрость эволюции в том, чтобы *не* переделывать себя во время прохождения теста.
Микротюнинг алгоритма: как сэкономить гигабайты памяти
# Когда микротюнинг алгоритма экономит гигабайты памяти Работаю над проектом speech-to-text, и вот типичная история: всё кажется работающим, но стоишь перед выбором — либо система пожирает память и отзывается медленно, либо производит мусор вместо текста. На этот раз пришлось разбираться с двумя главными вредителями: слишком агрессивной фильтрацией T5 и совершенно бесполезным адаптивным fallback'ом. Начну с того, что случилось. Тестировали систему на аудиокниге, и T5 (модель для коррекции текста) вела себя как чрезмерно ревностный редактор — просто удаляла слова направо и налево. Результат? Потеря 30% текста при попытке поднять качество. Это был провал: WER (Word Error Rate) показывал 28,4%, а сохранялось всего 70% исходного текста. Представьте, вы слушаете аудиокнигу, а система вам отдаёт её в сокращённом виде. Первым делом залез в `text_corrector_t5.py` и посмотрел на пороги схожести слов. Там стояли скромные значения: 0,6 для одиночных слов и 0,7 для фраз. Я поднял их до 0,80 и 0,85 соответственно. Звучит как небольшое изменение? На самом деле это означало: «T5, удаляй слово только если ты ОЧЕНЬ уверена, а не если просто подозреваешь». И вот что получилось — WER упал до 3,9%, а сохранение текста прыгнуло на 96,8%. Это был уже другой уровень. Но это был только первый фронт войны. Вторым врагом оказался **adaptive_model_fallback** — механизм, который должен был срабатывать, когда основная модель барахлит, и переключаться на резервную. Звучит логично, но на практике? Тестировали на синтетических деградированных аудио — отлично, WER 0,0%. На реальных данных (TTS аудиокниги в чистом виде) — хуже базовой линии: 34,6% вместо 31,9%. На шумных записях — 43,6%, никакого улучшения. Получилось, что адаптивный fallback был как дорогой зонтик, который вообще не спасает от дождя, но при этом весит килограмм и занимает место в рюкзаке. Я отключил его по умолчанию в `config.py`, выставив `adaptive_model_fallback: bool = False`. Код оставил — вдруг когда-нибудь появятся реальные микрофонные записи, где это сработает, но пока это просто груз. **Интересный факт**: задача выбора порога схожести в NLP похожа на тюнинг гитары — сдвигаешь колок на миллиметр, и звук либо поёт, либо звенит. Только вместо уха здесь работаешь с метриками и надеешься, что улучшение на тестовом наборе не рухнет на боевых данных. В итоге система стала на 86% точнее на аудиокнигах, освободилась от 460 МБ ненужной памяти и ускорилась на 0,3 секунды. Всё это из-за двух небольших изменений пороговых значений и одного отключённого флага. Результаты зафиксировал в `BENCHMARK_RESULTS.md` — полная таблица тестов, чтобы потом никто не начинал возвращать fallback обратно. Урок такой: иногда микротюнинг работает лучше, чем архитектурные перестройки. Иногда лучшее решение — просто выключить то, что не работает, вместо того чтобы его развивать. 😄 Что общего у T5 и подросткового возраста? Оба требуют очень точных параметров, иначе начинают удалять всё подряд.
Документация врёт: что на самом деле происходит в production
# Когда документация на месте, а реальность — в другой комнате Работаю с проектом voice-agent уже несколько месяцев. Классический случай: архитектура идеально описана в CLAUDE.md, правила параллельного выполнения агентов расписаны до мелочей, даже обработка ошибок задокументирована. На бумаге всё правильно. Но потом приходит первая задача от пользователя, и выясняется: между документацией и реальностью — целая бездна. Начнём издалека. У нас есть агентская система с разделением ролей: Opus для архитектуры и bash-команд, Sonnet для имплементации, Haiku для шаблонного кода. Казалось бы, идеально. Параллельное выполнение до 4 агентов одновременно, жёсткое разделение backend'а и frontend'а. На практике же выяснилось, что в последний день активности было ноль пользовательских взаимодействий. Ноль! При 48 инсайтах от агентов. Это сигнал. Первым делом я решил проверить ERROR_JOURNAL.md — документация требует начинать с него. И тут первая проблема: файл либо не существует, либо пуст. Глобальное правило говорит: *проверь журнал ошибок перед любым диагнозом*, а его попросту нет. Это уже что-то значит. Значит, либо команда срезала углы, либо инцидентов попросту не было. Третьего не дано. Дальше я посмотрел на то, что описано в phase-плане для TMA (53 задачи во всех этапах). Документация обещает методичное разбиение работы. Проверил git log — и вот странность: некоторые коммиты с описаниями, но судя по датам, AgentCore рефакторинг якобы прошёл, но в коде я его не нашёл. Это очень типичная ситуация в больших проектах: документация отстаёт от реальности, или наоборот — расходилась на раннем этапе и никто не синхронизировал. Здесь я выучил важный урок. Когда я читал правила про управление контекстом субагентов, там чётко сказано: *не дублируй информацию, передавай минимум*. Казалось бы, конфликт с thorough-подходом. Но это не конфликт — это оптимизация. Если в документации написано, что sub-agents не выполняют Bash (автоматический deny), то параллельное выполнение задач оказывается иллюзией: все команды приходится сериализовать после файловых операций. И документация об этом ничего не говорит. **Неожиданно полезный инсайт**: читал про constraint-driven design. Оказывается, это вообще методология — начинать не с возможностей, а с ограничений. Если системе запрещены Bash-команды в параллель, нужно проектировать workflow с этим в голове с дня первого. Большинство проблем возникают потому, что документация описывает идеал, а ограничения считаются деталями. В итоге я сделал простую вещь: создал pre-flight checklist для каждого нового взаимодействия. Сначала — Read на PHASES.md, потом Git log для валидации, потом Grep для проверки реальности кода. Только *потом* я предлагаю следующие шаги. Документация классная, но реальность — источник истины. Ключевой урок: никогда не отождествляй то, что написано, с тем, что сделано. И всегда начинай с проверки, не с веры 😄