Реализация маршрутизации запросов между несколькими LLM-провайдерами LLM Router

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
Реализация маршрутизации запросов между несколькими LLM-провайдерами LLM Router
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1218
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    853
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1047
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    825

Реализация маршрутизации запросов между LLM-провайдерами (LLM Router)

LLM Router — прослойка, которая направляет запросы к оптимальному провайдеру на основе характеристик запроса: сложности, типа задачи, требований к latency, стоимости. Простые классификации — GPT-4o-mini. Сложный code generation — Claude Opus. Realtime чат — Groq. Это снижает стоимость в 3–5× без потери качества.

Архитектура роутера

from anthropic import Anthropic
from openai import OpenAI
from groq import Groq
from dataclasses import dataclass
from typing import Callable, Optional
import re

@dataclass
class RoutingRule:
    name: str
    condition: Callable[[str, dict], bool]
    provider: str
    model: str
    reason: str

class LLMRouter:

    def __init__(self):
        self.openai_client = OpenAI()
        self.anthropic_client = Anthropic()
        self.groq_client = Groq()

        self.rules: list[RoutingRule] = [
            # Realtime/простые запросы → Groq (быстро и дёшево)
            RoutingRule(
                name="simple_chat",
                condition=lambda text, meta: len(text) < 200 and not meta.get("complex"),
                provider="groq",
                model="llama-3.1-8b-instant",
                reason="Short simple query — using fast inference",
            ),
            # Code generation → Claude (лучшее качество кода)
            RoutingRule(
                name="code_generation",
                condition=lambda text, meta: self._is_code_task(text),
                provider="anthropic",
                model="claude-sonnet-4-5",
                reason="Code task — Claude performs better",
            ),
            # Reasoning/математика → o3-mini
            RoutingRule(
                name="reasoning",
                condition=lambda text, meta: self._is_reasoning_task(text),
                provider="openai",
                model="o3-mini",
                reason="Reasoning task — using o3-mini",
            ),
            # Default — GPT-4o
            RoutingRule(
                name="default",
                condition=lambda text, meta: True,
                provider="openai",
                model="gpt-4o",
                reason="Default routing",
            ),
        ]

    def _is_code_task(self, text: str) -> bool:
        code_keywords = [
            "напиши код", "реализуй", "функция", "класс", "алгоритм",
            "python", "javascript", "sql", "рефакторинг", "отладь"
        ]
        return any(kw in text.lower() for kw in code_keywords)

    def _is_reasoning_task(self, text: str) -> bool:
        reasoning_keywords = [
            "докажи", "вычисли", "оптимизируй", "найди оптимальное",
            "математически", "логически следует", "минимизируй"
        ]
        return any(kw in text.lower() for kw in reasoning_keywords)

    def route(self, text: str, meta: dict = None) -> RoutingRule:
        """Определяет провайдера для запроса"""
        meta = meta or {}
        for rule in self.rules:
            if rule.condition(text, meta):
                return rule
        return self.rules[-1]  # Default

    def complete(
        self,
        messages: list[dict],
        system: str = None,
        **kwargs
    ) -> str:
        """Выполняет запрос через оптимального провайдера"""
        user_message = messages[-1]["content"] if messages else ""
        rule = self.route(user_message)

        print(f"[Router] {rule.name} → {rule.provider}/{rule.model}: {rule.reason}")

        if rule.provider == "anthropic":
            return self._call_anthropic(messages, rule.model, system, **kwargs)
        elif rule.provider == "openai":
            return self._call_openai(messages, rule.model, system, **kwargs)
        elif rule.provider == "groq":
            return self._call_groq(messages, rule.model, system, **kwargs)

    def _call_anthropic(self, messages, model, system, **kwargs) -> str:
        kwargs.pop("temperature", None) if "o" in model else None
        response = self.anthropic_client.messages.create(
            model=model,
            max_tokens=kwargs.get("max_tokens", 2048),
            system=system or "",
            messages=messages,
            temperature=kwargs.get("temperature", 0.1),
        )
        return response.content[0].text

    def _call_openai(self, messages, model, system, **kwargs) -> str:
        all_messages = []
        if system:
            all_messages.append({"role": "system", "content": system})
        all_messages.extend(messages)

        params = {"model": model, "messages": all_messages}
        if "o1" not in model and "o3" not in model:
            params["temperature"] = kwargs.get("temperature", 0.1)

        response = self.openai_client.chat.completions.create(**params)
        return response.choices[0].message.content

    def _call_groq(self, messages, model, system, **kwargs) -> str:
        all_messages = []
        if system:
            all_messages.append({"role": "system", "content": system})
        all_messages.extend(messages)

        response = self.groq_client.chat.completions.create(
            model=model,
            messages=all_messages,
            temperature=kwargs.get("temperature", 0),
        )
        return response.choices[0].message.content

Семантический роутер (ML-based)

from sentence_transformers import SentenceTransformer
import numpy as np

class SemanticRouter:
    """Роутинг на основе семантического сходства с примерами задач"""

    def __init__(self):
        self.model = SentenceTransformer("BAAI/bge-small-en-v1.5")

        # Примеры задач для каждого провайдера
        self.routes = {
            "groq_fast": [
                "what time is it", "hello", "who are you",
                "привет", "простой вопрос", "краткий ответ"
            ],
            "anthropic_code": [
                "write a python function", "debug this code",
                "refactor this class", "implement algorithm",
                "код на python", "напиши функцию"
            ],
            "openai_reasoning": [
                "solve this math problem", "prove this theorem",
                "optimize this algorithm", "logical deduction",
                "математическая задача", "докажи утверждение"
            ],
        }

        # Предвычисляем эмбеддинги
        self.route_embeddings = {}
        for route, examples in self.routes.items():
            self.route_embeddings[route] = self.model.encode(examples)

    def route(self, query: str) -> str:
        query_embedding = self.model.encode(query)

        best_route = "openai_default"
        best_score = 0.5  # Минимальный порог

        for route, embeddings in self.route_embeddings.items():
            scores = np.dot(embeddings, query_embedding) / (
                np.linalg.norm(embeddings, axis=1) * np.linalg.norm(query_embedding)
            )
            max_score = scores.max()
            if max_score > best_score:
                best_score = max_score
                best_route = route

        return best_route

Мониторинг и аналитика роутинга

from collections import defaultdict
import time

class RouterWithMetrics:
    def __init__(self, router: LLMRouter):
        self.router = router
        self.stats = defaultdict(lambda: {"count": 0, "total_time": 0, "total_cost": 0})

    def complete(self, messages: list[dict], **kwargs) -> str:
        user_message = messages[-1]["content"]
        rule = self.router.route(user_message)

        start = time.time()
        result = self.router.complete(messages, **kwargs)
        elapsed = time.time() - start

        self.stats[rule.name]["count"] += 1
        self.stats[rule.name]["total_time"] += elapsed

        return result

    def report(self) -> dict:
        return {
            route: {
                "count": stats["count"],
                "avg_time": stats["total_time"] / max(stats["count"], 1),
            }
            for route, stats in self.stats.items()
        }

Практический кейс: SaaS платформа

Профиль запросов: 60% — простые вопросы (FAQ, статусы), 25% — аналитика и суммаризация, 15% — генерация кода.

До роутинга: все запросы → GPT-4o = $450/месяц.

После роутинга:

  • Простые → Groq Llama 8B: $12/месяц
  • Аналитика → GPT-4o-mini: $45/месяц
  • Код → Claude Sonnet: $67/месяц
  • Итого: $124/месяц (-72%)

Качество при A/B тестировании: сопоставимое (98% пользователей не заметили разницу).

Сроки

  • Rule-based роутер: 2–3 дня
  • Семантический роутер с ML: 1 неделя
  • Мониторинг + A/B тестирование: 1 неделя