DanLevy.net

Не женитесь на своей модели

Маршрутизация LLM, сейчас это огонь

Большинство инженерных команд выбирают одну языковую модель и придерживаются её. Один провайдер, одна модель, все задачи. Это как нанять одного человека для кода, копирайтинга и налогов только потому, что он хорошо прошёл первое собеседование.

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

Я видел, как команда сжигала тысячи долларов, прогоняя анализ тональности через модель за 30 долларов за миллион токенов, когда модель за 0,50 долларов справилась бы не хуже. Простое JSON-форматирование, базовые задачи классификации — всё уходило через их премиум-провайдера. Единственное, что действительно нагревалось, — это счёт от AWS.

Есть способ лучше, и он не особенно сложен.

Делегирование вместо преданности

Что, если бы вы могли направлять запросы к модели, которая лучше всего подходит для этой конкретной задачи? Используйте свою дорогую «рабочую лошадку» для сложных вещей, а простой парсинг и форматирование спускайте на что-то подешевле. Получайте преимущества от нескольких провайдеров, не жонглируя ими вручную в коде.

Mastra позволяет построить именно такую систему. Вы создаёте агентов-специалистов для разных типов работ, а затем — супервизора, который решает, к какому специалисту направить каждый запрос. Идентификаторы моделей ниже используют текущий формат строки provider/model в Mastra; это примеры, а не таблица лидеров. Замените их на актуальные модели, которые побеждают в ваших eval’ах и вписываются в бюджет.

Представьте это так: у вас в команде три специалиста.

./src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra';
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { LibSQLStore } from '@mastra/libsql';
export const claudeAgent = new Agent({
id: 'claude-agent',
description: 'Handles implementation, refactoring, and code review tasks.',
instructions: 'You are an expert engineer. Write bugs? You are fired.',
model: process.env.CODE_MODEL ?? 'anthropic/claude-sonnet-4-6',
});
export const geminiAgent = new Agent({
id: 'gemini-agent',
description: 'Handles long-context synthesis and messy document analysis.',
instructions: 'You are a creative writer. Be weird.',
model: process.env.LONG_CONTEXT_MODEL ?? 'google/gemini-2.5-pro',
});
export const gptAgent = new Agent({
id: 'gpt-agent',
description: 'Handles routine classification, formatting, and general Q&A.',
instructions: 'You are a helpful assistant. Be boring.',
model: process.env.GENERAL_MODEL ?? 'openai/gpt-5-mini',
});

У каждого своя работа, и поле description — часть поверхности маршрутизации. Ваш код-агент должен быть моделью, которая проходит ваши репозиторий-специфичные eval’ы по коду. Ваш агент длинного контекста должен быть тем, кто выживает на ваших реальных документах, не превращая середину в кашу. Ваш общий агент должен быть дешёвым, надёжным и настолько скучным, насколько это возможно.

Вот где становится интересно. Вы добавляете лёгкого супервизора, который работает как интеллектуальный прокси:

export const supervisorAgent = new Agent({
id: 'supervisor-agent',
name: 'The Boss',
instructions: `You route work to the right specialist.
Delegate coding work to claude-agent.
Delegate long-context document work to gemini-agent.
Delegate routine classification and formatting to gpt-agent.
Do not do specialist work yourself unless delegation is unnecessary.`,
model: process.env.ROUTER_MODEL ?? 'openai/gpt-5-mini',
agents: {
claudeAgent,
geminiAgent,
gptAgent,
},
memory: new Memory({
storage: new LibSQLStore({ id: 'router-memory', url: 'file:mastra.db' }),
}),
});
export const mastra = new Mastra({
agents: { supervisorAgent, claudeAgent, geminiAgent, gptAgent },
});

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

Когда кто-то просит реализовать пузырьковую сортировку, маршрутизатор распознаёт это как работу с кодом и передаёт вашему специалисту по коду. Запрос на креативное письмо? Он уходит к модели, которую вы выбрали за голос и диапазон. Фактический вопрос об исторических событиях? Направляйте его общему агенту, в идеале с поиском (retrieval), если важны свежесть или цитирование.

Практические выгоды

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

Качество растёт, когда вы сопоставляете модели с задачами. Победитель меняется каждый месяц, в зависимости от задачи и формы промпта. Именно поэтому слой маршрутизации должен опираться на ваши eval’ы, а не на то, какая модель побеждала в твиттере на неделе, когда вы писали интеграцию.

Отказоустойчивость становится возможной, но не автоматической. Супервизор выше не повторяет запросы через другого агента в случае отказа провайдера, и сам он зависит от OpenAI для принятия решения о маршрутизации. Если вам важна отработка отказа провайдера, добавьте явный retry/fallback в прикладной код, держите запасной маршрутизатор на другом провайдере и тестируйте путь отказа. Набор агентов — это не circuit breaker только потому, что у моделей разные логотипы.

Это не про то, чтобы быть умным ради самого процесса. Это про то, чтобы строить системы, которые имеют смысл как с финансовой, так и с технической точки зрения. Вы же не будете использовать один и тот же молоток для всех строительных задач. И вам, вероятно, не следует использовать одну и ту же языковую модель для всех AI-задач.

Красота этого подхода в том, что ваш прикладной код не нуждается в разветвлённом лабиринте условных операторов. Вы по-прежнему вызываете одного агента. Сложность принятия решения о том, какую модель использовать для какой задачи, живёт в одном месте, настраивается один раз и не размазана по всей кодовой базе в виде кучи условной логики.

Ресурсы

Читайте серию

  1. Маршрутизация LLM (Этот пост)
  2. Безопасность и защитные механизмы
  3. Интеграции MCP и инструментов
  4. Рабочие процессы и память