Команда Fractal Agents выпустила FAIRouter, открытую библиотеку под лицензией Apache 2.0 для маршрутизации запросов между языковыми моделями, агентами и инструментами. Роутер до вызова прогнозирует, какая модель справится с задачей достаточно хорошо и при этом обойдётся дешевле. После ответа модель-судья сверяет результат с заданием и находит расхождения по каждому пункту, и на этих расхождениях система учится. Цифры и формулировки ниже приводятся по описанию авторов.
Логика авторов простая и проверяется на себе за минуту. Пользователю важно не соотношение цены и качества, а хватит ли качества для его задачи. Слабый ответ всё равно придётся переделывать, и сэкономленные деньги ничего не стоят. Поэтому роутер сначала отсеивает модели, которые прогнозируемо не дотянут до нужного уровня, а среди оставшихся выбирает по цене и скорости.
Масштаб проблемы авторы показывают на каталоге OpenRouter: 428 моделей и разброс цены выходных токенов в 20 тысяч раз, от $0.03 до $600 за миллион. Держать в голове такую матрицу «задача, модель, качество, цена» человек не может, а общий балл в лидерборде не отвечает на вопрос, справится ли конкретная модель с конкретным запросом.
Что такое FAIRouter и какую проблему он решает
FAIRouter работает в двух точках. До ответа он предсказывает качество и цену варианта. После ответа он проверяет результат по исходному заданию. Оба контура связаны: расхождение между прогнозом и проверкой становится обучающим сигналом, поэтому система со временем точнее понимает, где какая модель сильна.
Чем FAIRouter отличается от ручного выбора модели
Ручной выбор опирается на интуицию и сводные таблицы бенчмарков. Оба источника дают усреднённую картину: модель с хорошим средним баллом может проваливать именно ваш тип задач, а дешёвая модель может закрывать половину запросов не хуже флагмана.
FAIRouter прогнозирует качество по тексту запроса, до того как потрачен хоть один токен. Прогноз строится на признаках задачи, а не на громкости названия модели. Готовых весов для скачивания нет: веса обучаются локально и хранятся в одном файле SQLite, а стартовый снимок бенчмарков идёт в комплекте, чтобы роутеру было с чего начинать. Код написан в двух независимых версиях: Python с единственной зависимостью numpy и C# под .NET 10.
Похожую задачу решала команда LangChain, встроив маршрутизатор в harness кодинг-агента Open SWE: медианная стоимость обработки треда снизилась на 64% без измеримого падения качества. Разница в акценте: там роутер жил внутри одного агента и опирался на его контекст, здесь библиотека претендует на роль общего слоя между моделями, агентами и инструментами.
Как роутер выбирает модель под задачу: метрика, профили и планка достаточности
Решение принимается по одной формуле:
R = W_Q * q - W_C * c - W_t * t
Здесь q - прогноз качества, то есть скалярное произведение вектора признаков задачи на обучаемый вектор кандидата: грубо говоря, «на каких задачах я силён». c - логарифм стоимости, t - логарифм ожидаемого времени. Качество входит в метрику со знаком плюс, цена и время - со знаком минус: роутер максимизирует полезность варианта.
Логарифмы тут не для красоты. Цена за миллион токенов различается в десятки тысяч раз, время ответа - в разы. Без логарифма самый дорогой кандидат в каталоге перекрывал бы вклад качества и времени в любом сравнении. Логарифм сжимает порядки и делает шаг «в десять раз дороже» сопоставимым с шагом «в десять раз медленнее».
Профили «качество», «баланс» и «цена» на практике
Веса задают профиль: в «качестве» на качество приходится 0.8, в «балансе» 0.5, в «цене» 0.3. Это не набор готовых ответов «экономно или хорошо», а ручка, которая определяет, насколько агрессивно роутер готов рискнуть качеством ради экономии.
- «Качество» (0.8): код, юридические тексты, аналитика, где переделка дороже токенов и времени.
- «Баланс» (0.5): повседневные задачи, чат-помощники, черновики, где небольшой риск приемлем.
- «Цена» (0.3): массовые однотипные запросы, классификация, извлечение полей, короткие пересказы, где переделать дешевле, чем переплатить.
Зачем нужна планка достаточности
Профиль задаёт пропорции, но сам по себе не мешает выбрать откровенно слабую модель ради экономии. Для этого есть планка достаточности: прогноз переводится в вероятность того, что ответ понравится человеку, через калибровку вида σ(A·q + B), подобранную по журналу отзывов. Кандидаты ниже планки в выбор не попадают, даже если они в разы дешевле.
Пример. Есть две модели: одна на 20% дешевле, но её прогноз лежит ниже планки, вторая проходит порог. В профиле «цена» дешёвая модель всё равно отсекается арифметикой, и спорить о деньгах уже не о чем. Без планки роутер взял бы её и вернул ответ, который придётся переписывать.
Слабое место очевидно: калибровка σ(A·q + B) опирается на журнал отзывов, а обучение на живых отзывах людей авторы не проверяли. Пока журнала нет, планка работает на стартовом снимке бенчмарков, то есть на чужих задачах.
Модель-судья: как FAIRouter оценивает ответ по форме и содержанию
Судья смотрит на ответ по двум осям. Форма - до 19 явно заданных пунктов: структура, наличие нужных разделов, соблюдение ограничений задания. Содержание - восемь критериев, среди которых фактология с разбором ответа на атомарные утверждения.
Атомарное утверждение - это отдельный проверяемый факт, выделенный из текста: «библиотека под Apache 2.0», «единственная зависимость - numpy», «веса хранятся в SQLite». Каждый факт сверяется с заданием отдельно, поэтому ответ не получает поблажку за красивую форму, если внутри перепутаны числа.
Итоговый балл: 0.7 содержания и 0.3 формы. Пропорция отражает результат проверки: судья различает хороший и плохой по смыслу ответ на 0.19-0.69 балла, тогда как проверка одной только формы почти не видит разницы (0.01-0.02). Форма без содержания даёт слабый сигнал для обучения, поэтому она и весит меньше.
Два контура обучения: роутер до ответа, судья после
Роутер предсказывает качество до ответа, опираясь только на текст запроса. Судья измеряет качество после. Разница между прогнозом и оценкой судьи учит роутер, а разница между оценкой судьи и отзывом человека учит самого судью.
Смысл второго контура в том, что судья тоже ошибается, и его нужно подтягивать к человеческим оценкам. Здесь и появляется журнал отзывов, на который опирается калибровка планки. Веса обоих контуров обучаются локально и лежат в SQLite: обновлять их можно на своих данных, не отправляя запросы наружу.
Что показал тест на восьми новых задачах: цифры и их трактовка
Авторы прогнали несколько стратегий на восьми новых задачах. Сравнение по качеству и цене за задачу:
| Стратегия | Качество | Цена за задачу |
|---|---|---|
| Профиль «цена» (экономный) | 0.686 | $0.00059 |
| Всегда самая дешёвая модель | 0.682 | $0.00053 |
| Всегда самая дорогая модель | 0.687 | $0.00304 |
| Профиль «качество» | 0.701 | $0.00227 |
Что из этого видно. Экономный профиль даёт 0.686 при $0.00059 за задачу, самая дорогая модель - 0.687 при $0.00304: качество различается в тысячных, цена примерно в пять раз выше. Стратегия «всегда самая дешёвая модель» показывает 0.682 при $0.00053, то есть роутер чуть точнее при сопоставимых деньгах. Профиль «качество» выжимает 0.701 при $0.00227.
Первичные цифры и оговорки к ним есть в исходном материале. Там же авторы прямо пишут, что выборка мала.
Почему разница 0.686 против 0.687 не означает ни равенства, ни победы
Разница в 0.001 меньше разброса, который дают восемь задач. Утверждать, что экономный профиль обошёл дорогую модель, нельзя. Корректная формулировка звучит скромнее: на этом наборе роутер не хуже флагмана в пределах погрешности, но стоит в разы меньше.
Именно это и есть практический вывод теста. Он не доказывает превосходство автоматики над ручным выбором на любых данных. Он показывает, что качество можно удержать на уровне дорогой модели, платя за него кратно меньше, если маршрутизация опирается на признаки задачи.
Один набор задач - плохая линза для общих выводов. Похожая логика в разборе The Struggle Bench: бенчмарк может измерять не то, что кажется. Восемь задач показывают направление, а не закономерность.
Как запустить FAIRouter: Python, C# и локальное обучение весов
Две версии кода не зависят друг от друга: Python-версия тянет только numpy, C#-версия собрана под .NET 10. Выбор языка здесь про инфраструктуру, а не про скорость модели: в обоих случаях роутер работает рядом с вашим кодом и вызывает модели через API, в том числе через OpenRouter.
Готовых весов нет, и это осознанное решение. Стартовый снимок бенчмарков даёт начальные оценки моделей, дальше веса обучаются на ваших запросах и оценках судьи, а хранятся в одном файле SQLite. Плюс: данные не уходят в чужой сервис. Минус: запуск «в одну команду» не даст качества, пока не накопится история.
Для интеграции есть режим OpenAI-совместимого сервера с одной моделью auto: приложение отправляет запрос как в обычный API, а выбор модели остаётся за роутером. Так его можно поставить перед агентом или пайплайном, не переписывая код вызовов. Если кандидаты живут на разных машинах, логика близка к распределению задач между несколькими локальными моделями.
С чего начать: минимальный сценарий подключения
- Выберите 3-5 моделей-кандидатов разного ценового уровня: одну дешёвую, две средние и одну сильную. Лишние кандидаты размывают статистику.
- Прогоните на своих типовых задачах со стартовым снимком бенчмарков и профилем «баланс». Это даст базовую линию.
- Включите судью и накопите журнал оценок: без него не будет ни работающей калибровки планки, ни уточнения прогноза.
- Включите планку достаточности, когда журнала хватит для калибровки. До этого она опирается на бенчмарки, а не на ваши задачи.
Ограничения FAIRouter: где авторы сами ставят границы
Слабые места авторы перечисляют прямо, и читать их стоит буквально.
- Малая выборка: восемь новых задач. Для статистически значимых выводов этого мало.
- Разброс оценок, внутри которого лежит разница между стратегиями. Отсюда и осторожность с интерпретацией 0.686 против 0.687.
- Обучение на живых отзывах людей не проверено. Калибровка планки достаточности описана в теории и опирается на журнал, которого в тесте не было.
- Судья работает без веб-поиска. Фактологию он сверяет с заданием и собственными знаниями, свежие данные проверить не может.
Практические следствия. На узком домене роутер может систематически недооценивать модель, которая как раз хорошо закрывает этот тип задач, если похожих примеров в обучении не было. Для задач со свежими фактами оценка судьи ограничена по построению. Экономия на малых объёмах запросов может не окупить времени на настройку и накопление журнала. Формулировки ограничений даны по описанию авторов.
Кому подходит FAIRouter и как выбрать модель под задачу на практике
Подходит командам, у которых в проде несколько моделей, высокий объём запросов и повторяющиеся типы задач. Если вы платите за токены и хотите обосновать экономию цифрами, роутер даёт механику: прогноз, профиль, журнал оценок.
Не подходит, если запросов один-два в день: настройка и обучение съедят выгоду. Не подходит для задач, где цена ошибки критична и нужен строгий контроль: там разумнее фиксированная сильная модель и ручная проверка. И осторожно со сценариями, которым нужны свежие факты: судья без веб-поиска их не проверит.
Рамка выбора модели под задачу выглядит так: определите требования к качеству, соберите 10-20 своих задач с эталонными ответами, прогоните кандидатов и сравните стоимость за задачу, а не за миллион токенов. Примерно так же устроены прикладные сравнения: тест локальных и облачных моделей на 500+ заданиях по кодингу, pandas и RAG показывает, что разница между моделями проявляется в конкретных типах работы, а не в среднем балле.
FAIRouter не даёт готового результата из коробки: без своего журнала оценок он работает на чужих бенчмарках. Ценность в порядке действий: сначала достаточность, потом цена. Такой порядок проще проверить на своих данных, чем любое соотношение цены и качества, посчитанное по средним.