Проектирование систем с использованием множества моделей: когда одной модели недостаточно

Выбирайте самый простой работающий паттерн.

Содержимое страницы

Системы с одной моделью просты. Системы с множеством моделей мощны. Сложность не в выборе моделей — а в проектировании архитектуры, которая их оркестрирует.

Мульти-модельная система — это не о том, чтобы иметь больше моделей. Это о том, чтобы иметь правильную модель для правильной задачи в правильный момент.

Дизайн-паттерны мульти-модельных LLM-систем

Паттерны архитектуры

Пять паттернов покрывают большинство случаев использования:

Паттерн Сложность Когда использовать Компромисс
Одна модель Минимальная Прототипирование, простые задачи Ограниченные возможности
Последовательный Низкая Многошаговые рабочие процессы Более высокая задержка
Параллельный Средняя Независимые задачи Более высокая стоимость
Иерархический Высокая Сложное рассуждение Сложная оркестрация
Ансамблевый Максимальная Критические решения Максимальная стоимость

Выбирайте самый простой рабочий вариант. Сложность реальна, и она накапливается.

Последовательная архитектура

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

Паттерн 1: Конвейер (Pipeline)

Паттерн «Конвейер» — вывод одной модели подаётся следующей:

class ModelPipeline:
    def __init__(self):
        self.models = [
            {"model": "qwen3-1.7b", "task": "classify"},
            {"model": "qwen3-8b", "task": "extract"},
            {"model": "qwen3-32b", "task": "reason"},
        ]

    def process(self, input: str) -> str:
        current = input
        for model_config in self.models:
            current = self.call_model(
                model_config["model"],
                self.create_prompt(model_config["task"], current)
            )
        return current

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

Паттерн 2: Маршрутизатор (Router)

Паттерн «Маршрутизатор» — классифицируйте задачу, направьте на специалиста:

class ModelRouter:
    def __init__(self):
        self.classifier = "qwen3-1.7b"
        self.specialists = {
            "code": "qwen2.5-coder-7b",
            "math": "qwen3-32b",
            "creative": "claude-sonnet-4",
            "general": "qwen3-8b",
        }

    def route(self, prompt: str) -> str:
        task_type = self.classify(prompt)
        model = self.specialists.get(task_type, self.specialists["general"])
        return self.call_model(model, prompt)

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

Параллельная архитектура

Обрабатывайте независимые задачи одновременно.

Паттерн 1: Ветвление (Fan-Out)

Ветвление — пропустите один и тот же промпт через несколько моделей:

import asyncio

class ModelFanOut:
    def __init__(self):
        self.models = [
            "qwen3-8b",
            "qwen3-32b",
            "claude-sonnet-4",
        ]

    async def process(self, prompt: str) -> list[str]:
        tasks = [self.call_model(model, prompt) for model in self.models]
        return await asyncio.gather(*tasks)

Полезно для сравнения, A/B-тестов или когда нужно выбрать лучший вывод. Дорого, но прирост качества оправдан для критических решений.

Паттерн 2: Голосование (Voting)

Голосование — объедините выводы через консенсус:

class ModelVoting:
    def __init__(self):
        self.models = [
            "qwen3-8b",
            "qwen3-32b",
            "claude-sonnet-4",
        ]

    def vote(self, prompt: str) -> str:
        responses = [self.call_model(model, prompt) for model in self.models]
        from collections import Counter
        votes = Counter(responses)
        return votes.most_common(1)[0][0]

Большинственное голосование работает для классификации. Для генеративных задач это сложнее — вам нужна семантическая близость, а не точное совпадение.

Иерархическая архитектура

Используйте модели на разных уровнях абстракции.

Паттерн 1: Планировщик-Исполнитель (Planner-Executor)

Планировщик-исполнитель — сильная модель планирует, маленькие модели исполняют:

class PlannerExecutor:
    def __init__(self):
        self.planner = "qwen3-32b"
        self.executors = {
            "code": "qwen2.5-coder-7b",
            "search": "qwen3-8b",
            "math": "qwen3-8b",
        }

    def process(self, task: str) -> str:
        plan = self.call_model(self.planner, f"Plan: {task}")
        results = []
        for step in self.parse_plan(plan):
            executor = self.executors.get(step["type"], "qwen3-8b")
            result = self.call_model(executor, step["prompt"])
            results.append(result)
        return self.call_model(self.planner, f"Synthesize: {results}")

Планировщик выполняет основную работу. Исполнители обрабатывают конкретные задачи. Этот паттерн хорошо работает, когда этап планирования дорогой, а этапы выполнения — дешёвые.

Паттерн 2: Супервизор-Работник (Supervisor-Worker)

Супервизор-работник — супервизор делегирует и проверяет:

class SupervisorWorker:
    def __init__(self):
        self.supervisor = "qwen3-32b"
        self.workers = ["qwen3-8b", "qwen2.5-coder-7b"]

    def process(self, task: str) -> str:
        assignments = self.call_model(self.supervisor, f"Assign: {task}")
        results = []
        for assignment in self.parse_assignments(assignments):
            result = self.call_model(
                assignment["worker"], assignment["task"]
            )
            results.append(result)
        return self.call_model(self.supervisor, f"Review: {results}")

Супервизор — узкое место. Он планирует, делегирует и проверяет. Убедитесь, что он достаточно быстр, иначе вся система замедлится.

Ансамблевая архитектура

Объединяйте несколько моделей для критических решений.

Паттерн 1: Взвешенный ансамбль (Weighted Ensemble)

Взвешенный ансамбль — оцените вывод каждой модели, выберите самый высокий:

class WeightedEnsemble:
    def __init__(self):
        self.models = {
            "qwen3-32b": 0.5,
            "claude-sonnet-4": 0.3,
            "qwen3-8b": 0.2,
        }

    def decide(self, prompt: str) -> str:
        responses = {
            model: self.call_model(model, prompt)
            for model in self.models
        }
        scores = {}
        for model, response in responses.items():
            score = self.evaluate(response) * self.models[model]
            scores[response] = scores.get(response, 0) + score
        return max(scores, key=scores.get)

Веса отражают вашу уверенность в каждой модели. Настраивайте их на основе реальной производительности, а не бенчмарков.

Паттерн 2: Консенсусный ансамбль (Consensus Ensemble)

Консенсусный ансамбль — требуйте согласия, эскалируйте, если его нет:

class ConsensusEnsemble:
    def __init__(self, threshold: float = 0.7):
        self.threshold = threshold
        self.models = [
            "qwen3-32b",
            "claude-sonnet-4",
            "qwen3-8b",
        ]

    def decide(self, prompt: str) -> str:
        responses = [
            self.call_model(model, prompt)
            for model in self.models
        ]
        from collections import Counter
        votes = Counter(responses)
        max_votes = max(votes.values())

        if max_votes / len(self.models) >= self.threshold:
            return votes.most_common(1)[0][0]

        return self.call_model("qwen3-32b", prompt)

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

Когда мульти-модельные системы имеют смысл

Мульти-модельные системы оправданы, когда у вас смешанная нагрузка, нужен высокий уровень качества для критических решений или вы оптимизируете стоимость или задержку.

Они не оправданы, когда все задачи имеют схожую сложность, вы прототипируете или простота важнее оптимизации.

Правило большого пальца: начинайте с одной модели. Добавляйте больше, когда упёрлись в реальное ограничение — стоимость, задержку или качество. Не проектируйте сложность раньше, чем она нужна.

Компромиссы

Паттерн Стоимость Задержка Качество Сложность
Одна модель Минимальная Минимальная Переменное Минимальная
Последовательный Средняя Высокая Высокое Средняя
Параллельный Высокая Низкая Высокое Средняя
Иерархический Высокая Высокая Максимальное Высокая
Ансамблевый Максимальная Средняя Максимальное Максимальная

Каждый паттерн жертвует чем-то. Выберите тот, который соответствует вашим ограничениям.

Связанные статьи

Подписаться

Получайте новые материалы про системы, инфраструктуру и AI engineering.