The Jerusalem PostBesieged by the Houthis, Yemen’s Taiz is teetering on the edge of humanitarian catastropheCNN TürkFenerbahçe’den Kelechi Iheanacho hamlesiInquirer100 koi released into Burnham Lake in Baguio to control algae growthPunchThree killed, four injured in Osun building collapseEl ComercioQué canales TV transmiten Barcelona vs Alavés: horarios del partido por la fecha 11 de LaLigaBollywood HungamaDiljit Dosanjh addresses Satluj controversy at Delhi concert; says “Don't teach Punjabis how to fight”ESPNTransfer rumors, news: Man United to replace Fernandes with Fermin?한겨레168명 숨진 홍콩 아파트 화재 참사 공식 보고서 공개…“인간 부주의로 발생한 참사”ZDF heuteAktuelle Pressemitteilungen des ZDFUOLFrança vive novo dia de manifestações em meio a temor de confrontos violentos e saquesColliderOne of the Best Horror Movies of the Decade Is Officially Streaming Free Just in Time for Halloweenالنهارتركيا... اصطدام حافلة سياحية بشاحنة وإصابة عشرة أشخاص
The Daily Newsstand · Free, Always
Saturday, October 31, 2026

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

Translate

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

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

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

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

Установка

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

dotnet new console -f net10.0 -n RouterDemo
cd RouterDemo
dotnet 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.

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

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.