Перейти к содержанию
Публикация AiManual

Мультиагентная маршрутизация локальных LLM: как распределить задачи между несколькими машинами

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

Коротко

Что будет в материале

  1. 01

    Есть ли готовый роутер для локальных LLM? Короткий ответ

  2. 02

    Почему Claude Code распределяет работу, а Codex и Sol/Astra - нет

  3. 03

    Архитектура мультиагентной маршрутизации: как это работает

  4. 04

    Какие задачи отдавать слабым узлам, а какие - основной машине

Есть ли готовый роутер для локальных LLM? Короткий ответ

Готового единого роутера, который принимает задачу, сам разбивает её на подзадачи и распределяет их между разнородными локальными моделями на разных машинах, в доступных материалах нет. Такую потребность сформулировал пользователь r/LocalLLaMA: у него три машины разной мощности, и он хочет запустить эндпоинт или роутер, подключённый к harness, который делит задачу и раскидывает части по моделям (обсуждение на Reddit).

Частичные решения закрывают отдельные куски схемы. Оркестраторы сценариев вроде n8n принимают вебхуки, вызывают API нейросетей, складывают результаты в базу и отправляют ответ пользователю, но тяжёлая работа при этом идёт на стороне внешнего провайдера, а не на вашем железе (разбор сервера для нейросетей и n8n). Это оркестрация вызовов, а не маршрутизация инференса между вашими машинами. Разница принципиальная: в первом случае вы управляете последовательностью шагов, во втором - тем, на каком узле считается каждый токен.

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

Почему Claude Code распределяет работу, а Codex и Sol/Astra - нет

Идея появилась из наблюдения за Claude Code: работа там заметно распределяется между вызовами модели, и именно это, по словам пользователя, делает инструмент быстрым. В Codex и Sol/Astra такого распределения он не заметил (описание наблюдения).

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

Для локального контура это значит одно: агентную логику придётся строить самому. Деление задачи на шаги - это прослойка над моделью, а не её свойство. Модель на 3B не станет планировщиком только потому, что вы попросите её «думать пошагово»; планировщик должен быть отдельным кодом или отдельным вызовом с чётким форматом ответа.

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

Архитектура мультиагентной маршрутизации: как это работает

Цепочка выглядит так: клиент → эндпоинт или роутер → harness (декомпозиция) → выбор модели → выполнение → агрегация результатов. Клиент видит один адрес и один формат ответа, вся возня с машинами происходит внутри.

Роутеру нужно знать о доступных узлах и их реальных возможностях. В разобранном примере это три машины: основная запускает Qwen3.8 Flash Next со скоростью 13-15 tps, Mac Mini на 16 ГБ легко тянет Orninth 9B или Gemma4 12B, а Raspberry Pi 5 хорошо работает с моделью размером 3B (состав парка машин). Названия моделей приведены так, как они указаны в источнике, и могут содержать неточности. Для роутера эти данные превращаются в таблицу возможностей: кто умеет держать длинный контекст, кто отвечает быстро, а кто годится только для коротких операций.

Полезно сразу разделить два понятия, которые постоянно путают: оркестрация и запуск моделей у себя. При оркестрации сервер принимает вебхуки, дёргает API, обрабатывает JSON, а вычисления идут на чужой стороне. При запуске моделей локально всё считается на вашем железе, и требования к ресурсам возрастают на порядок (разбор двух задач). Роутер локальных LLM живёт во второй категории: он не столько оркестрирует, сколько решает, куда положить вычисление.

Компоненты системы: роутер, harness, воркеры

  • Роутер принимает запрос от клиента, определяет тип задачи и выбирает узел. Минимальная версия умеет только это, без разбиения на шаги.
  • Harness делит задачу на подзадачи. Он может быть отдельным сервисом, а может жить внутри роутера как функция. Вариантов реализации два: жёсткие правила по типу задачи или отдельный вызов лёгкой модели-планировщика, который возвращает список шагов в структурированном виде.
  • Воркеры - машины с запущенными моделями и HTTP-эндпоинтами. Каждая отдаёт роутеру свои характеристики: имя модели, размер, скорость генерации, максимальное контекстное окно.
  • Агрегатор собирает ответы подзадач в один текст. Здесь же логично считать, какой узел сколько запросов обработал и сколько времени занял каждый шаг.

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

Как роутер выбирает модель для подзадачи

Выбор строится на трёх признаках. Первый: тип задачи. Классификация, извлечение сущностей и короткие ответы не требуют длинных рассуждений. Генерация кода и многошаговые выводы требуют. Второй: длина контекста на входе. Модель на 3B с маленьким окном не примет длинный документ, даже если задача сама по себе простая. Третий: цена ошибки. Черновик письма можно переписать, неудачный рефакторинг в рабочем репозитории обойдётся дороже.

Практичная схема выбора: сначала отсечь узлы, которые физически не примут вход по длине контекста, затем разложить оставшиеся по ожидаемой сложности задачи. Скорость генерации на основном узле в 13-15 tps означает, что часовая задача на нём считается долго, а быстрый слабый узел успеет за это время обработать много мелких запросов.

Это эвристики, а не жёсткие правила. Порог, после которого задача уходит наверх, придётся подбирать под свои модели и свой текст: цифры по tps из чужого парка машин напрямую не переносятся.

Какие задачи отдавать слабым узлам, а какие - основной машине

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

Тип задачиКуда отправлятьПочему
Классификация, тегирование, извлечение сущностей, фильтрация спамаRaspberry Pi 5, модель 3BКороткий вход, короткий ответ, почти нет рассуждений, ошибку легко проверить по правилам
Суммаризация коротких текстов, перефразирование, черновики формулировокMac Mini 16 ГБ, Orninth 9B или Gemma4 12BСреднее качество и средний контекст при разумной скорости
Ответы по контексту (RAG), разбор небольших документовMac Mini 16 ГБНужен контекст на тысячи токенов, 3B здесь теряет связность
Генерация кода, многошаговые рассуждения, длинные документыОсновная машина, Qwen3.8 Flash NextТолько там хватает качества и объёма контекста, скорость 13-15 tps терпима для фоновых задач
Планирование декомпозицииОсновная машинаОшибка планировщика ломает всю цепочку, экономить на нём не стоит

Простая арифметика объясняет, зачем вообще возиться: 3B модель на Pi 5 отвечает заметно быстрее, чем 13-15 tps, но качество на сложных шагах у неё ниже. Забирать у большой машины десятки мелких классификаций имеет смысл именно потому, что они не требуют качества большой модели.

Порог качества стоит проверять на своих данных. Универсальный лидерборд редко отвечает на вопрос, какая модель подойдёт для ваших документов и таблиц, поэтому в разборе идеи прикладного бенчмарка LLM описан протокол оценки под конкретный сценарий, который можно повторить локально. Матрица выбора моделей до 12B по задачам есть в обзоре малых LLM и их практических кейсов.

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

Готовые инструменты для оркестрации: n8n и не только

n8n - визуальный конструктор сценариев: блоки соединяются стрелками, между ними ходят данные. Типичный сценарий выглядит так: пришло сообщение в Telegram → отправить в модель → сохранить ответ в таблицу → ответить пользователю. К ресурсам такой оркестратор нетребователен, потому что основная нагрузка - ожидание ответов от внешних API (описание роли n8n).

Ориентиры по железу для самого n8n такие: до 10 сценариев и десятки запусков в день хватит 2 ядер и 4 ГБ; десятки сценариев с очередями и отдельной базой - 4 ядра и 8 ГБ; сотни запусков в час с обработкой файлов - 8 ядер и 16 ГБ. На сервере с 2 ГБ n8n запускается, но памяти впритык: Node.js под нагрузкой легко выбирает гигабайт, а рядом должна жить база. Для боевых сценариев разумный минимум - от 4 ГБ (требования к ресурсам).

Как часть системы n8n пригодится: он принимает вебхук от клиента, вызывает ваш harness, а затем раскидывает подзадачи по HTTP-эндпоинтам машин. Но выбирать модель по типу задачи и следить за очередью подзадач он не станет - эту логику пишете вы.

Ограничения n8n для локальных LLM

n8n не знает характеристик локальных моделей. У него нет представления о том, что на одной машине 3B и малое контекстное окно, а на другой 9B и скорость 13-15 tps. Он не умеет выбирать модель по типу задачи, не управляет очередью подзадач и не ограничивает параллелизм так, чтобы Raspberry Pi 5 не захлебнулся десятком одновременных запросов. Всё это - код в отдельных нодах.

Есть и эксплуатационные детали. По умолчанию n8n хранит всё в SQLite, и для десятка сценариев этого достаточно. При активных запусках история выполнений быстро раздувает файл, интерфейс начинает тормозить; PostgreSQL снимает этот вопрос сразу. Открытый в интернет интерфейс n8n - это доступ к ключам от API, CRM, почты и базы в чужих руках. Минимальная защита - базовая авторизация и длинный пароль, а если сценарии не принимают внешние вебхуки, надёжнее не публиковать интерфейс наружу и заходить в него через SSH-туннель. Ключи вносите через раздел Credentials, а не в ноды кода: экспортированный сценарий легко уходит в чат или репозиторий вместе с ключом.

Ограничения и подводные камни при работе с разнородными локальными моделями

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

  • Разная скорость инференса. Узлы отвечают за разное время: 13-15 tps на основной машине против быстрых коротких ответов модели 3B. Любой шаг, который ждёт самый медленный узел, тормозит всю цепочку.
  • Сеть. Передача промпта и ответа между машинами добавляет задержку, а длинный контекст весит десятки и сотни килобайт. На домашней сети это терпимо, через интернет - уже нет.
  • Формат API. Ollama, llama.cpp и OpenAI-совместимые серверы отдают ответы похоже, но не идентично: отличаются поля, работа со стримингом, обработка ошибок. Роутеру придётся приводить ответы к одному виду или писать под каждый бэкенд свой адаптер.
  • Контекстное окно. У моделей на 3B, 9B и 12B оно разное. Декомпозиция, которая не учитывает этот лимит, отправит на слабый узел задачу, которую он физически не примет.
  • Память и VRAM. Mac Mini на 16 ГБ и Raspberry Pi 5 ограничены общей памятью, поэтому размер модели и длина контекста на них взаимосвязаны: чем больше окно, тем меньше остаётся на всё остальное.
  • Отладка. Когда ответ приходит из трёх мест, искать причину ошибки тяжелее. Без логирования по каждому шагу (какой узел, какая модель, сколько токенов, сколько времени) разбираться в сбоях придётся наугад.

Линейного ускорения от добавления машин не будет. Если узкое место - один медленный шаг на основной машине, вторая и третья машины его не ускорят, они просто не участвуют в этом шаге. Полезно сначала измерить, где реально уходит время, и только потом распределять задачи. Практика показывает, что оптимизация самого инференса иногда даёт больше, чем перестройка всей схемы: в разборе оптимизации локального инференса в llama.cpp видно, какие патчи давали прирост, а какие приносили регрессии.

Как собрать минимальный прототип роутера своими руками

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

  1. Поднимите на каждой машине HTTP-сервер с моделью: Ollama, llama.cpp или любой OpenAI-совместимый бэкенд. Приведите их к единому адресу вида /v1/chat/completions, чтобы роутер не разбирался с разными форматами.
  2. Напишите роутер на FastAPI или Node.js. Он принимает запрос, определяет тип задачи и перенаправляет его на нужный эндпоинт. Определение типа на старте - простые правила по ключевым словам, длине входа и требуемому формату ответа.
  3. Добавьте реестр узлов: имя машины, адрес, модель, размер, скорость, максимальное контекстное окно. Роутер должен уметь исключать узел из выбора, если вход не помещается в окно.
  4. Заведите очередь задач. Она нужна, чтобы пачка запросов не легла на слабый узел и не превратила его в бутылочное горло.
  5. Логируйте каждый шаг: узел, модель, время, число токенов. Без этих данных вы не поймёте, помогла ли схема.
  6. Только после этого добавляйте harness. Сначала правила: если задача длинная, режьте её на части по абзацам или по файлам. Автоматическую декомпозицию через отдельный вызов модели подключайте, когда видите, что правила перестали справляться.

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

Итог: что реально работает, а что - нет

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

Распределение задач окупается, когда типы операций действительно разные. Короткие классификации и извлечение сущностей на модели 3B, средние тексты на 9B-12B, код и многошаговые выводы на основной машине - такая раскладка снимает с мощного узла рутину и оставляет ему то, что он умеет делать лучше остальных.

Что не работает: ожидание линейного ускорения, автоматическое появление декомпозиции при подключении второй машины и попытки обойтись без логирования. Сетевые задержки, разные форматы API и разное контекстное окно - это не мелочи, а основные источники проблем в распределённой схеме.

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

Подписаться на канал