Merge remote-tracking branch 'origin/main' into HEAD
This commit is contained in:
commit
15528e84d0
8 changed files with 1228 additions and 0 deletions
137
agents/inbox/2026-08-25-A32-remove-desktop.md
Normal file
137
agents/inbox/2026-08-25-A32-remove-desktop.md
Normal file
|
|
@ -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`.
|
||||
177
agents/inbox/2026-08-30-A34-restore-wizard-providers-desktop.md
Normal file
177
agents/inbox/2026-08-30-A34-restore-wizard-providers-desktop.md
Normal file
|
|
@ -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`.
|
||||
145
agents/inbox/2026-08-30-A35-hub-must-actually-route-hermes.md
Normal file
145
agents/inbox/2026-08-30-A35-hub-must-actually-route-hermes.md
Normal file
|
|
@ -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`.
|
||||
144
agents/inbox/2026-08-30-A36-antigravity-pipeline.md
Normal file
144
agents/inbox/2026-08-30-A36-antigravity-pipeline.md
Normal file
|
|
@ -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`.
|
||||
143
agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md
Normal file
143
agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md
Normal file
|
|
@ -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`.
|
||||
167
agents/inbox/2026-08-30-A38-local-model-benchmark.md
Normal file
167
agents/inbox/2026-08-30-A38-local-model-benchmark.md
Normal file
|
|
@ -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`.
|
||||
142
agents/inbox/2026-08-31-A39-local-request-options.md
Normal file
142
agents/inbox/2026-08-31-A39-local-request-options.md
Normal file
|
|
@ -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`.
|
||||
173
agents/inbox/2026-08-31-A40-benchmark-redo.md
Normal file
173
agents/inbox/2026-08-31-A40-benchmark-redo.md
Normal file
|
|
@ -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`.
|
||||
Loading…
Reference in a new issue