# DEV TASK — ORCH-41: Конфигурируемые модели LLM per-agent + per-project ## Контекст / корень Сейчас модель агентов зашита в коде `src/agents/launcher.py` (`AGENT_CONFIGS`): - architect, reviewer → `"model": "opus"` - analyst, developer, tester, deployer → модели НЕТ (CLI берёт дефолт) - Алиас `opus` в CLI 2.1.142 резолвится в **opus-4-7** (подтверждено логами runs/102,103). Слава хочет: **модель выбирается в настройках, под каждого агента, с возможностью задать per-project.** Это первый шаг к мультипровайдерности (ORCH-13). НЕ хардкодить версию — вынести в конфиг. **Расширено (решение Славы 05.06):** 4.7 и 4.8 стоят одинаково → дефолт = `claude-opus-4-8`. Дополнительно конфигурируем **режим работы модели `--effort`** (low/medium/high/xhigh/max) — это рычаг «качество vs стоимость/время», per-agent + per-project, тем же резолвингом. Опционально: `--fallback-model` (авто-фолбэк при overloaded, работает с --print). ## Репозиторий / ветка - Репо: `orchestrator`, база `main` (HEAD `8da571d`) - Ветка: `feat/ORCH-41-agent-models`, PR в main, НЕ мержить. - Сервер: `sshpass -p 'motoZ@yaz2010' ssh slin@82.22.50.71`, контейнер `orchestrator`. ## Архитектура (изучено лично — используй эти точки) - `src/config.py` — `class Settings(BaseSettings)` (pydantic-settings, env-driven, префикс `ORCH_`). Паттерн уже есть: `plane_bot_analyst..deployer`. Делай модели по аналогии. - `src/projects.py` — `@dataclass(frozen=True) class ProjectConfig` (поля: plane_project_id, repo, work_item_prefix), парсится из `settings.projects_json`. Сюда добавляем per-project модели. - `src/agents/launcher.py` — `AGENT_CONFIGS` (dict по агентам) + сборка `model_flag` (строки ~88,99,207-208): ```python model = config.get("model", "") model_flag = f"--model {model} " if model else "" ``` ## Что сделать ### 1. Глобальные дефолты модели per-agent — в Settings (src/config.py) Добавить поля (env `ORCH_AGENT_MODEL_` и `ORCH_AGENT_EFFORT_`). Дефолт модели = `claude-opus-4-8` (цена 4.7==4.8, решение Славы). ```python # Per-agent LLM model (ORCH-41). Empty -> agent_model_default. Resolution order: # project-override > ORCH_AGENT_MODEL_ > agent_model_default > CLI default. agent_model_default: str = "claude-opus-4-8" agent_model_analyst: str = "" agent_model_architect: str = "" agent_model_developer: str = "" agent_model_reviewer: str = "" agent_model_tester: str = "" agent_model_deployer: str = "" # Per-agent effort / reasoning level (ORCH-41): low|medium|high|xhigh|max. # Empty -> agent_effort_default. Same resolution order as model. # Раскладка по умолчанию: думающие агенты — high, механические — medium. agent_effort_default: str = "high" agent_effort_analyst: str = "high" agent_effort_architect: str = "high" agent_effort_developer: str = "high" agent_effort_reviewer: str = "high" agent_effort_tester: str = "medium" agent_effort_deployer: str = "medium" # Optional per-agent fallback model when primary is overloaded (--fallback-model). agent_fallback_model: str = "" ``` ### 2. Per-project override — в ProjectConfig (src/projects.py) Добавить опциональные поля `agent_models: dict[str,str]` И `agent_efforts: dict[str,str]` (агент→модель / агент→effort), парсить из `projects_json`. Пример записи реестра: ```json {"plane_project_id":"...","repo":"orchestrator","work_item_prefix":"ORCH", "agent_models":{"developer":"claude-opus-4-8","reviewer":"claude-sonnet-4-6"}, "agent_efforts":{"developer":"xhigh","tester":"low"}} ``` Оба поля опциональны → старые записи работают (default = {}). ВНИМАНИЕ: dataclass `frozen=True` + mutable default → используй `field(default_factory=dict)`. ### 3. Резолвинг — helper(ы) (где удобнее: launcher или projects) `resolve_agent_model(agent, project_id) -> str` с приоритетом: 1. `ProjectConfig.agent_models[agent]` (per-project override) 2. `settings.agent_model_` (глобальный per-agent env, если непусто) 3. `settings.agent_model_default` 4. (пусто) → без `--model`, CLI-дефолт Аналогично `resolve_agent_effort(agent, project_id) -> str` (тот же приоритет, через `agent_efforts` / `agent_effort_` / `agent_effort_default`). Валидировать значение по множеству {low,medium,high,xhigh,max}; невалидное → лог-warning + опустить флаг. Желательно общий приватный резолвер, чтобы не дублировать логику приоритета. ### 4. Применить в launcher.py - Убрать хардкод `"model": "opus"` из AGENT_CONFIGS (architect, reviewer) — модель теперь резолвится. - При сборке команды: ```python model = resolve_agent_model(agent, project_id) effort = resolve_agent_effort(agent, project_id) model_flag = f"--model {model} " if model else "" effort_flag = f"--effort {effort} " if effort else "" fb = settings.agent_fallback_model fb_flag = f"--fallback-model {fb} " if fb else "" ``` и подставить `{model_flag}{effort_flag}{fb_flag}` в cmd. - `project_id` уже доступен в потоке запуска (передаётся в launch — проверь сигнатуру, протяни если надо). ### 5. Тесты (tests/) - `test_resolve_agent_model.py`: все 4 уровня приоритета (project > env > default > пусто), отсутствие проекта, неизвестный агент, frozen-dataclass с agent_models. - `test_resolve_agent_effort.py`: те же уровни + валидация значения (невалидное → опускается). - Проверить, что `--model` / `--effort` / `--fallback-model` корректно подставляются/опускаются в собранной команде (по флагу присутствия). ### 6. Документация - Обновить `docs/` (конфиг-референс орка): новые env `ORCH_AGENT_MODEL_*`, `ORCH_AGENT_EFFORT_*`, `ORCH_AGENT_FALLBACK_MODEL`, поля `agent_models`/`agent_efforts` в projects_json. - Кратко описать значения effort (low|medium|high|xhigh|max) и дефолтную раскладку по агентам. - CHANGELOG.md. ## Команды проверки (ОБЯЗАТЕЛЬНО до отчёта) ```bash # из клона ветки в окружении прод-образа орка: docker exec -w -e PYTHONPATH= orchestrator python3 -m pytest tests/ -q -p no:cacheprovider # Ожидаемо: 0 failed (>=402 passed + новые) # Проверь резолвинг руками: python3 -c "from src.agents.launcher import resolve_agent_model; print(resolve_agent_model('developer',''))" ``` ## Off-limits / грабли - НЕ менять логику гейтов, webhook, HMAC, очередь. - НЕ хардкодить версию модели в коде (вся суть задачи — конфигурируемость). - frozen dataclass + dict → `field(default_factory=dict)`, иначе TypeError. - Тесты НЕ в docker-образе → гонять из клона репо. - После push: `git log origin/main..origin/feat/ORCH-41-agent-models` показывает коммит ДО отчёта. - Не регистрировать раннеров, не nohup. - ⚠️ Бюджет (ORCH-38): дефолт 4-8 может быть дороже 4-7. НЕ менять дефолт на проде сам — только в коде как default; реальное переключение прода/env делаю я после ОК Славы. ## Результат PR `feat/ORCH-41-agent-models` → main. Модель агентов конфигурируема: глобально (env per-agent), с per-project override в projects_json. 0 failed. Отчёт: `tasks/orchestrator/reports/dev-2026-06-05-orch41-agent-models.md` + краткое резюме мне (что добавил, вывод pytest, ссылка на PR, пример конфига).