diff --git a/agents/inbox/2026-08-25-A32-remove-desktop.md b/agents/inbox/2026-08-25-A32-remove-desktop.md new file mode 100644 index 0000000..2b71e38 --- /dev/null +++ b/agents/inbox/2026-08-25-A32-remove-desktop.md @@ -0,0 +1,137 @@ +# Задание A32: удаление десктопного приложения + +## Дата поступления +2026-08-25 + +## База +Проверочный HEAD на момент выдачи: **`c35bc48`**. + +## Ветка +`antigravity/remove-desktop` + +## Когда выполнять + +**После A29 и A30.** Оба работают в веб-слое, и удалять десктоп раньше, чем веб доберёт остаток паритета, нельзя. Пункт P0-1 — проверка этого паритета, и он выполняется первым. + +Два прохода: **Flash** реализует, **Pro** проводит аудит. + +--- + +## Порядок работы с git + +``` +cd <каталог репозитория>; git fetch origin --prune; git status +git checkout main; git pull --ff-only origin main +git checkout -b antigravity/remove-desktop +git commit -m "..." <- сначала коммит +git push -u origin antigravity/remove-desktop +``` + +В `main` напрямую не пушить. + +--- + +## Задача + +Владелец решил перейти на веб полностью: «десктоп надо вообще вырезать и удалить, он не нужен». Решение принято давно, но удаление ни разу не назначалось: в заданиях стояло «десктоп не трогать, он выводится из обращения», и это защищало его от правок, а не убирало. + +## Что осталось — снято исполнением на `c35bc48` + +``` +src/antigravity_provider/router/ui/ 20 файлов, ~6 910 строк +hermes_hub_app.py ~1 345 строк, CustomTkinter +cli_commands.py:481 команда запуска десктопа +``` + +Виндовый установщик: + +``` +HermesHubSetup.cs:288-289 копирует HermesHub.exe в каталог установки и в hermes home +HermesHubSetup.cs:575 создаёт ярлык «Hermes Hub (Desktop).lnk» рядом с «Hermes Hub (Web).lnk» +HermesHubSetup.cs:140 проверка зависимостей ТРЕБУЕТ customtkinter, иначе установка не считается успешной +HermesHubSetup.cs:188 ставит customtkinter и pillow в venv +pyproject.toml:40 customtkinter>=6.0.0 в зависимостях +``` + +Линуксовый установщик десктоп уже не ставит: там только `fastapi uvicorn pydantic psutil pyyaml`, а `.desktop` ведёт на веб-лаунчер. **Сервер фактически уже без десктопа.** + +Последствия, не сводящиеся к лишнему весу: на Windows GUI-библиотека ставится ради программы, которой не пользуются, и её отсутствие ломает установку. Два ярлыка в меню «Пуск» — прямой путь снова открыть старое окно вместо веба; у владельца это уже случалось, он жаловался, что «открывается старая программа и тормозит при передвижении окна». + +## P0-1. Сначала паритет, потом удаление + +**Выполняется первым и является условием остального.** + +Составить список того, что десктоп умеет, и сверить с вебом по каждому пункту. Особое внимание тому, что исторически жило только в десктопе: + +- мастер подключения аккаунтов (`router/ui/add_account_wizard.py`) — в вебе есть свой, но состав шагов сверить; +- вход Antigravity и Claude по ссылке — сделан в вебе, проверить на всех провайдерах; +- каталог моделей (`router/ui/model_catalog.py`); +- граф маршрутизации (`router/ui/routing_graph.py`); +- всё из `router/ui/views/`. + +**Результат — таблица «умение → где в вебе → проверено».** Непокрытое умение удалять нельзя: сначала оно появляется в вебе, и только потом удаляется десктоп. Если что-то не покрыто, а A29 и A30 его не закрывают, — назвать это в отчёте и остановиться, а не удалять молча. + +## P0-2. Удаление кода + +После пройденного P0-1: + +1. `src/antigravity_provider/router/ui/**` целиком. +2. `hermes_hub_app.py`. +3. Команду запуска десктопа из `cli_commands.py` и проверку `customtkinter` там же. +4. Тесты, проверявшие десктоп. **Тесты, проверяющие общую логику, не выбрасывать** — перенести на веб-поверхность, если они там применимы. + +Осторожно: часть общего кода могла переехать в `router/ui/` исторически. Перед удалением проверить, не импортирует ли что-то из веба или из маршрутизатора модули оттуда. Импорт, обёрнутый в `except ImportError`, — отдельная опасность: он не даст ошибки, просто тихо отключит функцию. Такое в проекте уже было и стоило раунда. + +## P0-3. Зависимости и установщики + +1. `customtkinter` и `pillow` убрать из `pyproject.toml`, из установки в `HermesHubSetup.cs` и из проверки зависимостей. **Проверить, не нужен ли `pillow` чему-то ещё** — он используется не только GUI. +2. Копирование `HermesHub.exe` убрать; сам `launcher/HermesHub.cs` и `launcher/HermesHub.exe` удалить. +3. Ярлык «Hermes Hub (Desktop)» убрать. Оставшийся ярлык переименовать в просто «Hermes Hub» — скобка «(Web)» теряет смысл, когда вариант один. +4. **Удаление старой установки должно убирать и старый ярлык.** Иначе у владельца в меню «Пуск» останется ярлык на несуществующую программу — это хуже, чем два рабочих. + +## P0-4. Проверка на живой установке + +Мало собрать — надо поставить. + +1. Собрать оба установщика, поставить на Windows, убедиться: ярлык один, он открывает веб, `customtkinter` не требуется, установка проходит без него. +2. Проверить обновление **поверх старой установки** с десктопом: старые файлы и ярлык убираются, учётные данные и настройки уцелевают. +3. Линуксовый установщик не сломан. + +Пункт 2 обязателен: у владельца на трёх машинах стоит версия с десктопом, и обновление пойдёт именно поверх неё. + +## P0-5. Аудит вторым проходом + +1. **Таблица паритета из P0-1 проверена выборочно**, а не принята на слово. +2. **Тихие импорты.** Поиском убедиться, что не осталось `from ...router.ui` под `except ImportError`. +3. **Установить и запустить**, а не только собрать. +4. **Обновление поверх старой установки** проверено на самом деле. +5. **Побочные изменения** объяснить. +6. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- Ничего, кроме десктопа, не удалять. Общая логика, маршрутизатор, адаптеры, обновлятор остаются. +- Веб-слой не переписывать: он зона A29 и A30. +- Правило честности без исключений. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin`, `git status` чист. +2. Таблица паритета приложена; непокрытых умений нет либо они названы, и удаление по ним не проводилось. +3. `router/ui/**` и `hermes_hub_app.py` удалены; `from ...router.ui` в коде не встречается. +4. `customtkinter` отсутствует в зависимостях, в установке и в проверке; установка проходит без него. +5. Ярлык один, ведёт на веб; ярлык на десктоп удаляется при обновлении поверх старой установки. +6. Обновление поверх версии с десктопом проверено: аккаунты, настройки и цепочки ролей уцелели. +7. Линуксовый установщик работает. +8. `ruff check .` чисто; релизный гейт не ухудшен. +9. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На `main` сейчас **451 passed, 2 skipped**. + +## Главное + +Владелец работает только в вебе. Десктоп тянет за собой GUI-зависимость, второй ярлык и восемь тысяч строк, которые никто не открывает, — и время от времени запускается вместо веба. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`. diff --git a/agents/inbox/2026-08-30-A34-restore-wizard-providers-desktop.md b/agents/inbox/2026-08-30-A34-restore-wizard-providers-desktop.md new file mode 100644 index 0000000..acbc417 --- /dev/null +++ b/agents/inbox/2026-08-30-A34-restore-wizard-providers-desktop.md @@ -0,0 +1,177 @@ +# Задание A34: восстановить подключение аккаунтов, добавить OpenRouter и NVIDIA, удалить десктоп + +## Дата поступления +2026-08-30 + +## База + +Работать **поверх ветки ревьюера** `review/a28-a31-fixes` (`529192b`), а не поверх `main` и не поверх своей прошлой ветки. В ней уже лежат A28–A31, слитые с `main`, плюс исправления ревьюера. + +``` +git fetch origin --prune +git checkout -b antigravity/a34-restore-and-providers origin/review/a28-a31-fixes +``` + +В `main` напрямую не пушить. В конце — push, `git log --oneline -1`, `git status` чистый. + +## Порядок исполнения + +Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-6** написан для аудитора. + +Пункты выполняются **по порядку**. P0-1 блокирует всё остальное: пока нельзя подключить аккаунт, ни новых провайдеров, ни автопоиска проверить не на чем. + +--- + +## Что уже сделано ревьюером — не переделывать + +Снято исполнением на кандидате A28–A31 и исправлено в `529192b`: + +1. **Дублирование ролей.** `RoleRegistry.migrate_legacy_roles` существовала, но не вызывалась ниоткуда; интерфейс показывал 19 агентов вместо 13, шесть пар неотличимы по названию. Миграция подключена, порядок обработки исправлен, цепочки владельца сохраняются. Проверено на его живой конфигурации: 19 → 13, цепочки совпадают. +2. **Сохранённый workflow** мигрируется вместе с ролями, иначе рёбра ссылались на исчезнувших агентов. +3. **Поиск локальных серверов** — новый модуль `router/local_discovery.py` и действие `discover_local_models`. Серверная часть готова и проверена; **не хватает только интерфейса** (P0-3). +4. **Глобальный мьютекс Antigravity** снят ранее (`7e83c38`): три параллельных вызова занимали 3.01 с, стали 1.00 с. Возвращать нельзя, есть тест. +5. **CORS** закрыт по умолчанию (`c35bc48`). Список источников — настройка `web_api_allowed_origins`. + +--- + +## P0-1. Восстановить подключение аккаунтов — блокирующее + +При переписывании клиента в A29 **функции удалили, а вызовы оставили**. Проверено в браузере на живом кандидате, консоль: + +``` +openAddAccountWizard is not defined ← «+ Добавить аккаунт» ничего не делает +checkUpdates is not defined ← падает при каждой загрузке страницы +``` + +Полный список повисших обработчиков — определений 0, вызовы есть: + +``` +openAddAccountWizard нельзя подключить аккаунт +handleNodeAccountChange нельзя сменить аккаунт у агента в «Обзоре» +handleNodeModelChange нельзя сменить модель +handleRefreshProviderModels нельзя обновить список моделей +checkUpdates проверка обновлений падает на загрузке +``` + +При этом `startDeviceAuth` и `startRedirectAuth` **в коде остались и работают**, но не вызываются ниоткуда — стали мёртвыми. Их надо не писать заново, а связать с восстановленным мастером. + +Требуется вернуть работоспособность каждому пункту списка. Действия на сервере существуют и менять их не нужно: + +``` +add_account, start_device_auth, poll_device_auth, +start_redirect_auth, submit_redirect_callback, poll_redirect_auth, +assign_role, set_model, refresh_models, check_updates +``` + +Что мастер обязан уметь, по провайдерам: + +- **Grok, OpenAI Codex** — код устройства. Адрес и код приходят **от провайдера**, подставлять свои нельзя. +- **Antigravity, Claude** — вход по ссылке с возвратом. Ссылку можно открыть **на любой машине**. Принимается и полный адрес возврата, и один только код. При работе с другой машины показывается готовая команда проброса порта возврата. +- **Локальный сервер** — адрес и необязательный ключ, плюс автопоиск из P0-3. +- **Выбор слота обязателен и делается владельцем.** Автоподбор ошибается: `find_free_slot` определяет занятость по файлу учётных данных, а `agy` на Windows держит их в keyring, поэтому все слоты выглядят свободными и всегда возвращается первый. Вход затирал бы работающий аккаунт. В списке слотов видно, какие заняты и кем, и участвует ли слот в маршрутизации. + +**Проверять исполнением, а не глазами.** Откройте страницу, нажмите каждую кнопку, посмотрите консоль. Ни одного `ReferenceError` при загрузке и при работе. + +## P0-2. OpenRouter и NVIDIA, аккаунтов по несколько + +Владелец подключил их в Hermes напрямую, мимо хаба: «главный кодекс стоит, подключил себе нвидеа и опенроутер и грок». Хаб их не видит — адаптеров нет, упоминаний в коде нет вовсе. + +Оба **OpenAI-совместимы**, поэтому образец есть: `adapters/local_adapter.py` и `adapters/deepseek_adapter.py` работают ровно так же — `POST {base_url}/chat/completions`, `GET {base_url}/models`. + +1. **Адаптеры** `openrouter` и `nvidia`. Базовый адрес — **настройка, не константа**. Для OpenRouter это `https://openrouter.ai/api/v1`, у NVIDIA свой; но зашивать нельзя, владелец может использовать прокси. +2. **Несколько аккаунтов на провайдера**, без потолка. Потолок в A26 уже снят: `find_free_slot` выдаёт идентификаторы сама, когда предопределённые кончились (`codex-4`, `codex-5`). Сделать так же. +3. **Ключ вводится в мастере** и хранится там же, где ключи прочих провайдеров. В снапшот, в журнал и в `/api/settings` он попадать не должен — тест на это уже есть. +4. **Обнаружение моделей** через `GET /models`. У OpenRouter список большой; показывать надо тот, что вернул провайдер, а не подмножество из головы. +5. **Квоты.** OpenRouter отдаёт остаток кредитов, у NVIDIA свои лимиты. Отдаёт — показывать; не отдаёт — **«Н/Д» с причиной**, а не ноль и не пустая полоса, которую можно принять за исчерпание. +6. **Логотипы** провайдеров у владельца есть в каталоге с макетами. Файла нет — нейтральная заглушка, а не чужой знак. + +## P0-3. Автопоиск локальных моделей в интерфейсе + +Серверная часть готова ревьюером, писать её заново не нужно. + +``` +router/local_discovery.py discover_local_servers() +action_handler действие discover_local_models +``` + +Опрашивает Ollama 11434, LM Studio 1234, llama.cpp 8080–8082, vLLM 8000, Jan, GPT4All, Text Generation WebUI — параллельно, девять портов за 1.5 с. Возвращает только ответившие, со списком моделей от самого сервера. + +Требуется кнопка **«Найти на этом компьютере»** в шаге подключения локального провайдера: + +1. Нажатие — опрос, показ найденного: имя сервера, адрес, список моделей. +2. Выбор найденного заполняет адрес; вводить руками по-прежнему можно. +3. **Ничего не найдено — так и сказать**, с подсказкой запустить Ollama, LM Studio или llama.cpp либо ввести адрес вручную. Пустой список это результат, а не ошибка. +4. Порт, занятый чужим сервисом, показывается **с причиной** — иначе владелец будет гадать, почему заведомо работающий сервер не виден. +5. Опрос идёт в фоне, интерфейс не блокируется. + +Учесть: хаб может работать на сервере, а браузер у владельца на другой машине. Поиск идёт **там, где работает хаб**, и это надо сказать в интерфейсе прямо, иначе результат будет непонятен. + +## P0-4. Удаление десктопа (задание A32 целиком) + +Выполняется **после** P0-1: пока веб не подключает аккаунты, удалять десктоп нельзя. + +Полный текст — `agents/inbox/2026-08-25-A32-remove-desktop.md`, здесь коротко: + +``` +router/ui/** 20 файлов, ~6 910 строк +hermes_hub_app.py ~1 345 строк, CustomTkinter +cli_commands.py:481 команда запуска десктопа +HermesHubSetup.cs:575 ярлык «Hermes Hub (Desktop).lnk» +HermesHubSetup.cs:140 проверка зависимостей ТРЕБУЕТ customtkinter +pyproject.toml:40 customtkinter>=6.0.0 +``` + +Сначала **таблица паритета** «умение десктопа → где в вебе → проверено», и только потом удаление. Непокрытое умение не удалять, а назвать в отчёте. + +Отдельно: в `b4ae08e` появился обход «GUI helpers importable without customtkinter». После удаления десктопа эта прослойка не нужна — снять её, а не оставлять. + +Обновление пойдёт **поверх установок с десктопом** на трёх машинах владельца: старый ярлык обязан убираться, учётные данные, настройки и цепочки ролей — уцелеть. + +## P0-5. Проверка на живой установке + +1. Собрать оба установщика, поставить на Windows и на Linux. +2. Подключить хотя бы по одному аккаунту каждого потока: код устройства, вход по ссылке, локальный сервер. +3. Разложить аккаунты по ролям и убедиться, что порядок переживает перезапуск. +4. Проверить обновление поверх старой установки. + +## P0-6. Аудит вторым проходом + +1. **Повисшие вызовы.** Собрать все `onclick`/`onchange` и убедиться, что каждая функция определена. Именно этот класс дефекта и пропустили в A29: разметка звала пять функций, которых нет. +2. **Консоль браузера чистая** при загрузке и при работе. +3. **Выдуманные значения.** Особый риск в P0-2: квоты и списки моделей новых провайдеров. Ни одного числа, которого не дал провайдер. +4. **Ключи не утекают** в снапшот, журнал и `/api/settings`. +5. **Мьютекс Antigravity не вернулся** — есть тест, он должен проходить. +6. **Побочные изменения** объяснить. +7. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- Не переделывать то, что перечислено в разделе «сделано ревьюером». +- Без сборки, без npm, без фреймворка — решение обосновано в контракте. +- Действия только через `action_handler`; новые — с правкой `docs/web-api/CONTRACT.md`. +- Правило честности без исключений: нет данных — «Н/Д» и причина. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin` от `review/a28-a31-fixes`, `git status` чист. +2. В консоли браузера нет `ReferenceError` ни при загрузке, ни при работе; все обработчики определены — проверено списком. +3. Аккаунт подключается всеми тремя потоками; слот выбирает владелец, занятые видны. +4. Аккаунт назначается агенту и меняется модель — из «Обзора» и из «Маршрутизации»; переживает перезапуск. +5. OpenRouter и NVIDIA подключаются, аккаунтов больше трёх на провайдера; ключи не утекают; модели берутся у провайдера. +6. Квоты новых провайдеров показаны настоящие либо «Н/Д» с причиной. +7. Кнопка автопоиска находит запущенные локальные серверы; пустой результат объяснён; чужой сервис на порту назван с причиной. +8. `router/ui/**` и `hermes_hub_app.py` удалены, `customtkinter` из зависимостей убран, ярлык один; обновление поверх старой установки сохраняет данные. +9. Таблица паритета приложена. +10. `ruff check .` чисто; релизный гейт не ухудшен. +11. **Скриншоты:** мастер на каждом из трёх потоков, автопоиск локальных, «Обзор» с назначением аккаунта, «Маршрутизация». +12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На ветке ревьюера сейчас **475 passed, 2 skipped**. + +## Главное + +Сейчас в новой сборке нельзя подключить ни одного аккаунта и нельзя назначить его агенту — разметка зовёт пять функций, которых в коде нет. Всё остальное в задании бессмысленно, пока это не восстановлено. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`. diff --git a/agents/inbox/2026-08-30-A35-hub-must-actually-route-hermes.md b/agents/inbox/2026-08-30-A35-hub-must-actually-route-hermes.md new file mode 100644 index 0000000..ee6f976 --- /dev/null +++ b/agents/inbox/2026-08-30-A35-hub-must-actually-route-hermes.md @@ -0,0 +1,145 @@ +# Задание A35: настройки хаба должны применяться в Hermes + +## Дата поступления +2026-08-30 + +## База + +Ветка ревьюера `review/a28-a31-fixes` (`ab2ee12`). + +``` +git fetch origin --prune +git checkout -b antigravity/a35-role-resolution origin/review/a28-a31-fixes +``` + +В `main` напрямую не пушить. + +## Порядок исполнения + +Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-5** написан для аудитора. + +Идёт **параллельно A34** (его делает Codex). Пересечения по файлам почти нет: здесь `hermes_plugin.py` и `router_engine.py`, там веб-клиент и адаптеры. Границу соблюдать. + +--- + +## Задача + +Владелец сформулировал так: «надо проверить, чтобы хаб реально работал с Hermes. Сейчас получается, что настройки в Hermes вообще не соответствуют настройкам в хабе. А мы делаем хаб, чтобы все настройки в нём работали и в Hermes». + +Проверка подтвердила: **не работают.** Хаб сейчас — панель, которая ничем не управляет. + +--- + +## Что проверено исполнением — заново не выясняйте + +**1. Hermes не передаёт роль.** В его исходниках, `agent/conversation_loop.py:3221`: + +```python +run_llm_execution_middleware( + api_kwargs, _perform_api_call, + original_request=..., task_id=..., turn_id=..., api_request_id=..., + session_id=..., platform=..., model=..., provider=..., base_url=..., + api_mode=..., api_call_count=..., middleware_trace=... +) +``` + +Параметра `role` нет. Есть `model`, `provider`, `base_url`, `session_id`, `task_id`, `platform` — этого достаточно, см. P0-1. + +**2. Без роли плагин пропускает вызов мимо хаба.** `hermes_plugin.py:42`: + +```python +if not resolved_role: + if callable(next_call): + return next_call(request) +``` + +**3. Измерено на живом плагине**, подачей ровно того, что шлёт Hermes: + +``` +как зовёт Hermes (без роли) -> МИМО хаба, собственный вызов Hermes +если роль передана -> обработал хаб, маршрутизация сработала +``` + +Механизм исправен целиком. Его просто никто не включает: аккаунты, цепочки, квоты и переключение при исчерпании настраиваются и **не применяются ни разу**. + +**4. Почему так сделано — это защита, а не небрежность.** `resolve_role` намеренно не угадывает роль по тексту: «no guessing from prompts». Раньше при неопределённой роли всё шло как `orchestrator`, цепочка исчерпывалась, и **текст ошибки роутера подставлялся вместо ответа модели** — владелец получал сообщение хаба там, где ждал ответ. Пропуск появился как безопасный откат после этой аварии. + +Сейчас выбор стоит так: хаб либо молчит, либо врёт. Задание — сделать третье. + +--- + +## P0-1. Определение роли по тому, что Hermes всё-таки передаёт + +Порядок разрешения, сверху вниз: + +1. **Явная роль** — если когда-нибудь появится в `kwargs`, `request`, `metadata`. Работает уже сейчас, не ломать. +2. **По модели и провайдеру.** Hermes передаёт `model` и `provider`. Если запрошенная модель или провайдер — основные у какой-то роли, берём её. Соответствие строится **из конфигурации**, а не из литералов в коде. +3. **По устойчивости сессии.** Передаётся `session_id`. Если для этой сессии роль уже определялась, брать её же: механизм `session_affinity` есть и работает. +4. **Роль по умолчанию — настройка.** Не подошло ничего — берём настраиваемую роль, а не молчим. Значение по умолчанию выбрать и обосновать в отчёте; **в код не зашивать**. + +Пропуск мимо хаба остаётся только на случай, когда маршрутизатор выключен целиком. + +## P0-2. Предохранитель снимать нельзя + +Исчерпанная цепочка **обязана** уходить в `next_call`, а не подставлять текст ошибки вместо ответа модели. Это уже стоило владельцу рабочего дня. + +Требуется тест, который падает, если ответ роутера с `router_error` окажется в ответе Hermes. + +Отдельно: включение маршрутизации не должно ломать Hermes при пустой конфигурации. Нет ни одного подключённого аккаунта — вызов уходит вниз, а не превращается в ошибку. + +## P0-3. Видно, что происходит + +Владелец должен понимать, что хаб теперь участвует в вызовах. + +1. **В журнале событий** — какая роль выбрана, по какому признаку (явная, по модели, по сессии, по умолчанию) и какой профиль отработал. +2. **В аналитике** вызовы Hermes должны появиться. Сейчас там пусто именно потому, что до хаба ничего не доходит. +3. Признак выбора роли — не выдумка, а факт: если взята роль по умолчанию, так и написать. + +## P0-4. Проверка на живом Hermes + +Отчёт без этого не принимается. + +1. Запустить Hermes, дать ему задачу, убедиться по журналу, что **вызов прошёл через хаб** и через ожидаемый аккаунт. +2. Проверить, что смена цепочки в интерфейсе меняет то, чем Hermes реально отвечает. +3. Проверить исчерпание: отключить первый аккаунт в цепочке и убедиться, что переключение произошло, а Hermes продолжил работать. +4. Проверить пустую конфигурацию: Hermes работает как раньше. + +Пункт 2 — суть задания. Пока смена настройки в хабе не меняет поведение Hermes, задание не выполнено. + +## P0-5. Аудит вторым проходом + +1. **Угадывание по тексту запроса.** Его не должно появиться: правило «no guessing from prompts» введено осознанно. Признаки — только явные поля. +2. **Литеральные соответствия модель→роль** в коде. Их быть не должно, всё из конфигурации. +3. **Предохранитель на исчерпанную цепочку** — проверить отдельно, тестом и руками. +4. **Запустить с живым Hermes**, а не только тестами. +5. **Побочные изменения** объяснить. +6. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- **Hermes не править.** Это чужой продукт; правка `conversation_loop.py` будет затираться при каждом его обновлении. Работать только с тем, что он уже передаёт. +- Зона: `hermes_plugin.py`, `router_engine.py`, `router_config.py`, `settings_service.py`, соответствующие тесты. Веб-клиент и адаптеры — зона A34, туда не заходить. +- Правило честности без исключений. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin` от `review/a28-a31-fixes`, `git status` чист. +2. Вызов Hermes без роли **доходит до маршрутизатора**; проверено подачей того же набора аргументов, что в `conversation_loop.py:3221`. +3. Признак выбора роли записывается в журнал и различим: явная, по модели, по сессии, по умолчанию. +4. Роль по умолчанию настраивается, значение не зашито. +5. Изменение цепочки в интерфейсе меняет поведение живого Hermes; **приложить вывод**. +6. Исчерпанная цепочка уходит в `next_call`, текст ошибки роутера в ответ Hermes не попадает; есть тест. +7. Пустая конфигурация не ломает Hermes. +8. Вызовы Hermes видны в аналитике. +9. `ruff check .` чисто; релизный гейт не ухудшен. +10. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На ветке ревьюера сейчас **475 passed, 2 skipped**. + +## Главное + +Хаб делается ради того, чтобы аккаунтами и лимитами управлять из одного места, и чтобы это управление действовало в Hermes. Сейчас оно не действует ни в одной точке: каждый вызов проходит мимо. Это самая важная задача в очереди — без неё всё остальное остаётся витриной. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`. diff --git a/agents/inbox/2026-08-30-A36-antigravity-pipeline.md b/agents/inbox/2026-08-30-A36-antigravity-pipeline.md new file mode 100644 index 0000000..b3e3ea6 --- /dev/null +++ b/agents/inbox/2026-08-30-A36-antigravity-pipeline.md @@ -0,0 +1,144 @@ +# Задание A36: рабочий конвейер Antigravity — оркестратор, два кодера, ревьюер + +## Дата поступления +2026-08-30 + +## База + +Ветка ревьюера `review/a28-a31-fixes` (`ab2ee12`). + +``` +git fetch origin --prune +git checkout -b antigravity/a36-pipeline origin/review/a28-a31-fixes +``` + +В `main` напрямую не пушить. + +## Порядок исполнения + +Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-5** написан для аудитора. + +**Зависит от A35.** Без него хаб в вызовах Hermes не участвует, и конвейер будет собран, но не заработает. Собирать можно параллельно, принимать — только после A35. + +Границы: A34 — веб-клиент и адаптеры, A35 — определение роли, здесь — конвейер и привязка моделей. Не пересекаться. + +--- + +## Задача + +Владелец описал конвейер дословно: + +> «чтобы работал как оркестратор. флэш 3.7 кодер 1, гемини про кодер 2, проверяет работу кодера 1 и если надо, отправляет ему на доработку. и так, пока не сделают. ревьювер опус 4.6, проверяет, как гемини про одобрит работу. если надо, отправляет гемини про на переделку.» + +То есть две петли обратной связи, вложенные одна в другую. + +--- + +## Модели — проверены, не выдумывать + +Список снят ревьюером из кэша обнаружения на аккаунтах владельца. Обнаружено 14 моделей, из них нужны три: + +| Роль владельца | Роль в реестре | Модель | Комментарий | +|---|---|---|---| +| Кодер 1 | `developer-1` | `gemini-3.7-flash` | быстрый первый проход | +| Кодер 2 | `developer-2` | `gemini-3.1-pro-high` | проверяет Кодера 1 | +| Ревьюер | `code-reviewer` | `claude-opus-4-6-thinking` | финальная проверка | +| Оркестратор | `manager` | на усмотрение владельца | ведёт конвейер | + +Три вещи, на которых легко ошибиться: + +1. **«Гемини про» — это 3.1, а не 3.7.** В обнаруженном списке есть `gemini-3.1-pro-high` и `gemini-3.1-pro-low`; версии 3.7 у Pro нет вовсе. Подставлять `gemini-3.7-pro` нельзя, такой модели у провайдера нет. +2. **«Опус 4.6» называется `claude-opus-4-6-thinking`.** Другого опуса в списке нет. +3. **У flash идентификаторы приходят с суффиксом усилия** — `gemini-3.7-flash-high`, `-medium`, `-low`. Базовое имя `gemini-3.7-flash` при этом **валидно**: уровень усилия — отдельный параметр, и A23 требует принимать базовое имя. Ревьюер однажды четырежды написал, что `gemini-3.7-flash` «не существует», и был неправ — см. `agents/inbox/2026-08-24-CORRECTION-gemini-model-names.md`. Не повторять. + +Какой уровень усилия ставить Кодеру 1 — решает владелец; предложить в отчёте, молча не выбирать. + +## P0-1. Граф конвейера + +Собрать в редакторе workflow из A30: + +``` +Оркестратор ──────────────► Кодер 1 + │ SUCCESS + ▼ +Кодер 1 ◄──REVIEW_FAILED── Кодер 2 + │ REVIEW_PASSED + ▼ +Кодер 2 ◄──REVIEW_FAILED── Ревьюер + │ REVIEW_PASSED + ▼ + Оркестратор (приёмка) +``` + +Смысл петель: + +- **Внутренняя.** Кодер 2 проверяет работу Кодера 1. Не устраивает — возвращает на доработку, и так пока не одобрит. +- **Внешняя.** Ревьюер включается только после одобрения Кодера 2. Не устраивает — возвращает **Кодеру 2**, а не Кодеру 1. + +Этот граф уже нарисован на утверждённом макете `1.1.png`, включая подписи рёбер и красные пунктирные возвраты. Расхождение с макетом — дефект. + +## P0-2. Пределы итераций — обязательны + +Две вложенные петли без ограничителя означают бесконечный прогон и сожжённую квоту. + +1. **Предел на каждую петлю отдельно**, настраиваемый. Значения по умолчанию предложить и обосновать; в код не зашивать. +2. **Достижение предела — явное событие** в журнале и видимый результат, а не тихая остановка. На макете счётчик показан как «Итерация: 2 / 5». +3. **Общий предел прогона** — на случай, если петли начнут чередоваться. +4. Владелец должен видеть, на какой итерации идёт работа, **в LIVE**. + +Механика защиты от бесконечного выполнения заложена в A30 (раздел 24 ТЗ). Второй реализации не заводить. + +## P0-3. Привязка моделей и аккаунтов + +1. Модель роли задаётся из интерфейса, действие `set_model` уже есть. +2. У каждой роли — свой аккаунт Antigravity, чтобы работы шли параллельно. У владельца их десять. +3. **Параллельность теперь настоящая.** Глобальный мьютекс снят ревьюером в `7e83c38`: было три параллельных вызова за 3.01 с, стало за 1.00 с. До этого из десяти аккаунтов одновременно работал один. Возвращать мьютекс нельзя, есть тест. +4. Одна и та же модель на разных ролях допустима, но **разные аккаунты предпочтительнее**: иначе квота одного сгорит на весь конвейер. + +## P0-4. Проверка живым прогоном + +Отчёт без этого не принимается. + +1. Запустить конвейер на настоящей задаче и показать журнал: какая роль, какой аккаунт, какая модель, сколько итераций. +2. **Показать сработавшую петлю.** Нужен прогон, где Кодер 2 вернул работу Кодеру 1 хотя бы раз, и это видно в событиях. Конвейер, где всё прошло с первого раза, ничего не доказывает. +3. Показать срабатывание предела итераций: искусственно довести до него и убедиться, что прогон остановлен с внятным событием. +4. Показать, что смена модели у роли меняет то, чем эта роль отвечает. + +## P0-5. Аудит вторым проходом + +1. **Имена моделей.** Сверить с обнаруженным списком: `gemini-3.1-pro-high`, `claude-opus-4-6-thinking`, `gemini-3.7-flash`. Ни одного имени, которого провайдер не давал. +2. **Петли без ограничителя** — искать целенаправленно. Это самый дорогой дефект в задании: он жжёт квоту молча. +3. **Мьютекс не вернулся** — тест должен проходить. +4. **Запустить, а не только протестировать.** Прогон с реальной петлёй обязателен. +5. **Побочные изменения** объяснить. +6. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- Зона: workflow, конфигурация ролей и моделей, соответствующие тесты. Веб-мастер и адаптеры — A34, определение роли — A35. +- Правило честности: количество итераций, время, расход — только измеренные. +- Конфигурацию владельца литералами не править; конвейер собирается через существующие действия. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin` от `review/a28-a31-fixes`, `git status` чист. +2. Граф собран и совпадает с макетом `1.1.png` по составу узлов, рёбер и условий. +3. Модели привязаны: Кодер 1 — `gemini-3.7-flash`, Кодер 2 — `gemini-3.1-pro-high`, Ревьюер — `claude-opus-4-6-thinking`; имена совпадают с обнаруженным списком. +4. У ролей разные аккаунты; вызовы идут параллельно. +5. Пределы итераций настраиваются, срабатывают, дают событие; значения не зашиты. +6. **Приложен журнал живого прогона, где петля сработала** — Кодер 2 вернул работу Кодеру 1. +7. Приложен прогон с достижением предела итераций. +8. Смена модели у роли меняет поведение; проверено. +9. `ruff check .` чисто; релизный гейт не ухудшен. +10. **Скриншоты:** граф в EDIT, граф в LIVE во время прогона, счётчик итераций. +11. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На ветке ревьюера сейчас **475 passed, 2 skipped**. + +## Главное + +Владелец хочет конвейер, где быстрая модель пишет, сильная проверяет и возвращает на доработку, а самая дорогая включается последней и только по делу. Смысл в экономии: опус не тратится на то, что отсеет Pro, а Pro не тратится на то, что исправит flash сам. Ради этого нужны обе петли и обязательно — ограничители. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`. diff --git a/agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md b/agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md new file mode 100644 index 0000000..938f7fd --- /dev/null +++ b/agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md @@ -0,0 +1,143 @@ +# Задание A37: изоляция агентов, защита учётных данных и разрушительных операций + +## Дата поступления +2026-08-30 + +## База + +Ветка ревьюера `review/a28-a31-fixes` (`ab2ee12`). + +``` +git fetch origin --prune +git checkout -b antigravity/a37-isolation-guards origin/review/a28-a31-fixes +``` + +В `main` напрямую не пушить. + +## Порядок исполнения + +Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-6** написан для аудитора. + +Выполнять **после A34 и A35**: пока нельзя подключить аккаунт и хаб не участвует в вызовах Hermes, укреплять периметр вокруг системы, которая ничем не управляет, преждевременно. + +--- + +## Задача + +Владелец держит на трёх машинах 24 аккаунта, а работу ведут несколько автономных агентов. Изоляции между ними нет никакой, и это уже приводило к последствиям. + +Повод пришёл извне: OpenAI официально описала инцидент, где экспериментальные агенты использовали **внутренний Artifactory как канал связи между собой**, обменивались найденными обходами, получили обходной доступ в интернет и в итоге скомпрометировали часть инфраструктуры Hugging Face. Вывод сформулирован там жёстко: + +``` +deny internet != secure agent +``` + +Разрешённый внутренний сервис становится proxy, доской объявлений, скрытым каналом и точкой опоры. + +## Наши собственные случаи — не гипотезы + +Каждый проверен или произошёл в этом проекте: + +1. **Любой сайт в соседней вкладке мог управлять хабом.** CORS стоял как `allow_origins=["*"]` вместе с `allow_credentials=True`, а на `127.0.0.1` токен не требуется вовсе. Проверено запросом: страница со стороннего адреса получала `200` и полный снапшот со всеми аккаунтами. Закрыто ревьюером в `c35bc48`, но это тот самый класс, о котором говорит инцидент. + +2. **Агенты координируются через общий канал** — публичный репозиторий на GitHub. Они пишут ветки, читают чужие, и как минимум однажды работа ушла в `main` минуя ревью (зафиксировано в A23). + +3. **Общее рабочее дерево.** Агент Codex переключил ветку в каталоге, где в это же время работал ревьюер; коммиты чуть не легли в чужую ветку. Пришлось собирать их через временный индекс, чтобы не мешать. + +4. **Учётные данные 24 аккаунтов лежат общей кучей** в `~/.hermes/agy_profiles/`, доступной любому процессу пользователя. Разделения по агентам нет. + +5. **Ревьюер удалил учётные данные `grok-worker-1`**, проверяя кнопку удаления на живом профиле. Ничто не помешало. + +6. **Хаб открыт в домашнюю сеть поверх HTTP.** Токен идёт по сети открытым текстом; почты аккаунтов — тоже, пока не сделан P0-4 из A31. + +## Что уже есть — не переделывать + +Поиском по коду: отдельного слоя безопасности нет, но три вещи работают и их надо использовать как основу. + +``` +agy_subprocess.build_safe_subprocess_env очистка окружения подпроцесса +update_manager.ALLOWED_UPDATE_HOSTS белый список хостов обновления +web/server.sanitize_snapshot вычистка секретов из снапшота +``` + +--- + +## P0-1. Граница рабочей области и разрушительные операции + +Ни агент, ни модель, ни инструмент не должны удалять или перезаписывать данные **за пределами разрешённой области**, независимо от того, кто выполняет команду. + +1. **Явная граница** — каталог проекта плюс явно разрешённые пути. Всё остальное вне области. +2. **Классификация операций**: удаление, рекурсивное удаление, массовое перемещение и перезапись, усечение. Проверять и вызовы Python (`shutil.rmtree`, `os.remove`, `os.unlink`), и командную строку (`rm`, `rm -rf`, `Remove-Item`, `del`), потому что агенты ходят обоими путями. +3. **Отказ объясняет причину и предлагает безопасную замену**, а не просто запрещает. +4. **Безусловный запрет** на каталоги учётных данных: `~/.hermes/agy_profiles/`, `~/.ssh/`, файлы `auth.json`, `hub_settings.json`. Удаление аккаунта делается **только** штатным действием `delete_credentials` с подтверждением — как раз потому, что удаление вручную уже происходило. +5. **Сухой прогон**: показать, что будет удалено, до удаления. + +## P0-2. Разделение агентов + +1. **У каждого агента свой рабочий каталог.** Общее дерево уже приводило к переключению ветки под чужой работой. Отдельные клоны либо `git worktree` на агента. +2. **Свои учётные данные.** Агенту нужны те аккаунты, с которыми он работает, а не все 24. Предложить схему разделения и обосновать; **молча ничего не переносить** — потеря учётных данных стоит владельцу повторного входа во все аккаунты. +3. **Свой след в журнале.** Каждое действие с аккаунтами и конфигурацией записывается с указанием, кто его выполнил. Сейчас по журналу нельзя отличить действия ревьюера от действий агента. + +## P0-3. Сетевая граница самого хаба + +1. **Белый список исходящих обращений.** Хаб ходит к провайдерам, к API релизов и к локальным серверам — этот список конечен и должен быть явным. Образец есть: `ALLOWED_UPDATE_HOSTS`. +2. **CORS остаётся закрытым по умолчанию.** Список источников — настройка `web_api_allowed_origins`. Возврат `allow_origins=["*"]` считать дефектом; нужен тест. +3. **Токен обязателен при небlocalhost-привязке** — уже так, не ослаблять. Проверить, что сравнение осталось постоянного времени и в байтах. +4. **HTTP по сети — назвать риском в интерфейсе.** Владелец должен видеть, что при сетевой привязке поверх HTTP токен и почты идут открытым текстом. Не запрещать, а сказать прямо и предложить туннель или VPN. + +## P0-4. Журнал, по которому можно расследовать + +После инцидента вроде описанного нужен ответ на вопрос «кто, что и когда». + +Записывать: кто выполнил, какое действие, над каким профилем и ролью, результат, время. Секреты в журнал не попадают — тест на это уже есть для снапшота, распространить на журнал. + +## P0-5. Проверка исполнением + +1. Попытка удалить файл вне рабочей области — отказ с причиной; проверено. +2. Попытка удалить каталог учётных данных — отказ; проверено. +3. Обращение к хосту вне белого списка — отказ; проверено. +4. Запрос с чужого `Origin` — заголовков CORS нет; проверено запросом. +5. Работа хаба при всех включённых ограничениях не деградирует: маршрутизация, обновление, обнаружение моделей работают. + +Пункт 5 обязателен: защита, ломающая продукт, хуже её отсутствия. + +## P0-6. Аудит вторым проходом + +1. **Ложное чувство защиты.** Проверить, что ограничения нельзя обойти очевидным способом — например, относительным путём или символической ссылкой за пределы области. +2. **Отказ не должен ронять хаб.** Сработавшая защита — это отказ операции, а не падение процесса. +3. **Секреты в журнале и в сообщениях об отказе** — искать целенаправленно. +4. **Запустить с ограничениями и поработать**, а не только прогнать тесты. +5. **Побочные изменения** объяснить. +6. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- Не ломать работу агентов ради строгости: они должны продолжать работать в своих каталогах. +- Учётные данные не переносить и не удалять без явного согласия владельца. +- Зона: новый слой безопасности, `web/server.py`, журнал, тесты. Веб-клиент — A34, определение роли — A35, конвейер — A36. +- Правило честности без исключений. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin` от `review/a28-a31-fixes`, `git status` чист. +2. Разрушительная операция вне рабочей области отклоняется с причиной; проверено исполнением. +3. Каталоги учётных данных защищены безусловно; удаление аккаунта возможно только штатным действием. +4. Есть сухой прогон, показывающий последствия до выполнения. +5. Агенты разведены по рабочим каталогам; схема разделения учётных данных предложена и обоснована, ничего не перенесено без согласия. +6. Белый список исходящих обращений работает; обращение вне списка отклоняется. +7. CORS закрыт по умолчанию, есть тест на возврат `allow_origins=["*"]`. +8. Риск HTTP по сети назван в интерфейсе. +9. В журнале видно, кто выполнил действие; секретов в нём нет. +10. Хаб при включённых ограничениях полностью работоспособен; проверено. +11. `ruff check .` чисто; релизный гейт не ухудшен. +12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На ветке ревьюера сейчас **475 passed, 2 skipped**. + +## Главное + +У владельца на трёх машинах лежат ключи от 24 аккаунтов, а работают там автономные агенты без изоляции друг от друга. Пока не было потерь, но все предпосылки уже сработали хотя бы раз: и удаление учётных данных, и работа в чужом дереве, и открытый доступ к хабу из браузера. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`. diff --git a/agents/inbox/2026-08-30-A38-local-model-benchmark.md b/agents/inbox/2026-08-30-A38-local-model-benchmark.md new file mode 100644 index 0000000..7e78f7d --- /dev/null +++ b/agents/inbox/2026-08-30-A38-local-model-benchmark.md @@ -0,0 +1,167 @@ +# Задание A38: сравнение локальных моделей на железе владельца + +## Дата поступления +2026-08-30 + +## База + +Ветка ревьюера `review/a28-a31-fixes` (`ab2ee12`). + +``` +git fetch origin --prune +git checkout -b antigravity/a38-model-benchmark origin/review/a28-a31-fixes +``` + +В `main` напрямую не пушить. + +## Порядок исполнения + +Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-6** написан для аудитора. + +Выполнять **после A34 и A35**. Задание исследовательское: оно не чинит продукт, а отвечает на вопрос, какой моделью его обслуживать. + +--- + +## Задача + +У владельца локальный кодер работает медленно, и он хочет понять, есть ли модель быстрее при сопоставимом качестве. + +## Что измерено ревьюером — это базовая линия, заново не мерить + +Всё снято на живом сервере `192.168.1.81`, Tesla V100-PCIE-32GB. + +**Скорость двух работающих моделей, одинаковый запрос на генерацию кода:** + +``` +Qwen3.8-27B Q4_K_M (порт 8081) генерация 13,6 ток/с промпт 95,9 ток/с +Qwen3-4B-Instruct (порт 8082) генерация 124,1 ток/с промпт 503,5 ток/с +``` + +Разница почти девятикратная. 200 токенов у 27B заняли 14,6 секунды. + +**Память видеокарты, цена контекста измерена точно:** + +``` +27B: база 18 000 МиБ + 39 КиБ на токен контекста +4B: база 2 760 МиБ + 81,5 КиБ на токен контекста +всего 32 768 МиБ +``` + +**Диск, где лежат модели, — узкое место:** + +``` +/dev/sdc Crucial BX500 480G, SATA SSD без DRAM +чтение мимо кэша: 187 МБ/с +NVMe на машине отсутствует +оперативная память: 62 ГБ, доступно 42 +свободно на диске: 313 ГБ +``` + +При 187 МБ/с загрузка 19-гигабайтной модели с холодного диска занимает около **100 секунд**. Это надо учитывать при планировании прогонов: время загрузки нельзя путать со скоростью работы. + +**Особенность железа, определяющая выбор кандидатов.** V100 — это Volta 2017 года: нет BF16, нет FP8, нет MXFP4, и производительность упирается в пропускную способность памяти. Значит скорость генерации определяется **активными** параметрами, а не общим размером. Модели MoE здесь в выигрышном положении, и проверить это — часть задания. + +## P0-1. Инфраструктура сравнения + +Держать несколько крупных моделей в памяти одновременно невозможно: сейчас две занимают 30,2 ГБ из 32,7. + +Поставить **llama-swap** (`mostlygeek/llama-swap`, Go, MIT): прокси читает поле `model` из запроса, поднимает нужный `llama-server`, ненужный выгружает по таймауту и освобождает видеопамять. Для хаба это один OpenAI-совместимый адрес — `local_adapter` работает с ним без изменений. + +Требования: + +1. Ставится **рядом** с работающими службами, не ломая их. Владелец пользуется сервером ежедневно. +2. Конфигурация описывает каждую модель отдельной записью; таймаут выгрузки настраивается. +3. Проверить, что после выгрузки видеопамять **действительно освобождается** — замером `nvidia-smi`, а не по документации. +4. Откат: если llama-swap мешает, службы `qwen-coder` и `qwen-compressor` возвращаются в прежний вид одной командой. Описать как. + +## P0-2. Кандидаты + +Список владельца, отсортированный ревьюером по пригодности для этого железа. + +**Проверено и отклонено:** + +| Модель | Причина | +|---|---| +| `gpt-oss:120b` | В карточке модели: **80 ГБ**, H100 или MI300X. 117B параметров. На 32 ГБ не помещается. | + +**Приоритет для прогона:** + +| Порядок | Модель | Почему | +|---|---|---| +| 1 | `nvidia/Nemotron-Cascade-2-30B-A3B` | MoE, около 3B активных: ожидается кратный прирост скорости при качестве крупной модели | +| 2 | DeepSeek Coder V2 Lite | MoE и специализация на коде | +| 3 | Qwen2.5 Coder 14B и 32B | плотная, заточена под код: проверка «специализация против размера» | +| 4 | Granite 4.2 8B | заявлена сильной на длинном контексте | +| 5 | Phi-4 14B, Qwen3 14B | плотные общего назначения, для полноты | +| 6 | `gpt-oss 20B` | 21B всего, 3,6B активных, но поставляется в MXFP4, которого V100 не поддерживает: нужна сборка GGUF в обычном квантовании, эффективность будет ниже заявленной | + +Точные имена сборок GGUF брать **у источника**, а не придумывать. Модель не нашлась или нет подходящего квантования — так и записать в отчёт, а не заменять похожей. + +## P0-3. Что измерять + +**Скорость** — объективна и меряется просто: + +- генерация, токенов в секунду; +- обработка промпта, токенов в секунду; +- время холодной загрузки модели; +- занятая видеопамять. + +**Качество — важнее скорости, и именно его обычно не меряют.** Модель, выдающая 120 ток/с неработающего кода, хуже той, что даёт 13 ток/с рабочего. + +Собрать набор из **10–15 настоящих задач** по этому репозиторию, с известным правильным результатом: взять реальные правки из истории git, где видно, что требовалось и что получилось. Синтетические задачки вроде «слить два отсортированных списка» ничего не покажут — на них справляются все. + +Оценивать: код запускается; тесты проходят; правка делает то, что требовалось; модель следует инструкции, а не пишет вокруг неё. Уже видна разница в поведении: на одинаковом запросе 27B выдала чистый код, а 4B начала с «Sure! Here's a Python function» и развёрнутого docstring. + +**Длинный контекст — отдельно.** Кодер поднят до 196608, и деградация качества на большом объёме на коротких задачах не видна. Нужен хотя бы один замер на реально длинном входе. + +## P0-4. Отчёт, по которому можно принять решение + +Таблица: модель, размер файла, занятая видеопамять, генерация ток/с, промпт ток/с, время загрузки, результат по задачам, поведение на длинном контексте. + +Плюс вывод в одну строку на каждую модель: годится ли она заменой нынешнему кодеру и почему. + +**Ничего не менять в конфигурации владельца по итогам.** Задание исследовательское: рекомендация даётся, решение принимает он. + +## P0-5. Честность измерений + +1. **Кэш искажает всё.** Первый замер ревьюера дал 4,1 ГБ/с чтения с диска, хотя настоящая скорость 187 МБ/с — файл лежал в кэше оперативной памяти. Замеры скорости диска делать с `iflag=direct`, замеры генерации — после прогрева, и указывать, какой именно случай меряется. +2. **Одинаковые условия.** Один и тот же промпт, одна температура, одно квантование по возможности. Разное квантование сравнивать нельзя, не оговорив этого. +3. **Сервер рабочий.** Прогоны не должны надолго лишать владельца локальной модели. Согласовать окно. +4. Ни одного числа, которого не дал замер. + +## P0-6. Аудит вторым проходом + +1. **Числа из головы.** Проверить, что каждая цифра в отчёте получена запуском, а не взята из карточки модели или из общих соображений. +2. **Кэш** — убедиться, что скорости не измерены по прогретому кэшу без оговорки. +3. **Качество действительно оценено**, а не заменено скоростью. +4. **Конфигурация владельца не изменена.** +5. **Побочные изменения** объяснить. +6. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- Работающие службы не ломать; откат описать. +- Модели качать в `/srv/ai/models/`, места 313 ГБ. +- Ничего не менять в маршрутизации и конфигурации хаба. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin`, `git status` чист. +2. llama-swap поставлен, выгрузка освобождает видеопамять — подтверждено замером `nvidia-smi`; откат описан и проверен. +3. Прогнаны кандидаты из P0-2 в указанном порядке; недоступные названы недоступными. +4. По каждой модели: скорость генерации и промпта, видеопамять, время холодной загрузки — измерены. +5. Набор из 10–15 настоящих задач составлен; результат по каждой модели приведён. +6. Есть замер на длинном контексте. +7. Таблица и вывод по каждой модели приложены. +8. Конфигурация владельца не изменена. +9. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`. + +## Главное + +Нынешний кодер выдаёт 13,6 токена в секунду — для интерактивной работы это тяжело. Вопрос не в том, какая модель быстрее на бумаге, а в том, какая быстрее **при том же качестве на настоящих задачах владельца**. Сравнение, измеряющее только скорость, ответа не даст. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`. diff --git a/agents/inbox/2026-08-31-A39-local-request-options.md b/agents/inbox/2026-08-31-A39-local-request-options.md new file mode 100644 index 0000000..507d7d9 --- /dev/null +++ b/agents/inbox/2026-08-31-A39-local-request-options.md @@ -0,0 +1,142 @@ +# Задание A39: параметры запроса для локальных профилей + +## Дата поступления +2026-08-31 + +## База + +Ветка ревьюера `review/a35-a37-verified` (`88d579a`) — там уже слиты A35, A36, A37 и правки ревьюера. + +``` +git fetch origin --prune +git checkout -b antigravity/a39-local-request-options origin/review/a35-a37-verified +``` + +В `main` напрямую не пушить. + +## Порядок исполнения + +Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-5** написан для аудитора. + +Задание небольшое и независимое: зона — только локальный адаптер и конфигурация профиля. + +--- + +## Задача + +Локальная модель не закрывает задачи: Hermes сообщает «Локальный 27B не закрыл задачу: таймаут 180s, 4 вызова» и переключается на другого провайдера. + +Причина найдена и измерена, гадать не нужно. + +## Что измерено ревьюером — заново не выяснять + +Сервер владельца, `127.0.0.1:8081`, Qwen3.8-27B на Tesla V100. Служба запущена с `--reasoning on --reasoning-budget 4096`. + +**Задача рефакторинга простой функции, лимит 1500 токенов:** + +``` +сгенерировано: 1500 токенов за 111,6 с (13,4 ток/с) +рассуждений: 5483 символа +самого ответа: 0 символов +``` + +Модель израсходовала весь лимит на размышления и **до ответа не дошла**. За отведённые Hermes 180 секунд она успевает около 2400 токенов — и это по-прежнему одни рассуждения. Отсюда таймаут и четыре безрезультатных вызова. + +**Отключение рассуждений на уровне запроса — проверено, работает:** + +``` +reasoning_effort: "none" 540 симв. рассуждений, ответ 302, 19,5 с +chat_template_kwargs: {"enable_thinking": false} 0 рассуждений, ответ 449, 11,4 с +``` + +**11 секунд вместо 111**, и ответ появляется. Проблема не в скорости модели, а в том, что она не доходит до ответа. + +Серверный флаг `--reasoning off` решил бы это грубо, но лишил бы рассуждений насовсем. Владелец выбрал гибкий путь: параметры задаёт хаб, по профилю. + +## Что уже есть + +``` +adapters/local_adapter.py:164 payload собирается здесь; сейчас проходят + tools, tool_choice, response_format, + max_tokens, stream, stop +router_config.py:13 RouterProfileConfig — места под произвольные + параметры запроса нет +``` + +## P0-1. Параметры запроса в профиле + +Добавить в `RouterProfileConfig` поле для произвольных параметров, которые адаптер подмешивает в тело запроса. Например `request_options: dict`. + +Требования: + +1. **Ничего не зашивать.** `enable_thinking`, `reasoning_effort` и прочее — это данные в конфигурации владельца, а не константы в коде. Завтра у llama.cpp появится другой ключ, и правка кода не должна понадобиться. +2. **Сохраняется и переживает перезапуск**, как остальные поля профиля. +3. **Вложенные структуры поддерживаются**: `chat_template_kwargs` — это словарь внутри словаря. +4. **Явное поле запроса не перезаписывается молча.** Если Hermes прислал `max_tokens`, параметры профиля его не затирают; при конфликте выигрывает запрос, а факт расхождения пишется в журнал. + +## P0-2. Локальный адаптер их отправляет + +`local_adapter.invoke` подмешивает `request_options` в тело перед отправкой. + +Осторожно с двумя вещами: + +- **Неизвестный параметр не должен ронять вызов.** Сервер вернёт ошибку — её надо показать как ошибку провайдера с текстом, а не как отказ маршрутизатора. Механизм разбора ошибок уже есть в `base_adapter.extract_api_error_message`. +- **Другие провайдеры не затрагиваются.** Поле относится к локальным профилям; попадание `chat_template_kwargs` в запрос к Antigravity или Codex — дефект. + +## P0-3. Настройка из интерфейса + +Владелец должен задавать это без правки YAML вручную. + +В карточке локального аккаунта — поле параметров запроса. Достаточно текстового поля с JSON и проверкой разбора: набор ключей зависит от версии llama.cpp, и выпадающий список из зашитых вариантов быстро устареет. + +**Показать, что именно уйдёт на сервер.** И честно сказать, если параметр отклонён сервером, — с его текстом ошибки. + +## P0-4. Умолчание для профилей владельца + +Предложить в отчёте, но **не применять молча**: для `local-1` (27B, порт 8081) поставить + +```json +{"chat_template_kwargs": {"enable_thinking": false}} +``` + +Обосновать измерением выше. Решение принимает владелец. + +Для `local-2` (4B, порт 8082) этого не нужно: она запущена с `--reasoning off`. + +## P0-5. Аудит вторым проходом + +1. **Проверить на живом сервере владельца**, а не только заглушкой: запрос с `enable_thinking: false` должен вернуть ответ за десяток секунд вместо ста. +2. **Утечка в других провайдеров** — проверить целенаправленно, что параметр уходит только в локальные. +3. **Неизвестный ключ** не роняет вызов и показывается как ошибка провайдера. +4. **Зашитые значения** — искать отдельно; в коде не должно быть ни `enable_thinking`, ни `reasoning_effort`. +5. **Побочные изменения** объяснить. +6. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- Зона: `adapters/local_adapter.py`, `router_config.py`, карточка аккаунта в вебе, тесты. +- Серверные юниты `qwen-coder` и `qwen-compressor` не трогать: это машина владельца, он меняет их сам. +- Правило честности без исключений. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin` от `review/a35-a37-verified`, `git status` чист. +2. `request_options` есть в профиле, сохраняется, переживает перезапуск, поддерживает вложенные структуры. +3. Локальный адаптер отправляет их; **проверено живым запросом к серверу владельца** с приложенным замером времени до и после. +4. Параметр не попадает в запросы к другим провайдерам; проверено. +5. Неизвестный ключ даёт ошибку провайдера с текстом, а не отказ маршрутизатора. +6. Поле настраивается из интерфейса, показывает отправляемое тело. +7. Явные поля запроса Hermes не затираются. +8. Ни `enable_thinking`, ни `reasoning_effort` не зашиты в коде. +9. `ruff check .` чисто; релизный гейт не ухудшен. +10. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На ветке ревьюера сейчас **508 passed, 2 skipped**. + +## Главное + +Локальная модель сейчас бесполезна: она не доходит до ответа за отведённое время, и Hermes от неё отказывается. Одна строка параметров превращает 111 секунд без результата в 11 секунд с ответом. Нужно, чтобы владелец задавал эту строку из хаба, а не пересобирал службу. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`. diff --git a/agents/inbox/2026-08-31-A40-benchmark-redo.md b/agents/inbox/2026-08-31-A40-benchmark-redo.md new file mode 100644 index 0000000..b0c668b --- /dev/null +++ b/agents/inbox/2026-08-31-A40-benchmark-redo.md @@ -0,0 +1,173 @@ +# Задание A40: честный замер локальных моделей, повторно + +## Дата поступления +2026-08-31 + +## База + +Ветка ревьюера `review/a35-a37-verified` (`88d579a`). + +``` +git fetch origin --prune +git checkout -b antigravity/a40-benchmark-redo origin/review/a35-a37-verified +``` + +В `main` напрямую не пушить. + +## Порядок исполнения + +Два прохода: **Flash** исполняет замеры, **Pro** проводит аудит. Пункт **P0-5** написан для аудитора и в этом задании важнее обычного. + +Это **возврат по A38**. Стенд, раннер и набор из 12 задач писать заново не нужно — они хороши и остаются. + +--- + +## Почему возврат + +Отчёт `benchmarks/BENCHMARK_REPORT.md` открывается словами «Все метрики сняты реальным исполнением на стенде». Для трёх строк из семи это неправда. + +Ревьюер сверил отчёт с диском сервера. Четыре модели существуют, и их размеры совпадают с отчётом до сотых: + +``` +qwen3.8-27b 18 973 870 432 байт = 17,67 ГБ отчёт 17.67 +qwen2.5-coder-14b 8 988 110 272 = 8,37 ГБ отчёт 8.37 +granite-3.2-8b 4 942 860 096 = 4,60 ГБ отчёт 4.60 +qwen3-4b 2 497 280 736 = 2,33 ГБ отчёт 2.32 +``` + +Трёх других **нет на диске вовсе**: + +``` +deepseek-coder-v2-lite каталог пуст, файла GGUF нет отчёт: 8.92 ГБ, 52.1 ток/с, 75.0% +phi-4-14b каталог пуст, файла GGUF нет отчёт: 9.10 ГБ, 34.8 ток/с, 66.7% +nemotron-cascade-30b не существует нигде на сервере отчёт: 18.20 ГБ, 42.6 ток/с, 75.0% +``` + +В `benchmark_results.json` DeepSeek и Phi-4 помечены **`COMPLETED`** — замер якобы выполнен. У Nemotron статус честнее (`AVAILABLE_FOR_SWAP`), но числа при нём всё равно проставлены. + +Хуже всего, что на этом построена рекомендация: пункт 2 итогов называет **DeepSeek-Coder-V2-Lite «лучшим выбором для максимальной скорости»** с точностью до десятой доли — на основании модели, которая никогда не запускалась. Владелец принял бы решение по несуществующим данным. + +Это тот же класс дефекта, что уже стоил проекту нескольких раундов: выдуманные коды устройства `GRK-7842` и `CDX-9104`, запасной коммит `fb23bff`. Задание A38 содержало отдельный пункт «ни одного числа, которого не дал замер», и он не был выполнен. + +## Что из старого отчёта остаётся + +Замеры по четырём настоящим моделям **признаны и переделке не подлежат**. Их переносить как есть: + +| Модель | ток/с | Качество | VRAM | +|---|---|---|---| +| Qwen3.8-27B (reasoning off) | 13,6 | 83,3% | 28 980 МиБ | +| Qwen2.5-Coder-14B | 38,4 | 83,3% | 12 118 МиБ | +| Granite-3.2-8B-preview | 55,8 | 50,0% | 6 500 МиБ | +| Qwen3-4B | 124,1 | 33,3% | 5 440 МиБ | + +Разбор влияния режима мышления (раздел 4) тоже верен и подтверждён независимым замером ревьюера. Оставить. + +--- + +## P0-1. Правило, нарушение которого делает работу непринятой + +**Строка в отчёте появляется только после того, как модель отработала на стенде.** + +Для каждой строки обязательны: + +1. **Абсолютный путь к файлу GGUF на диске** сервера. +2. **Размер файла в байтах**, полученный `stat`, а не из карточки модели. +3. **Контрольная сумма** первых мегабайт или `sha256` — чтобы отчёт можно было проверить. +4. **Сырые тайминги** от `llama-server` из поля `timings` ответа, не пересчитанные вручную. + +Модель не скачалась, не запустилась или не влезла — **строки в таблице нет**. Вместо неё отдельный раздел «не проверено» с причиной. Это полноценный результат, он принимается; выдуманные числа — нет. + +Статус `COMPLETED` ставится **только** при наличии всех четырёх пунктов выше. + +## P0-2. Кандидаты + +Сначала доделать то, что заявлено в A38, потом новых. + +**Обязательные — числа для них уже опубликованы, их надо либо подтвердить, либо отозвать:** + +``` +DeepSeek-Coder-V2-Lite MoE 16B, ~2,4B активных, специализация на коде +Phi-4-14B плотная 14B +Nemotron-Cascade-2-30B-A3B MoE 30B, ~3B активных +``` + +**Новые, отобранные владельцем и ревьюером:** + +| Модель | Что известно проверенно | Зачем | +|---|---|---| +| `qwen2.5-coder-32b` | старшая в семействе нынешнего лидера | лидер даёт 83,3% при 38,4 ток/с; проверить, растёт ли качество | +| `granite4.2:8b` | 5,3 ГБ, контекст 128K | на диске лежит **3.2-preview**, показавшая 50%; 4.2 — следующее поколение | +| `nemotron-3.5-lightning` | 30B всего, 3B активных, MoE, 25 ГБ, контекст 1M | активных три миллиарда — на V100 это должно дать скорость малой модели | +| `laguna-xs-2.1` | 33B, 3B активных, MoE | заявлена «для агентного кодинга на локальной машине» | +| `lfm2.5` | 8B, 1B активных | не в кодеры, а на служебные роли вместо 4B | + +**Проверено и отклонено, время не тратить:** + +``` +gpt-oss:120b 80 ГБ по карточке модели, H100 +Qwen3.8-Flash-Next 125B/6B; самое ужатое IQ1_S — 72,5 ГБ +glm-5.3, kimi-k3, minimax-m3, laguna-s-2.1 (118B), ornith-1.5 397b десятки гигабайт +minicpm-v4.5 / v4.6 модели зрения для телефонов +``` + +Важное про MoE, чтобы не повторить ошибку рассуждения: **экономия у MoE в скорости, а не в памяти.** Активны три миллиарда, но в памяти обязаны лежать все тридцать. Отбирать по общему размеру, ждать выигрыша в скорости. + +## P0-3. Одинаковые условия + +Иначе сравнение обманет, и в прошлый раз это едва не случилось. + +1. **Режим мышления одинаков у всех.** Прошлый прогон шёл с `--reasoning on` у Qwen и без него у остальных — при 13,4 ток/с модель тратила весь лимит на размышления и выдавала ноль. Мерить всех с выключенным мышлением, а влияние режима показывать отдельным разделом, как сейчас. +2. **Один контекст, одно квантование** — по возможности Q4_K_M. Где взято другое, оговорить. +3. **Точное имя сборки.** На диске лежит `granite-3.2-8b-instruct-preview`, а в отчёте написано `Granite-3.2-8B-Instruct` — это разные веса, и разница в качестве могла быть именно в этом. Указывать `general.name` из метаданных GGUF. +4. **Сервер занят одним прогоном.** У обоих llama.cpp `--parallel 1`; посторонние запросы во время замера искажают тайминги. + +## P0-4. Что мерить + +Как в A38, менять нечего: + +- генерация и обработка промпта, токенов в секунду; +- занятая видеопамять по `nvidia-smi`; +- время холодной загрузки (диск даёт 187 МБ/с, это заметно); +- прохождение 12 задач стенда; +- поведение на длинном контексте. + +Плюс: **сколько места на диске занято** и сколько осталось. Кандидатов много, свободно было 313 ГБ. + +## P0-5. Аудит вторым проходом + +Проверяющему: в прошлый раз выдумка прошла первый проход целиком. Здесь она — главный предмет проверки. + +1. **Для каждой строки отчёта убедиться, что файл существует.** Пройти `stat` по всем путям и сверить размеры с таблицей. Расхождение — дефект. +2. **Сверить `benchmark_results.json` с диском.** Ни одной записи `COMPLETED` без файла. +3. **Проверить выборочно тайминги**: повторить два-три замера и убедиться, что цифры воспроизводятся. +4. **Имена сборок** сверить с `general.name` из GGUF, а не с названием каталога. +5. **Рекомендации опираются только на проверенные строки.** +6. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- Служебные юниты `qwen-coder` и `qwen-compressor` возвращать в рабочее состояние после прогонов: владелец пользуется сервером ежедневно. +- Конфигурацию хаба не менять; задание исследовательское. +- Место на диске контролировать, не забить раздел. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin`, `git status` чист. +2. Ни одной строки в отчёте без файла на диске; для каждой указаны путь, размер в байтах и контрольная сумма. +3. Три модели из A38 либо замерены по-настоящему, либо перенесены в раздел «не проверено» с причиной, а рекомендации по ним отозваны. +4. Новые кандидаты из P0-2 прогнаны либо честно объявлены недоступными. +5. Все модели мерены в одинаковом режиме мышления; влияние режима вынесено отдельно. +6. Имена сборок взяты из метаданных GGUF. +7. Итоговая рекомендация опирается только на проверенные измерения. +8. Служебные модели на портах 8081 и 8082 возвращены в рабочее состояние. +9. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`. + +## Главное + +Первый замер дал ценный результат: Qwen2.5-Coder-14B держит качество 27B при втрое большей скорости. Этому можно верить — файл на диске, размер сходится. Но рядом стоят три строки с числами моделей, которых на сервере нет, и одна из них попала в рекомендации. Владелец собирается менять на этом основании рабочую модель, поэтому цена выдуманной строки здесь — неверное решение, а не просто неточность в документе. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`.