hermes-hub/agents/inbox/2026-08-31-A52-local-model-supervisor.md
Hermes Team 8b67f0dadb docs(agents): вернуть в репозиторий постановки A42-A56 и отчёт A30
Тринадцать файлов существовали только на диске ПК владельца, в рабочей
копии, отставшей от 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>
2026-09-03 18:16:35 +07:00

22 KiB
Raw Permalink Blame History

Задание 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 ГиБ меньше.

  1. Контекст не ниже 65536 — порог отбора у Hermes.
  2. Условия запуска взять у нынешнего юнита: --flash-attn on, --cache-type-k/v q8_0, --reasoning off, --parallel 1.
  3. Прежний юнит сохранить; откат одной командой описать и проверить.
  4. Скорость и видеопамять замерить на живой службе, а не переносить из A45.

P0-2. Заменить компрессор

Поставить LFM2.5-2.6B вместо Qwen3-4B на порт 8082.

  1. Сначала замерить качество сжатия на том же наборе, что у нынешнего. Быстрее — не значит лучше; сожмёт хуже, замену не делать и так и написать.
  2. Рассмотреть запуск на процессоре (-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. Роль надзирателя

  1. Новая каноническая роль local-supervisor в реестре.
  2. Включается автоматически, когда выбранный профиль локальный (local, llama.cpp, ollama, vllm). Вручную назначать не нужно.
  3. Не встаёт между ролью и платным провайдером.
  4. Надзиратель — не модель, а распорядитель. Считает, режет, подаёт, наблюдает; работу делает локальная модель. Тратить на него платный вызов нельзя.

Основная его работа — счёт и разбор, а не рассуждение: токены считает /tokenize, предел даёт /props, границы кусков определяются разбором кода. Ставить сюда слабую модель значит сделать надзирателя менее надёжным.

P0-5. Замер перед подачей, а не догадка

  1. Предел контекста брать у живого сервера через /props. Умолчание model_registry = 128000 к модели владельца отношения не имеет.
  2. Размер задания считать через /tokenize — точно. Оценка по символам допустима только запасным путём и должна быть помечена как оценка.
  3. Учитывать место под ответ: в контекст входят задание, история и ожидаемый ответ. Запас обосновать.
  4. Не выдумывать пределы. Сервер не ответил — так и записать.

P0-6. Разбиение и подача

  1. Помещается — подавать целиком. Резать без нужды вредно: теряется связность.
  2. Не помещается — резать по смысловым границам: файл, функция, класс, раздел. Посреди выражения — нельзя.
  3. Подавать последовательно, передавая накопленный результат, и собирать ответ.
  4. Неделимая задача — честный отказ, а не разрез наугад.
  5. Число попыток ограничено и настраивается. Бесконечный цикл на единственной видеокарте недопустим.
  6. После исчерпания попыток — отказ с причиной, дальше обычная отказоустойчивость. Надзиратель не прячет неудачу, удерживая работу на локальной модели любой ценой.

P0-7. Наблюдение за ходом

  1. Видеть, что модель работает, а не висит: поток ответа или тайминги сервера.
  2. Различать три исхода: успел, не успел, ответил ошибкой. Сейчас всё сваливается в «таймаут».
  3. Показывать ход на «Обзоре»: какой кусок из скольких, сколько токенов подано.
  4. Отдельно ловить случай A39: весь лимит ушёл на рассуждения, ответа нет. Измерено: 1500 токенов за 111 секунд и ноль символов ответа; с enable_thinking: false — ответ за 11 секунд. Признак — пустой ответ при полном расходе лимита; лечится request_options, механизм есть после A39.

P0-8. Память: какой объём модель тянет

  1. Записывать по каждой модели: при каком размере получался ответ, при каком нет, сколько занимало, какой кусок оказался рабочим.
  2. Хранить в общей памяти (/srv/projects/AI-Memory, структура после A47). Запись подписывается: модель, когда, по какому заданию.
  3. Использовать при следующей подаче: начинать с размера, который уже работал.
  4. Привязывать к имени сборки из метаданных GGUF, а не к порту или имени профиля. Урок Tiel-Coder: в файле оказалась Ornith-1.5-35B.
  5. Показывать во вкладке «Память» надзирателя, что он усвоил.

Часть 3. Пара кодеров и облачный судья

Планировать по числам части 1.

P0-9. Схема и её цена

задание → Кодер A (локальный)  ┐
        → Кодер B (локальный)  ┘→ судья (облачная модель)
                                    ├ принято → дальше по конвейеру
                                    └ не принято → обоим на доработку
  1. Кодеры не видят работу друг друга до суда. Иначе второе решение не независимо и смысл теряется.
  2. Судья облачный, видеопамяти не занимает. Это роль developer-2 существующего конвейера; модель задаёт владелец в интерфейсе — зашивать имя модели или провайдера в код нельзя.
  3. Судья получает задание и оба решения, возвращает: какое принято либо что доработать каждому.
  4. Ревьюер остаётся на своём месте после судьи; конвейер не переделывать.
  5. Пара включается настройкой; возврат к одному кодеру возможен.

Предел итераций обязателен. Круг «пока не сделают правильно» тратит платную квоту судьи на каждом обороте.

  1. Предел кругов настраивается, умолчание обосновать. Механизм ограничения итераций в конвейере уже есть — использовать его.
  2. Показывать номер круга.
  3. Круги исчерпаны — честный отказ с последним состоянием обеих работ и мнением судьи. Частичный результат за готовый не выдавать.
  4. Считать расход: сколько вызовов судьи ушло на задачу.
  5. Круг без изменений — застревание. Оба вернули то же, что и в прошлый раз — прекратить и сказать.

P0-10. Аудит вторым проходом

  1. Числа части 1 сняты на живой службе, а не перенесены из A45.
  2. Откат к прежнему кодеру выполнен и проверен.
  3. Пара проверена запуском, а не расчётом: обе модели подняты, памяти хватило, обе отвечают.
  4. Утверждение о полосе памяти проверено: суммарная выработка двух моделей против одиночной. Результат записать, каким бы он ни был.
  5. Заведомо большая задача разбита, подана и собрана; неделимая дала честный отказ.
  6. Предел попыток и предел кругов проверены задачей, которая не выполнится никогда.
  7. Предел контекста взят у сервера, счёт токенов сверен с /tokenize независимо.
  8. Независимость кодеров: решение одного не попадает в контекст другого.
  9. Модель судьи задаётся из интерфейса, а не зашита.
  10. Для платных провайдеров путь не изменился, надзиратель туда не лезет.
  11. Службы владельца вернуть в рабочее состояние.
  12. Побочные изменения объяснить.
  13. Пропущенный пункт назвать пропущенным.

Ограничения

  • Видеокарта одна, сервер рабочий: окна для замеров согласовать, службы возвращать в строй.
  • Прежние юниты сохранять, откат описывать и проверять.
  • Учётные данные и ~/.hermes/agy_profiles/ не трогать.
  • Имена моделей и провайдеров в код не зашивать.
  • Конфигурацию Hermes не править.
  • Версию 0.1.1 не поднимать.
  • Правило честности без исключений: ни одного числа без замера; ускорение не обещать без подтверждения.

Критерии приёмки

Часть 1

  1. Кодер заменён на Qwen3-Coder-30B-A3B, контекст не ниже 65536; скорость и видеопамять замерены на живой службе; откат проверен.
  2. Компрессор замерен в обоих вариантах; замена сделана либо обоснованно отклонена.
  3. Видеопамять четырёх кандидатов при 64К измерена по процессу.
  4. Проверено запуском, какие пары помещаются; измерено, что со скоростью при одновременной работе.

Часть 2

  1. Роль local-supervisor включается автоматически для локальных профилей; для остальных путь не изменился.
  2. Предел контекста берётся через /props; размер задания считается через /tokenize; проверено.
  3. Большая задача разбивается по смысловым границам и собирается; неделимая даёт отказ.
  4. Число попыток ограничено; бесконечного цикла нет.
  5. Ход виден владельцу; случай «весь лимит на рассуждения» распознаётся отдельно от таймаута.
  6. Рабочий объём записан в общую память с привязкой к имени сборки GGUF и используется при следующей подаче.

Часть 3

  1. Пара работает независимо; облачный судья сравнивает и возвращает на доработку.
  2. Модель судьи задаётся из интерфейса.
  3. Предел кругов работает; застревание распознаётся; расход вызовов судьи показан.
  4. Пара включается и отключается настройкой.

Общее

  1. Неизмеренное показано как Н с причиной; неудачи не скрываются.
  2. ruff check . чисто; релизный гейт 10/10; тестов не меньше 517.
  3. Службы владельца работают.
  4. Память проекта в AI-Memory обновлена.
  5. Отчёт: START_HEAD, FINAL_HEAD, origin/main, git status, X passed / Y skipped / Z failed.

Главное

Сейчас локальная модель получает задачу целиком, не успевает за отведённое время и отдаёт работу платному провайдеру. Так на каждом запросе: она не выбывает из цепочки, она просто всякий раз проигрывает.

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

Порядок сдачи

Передать точный FINAL_COMMIT_SHA.