Счёт, с которого всё началось, — 312 долларов за один день. Один разработчик, одна сессия Octomind, одна модель, делающая всю работу: читает файлы, гоняет grep'ы, суммирует диффы, решает, когда сжимать контекст, и делает по-настоящему сложные рассуждения. Всё это на флагманской модели, потому что именно её выбираешь, когда хочешь, чтобы агент был хорош.

Потом OpenRouter начал возвращать 429 посреди рефакторинга, и я смотрел, как агент застрял на лимите запросов, делая то, что модель в десять раз дешевле сделала бы идеально. Вот в этот момент очевидное и сложилось: большая часть того, что делает агент, не сложна. Перечислить файлы — не сложно. Уточнить расплывчатую задачу — не сложно. Решить, достаточно ли большая сессия, чтобы её сжать, — не сложно. Прочитать выгрузку исследования и вытащить три нужных файла — не сложно.

Для всего этого лучшая модель не нужна. Она нужна, может, для десяти процентов вызовов — ревью, каверзное проектное решение, дифф, затрагивающий аутентификацию. Остальные девяносто процентов — это чёрная работа, а чёрная работа — ровно то, где блистают дешёвые и локальные модели.

Это история о том, как разнести одного агента Octomind по многим моделям, опираясь на реальные ключи конфигурации, которые он поставляет. К концу у вас будет рабочая мультимодельная настройка, шпаргалка по выбору модели и чёткий ответ на главный вопрос: что куда ставить.


Одна строка — любой бэкенд

Octomind общается с каждым провайдером через одну библиотеку — octolib, о которой я писал раньше. То, что даёт octolib, то, что делает всё это возможным, — это что каждая модель именуется одинаково: строкой provider:model.

model = "anthropic:claude-sonnet-5"
model = "openai:gpt-5.5"
model = "deepseek:deepseek-chat"
model = "openrouter:google/gemini-3.5-flash"
model = "ollama:glm-5"

То же поле, та же форма везде, где настраивается модель. Octolib разбирает префикс, маршрутизирует к нужному бэкенду и нормализует ответ — расход токенов, стоимость, структурированный вывод — так что остальному Octomind всё равно, какой вендор ответил.

Префиксы провайдеров, которые octolib распознаёт на момент текущего релиза:

openrouter  openai      anthropic   google      amazon
cloudflare  deepseek    cerebras    groq        together
fireworks   nvidia      minimax     moonshot    zai
byteplus    featherless octohub     ollama      local

Двадцать сетевых провайдеров плюс особый мета-провайдер cli для локальных агентов на базе CLI (об этом ниже). moonshot и kimi — псевдонимы одного провайдера. Новые провайдеры попадают в octolib и становятся доступны в Octomind автоматически — без нового релиза Octomind.

Кстати, ключи API берутся только из окружения. Их нельзя положить в файл конфигурации; это убрали из соображений безопасности. Вы делаете export OPENROUTER_API_KEY=... (или кладёте .env в рабочую директорию), а octomind config --show сообщит, какие ключи обнаружены и откуда они взялись. Один ключ OpenRouter даёт почти весь каталог через единый префикс; ключи отдельных провайдеров дают остальное.


Где выбирается модель

Прежде чем разносить работу по моделям, нужно знать четыре места, где модель может быть задана, и какое из них побеждает, когда применимо больше одного. Octomind разрешает эффективную модель в таком порядке, от высшего приоритета к низшему:

CLI --model  >  role.model  >  config.model

Плюс четвёртый шов для tap-агентов: запись [taps] переопределяет config.model для конкретного тега tap-агента.

  • config.model — корневое значение по умолчанию. В свежесгенерированной конфигурации это Claude класса Sonnet через OpenRouter — сегодня стоит поставить openrouter:anthropic/claude-sonnet-5. Всё опускается до него, когда не задано ничего более конкретного.
  • role.model — переопределение на уровне роли. Это главный рычаг. Каждая роль может назвать свою модель, и модель роли соблюдается напрямую поверх корневого значения.
  • Переопределение [taps] — для tap-агентов (агенты вида category:variant, которые вы ставите из реестра) можно закрепить модель по тегу, не трогая манифест агента:
[taps]
"developer:general" = "anthropic:claude-sonnet-5"
"octomind:assistant" = "openai:gpt-5.5"
  • CLI --model — побеждает всё остальное для одного запуска: octomind run -m anthropic:claude-sonnet-5. А посреди сессии можно переключиться вживую через /model deepseek:deepseek-chat или подкрутить усилие рассуждения через /effort high.

Так что стратегия проста: выберите разумное значение по умолчанию в config.model, затем переопределяйте по ролям там, где конкретная работа заслуживает другую модель. Большая часть экономии приходит со слоя ролей.


Роли: единица выбора модели

Роль в Octomind — это именованный набор поведения: системный промпт, температура, набор MCP-серверов и разрешений на инструменты — и, опционально, модель. Каждая сессия идёт с ролью. Роль решает, что агент может делать, и какая модель это делает.

Поставляемая конфигурация уже практикует то, что проповедует этот пост. Она определяет четыре простые роли, и три из них намеренно идут на моделях дешевле основной — здесь строки моделей обновлены до текущего поколения:

# Лёгкое уточнение запроса — сделать расплывчатую задачу яснее. Без инструментов.
[[roles]]
name = "task_refiner"
model = "openrouter:openai/gpt-5.4-mini"
# ...

# Разведка — собрать контекст, прочитать файлы, доложить находки.
[[roles]]
name = "task_researcher"
model = "openrouter:google/gemini-3.5-flash"
# ...

# Сжатие истории сессии — суммировать и сокращать.
[[roles]]
name = "reduce"
model = "openrouter:openai/gpt-5-mini"
# ...

Читайте этот выбор моделей как проектное заявление. Уточнить расплывчатый запрос — работа для маленькой быстрой модели (gpt-5.4-mini). Исследование, читающее много файлов, хочет большое дешёвое окно контекста (gemini-3.5-flash). Сжатие истории — задача структурированного суммирования, с которой малая reasoning-модель справляется отлично (gpt-5-mini). Ни одной из них не нужна флагманская модель, и конфигурация по умолчанию её на них не тратит.

Свои роли вы определяете так же. Все поля, кроме model, обязательны — system, welcome, temperature, top_p и top_k должны присутствовать, иначе конфигурация не распарсится:

# Дешёвый широкий контекст для прочёсывания кода
[[roles]]
name = "researcher"
model = "openrouter:google/gemini-3.5-flash"
temperature = 0.3
top_p = 0.7
top_k = 20
system = "You gather context and report findings. You do not modify files."
welcome = ""
[roles.mcp]
server_refs = ["filesystem"]
allowed_tools = ["view"]

# Точность там, где это важно — ревью и сложные решения
[[roles]]
name = "reviewer"
model = "anthropic:claude-opus-4-8"
temperature = 0.2
top_p = 0.7
top_k = 20
system = "You review changes for correctness, security, and edge cases."
welcome = "Reviewer ready."
[roles.mcp]
server_refs = ["filesystem"]
allowed_tools = ["view", "ast_grep"]

Теперь octomind run researcher прочёсывает репозиторий на дешёвой модели с большим контекстом, а octomind run reviewer выводит дорогую модель только на ту работу, что её заслуживает. Переключайтесь между ними посреди сессии через /role reviewer. Тот же агент, та же сессия, две модели — выбранные под задачу.


Слои (layers): цепочка моделей за один проход

Роли позволяют вам выбрать модель на сессию. Слои позволяют системе выстраивать цепочку моделей автоматически внутри одного потока.

Слой — это вызываемый по ACP этап, ссылающийся на роль через поле command. Роль несёт модель, системный промпт и инструменты; слой говорит, как разговор входит в этот этап и выходит из него. Полей немного, и все поля режимов обязательны:

[[layers]]
name = "analysis"
description = "Detailed analysis of code, systems, or requirements"
command = "octomind acp analysis"   # spawns the `analysis` role over ACP
input_mode = "last"                 # last | all | summary
output_mode = "append"              # none | append | replace | last | restart
output_role = "assistant"

Поскольку слой указывает на роль, а роль несёт модель, каждый слой в цепочке может идти на своей модели. Дешёвая модель собирает контекст, передаёт вывод следующему этапу, более сильная модель рассуждает над ним. Та же структура LayerConfig стоит за [[commands]] — слоями, которые вы запускаете интерактивно через /run <name>, — так что поставляемая команда reduce сама является слоем, который запускает дешёвую роль reduce, чтобы сжать историю по требованию.

Мысленная модель: роли отвечают на «какая модель для этой сессии», слои отвечают на «какая модель для этого этапа». Вместе они дают вам маршрутизировать один запрос через конвейер «дешёвая-потом-дорогая» без написания кода оркестрации — всё это TOML.

Примечание: записи [[layers]] нужна соответствующая роль в [[roles]]. Модель живёт в роли, никогда в слое. Если вам захотелось задать модель на слое — вам нужна роль.


У сжатия своя модель — и она должна быть дешёвой

Есть ещё один выбор модели, который легко пропустить и который окупается мгновенно. Octomind сжимает длинные сессии автоматически, и вызов «решение-плюс-сводка», который движет сжатием, идёт на своей настроенной модели, отдельной от основной:

[compression.decision]
model = "openai:gpt-5-mini"   # дёшево по замыслу
max_tokens = 16000
temperature = 0.3

Значение по умолчанию openai:gpt-5-mini стоит не просто так: этот вызов срабатывает снова и снова на протяжении длинной сессии, и вам не нужна флагманская модель, решающая, сжимать ли. anthropic:claude-haiku-4-5 — хорошая альтернатива, если вы уже на Anthropic. Идея та же, что и везде в этом посте — рутинная структурная работа получает рутинную дешёвую модель, и экономия накапливается, потому что она запускается часто.

Сам движок сжатия учитывает стоимость: перед сжатием он вычисляет чистую выгоду, используя цены по моделям, полученные от провайдера, и пропускает сжатие, когда оно обойдётся дороже, чем сэкономит. Для бесплатной модели сессии или модели с нулевой ценой — скажем, локальной модели Ollama — он сжимает всегда, потому что стоимость нерелевантна и важно лишь держать контекст управляемым. Что хорошо подводит к следующему.


Локальные модели на массовую работу

Не каждому вызову нужно покидать вашу машину. Octomind общается с локальными моделями двумя способами.

Ollama и OpenAI-совместимые эндпоинты через префиксы ollama: и local::

model = "ollama:glm-5"
model = "local:my-finetune"   # set LOCAL_API_URL to your endpoint

ollama: указывает на ваш локальный демон Ollama; local: указывает на любой OpenAI-совместимый сервер через LOCAL_API_URL. Любой из них — та же строка provider:model, что использует остальная конфигурация — поместите её в поле model роли, и эта роль работает локально.

Локальные CLI-агенты через особый мета-провайдер cli:, который запускает агента командной строки на вашей машине вместо вызова сетевого API:

model = "cli:codex/gpt-5.2-codex"

Формат — cli:<backend>/<model>, где бэкенд — один из codex, claude, cursor, gemini или обобщённая команда. Поскольку он работает через локальный CLI, ключ API не нужен — Octomind полностью пропускает проверку учётных данных для моделей cli:. Поведение настраивается переменными окружения конкретного бэкенда (для бэкенда codex — CODEX_COMMAND, CODEX_REASONING_EFFORT и компания).

Где локальные модели отрабатывают свой хлеб: высокообъёмная работа с низкими ставками, где задержка и приватность важнее последних пунктов качества. Массовое суммирование, классификация, первичное исследование, всё, что вы гоняете тысячи раз. Поставьте локальную модель на роль researcher, и фаза разведки не стоит вам ничего за токен. Оставьте облачную модель на роли reviewer, где разрыв в качестве реально виден.


OctoHub: маршрутизация и резерв снизу

Пока что маршрутизация идёт по ролям и по слоям, решаясь в вашей конфигурации Octomind. Но есть слой ниже — настоящий прокси — и именно там живут резерв (fallback), балансировка нагрузки и централизованный учёт.

OctoHub — это наш прокси для LLM с открытым исходным кодом. Для Octomind это просто ещё один провайдер: вы задаёте модель вроде octohub:my-model, направляете Octomind на эндпоинт OctoHub, и со стороны Octomind это ещё одна строка provider:model. То, что OctoHub делает за этой строкой, — вот где становится полезно. Его конфигурация сопоставляет короткое имя модели со списком полностью квалифицированных целей provider:model и выбирает одну случайно на каждый запрос:

[models]
# Один псевдоним, несколько реальных бэкендов — случайный выбор на запрос = простая балансировка
"fast" = ["minimax:minimax-m3", "ollama:minimax-m3"]

Это единственное сопоставление даёт вам балансировку нагрузки и мягкую форму отказоустойчивости: если один бэкенд ограничен по лимиту или лежит, остальные в списке поглощают трафик. OctoHub также делает полное логирование запросов/ответов, мультиарендные ключи API (чтобы у каждого разработчика или сервиса был свой ключ, а его использование отслеживалось отдельно) и агрегированную аналитику использования по ключу и временному интервалу. Для команды, гоняющей много агентов, это разница между «мы потратили 4 тысячи долларов на LLM в прошлом месяце» и «конвейер ревью на сервисе аутентификации потратил 4 тысячи долларов в прошлом месяце, вот разбивка по ключам».

Разделение труда чистое:

  • octolib — библиотека, которая знает, как говорить с каждым провайдером — форма запроса, разбор ответа, таблицы стоимости.
  • OctoHub — прокси, который стоит перед провайдерами и решает, в какой из них попадает запрос, логирует его и учитывает по ключу.
  • Octomind — агент, который решает, по роли и по слою, какую строку модели отправить в первую очередь.

Можно запускать Octomind вообще без прокси и маршрутизировать чисто через роли и OpenRouter. OctoHub добавляют, когда хотят централизованный контроль: одно место для ротации ключей, перебалансировки трафика и понимания, куда ушли деньги.


Шпаргалка по выбору модели

Вот куда я бы реально ставил каждый класс модели. Относитесь к конкретным именам как к актуальным примерам, а не к заповедям — правильная модель в этом квартале не будет правильной в следующем, но форма решения держится.

Работа в агенте Класс модели Конкретный пример Почему
Финальное ревью, сложные проектные решения, диффы рядом с аутентификацией Флагман anthropic:claude-opus-4-8 Качество стоит цены на тех 10%, что решают
Основной цикл разработки Сильная, с кэшем anthropic:claude-sonnet-5 Хороший код + кэш промпта держат стоимость в узде
Исследование / прочёсывание контекста Большой контекст, дёшево openrouter:google/gemini-3.5-flash Читает много файлов, не обязана быть гениальной
Уточнение задачи Маленькая, быстрая openrouter:openai/gpt-5.4-mini Подчистить расплывчатый запрос — тривиальная работа
Решение о сжатии Маленькая, дешёвая openai:gpt-5-mini Срабатывает постоянно; никогда не ставьте сюда флагман
Сокращение сессии / сводки Маленькая reasoning openrouter:openai/gpt-5-mini Структурированное суммирование, запускается часто
Массовое / высокообъёмное / приватное Локальная ollama:glm-5, cli:codex/gpt-5.2-codex Задержка + приватность + нулевая цена за токен
Экономный облачный резерв Дешёвый нативный deepseek:deepseek-chat Минимальная цена, когда всё же нужен реальный API

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


Полная мультимодельная конфигурация

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

# Дешёвое, способное значение по умолчанию через один ключ OpenRouter
model = "openrouter:anthropic/claude-sonnet-5"

# Жёсткие лимиты — применяются, а не советуют
max_request_spending_threshold = 0.50   # USD за запрос, останавливает выполнение
max_session_spending_threshold = 5.00   # USD за сессию, спрашивает/останавливает

# Сжатие идёт на дешёвой модели, отдельной от основной
[compression.decision]
model = "openai:gpt-5-mini"
max_tokens = 16000
temperature = 0.3

# Локальная модель для высокообъёмной фазы разведки
[[roles]]
name = "researcher"
model = "ollama:glm-5"
temperature = 0.3
top_p = 0.7
top_k = 20
system = "You gather context and report findings. You do not modify files."
welcome = ""
[roles.mcp]
server_refs = ["filesystem"]
allowed_tools = ["view"]

# Флагманская модель на работу, что её заслуживает
[[roles]]
name = "reviewer"
model = "anthropic:claude-opus-4-8"
temperature = 0.2
top_p = 0.7
top_k = 20
system = "You review changes for correctness, security, and edge cases."
welcome = "Reviewer ready."
[roles.mcp]
server_refs = ["filesystem"]
allowed_tools = ["view", "ast_grep"]

Проверьте её, прежде чем доверять:

octomind config --validate
octomind config --show     # confirms which provider keys were detected

Затем запускайте дешёвый путь по умолчанию и зовите дорогой только когда намерены:

octomind run researcher              # local model, free per token
octomind run reviewer                # frontier model, on purpose
octomind run -m deepseek:deepseek-chat   # one-off override for a session

Следите, сколько это реально стоит, через /info для обзора сессии и /report для разбивки по запросам. Когда вы впервые прогоните реальную задачу так и увидите, что фаза исследования не стоит ничего, а ревью стоит пару центов, разделение перестаёт быть теоретическим.


Что изменилось после разделения

Дня за 312 долларов больше не случается, и не потому, что мы реже используем агента. Трафик исследования и уточнения переехал на дешёвые и локальные модели, решение о сжатии перестало ездить на флагманской модели, а дорогая модель теперь появляется только для ревью и по-настоящему сложных решений. Доля флагманской модели в общем числе вызовов упала примерно до тех десяти процентов, какими ей всегда и полагалось быть.

Зависания на лимитах запросов тоже ушли, чего я не ожидал. Распределение трафика по провайдерам — часть через OpenRouter, часть напрямую, часть локально, часть за случайным сопоставлением OctoHub — означает, что ни один 429 одного провайдера не останавливает всю сессию. Агент, который раньше замерзал на лимите одного апстрима, теперь просто обходит его стороной.

Ничего из этого не потребовало фреймворка, библиотеки маршрутизации или кода оркестрации. Это строки provider:model в TOML, пара дополнительных ролей и одна дешёвая модель сжатия. Сложным была не проводка — у octolib и Octomind всё это уже было. Сложным был сдвиг мышления: перестаньте платить флагманские цены за чёрную работу и ставьте лучшую модель только туда, где качество вывода реально меняет то, что произойдёт дальше.

Если вы также оборачиваете специфичные для проекта инструменты для этих агентов, паттерн кастомные MCP в вашем репозитории чисто сочетается с мультимодельными ролями — узкие инструменты и модели нужного размера тянут в одну сторону.


Частые вопросы

Нужен ли мне OpenRouter, или можно использовать провайдеров напрямую?

И то, и другое. Один OPENROUTER_API_KEY даёт вам почти весь каталог через префикс openrouter:, и это самый простой старт. Или задайте отдельные ключи (ANTHROPIC_API_KEY, DEEPSEEK_API_KEY, …) и используйте нативные префиксы. Можно смешивать оба в одной конфигурации — разные роли могут бить в совершенно разных провайдеров.

Как переключить модель, не редактируя конфигурацию?

На запуск: octomind run -m anthropic:claude-sonnet-5. Посреди сессии: /model deepseek:deepseek-chat. Флаг CLI побеждает модели роли и конфигурации; слэш-команда меняет живую сессию и сохраняет её.

Где именно решается модель?

Порядок разрешения — CLI --model > role.model > config.model, с переопределением [taps], вклинивающимся на уровне config.model для tap-агентов. Задайте разумное значение config.model по умолчанию, затем переопределяйте по ролям для работ, заслуживающих другой модели.

Могут ли разные шаги одной задачи использовать разные модели?

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

Сломают ли локальные модели учёт стоимости?

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

Что OctoHub добавляет такого, чего нет у ролей?

Роли маршрутизируют в вашей конфигурации; OctoHub маршрутизирует на прокси. Он сопоставляет один псевдоним с несколькими реальными бэкендами со случайной балансировкой нагрузки, логирует каждый запрос, выдаёт ключи API на каждого для мультиарендного учёта использования и агрегирует аналитику. Он нужен, когда команде требуется одно место, чтобы перебалансировать трафик и отчитываться за траты.


Смысл никогда не был в «используйте модель подешевле». Он в «используйте правильную модель для каждого вызова, и пусть маршрутизация будет конфигурацией, а не кодом». Octomind даёт вам швы — корневое значение по умолчанию, по роли, по слою, по запуску, плюс прокси снизу — и одна строка provider:model связывает их все.

Тратьте лучшую модель там, где ответ реально важен. Пусть всё остальное работает дёшево.

— Don


Octomind — открытый код под Apache-2.0, и так же octolib и OctoHub. Мультимодельная маршрутизация — это конфигурация, а не плагин: если нужного провайдера нет, он попадает в octolib и появляется в Octomind автоматически. Откройте issue, если что-то здесь не сходится.