Как сделать простой LLM Router для AI coding

OdiRouter Team
Схема LLM Router распределяет задачи AI coding между быстрыми, сильными и review-моделями

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

Пошаговый LLM Router для AI coding: профили задач, OpenAI-compatible API, выбор моделей, безопасный Fallback, Token-бюджет, Review и метрики.

Программа Яндекса 75/75/75 задаёт амбициозную цель: к концу 2026 года AI регулярно используют не менее 75% разработчиков, он участвует минимум в 75% изменений и генерирует не менее 75% кода внутри таких изменений. Независимо от отношения к этой метрике, масштаб меняет экономику. Когда AI coding становится повседневной инфраструктурой, модель нужно выбирать не вручную в каждом IDE, а по правилам.

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

Сначала определим не модели, а классы работы

Профиль

Задачи

Требования

FAST

Commit message, объяснение ошибки, поиск символа, короткая документация

Низкая задержка, низкая цена, небольшой контекст

BUILD

Unit-тесты, локальная функция, типизация, небольшой рефакторинг

Хороший код, Tool Calling, средний контекст

DEEP

Архитектурный анализ, сложная миграция, межсервисный баг

Reasoning, большой контекст, устойчивость на длинной задаче

REVIEW

Security Review, критичный Diff, поиск нарушения инвариантов

Независимый контекст, другая модель или семейство

Названия моделей вынесем в конфигурацию. Приложение должно просить профиль BUILD, а не знать, какой релиз провайдера сегодня закреплён за ним.

Архитектура в пяти компонентах

1. Classifier определяет профиль задачи.

2. Policy учитывает риск, данные и максимальную стоимость.

3. Registry хранит модели, возможности и допустимые Fallback.

4. Gateway client отправляет OpenAI-compatible запрос.

5. Telemetry записывает выбор, Token, задержку и результат Eval.

Для первой версии не нужен отдельный ML-классификатор. Явный параметр от клиента и несколько правил прозрачнее модели, которая тратит вызов только на выбор другой модели.

Конфигурация профилей

profiles:
  fast:
    primary: fast_model
    fallback: fast_model_backup
    max_input_tokens: 16000
    max_cost_usd: 0.02

  build:
    primary: coding_model
    fallback: coding_model_backup
    max_input_tokens: 64000
    max_cost_usd: 0.15

  deep:
    primary: reasoning_model
    fallback: reasoning_model_backup
    max_input_tokens: 200000
    max_cost_usd: 1.20

  review:
    primary: independent_review_model
    max_cost_usd: 0.40

Здесь используются алиасы. Реальные Model ID и цены меняются без изменений клиентского кода. Главное — не назначать один и тот же Fallback всем профилям.

Простой классификатор

from enum import StrEnum

class Profile(StrEnum):
    FAST = "fast"
    BUILD = "build"
    DEEP = "deep"
    REVIEW = "review"

def choose_profile(task: str, files: int, context_tokens: int,
                   production: bool, security_review: bool) -> Profile:
    if security_review:
        return Profile.REVIEW
    if production or files > 12 or context_tokens > 60_000:
        return Profile.DEEP
    if task in {"commit_message", "explain_error", "write_docstring"}:
        return Profile.FAST
    return Profile.BUILD

Это не «умный» Router, зато его решение легко объяснить. Позже правила можно дополнить историческими данными: какой профиль успешнее проходил тесты, сколько требовал повторов и сколько стоил принятый Diff.

Вызов через единый OpenAI-compatible API

from openai import OpenAI

client = OpenAI(
    base_url=os.environ["LLM_BASE_URL"],
    api_key=os.environ["LLM_API_KEY"],
)

def run_task(profile, messages, request_id):
    model = registry[profile]["primary"]
    return client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0.1,
        extra_headers={
            "X-Request-ID": request_id,
            "X-Task-Profile": profile,
        },
    )

OdiRouter.ai и другие OpenAI-compatible gateways позволяют поставить разные модели за таким интерфейсом. Router остаётся в вашем приложении: именно он понимает, что задача относится к production или требует независимого Review.

Fallback без неприятных сюрпризов

Переключаться стоит только при технической ошибке: Timeout, Rate Limit, временная недоступность. Плохой ответ нельзя надёжно определить по HTTP-коду.

TRANSIENT = {408, 429, 500, 502, 503, 504}

def call_with_fallback(profile, messages, request_id):
    candidates = registry[profile]["models"]
    errors = []

    for model in candidates:
        try:
            return call(model, messages, request_id)
        except APIStatusError as exc:
            errors.append((model, exc.status_code))
            if exc.status_code not in TRANSIENT:
                raise

    raise AllModelsUnavailable(profile, errors)

Для DEEP-профиля резерв должен поддерживать нужный контекст и инструменты. Если такого резерва нет, лучше вернуть контролируемую ошибку, чем незаметно выполнить архитектурную задачу моделью для Commit Message.

Бюджет считаем до и после вызова

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

Автоматически переключать задачу на слабую модель только из-за цены опасно: пользователь думает, что получил тот же уровень анализа.

Контекст дороже модели

Router не исправит ситуацию, если в каждый запрос отправляется весь репозиторий. Для профилей полезны разные стратегии:

· FAST получает сообщение об ошибке и ближайший фрагмент;

· BUILD — сигнатуры, связанные файлы, тесты и локальные правила;

· DEEP — карту компонентов, ADR, критические пути и выбранные исходники;

· REVIEW — задачу, Diff, инварианты и тесты, но не оправдания модели-автора.

Последний пункт снижает confirmation bias: Reviewer не должен сначала прочитать уверенное объяснение, почему решение прекрасно.

Маршрутизация после генерации

Для критичных изменений pipeline не заканчивается первым ответом:

BUILD → tests → static analysis → REVIEW → human approval
DEEP  → plan approval → implementation → tests → REVIEW → canary

Модель Review ищет нарушение инвариантов, проблемы безопасности, лишние зависимости и отсутствующие тесты. Она не переписывает код автоматически: замечания возвращаются автору или человеку.

Какие события логировать

Поле

Зачем

request_id, repository, task_type

Связать вызов с задачей и Diff

selected_profile, model, fallback_reason

Объяснить решение Router

input/output tokens, cost, latency

Считать экономику целиком

tests_passed, review_findings

Связать цену с качеством

accepted_lines, reverted_30d

Не путать генерацию с полезным кодом

Не оптимизируйте только Token

Дешёвая модель, которая требует три повтора и час Review, может быть дороже сильной. Для каждой категории считайте Cost per Accepted Task:

(стоимость всех вызовов + стоимость Review + стоимость исправлений)
──────────────────────────────────────────────────────────────
                 число принятых задач

Это превращает выбор модели из вкусового спора в инженерный эксперимент.

Что говорит 75/75/75 — и чего не говорит

По данным Яндекса, 73% его разработчиков уже регулярно используют AI, а более половины нового кода создаётся с помощью генеративных моделей. Компания подчёркивает, что архитектура, контроль качества и ответственность остаются у инженеров.

Процент сгенерированного кода измеряет проникновение инструмента, но не качество системы. Для своей команды добавьте Accepted Task Rate, Rework after 30 days, число найденных Review-дефектов и время восстановления после AI-изменений.

Вывод

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

Commit Message, Unit-тест и архитектурная миграция — не три Prompt для одной модели. Это три класса работы. Как только команда признаёт это в коде, AI coding перестаёт быть набором личных привычек и становится управляемой инженерной системой.


#AI coding#LLM Router#OdiRouter.ai#OpenAI-compatible API#Yandex 75/75/75#code review AI#coding agent#fallback LLM#ИИ для разработки#маршрутизация моделей