FAIRouter вышел на NuGet. Выбираем языковую модель под задачу в несколько строк на C#

—

от автора

Ранее мы рассказывали о FAIRouter, нашей библиотеке для маршрутизации запросов. Коротко напомню, что она делает. Есть несколько языковых моделей, у них разные цены, скорость и сильные стороны. FAIRouter сам выбирает модель под задачу, проверяет готовый ответ и учится на отзывах. Общая схема работы показана на рисунке 1.

Рисунок 1. Как устроен FAIRouter: маршрутизатор выбирает исполнителя, судья принимает ответ, и оба учатся

Рисунок 1. Как устроен FAIRouter: маршрутизатор выбирает исполнителя, судья принимает ответ, и оба учатся

Версия на C# теперь опубликована на NuGet, поэтому собирать ее из исходников больше не нужно. В этой статье покажу, как поставить пакет и что с ним можно делать. Все примеры ниже были собраны на пакете версии 0.1.0.

Установка

Понадобится .NET 10. Создадим консольный проект и подключим пакет:

dotnet new console -f net10.0 -n RouterDemocd RouterDemodotnet add package FAI.Router

Также нужен ключ поставщика моделей. В примерах будем использовать OpenRouter, ключ положим в переменную среды:

$env:OPENROUTER_API_KEY = "ваш ключ"

Первый запрос

Заменим содержимое Program.cs следующим кодом:

using FAI.Router;FaiRouter router = await FaiRouter.FromOpenRouterAsync(    apiKey: Environment.GetEnvironmentVariable("OPENROUTER_API_KEY")!,    modelIds:    [        "google/gemini-2.5-flash",        "openai/gpt-4.1-mini",        "anthropic/claude-haiku-4.5"    ],    databasePath: "fai-router.db");RouterAnswer answer = await router.AskAsync(    "Напиши обзор методов кластеризации на 1500 знаков");Console.WriteLine($"{answer.Winner.Name}: {answer.Score}");Console.WriteLine(answer.Text);Console.WriteLine(answer.Critic);

Здесь modelIds задает список кандидатов, а в файле fai-router.db хранятся веса и журнал ходов. Если файла нет, он будет создан. В ответе нас интересуют четыре поля:

  • Winner показывает, какая модель была выбрана;

  • Text содержит ответ модели;

  • Score содержит оценку судьи от 0 до 1;

  • Critic показывает, чем ответ отличается от заказа, по пунктам.

Судья проверяет ответ автоматически. Например, если в задании было четыре раздела и таблица, а в ответе один раздел без таблицы, это будет видно в Critic.

Список моделей можно вообще не задавать. Тогда выбор идет среди 54 популярных моделей из комплекта пакета:

FaiRouter router = await FaiRouter.FromOpenRouterAsync(    apiKey: Environment.GetEnvironmentVariable("OPENROUTER_API_KEY")!,    databasePath: "fai-router.db");

Обучение на отзывах

Самое интересное начинается, когда маршрутизатор получает отзывы. Отзыв задается числом от 0 до 1, где 1 означает отличный ответ, а 0.2 слабый:

router.Feedback(answer.RoundId!.Value, score: 1.0);router.Train(epochs: 10);router.Save();

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

Что важнее, цена или качество

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

RouterAnswer cheap = await router.AskAsync(    prompt, weights: RouteWeights.Price);RouterAnswer strong = await router.AskAsync(    prompt, weights: RouteWeights.Quality);

Часто удобнее другое правило: найти тех, кто справится с задачей, и среди них выбрать самого дешевого и быстрого. Для этого есть планка достаточности, т.е. минимальная вероятность того, что ответ понравится человеку:

RouterAnswer enough = await router.AskAsync(prompt, bar: 0.7);Console.WriteLine(enough.Reached);

Поле Reached показывает, дотянул ли кто-то до планки. Планка начинает работать, когда накопится хотя бы три отзыва человека.

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

В AskAsync можно передать диалог целиком. Задание распознается по последней реплике пользователя, а модель видит всю историю:

using AI.LLM.Core.Models.Common.Messages;LLMMessage[] dialog =[    new(LLMMessage.UserRole, "Что такое кластеризация?"),    new(LLMMessage.AssistantRole, "Это разбиение объектов на группы."),    new(LLMMessage.UserRole, "Теперь объясни это школьнику")];RouterAnswer replied = await router.AskAsync(dialog);

Другие поставщики

Пакет работает не только с OpenRouter. Всего есть три способа подключения:

Метод

Куда подключается

FromOpenRouterAsync

OpenRouter

FromFractalRouterAsync

FractalRouter

FromOpenAiCompatibleAsync

любой сервер с протоколом OpenAI chat completions

Последний вариант подойдет и для своей модели. Цену такой модели нужно указать самому:

FaiRouter local = await FaiRouter.FromOpenAiCompatibleAsync(    baseUrl: "http://localhost:8000/v1",    apiKey: "local",    modelIds: ["my-model"],    prices: new Dictionary<string, Price>    {        ["my-model"] = new Price(0, 0)    },    judgeModel: "my-model");

Судья работает через того же поставщика, по умолчанию на модели openai/gpt-4o-mini. Если на вашем сервере такой модели нет, укажите другую в параметре judgeModel, как в примере выше.

Не только модели

Выбирать можно не только модели, но и инструменты с агентами. Для этого в конструктор передаются свои кандидаты и функция, которая их запускает. Рассмотрим пример с двумя вариантами поиска:

using AI.LLM.Core.Models.Common.Messages;using AI.LLM.Services.LLM;using FAI.Router;using FAI.Router.LLM;using FAI.Router.RoutedElements;BaseRoutedElement deepSearch = new(){    Name = "deep-search",    DPMTInp = 5,    DPMTOutp = 20,    TPS = 30};BaseRoutedElement fastSearch = new(){    Name = "fast-search",    DPMTInp = 1,    DPMTOutp = 2,    TPS = 200};LLMBase judge = new OpenAiCompatibleLlm(    Providers.OpenRouter,    "ваш ключ",    "openai/gpt-4o-mini");FaiRouter tools = new(    [deepSearch, fastSearch],    (candidate, messages, token) =>        RunSearchAsync(candidate.Name!, messages, token),    "tools.db",    llm: judge);

Здесь DPMTInp и DPMTOutp задают цену за миллион токенов, а TPS скорость в токенах в секунду. Для инструмента эти числа условные, важно только их соотношение. Функцию RunSearchAsync вы пишете сами, она запускает нужный поиск и возвращает текст. Такие кандидаты стартуют со случайными весами, поэтому первое время им нужны ваши отзывы.

Итоги

Таким образом, чтобы попробовать FAIRouter, достаточно одной команды dotnet add package FAI.Router и нескольких строк кода. Пакет распространяется под лицензией Apache-2.0, исходный код и подробная документация опубликованы на GitHub, а замечания и вопросы можно оставить в разделе issues.

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

ссылка на оригинал статьи https://habr.com/ru/articles/1101942/