Предыдущие части "учебника по харнессу" были достаточно очевидными: durable state, события, approvals и фоновые задачи хорошо раскладываются на отдельные сущности и переходы состояния. Их можно описывать почти как учебную модель, постепенно добавляя таблицы, статусы и обработчики.

Ии-ассистент перед выполнением запроса
Ии-ассистент перед выполнением запроса

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

Если вы начинали пилить своего ии-ассистента, то в какой-то момент вам точно должно было показаться, что он беспросветно туп, и вы просто не понимаете, почему ваш агент, запущенный на Qwen или Gemma с, в лучшем случае, 30b параметрами, в закрытом контуре со своими инструментами, конкурирующий за видеокарту со всеми прочими корпоративными сервисами, работает не так классно, как последние модели Claude Code и ChatGPT, с которыми вас теперь все будут сравнивать.

Ладно, окей. Слабые модели не для вас. У вас есть апи-ключ от мощной модели и langchain, с помощью которых вы быстро накидали durable агента с тулзами, долговременной и короткой памятью и счастливо потираете ручки, ожидая профита. Через две недели вы взглянули на статистику запросов и поняли, что если вы сейчас же его не вырубите, то вся ваша зп уйдет на оплату счета за токены.

Используем ChatGPT, когда пользователь написал в чат "спасибо"
Используем ChatGPT, когда пользователь написал в чат "спасибо"

Мне потребовалось месяца три, чтобы привести в порядок систему, при которой ии не кажется попугаем, а счет за токены не улетает в стратосферу. Поэтому собственно третья часть "учебника по харнессу" выходит сильно позже, чем предыдущая.

В этой части мы начнем собирать целостную картину. Будем говорить не о конкретном продукте, а об обобщённой системе, которая принимает естественный язык, выбирает действие и умеет безопасно запускать долгие операции. Имена файлов и полей в примерах обобщены, но архитектурные приёмы взяты из реальной production-практики.

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

Принципы строительства харнесса.

Принцип 1. Чем сильнее модель, тем тоньше харнесс

В идеальном мире сильная модель сама понимает запрос, выбирает capability и строит план. Тогда харнессу остается проверять права, policy, состояние и безопасно запускать воркфлоу. Но сильные модели доступны не всем. Часто приходится работать с моделями, которые заметно хуже справляются с запросами, и не способны занимать первые места на агентстких бенчмарках.

Поэтому на практике Харнесс получается толстым: он помогает модели разобрать запрос, выбрать сценарий, проверить обязательный контекст и вызвать неподходящий инструмент. Это вынужденная мера, компенсирующая ее слабость. Здесь появляются intent-классификация, детерминированные shortcuts, capability-контракты, проверки входов и дополнительные ограничения на маршруты. Главное — понимать границу.

Толстый харнесс может компенсировать слабое понимание, но не должен забирать на себя policy, права и approvals. Модель предлагает действие, а харнесс решает, разрешено ли его выполнять. Сильная модель позволяет сделать харнесс тонким. Если сильной модели нет, харнесс временно становится её костылём — но границы безопасности всё равно должны оставаться явными.

При толстом харнессе

Харнесс сам помогает слабой модели выбрать правильный маршрут. Поэтому появляются дополнительные сущности:

  • Intent — классификация намерения пользователя;

  • Route — правило выбора сценария;

  • Shortcut — детерминированный маршрут для известных случаев;

  • Capability — формализованная возможность системы;

  • ContextRequirement — список обязательных входных данных;

  • ToolEligibility — условия, при которых инструмент можно вызвать;

  • FallbackRoute — запасной сценарий, если классификация неуверенная;

  • ClarificationRequest — запрос недостающего уточнения;

  • RouteDecision — сохранённое решение маршрутизатора с причиной и уверенностью.

Поток тогда выглядит примерно так:

Input
  → Intent / RouteDecision
  → Capability
  → ContextRequirement
  → Workflow
  → Policy
  → Tool / LLM

Эти сущности нужны, потому что модель не всегда стабильно отличает похожие запросы. Харнесс должен уметь проверить её решение, заменить его детерминированным shortcut, запросить уточнение или отправить запрос в безопасный fallback.

При тонком харнессе

Сильная модель сама выполняет большую часть семантической работы. Поэтому отдельные сущности Intent, Shortcut и сложные Route могут быть минимальными или вообще не понадобиться.

Основными становятся:

  • Capability — что система умеет;

  • Workflow — как это выполняется;

  • ToolContract — какие входы и эффекты разрешены;

  • Policy — можно ли запускать действие;

  • Approval — требуется ли подтверждение;

  • Task и Event — состояние и история выполнения;

  • Trace — почему было выбрано и выполнено конкретное действие.

Поток упрощается:

Input
  → Model Decision
  → Capability / Workflow
  → Policy
  → Tool

Здесь харнесс не пытается описать все возможные формулировки пользователя. Он предоставляет модели каталог capability и проверяет, что предложенное действие соответствует контракту, правам и policy.

Главное различие

При толстом харнессе появляются сущности, описывающие, как понять запрос:

Intent, Route, Shortcut, Fallback, ContextRequirement

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

Capability, Workflow, ToolContract, Policy, Approval, Task

Но это не две полностью разные архитектуры. Толстый харнесс можно постепенно «утончать»: сначала переносить распознавание интентов в модель, затем объединять дублирующиеся маршруты, оставляя в коде только capability-контракты, policy, approvals и гарантии исполнения.

Толстый харнесс моделирует и смысл запроса, и его выполнение.
Тонкий харнесс в основном моделирует границы и безопасное выполнение.

По сути, самый главный вопрос проектирования всего ии-пайплайна - это именно: «насколько толстым будет ваш харнесс». От этого зависит буквально все.

Где-то между запросом пользователя и ответом агента
Где-то между запросом пользователя и ответом агента

Принцип 2. Запрос, который не следует исполнять, должен быть отклонен как можно раньше

Когда пользователь пишет в приложении «запусти анализ», кажется, что система должна сделать одну вещь: отправить строку в модель и выполнить полученный ответ. В production всё интереснее. Между кнопкой «Отправить» и реальным действием есть несколько границ: интерфейс добавляет контекст, API проверяет права и состояние, маршрутизатор выбирает сценарий, policy-слой решает, можно ли его запускать, а исполнитель либо работает, либо ставит задачу на паузу и ждёт подтверждение.

Если запрос не имеет права на исполнение, требует недостающего контекста или явно нарушает policy, нет смысла тратить ресурсы на длинное планирование и вызов инструментов. Зачем, опять же, тратить компьют, если результат работы все равно никто не увидит, а пользователь получит что-то вроде «я не могу вам подсказать, как делать незаконные вещи». Тем более, если у вас видеокарта одна, а на ней висит зоопарк моделей.

При этом ранняя остановка не должна быть грубым поиском фраз в тексте. Надёжная последовательность выглядит так:

normalize input
  -> bind session and context
  -> check capability and access
  -> select workflow
  -> run policy gate
  -> call LLM/tools only when execution is possible

Это этапы, через которые проходит пользовательский запрос.

  • normalize input — привести запрос к стандартному виду: убрать лишние поля, проверить формат, определить тип события. Например, отделить текст сообщения от resource_id или callback-кнопки;

  • bind session and context — понять, кто отправил запрос и к какой ситуации он относится: пользователь, организация, текущая задача, активная сессия, выбранный ресурс, история диалога;

  • check capability and access — определить, какую возможность хочет использовать пользователь, и проверить, имеет ли он к ней доступ. Например, может ли он запускать анализ конкретного проекта;

  • select workflow — выбрать конкретный сценарий выполнения. Например: запустить новый анализ, продолжить существующий, скачать результат или запросить уточнение.

  • run policy gate — проверить ограничения безопасности и бизнес-правила: не запрещено ли действие, не требуется ли подтверждение, не хватает ли обязательного контекста;

  • call LLM/tools only when execution is possible — обращаться к модели и инструментам только после успешных проверок. Если у пользователя нет прав или задача находится в неподходящем состоянии, незачем тратить ресурсы на планирование.

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

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

Принцип 3. Не используй LLM там, где это не нужно

Стараемся экономить ресурсы там, где можно. Например, когда вы просите Алису включить музыку, сообщить погоду, использовать калькулятор, включить таймер и пр., то для этого не нужно тратить драгоценный gpu-компьют на мысли агента. Правильный маршрут делает то, что нужно, без вызова llm.

Гугл может себе позволить использовать ии вместо калькулятора, а ты нет
Гугл может себе позволить использовать ии вместо калькулятора, а ты нет

LLM должна подключаться только там, где системе действительно нужно интерпретировать смысл запроса. Всё, что уже известно из события, структуры payload или состояния backend, лучше обрабатывать обычным кодом.

Если пользователь нажал «скачать последний результат», а интерфейс передал resource_id, нет смысла отправлять этот запрос в модель. Нужно проверить права, найти ресурс и вернуть файл. Если пришёл callback «повторить задачу», это уже готовая команда. Если нужно проверить существование активной сессии, статус задачи или наличие файла, это задача backend, а не агента.

Вызов модели в таких случаях не просто тратит токены и GPU. Он добавляет задержку, вероятность неправильной интерпретации и ещё одну точку отказа. Модель может неправильно понять уже однозначный resource_id, выбрать не тот инструмент или начать строить план там, где достаточно одного вызова функции.

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

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

  • event route — пришло структурированное событие из интерфейса;

  • exact route — пришла однозначная команда с понятными параметрами;

  • semantic route — пришёл свободный текст, который нужно интерпретировать.

NB! «меньше вызовов LLM» не означает «давайте захардкодим как можно больше фраз». Точный маршрут должен обслуживать стабильные capability и события. Составной, шумный или неоднозначный запрос должен попасть в semantic path, иначе shortcut начнёт перехватывать чужие сценарии.

В следующей главе рассмотрим capability-контракты, структурированные события и обработку свободного текста.

Телеграм канал автора, где он что‑то пишет про ML, NLP и разработку

Теги: агент, агенты, ии-агенты, ии, ии чат-бот, ии-ассистент, ии-модель, ии помощник, ии агенты, ии-агент

Хабы: Искусственный интеллект, Python

Комментарии (2)


  1. danilovmy
    01.08.2026 19:37

    на агентстких бенчмарках.

    Именно на них...



    Я не согласен что, "толщина обвязки" зависит от умности модели. Поскольку харнесс (статья вообще то про роутинг) - это агностичное к LLM инженерное решение или:

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

    Вот как раз LLM-agnostic позиция и дает нам стабильность в работе  ии-пайплайна. Ну или стабильную ненадежность в работе.

    p.s. Плиз, подправьте стиль llm-ке, которая пишет, очень нечеловекочитаемо.


    1. kobubu Автор
      01.08.2026 19:37

      Как бы вы ни старались, вы не добьетесь от модели на 2,5 млрд параметров тех же результатов, что и от модели на 2,5 трлн параметров. Тут никакая обвязка не поможет. А вот "отупить" харнессом сильную модель можно. Но зачем?

      Про "поправьте стиль ллмке" некрасиво, называть те же понятия чуть другими словами, не делает вас великим поэтом.