Тринадцать файлов существовали только на диске ПК владельца, в рабочей копии, отставшей от origin/main на 122 коммита. Их реализация и тесты давно влиты: tests/test_a42_provider_connect.py, test_a49_subagents_skills_memory.py, test_a51_hub_controls_hermes.py, test_a52_local_models_supervisor_dual.py, test_a55_account_connection.py, test_a56_context_compression.py и другие. Постановок, объясняющих, что эти тесты обязаны доказывать, в репозитории не было. Правило записано в agents/AGENTS.md: задание живёт в репозитории, а не в переписке и не в личных папках на диске. A48, A50 и A54 не переносятся: они уже есть на origin под другими именами, содержимое совпадает с точностью до перевода строки в конце файла. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
22 KiB
Задание A52: локальные модели — замена, надзиратель, пара кодеров с облачным судьёй
Дата поступления
2026-08-31 (переработано в тот же день: добавлены части 1 и 3)
База
origin/main (17b368a).
git fetch origin --prune
git checkout -b antigravity/a52-local-models origin/main
В main напрямую не пушить.
Порядок исполнения
Два прохода: Flash исполняет, Pro проводит аудит. Пункт P0-10 написан для аудитора.
Задание из трёх частей, и они идут строго по порядку:
Часть 1 замена моделей и замеры сдаётся отдельно, дальше по её числам
Часть 2 надзиратель локальных моделей
Часть 3 пара кодеров и облачный судья
Часть 3 планировать по измеренным числам части 1, а не заранее: без замера видеопамяти схема не проверяема.
Связано с A49 (расстановка субагентов и память) и A51 (аккаунт реально используется). Выполнять после них: надзирателю нужны и место в графе, и работающее назначение.
Что проверено ревьюером — заново не выяснять
Видеопамять занята почти полностью
Qwen3.8-27B кодер @196K 25 488 МиБ
qwen3-4b компрессор @32K 5 368 МиБ
──────────
30 856 из 32 768 свободно 1 912
Измерено у кандидатов (A45, файлы и контрольные суммы сверены по диску)
Qwen3-Coder-30B-A3B @64K 21 368 МиБ 109,6 ток/с 83,3% MoE 30B/3B
Qwen3-Coder-30B-A3B @32K 19 640 МиБ 110,2 ток/с
Qwen2.5-Coder-32B @64K 27 938 МиБ 29,3 ток/с 83,3% плотная
Расход контекста у Qwen3-Coder — около 54 КиБ на токен.
Качество на стенде из 12 задач (A40, признано)
Phi-4-14B 83,3% файл 8,28 ГиБ
Qwen2.5-Coder-14B 75,0% файл 8,37 ГиБ
Qwen3-4B-2507 83,3% файл 2,33 ГиБ
Granite-4.2-8B 66,7% файл 5,16 ГиБ
Расход видеопамяти у 14B при 64К не измерен ни разу. В A45 их не было, столбец VRAM из A40 непригоден: он собрал занятость всей карты, а не процесса.
Процессор годится для служебных ролей
Замер ревьюера, 32 потока из 72, AVX2:
LFM2.5-2.6B промпт 1150,8 ток/с генерация 13,4 ток/с
Qwen3-4B промпт 854,2 ток/с генерация 8,7 ток/с
Обработка промпта на процессоре быстрая, генерация медленная: она упирается в память и идёт последовательно.
Сервер отдаёт всё нужное для честной работы
Проверено на живом 127.0.0.1:8081:
GET /props → default_generation_settings.n_ctx = 196608, total_slots = 1
POST /tokenize → точное число токенов («def add(a, b): return a + b» = 10)
Предел контекста и размер задания измеряются, а не прикидываются.
Чего в коде нет
разбиения задачи на части — нет
подсчёта токенов для планирования — нет; format_token_count только для показа
model_registry.context_window = 128000 — статическое умолчание,
а у модели владельца 196608
llama-swap — только ttl, групп нет: держать две модели резидентно не станет
Поправка к постановке владельца
Оркестратор не переставал давать задачи локальной модели:
local_adapter.classify_error: "timeout", "timed out", "502", "503", "504"
→ ErrorCategory.TRANSIENT, retry_delay_seconds=2
Профиль не помечается исчерпанным и из цепочки не выбывает. Происходит другое: на каждом запросе локальная модель забирает отведённые Hermes 180 секунд, не успевает, и работу доделывает следующий в цепочке — платный. Чинить надо подачу работы, а не возврат в цепочку.
Физика, которую нельзя обойти
Две модели на одной V100 не работают вдвое быстрее. Генерация упирается в пропускную способность памяти; две модели делят одну полосу. Вместе они выдадут примерно столько же, сколько одна.
Значит «параллельно» здесь означает два независимых решения, а не выигрыш во времени. Ускорение в интерфейсе обещать нельзя.
Оговорка: у MoE активны три миллиарда из тридцати, полосу они едят иначе. Два MoE могут ужиться лучше — это гипотеза, её измеряют, а не закладывают.
Часть 1. Замена моделей и замеры
P0-1. Заменить кодер
Поставить Qwen3-Coder-30B-A3B-Instruct-Q4_K_M вместо Qwen3.8-27B.
Основание измерено: 109,6 против 30,3 ток/с, то же качество 83,3%, и на 4 ГиБ меньше.
- Контекст не ниже 65536 — порог отбора у Hermes.
- Условия запуска взять у нынешнего юнита:
--flash-attn on,--cache-type-k/v q8_0,--reasoning off,--parallel 1. - Прежний юнит сохранить; откат одной командой описать и проверить.
- Скорость и видеопамять замерить на живой службе, а не переносить из A45.
P0-2. Заменить компрессор
Поставить LFM2.5-2.6B вместо Qwen3-4B на порт 8082.
- Сначала замерить качество сжатия на том же наборе, что у нынешнего. Быстрее — не значит лучше; сожмёт хуже, замену не делать и так и написать.
- Рассмотреть запуск на процессоре (
-ngl 0): освобождает видеопамять, а сжатие — чтение многого и запись малого, где процессор силён. Замерить оба варианта и дать владельцу числа для решения.
P0-3. Измерить кандидатов в пару
Замерить расход видеопамяти по процессу при 64К для Phi-4-14B, Qwen2.5-Coder-14B, Qwen3-4B-2507, Granite-4.2-8B — через nvidia-smi --query-compute-apps=pid,used_memory, а не по занятости карты.
Затем проверить запуском, какие пары помещаются вместе с компрессором в 32 768 МиБ. Не расчётом.
Отдельно замерить, что происходит со скоростью при одновременной работе двух моделей: суммарная выработка против одиночной. Это проверка утверждения о полосе памяти, и её результат решает, имеет ли смысл держать пару резидентно.
Часть 1 сдаётся отдельно.
Часть 2. Надзиратель локальных моделей
Владелец: «если выбирается локальная модель, должен появляться субагент, который мониторит подачу работы. Если у модели не хватает контекста, он разбивает задачу на куски и подаёт, пока не заработает. Потом формирует память, какой объём давать модели».
P0-4. Роль надзирателя
- Новая каноническая роль
local-supervisorв реестре. - Включается автоматически, когда выбранный профиль локальный (
local,llama.cpp,ollama,vllm). Вручную назначать не нужно. - Не встаёт между ролью и платным провайдером.
- Надзиратель — не модель, а распорядитель. Считает, режет, подаёт, наблюдает; работу делает локальная модель. Тратить на него платный вызов нельзя.
Основная его работа — счёт и разбор, а не рассуждение: токены считает /tokenize, предел даёт /props, границы кусков определяются разбором кода. Ставить сюда слабую модель значит сделать надзирателя менее надёжным.
P0-5. Замер перед подачей, а не догадка
- Предел контекста брать у живого сервера через
/props. Умолчаниеmodel_registry= 128000 к модели владельца отношения не имеет. - Размер задания считать через
/tokenize— точно. Оценка по символам допустима только запасным путём и должна быть помечена как оценка. - Учитывать место под ответ: в контекст входят задание, история и ожидаемый ответ. Запас обосновать.
- Не выдумывать пределы. Сервер не ответил — так и записать.
P0-6. Разбиение и подача
- Помещается — подавать целиком. Резать без нужды вредно: теряется связность.
- Не помещается — резать по смысловым границам: файл, функция, класс, раздел. Посреди выражения — нельзя.
- Подавать последовательно, передавая накопленный результат, и собирать ответ.
- Неделимая задача — честный отказ, а не разрез наугад.
- Число попыток ограничено и настраивается. Бесконечный цикл на единственной видеокарте недопустим.
- После исчерпания попыток — отказ с причиной, дальше обычная отказоустойчивость. Надзиратель не прячет неудачу, удерживая работу на локальной модели любой ценой.
P0-7. Наблюдение за ходом
- Видеть, что модель работает, а не висит: поток ответа или тайминги сервера.
- Различать три исхода: успел, не успел, ответил ошибкой. Сейчас всё сваливается в «таймаут».
- Показывать ход на «Обзоре»: какой кусок из скольких, сколько токенов подано.
- Отдельно ловить случай A39: весь лимит ушёл на рассуждения, ответа нет. Измерено: 1500 токенов за 111 секунд и ноль символов ответа; с
enable_thinking: false— ответ за 11 секунд. Признак — пустой ответ при полном расходе лимита; лечитсяrequest_options, механизм есть после A39.
P0-8. Память: какой объём модель тянет
- Записывать по каждой модели: при каком размере получался ответ, при каком нет, сколько занимало, какой кусок оказался рабочим.
- Хранить в общей памяти (
/srv/projects/AI-Memory, структура после A47). Запись подписывается: модель, когда, по какому заданию. - Использовать при следующей подаче: начинать с размера, который уже работал.
- Привязывать к имени сборки из метаданных GGUF, а не к порту или имени профиля. Урок Tiel-Coder: в файле оказалась
Ornith-1.5-35B. - Показывать во вкладке «Память» надзирателя, что он усвоил.
Часть 3. Пара кодеров и облачный судья
Планировать по числам части 1.
P0-9. Схема и её цена
задание → Кодер A (локальный) ┐
→ Кодер B (локальный) ┘→ судья (облачная модель)
├ принято → дальше по конвейеру
└ не принято → обоим на доработку
- Кодеры не видят работу друг друга до суда. Иначе второе решение не независимо и смысл теряется.
- Судья облачный, видеопамяти не занимает. Это роль
developer-2существующего конвейера; модель задаёт владелец в интерфейсе — зашивать имя модели или провайдера в код нельзя. - Судья получает задание и оба решения, возвращает: какое принято либо что доработать каждому.
- Ревьюер остаётся на своём месте после судьи; конвейер не переделывать.
- Пара включается настройкой; возврат к одному кодеру возможен.
Предел итераций обязателен. Круг «пока не сделают правильно» тратит платную квоту судьи на каждом обороте.
- Предел кругов настраивается, умолчание обосновать. Механизм ограничения итераций в конвейере уже есть — использовать его.
- Показывать номер круга.
- Круги исчерпаны — честный отказ с последним состоянием обеих работ и мнением судьи. Частичный результат за готовый не выдавать.
- Считать расход: сколько вызовов судьи ушло на задачу.
- Круг без изменений — застревание. Оба вернули то же, что и в прошлый раз — прекратить и сказать.
P0-10. Аудит вторым проходом
- Числа части 1 сняты на живой службе, а не перенесены из A45.
- Откат к прежнему кодеру выполнен и проверен.
- Пара проверена запуском, а не расчётом: обе модели подняты, памяти хватило, обе отвечают.
- Утверждение о полосе памяти проверено: суммарная выработка двух моделей против одиночной. Результат записать, каким бы он ни был.
- Заведомо большая задача разбита, подана и собрана; неделимая дала честный отказ.
- Предел попыток и предел кругов проверены задачей, которая не выполнится никогда.
- Предел контекста взят у сервера, счёт токенов сверен с
/tokenizeнезависимо. - Независимость кодеров: решение одного не попадает в контекст другого.
- Модель судьи задаётся из интерфейса, а не зашита.
- Для платных провайдеров путь не изменился, надзиратель туда не лезет.
- Службы владельца вернуть в рабочее состояние.
- Побочные изменения объяснить.
- Пропущенный пункт назвать пропущенным.
Ограничения
- Видеокарта одна, сервер рабочий: окна для замеров согласовать, службы возвращать в строй.
- Прежние юниты сохранять, откат описывать и проверять.
- Учётные данные и
~/.hermes/agy_profiles/не трогать. - Имена моделей и провайдеров в код не зашивать.
- Конфигурацию Hermes не править.
- Версию
0.1.1не поднимать. - Правило честности без исключений: ни одного числа без замера; ускорение не обещать без подтверждения.
Критерии приёмки
Часть 1
- Кодер заменён на Qwen3-Coder-30B-A3B, контекст не ниже 65536; скорость и видеопамять замерены на живой службе; откат проверен.
- Компрессор замерен в обоих вариантах; замена сделана либо обоснованно отклонена.
- Видеопамять четырёх кандидатов при 64К измерена по процессу.
- Проверено запуском, какие пары помещаются; измерено, что со скоростью при одновременной работе.
Часть 2
- Роль
local-supervisorвключается автоматически для локальных профилей; для остальных путь не изменился. - Предел контекста берётся через
/props; размер задания считается через/tokenize; проверено. - Большая задача разбивается по смысловым границам и собирается; неделимая даёт отказ.
- Число попыток ограничено; бесконечного цикла нет.
- Ход виден владельцу; случай «весь лимит на рассуждения» распознаётся отдельно от таймаута.
- Рабочий объём записан в общую память с привязкой к имени сборки GGUF и используется при следующей подаче.
Часть 3
- Пара работает независимо; облачный судья сравнивает и возвращает на доработку.
- Модель судьи задаётся из интерфейса.
- Предел кругов работает; застревание распознаётся; расход вызовов судьи показан.
- Пара включается и отключается настройкой.
Общее
- Неизмеренное показано как
Н/Дс причиной; неудачи не скрываются. ruff check .чисто; релизный гейт 10/10; тестов не меньше 517.- Службы владельца работают.
- Память проекта в AI-Memory обновлена.
- Отчёт:
START_HEAD,FINAL_HEAD,origin/main,git status,X passed / Y skipped / Z failed.
Главное
Сейчас локальная модель получает задачу целиком, не успевает за отведённое время и отдаёт работу платному провайдеру. Так на каждом запросе: она не выбывает из цепочки, она просто всякий раз проигрывает.
Замена кодера окупается сама по себе — вчетверо быстрее при том же качестве и на четыре гигабайта меньше. Надзиратель делает подачу работы соразмерной модели: измеряет, а не предполагает, режет по смыслу и запоминает рабочий объём. Пара кодеров с облачным судьёй добавляет вторую независимую попытку — но её ценность в разных ошибках, а не в скорости, и каждый круг доработки стоит платного вызова.
Порядок сдачи
Передать точный FINAL_COMMIT_SHA.