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

Агентные системы — это приложения, где LLM не просто отвечает на подсказку, а решает, что делать дальше: какие инструменты вызывать, какие данные получить, какие шаги выполнить и когда считать задачу «завершённой». Они объединяют модель, набор инструментов (API, базы данных, сервисы), цикл планирования/исполнения и инфраструктуру, связывающую всё это вместе.
В демо это выглядит волшебно: агент придумывает план, вызывает несколько инструментов и возвращает идеальный результат. Путь «по счастливому сценарию» короткий, задержка низкая, и ничего не ломается одновременно.
Под реальной нагрузкой тот же агент испытывается иначе, чем в демо:
Результат: нестабильное поведение, трудно воспроизводимые сбои, молчаливое повреждение данных и пользовательские сценарии, которые иногда зависают или застревают в петле.
Хрупкие агенты вредят не только «впечатлению». Они:
Эта статья про инженерные паттерны, а не про «лучшие подсказки». Мы рассмотрим state machines, явные контракты инструментов, стратегии повторных попыток и обработки ошибок, контроль памяти и конкуренции, а также паттерны наблюдаемости, которые делают агентные системы предсказуемыми под нагрузкой — не только впечатляющими на сцене.
Большинство систем выглядят нормально в одном демонстрационном сценарии. Они терпят неудачу, когда одновременно приходят трафик, инструменты и краевые случаи.
Наивная оркестрация предполагает, что модель «сделает правильное» в один‑два вызова. В реальности вы наблюдаете повторяющиеся узоры:
Без явных состояний и условий завершения такие поведения закономерны.
Сэмплирование LLM, изменчивость задержки и тайминги инструментов создают скрытую недетерминированность. Один и тот же вход может пройти по разным ветвям, вызвать разные инструменты или по‑разному интерпретировать результаты инструментов.
На масштабе доминируют проблемы инструментов:
Каждая из этих проблем превращается в ложные петли, повторы или неверные итоговые ответы.
То, что редко ломается при 10 RPS, будет ломаться постоянно при 1 000 RPS. Конкуренция обнажает:
Команды продукта часто ожидают детерминированных рабочих процессов, ясных SLA и аудита. Агенты, оставленные без ограничений, дают вероятностное, best‑effort поведение с слабыми гарантиями.
Когда архитектуры игнорируют это несоответствие — считая агентов традиционными сервисами вместо стохастических планировщиков — системы ведут себя непредсказуемо в моменты, когда надёжность наиболее важна.
Агенты для продакшена — это не про «умные подсказки», а про дисциплинированное системное проектирование. Полезно думать об агенте как о маленькой предсказуемой машине, которая иногда вызывает LLM, а не как о загадочном LLM‑компоненте, который иногда трогает ваши системы.
Четыре свойства важнее всего:
Эти свойства не возникают от одних подсказок. Они появляются от структуры.
Стандартный паттерн, с которого многие команды начинают: «пока не готово, вызывай модель, пусть думает, возможно вызовет инструмент, повторяй». Это просто прототипировать и тяжело эксплуатировать.
Более безопасный паттерн — представлять агента как явный рабочий процесс:
COLLECTING_INPUT, PLANNING, EXECUTING_STEP, WAITING_ON_HUMAN, DONE).Это превращает агента в state machine, где каждый шаг инспектируем, тестируем и воспроизводим. Свободные петли кажутся гибкими, но именно явные рабочие процессы делают инциденты отлаживаемыми и поведение — аудируемым.
Монолитные агенты, которые «умеют всё», привлекательны, но они порождают сильную связанность между несвязанными ответственностями: планирование, поиск, бизнес‑логика, оркестрация UI и т.д.
Вместо этого составляйте малые, хорошо ограниченные агенты или скиллы:
Каждый скилл может иметь свой state machine, инструменты и правила безопасности. Логика композиции становится высшего уровня рабочим процессом, а не бесконечно разрастающейся подсказкой внутри одного агента.
Такая модульность делает каждый агент достаточно простым для рассуждений и позволяет развивать одну возможность без дестабилизации остальных.
Полезная модель мышления — разбить агента на три слоя:
Политика принятия решений (LLM-подсказки + модель)
Инкапсулирует как агент выбирает действия, применяемые в жёстких рамках. Вы должны иметь возможность менять модель, температуру или уточнять подсказки, не лезя в проводку системы.
State machine / workflow engine
Отвечает за где вы находитесь в процессе, какие переходы возможны и как сохранять прогресс. Политика предлагает ход; state machine валидирует и применяет его.
Слой инструментов
Реализует что реально может случиться в мире: API, БД, очереди, внешние сервисы. Инструменты открывают узкие, хорошо типизированные контракты и обеспечивают авторизацию, лимиты и валидацию входа.
Принудительное разделение помогает избежать соблазна прятать бизнес‑логику в подсказках или описаниях инструментов. LLM становится компонентом принятия решений внутри понятной, детерминистической оболочки, а не оболочкой сама по себе.
Самые надёжные агентные системы — это не самые впечатляющие демо, а те, поведение которых можно объяснить на доске.
Конкретно:
Такая склонность к маленьким, компонуемым и структурированным агентам позволяет системам расти, не раздавливаясь собственной сложностью.
Большинство реализаций агентов начинаются как цикл «подумай, действуй, наблюдай» вокруг вызова LLM. Это годится для демо, но быстро становится непрозрачным и хрупким. Лучший подход — рассматривать агента как явный state machine: конечный набор состояний с чётко определёнными переходами, инициируемыми событиями.
Вместо того, чтобы позволять модели неявно решать, что делать дальше, определите небольшую диаграмму состояний:
Переходы между состояниями инициируются типизированными событиями, такими как UserRequestReceived, ToolCallSucceeded, ToolValidationFailed, TimeoutExceeded или HumanOverride. Каждое событие и текущее состояние определяют следующий шаг и действия.
Это делает повторы и таймауты простыми: политики навешиваются на отдельные состояния (например, CALL_TOOL может пытаться 3 раза с экспоненциальным откатом, PLAN может вообще не ретраить), вместо того чтобы разбрасывать логику повтора по всему коду.
Персистируйте текущее состояние и минимальный контекст во внешнем хранилище (БД, очередь или workflow‑движке). Агент тогда становится чистой функцией:
next_state, actions = transition(current_state, event, context)
Это даёт:
С state machine каждый шаг поведения агента явен: в каком состоянии он был, какое событие произошло, какой переход сработал и какие сайд‑эффекты были произведены. Такая ясность ускоряет отладку, упрощает расследование инцидентов и создаёт естественный след аудита для соответствия требованиям. Вы можете доказать из логов и истории состояния, что рискованные действия делаются только из определённых состояний и при чётких условиях.
Агенты ведут себя намного предсказуемее, когда инструменты меньше похожи на «API, спрятанные в прозе», и больше — на хорошо спроектированные интерфейсы с явными гарантиями.
Каждый инструмент должен описывать контракт, охватывающий:
InvalidInput, NotFound, RateLimited, TransientFailure) с ясной семантикой.Предоставляйте этот контракт модели в структурированном виде, а не стеной текста. Планировщик агента должен знать, какие ошибки можно повторять, какие требуют вмешательства пользователя, а какие останавливают рабочий процесс.
Обращайтесь с вводом/выводом инструментов как с любым другим продакшен‑API:
Это упрощает подсказки: вместо многословных инструкций полагайтесь на схему. Ясные ограничения снижают галлюцинации аргументов и бессмысленных последовательностей вызовов инструментов.
Инструменты развиваются; агенты не должны ломаться при каждом изменении.
v1, v1.1, v2) и фиксируйте агентов на версии.Логика планирования тогда сможет безопасно миксовать агентов и инструменты на разных стадиях зрелости.
Проектируйте контракты с мыслью о частичных отказах:
Агент сможет адаптироваться: продолжить поток с уменьшенной функциональностью, спросить подтверждение у пользователя или переключиться на запасной инструмент.
Контракты инструментов — естественное место для кодирования ограничений безопасности:
confirm: true).Комбинируйте это с серверной проверкой; никогда не полагайтесь исключительно на то, что модель «ведёт себя правильно».
Когда инструменты имеют ясные, валидированные и версионированные контракты, подсказки становятся короче, оркестрация проще, а отладка значительно легче. Вы переносите сложность из хрупких инструкций на детерминистические схемы и политики, сокращая количество выдуманных вызовов инструментов и неожиданных сайд‑эффектов.
Надёжные агентные системы предполагают, что всё временами будет падать: модели, инструменты, сеть и даже ваш слой координации. Цель — не избежать ошибок, а сделать их дешёвыми и безопасными.
Идемпотентность означает: повторение одного и того же запроса имеет тот же внешний эффект, что и однократное выполнение. Это критично для агентов, которые часто повторно отправляют вызовы инструментов после частичных отказов или неоднозначных ответов.
Сделайте инструменты идемпотентными проектно:
request_id. Инструмент хранит это и возвращает тот же результат при повторном поступлении того же ID.Используйте структурированные повторы для транзиентных ошибок (таймауты, лимиты, 5xx): экспоненциальный откат, джиттер и жёсткое макс‑число попыток. Логируйте каждую попытку с корреляционными ID, чтобы можно было отследить поведение агента.
Для постоянных ошибок (4xx, валидационные ошибки, нарушение бизнес‑правил) повторы не делайте. Поверхностную ошибку возвратите в политику агента, чтобы она могла перепланировать, спросить пользователя или выбрать другой инструмент.
Реализуйте circuit breakers и на уровне агента, и на уровне инструментов: после серии ошибок временно блокируйте вызовы к инструменту и быстро возвращайте отказ. Сопровождайте это налаженными запасными вариантами: деградированные режимы, кэшированные данные или альтернативные инструменты.
Избегайте слепых повторов из цикла агента. Без идемпотентных инструментов и явных классов ошибок вы лишь умножите сайд‑эффекты, задержки и расходы.
Надёжные агенты начинаются с ясного понимания что такое состояние и где оно живёт.
Относитесь к агенту как к сервису, обрабатывающему запрос:
Смешивание этих уровней ведёт к путанице и багам. Например, помещение эпизодических результатов инструментов в «память» заставит агентов переиспользовать устаревший контекст в будущих разговорах.
Есть три основных варианта:
Хорошее правило: LLM — stateless функция над явным объектом состояния. Сохраняйте этот объект вне модели и генерируйте подсказки из него.
Распространённая ошибка — использовать логи разговоров, трассы или необработанные подсказки как память.
Проблемы:
Вместо этого определяйте структурированные схемы памяти: user_profile, project, task_history и т.д. Логи выводите из состояния, а не стройте состояние из логов.
Когда несколько инструментов или агентов обновляют одни и те же сущности (например, запись CRM или статус задачи), нужны базовые механизмы согласованности:
Для операций высокой ценности ведите отдельный лог решений помимо разговорного лога: что изменилось, почему и на основе какого входа.
Чтобы пережить падения, деплои и лимиты, рабочие процессы должны быть возобновляемыми:
Это также даёт возможность «путешествия во времени» при отладке: вы можете инспектировать и переиграть точное состояние, приведшее к ошибочному решению.
Память — это одновременно актив и риск. Для продакшен‑агентов:
Рассматривайте память как продукт: проектируйте, версиируйте и управляйте ею — а не как бесконтрольный текстовый дамп, прикреплённый к агенту.
Агенты выглядят последовательными на доске, но ведут себя как распределённые системы под нагрузкой. Как только у вас много параллельных пользователей, инструментов и фоновых задач, вы управляете условиями гонки, дублирующей работой и проблемами упорядочивания.
Распространённые режимы отказа:
Снижают риск идемпотентные контракты, явное состояние рабочего процесса и оптимистичная/пессимистичная блокировка на уровне данных.
Синхронные request–response потоки просты, но хрупки: каждая зависимость должна быть доступна, в пределах лимитов и быстрая. Как только агенты распараллеливают работу или запускают множество подзадач, переместите долгие или сайд‑эффектные шаги в очереди.
Оркестрация через очереди позволяет:
Агенты обычно сталкиваются с тремя классами ограничений:
Нужен явный слой лимитов скорости с ограничениями на пользователя, тестант и глобальные политики. Используйте token‑bucket или leaky‑bucket, и возвращайте понятные типы ошибок (например, RATE_LIMIT_SOFT, RATE_LIMIT_HARD), чтобы агенты могли корректно отходить.
Обратное давление — это механизм защиты системы под нагрузкой. Стратегии включают:
Следите за сигналами насыщения: глубиной очередей, загрузкой воркеров, частотой ошибок и задержкой. Расти очередей вкупе с увеличением задержки или ошибками 429/503 — раннее предупреждение о перегрузке среды агентов.
Вы не сможете сделать агента надёжным, если не можете быстро ответить на два вопроса: «что он сделал?» и «почему он так сделал?». Наблюдаемость для агентных систем — это сделать эти ответы дешёвыми и точными.
Проектируйте наблюдаемость так, чтобы единичная задача имела трейс, охватывающий:
Внутри трейса прикрепляйте структурированные логи для ключевых решений (выбор маршрута, ревизия плана, срабатывания защит) и метрики по объёму и здоровью.
Полезный трейс обычно включает:
Логируйте подсказки, входы и выходы инструментов в структурированном виде, но пропускайте их через слой редактирования:
Держите необработанный контент за feature‑флагами в тестовых средах; в продакшене по умолчанию — редактированные представления.
Минимально отслеживайте:
Когда происходит инцидент, хорошие трейсы и метрики переводят проблему из «агент кажется ненадёжным» в точное утверждение вроде: «P95 задач падают в ToolSelection после 2 повторов из‑за новой схемы в billing_service», что сокращает время диагностики с часов до минут и даёт конкретные рычаги для исправления.
Тестирование агентов — это тестирование и инструментов, которые они вызывают, и потоков, которые всё это связывают. Относитесь к этому как к тестированию распределённых систем, а не только к настройке подсказок.
Начните с unit‑тестов на границе инструментов:
Эти тесты не зависят от LLM. Вы вызываете инструмент напрямую с синтетическими входами и проверяете точный выход или контракт ошибки.
Интеграционные тесты прогоняют рабочий процесс агента end‑to‑end: LLM + инструменты + оркестрация.
Сценарии для тестирования:
Эти тесты проверяют переходы состояний и вызовы инструментов, а не каждое токен‑словцо LLM. Проверяйте: какие инструменты были вызваны, с какими аргументами, в каком порядке и в каком конечном состоянии/результате агент оказался.
Чтобы тесты были воспроизводимы, фиксируйте ответы LLM и выходы инструментов.
Типичный паттерн:
with mocked_llm(fixtures_dir="fixtures/llm"), mocked_tools():
result = run_agent_scenario(input_case)
assert result.state == "COMPLETED"
Любое изменение подсказки или схемы должно запускать обязательный регрессионный прогон:
Эволюция схемы (добавление полей, ужесточение типов) требует своих регрессионных кейсов, чтобы поймать агентов или инструменты, всё ещё зависящие от старого контракта.
Никогда не выкатывайте новую модель, политику или стратегию маршрутизации напрямую в продакшен. Вместо этого:
Только после прохождения офлайн‑ворот новая версия идёт в продакшен, желательно за feature‑флагами и с постепенным развёртыванием.
Логи агентов часто содержат чувствительные данные. Тестирование должно это учитывать.
Интегрируйте эти правила в CI, чтобы ни один тестовый артефакт не был создан или сохранён без проверки анонимизации.
Эксплуатация агентов ближе к управлению распределённой системой, чем к выпуску статической модели. Нужны механизмы для безопасного релиза, ясные цели надёжности и дисциплинированный контроль изменений.
Вводите новых агентов или поведение постепенно:
Поддерживайте это feature‑флагами и политиками конфигурации: правила маршрутизации, включаемые инструменты, температура, настройки безопасности. Изменения должны быть настраиваемы конфигом, а не кодом, и мгновенно откатываемы.
Определите SLO‑ы, отражающие здоровье системы и ценность для пользователя:
Подключите это к алертам и ведите инциденты как для обычных продакшен‑сервисов: явные владельцы, плейбуки для триажа и стандартные шаги смягчения (откат флага, слив трафика, безопасный режим).
Используйте логи, трейсы и транскрипты для улучшения подсказок, инструментов и политик. Рассматривайте каждое изменение как версионированный артефакт с обзором, утверждением и возможностью отката.
Избегайте тихих изменений подсказок или инструментов. Без контроля изменений невозможно соотнести регрессии с конкретными правками, и реакция на инциденты превращается в домыслы вместо инженерной работы.
Продакшен‑готовая агентная система выигрывает от чёткого разделения ответственности. Цель — сделать агента умным в решениях и «тупым» в инфраструктуре.
1. Gateway / API edge
Единая точка входа для клиентов (приложений, сервисов, UI). Обрабатывает:
2. Orchestrator
Оркестратор — это «ствол», а не мозг. Он координирует:
LLM(ы) живут за оркестратором, используются планировщиком и отдельными инструментами, которым нужна языковая обработка.
3. Слой инструментов и хранилищ
Бизнес‑логика остаётся в существующих микросервисах, очередях и данных. Инструменты — тонкие обёртки вокруг:
Оркестратор вызывает инструменты по строгим контрактам, а системы хранения остаются источником правды.
Применяйте авторизацию и квоты на gateway; применяйте безопасность, доступ к данным и политику в оркестраторе. Все вызовы (LLM и инструменты) эмитят структурированную телеметрию в пайплайн, который питает:
Проще архитектура (gateway → единый orchestrator → инструменты) легче в эксплуатации; добавление отдельных планировщиков, движков политик и шлюзов моделей даёт гибкость, но увеличивает координацию, задержку и операционную сложность.
Теперь у вас есть основные ингредиенты для агентов, которые ведут себя предсказуемо под реальной нагрузкой: явные state machines, чёткие контракты инструментов, дисциплинированные повторы и глубокая наблюдаемость. Последний шаг — превратить эти идеи в повторимую практику в вашей команде.
Думайте об агенте как о состоявшемся рабочем процессе:
Когда все эти куски согласованы, вы получаете системы, которые деградируют грациозно, а не рушатся на краевых случаях.
Прежде чем отдать прототип агентной функции реальным пользователям, проверьте:
Если хотя бы один пункт отсутствует — вы всё ещё на уровне прототипа.
Устойчивое устройство обычно разделяет:
Это позволяет продуктовым командам двигаться быстро, а платформенным — обеспечивать надёжность, безопасность и контроль затрат.
Когда фундаменты стабильны, можно изучать:
Движение в этом направлении должно быть поэтапным: вводите новые компоненты обучения за feature‑флагами, с офлайн‑оценкой и жёсткими защитами.
Тема постоянна: проектируйте на отказ, предпочитайте ясность изощрённости и итеративно улучшайте там, где есть наблюдаемость и возможность отката. С такими ограничениями агентные системы перестают быть страшными прототипами и превращаются в инфраструктуру, на которую ваша организация может опереться.
Агентная система — это приложение, в котором LLM не просто отвечает на одиночный запрос, а принимает решения о дальнейших шагах: какие инструменты вызвать, какие данные получить, какой шаг в рабочем процессе выполнить и когда завершить работу.
В отличие от обычного чат‑бота, агентная система сочетает:
В продакшене LLM становится одним из компонентов принятия решений внутри более детерминистической оболочки — а не всей системой целиком.
Демо обычно работает по «счастливому пути»: один пользователь, инструменты ведут себя идеально, нет таймаутов, нет дрейфа схем, разговор короткий. В продакшене агенты сталкиваются с:
При отсутствии явных рабочих процессов, контрактов и обработки ошибок это приводит к петлям, зависаниям, частично выполненной работе и «молчаливым» ошибкам, которые не проявляются в демо-среде.
Поместите LLM в рамки чёткой структуры, а не в свободноформатный цикл:
Моделируйте агента как рабочий процесс с именованными состояниями и типизированными событиями вместо while not done: call LLM.
Типичные состояния:
Делайте инструменты похожими на производственные API, а не на описания в прозе. Каждый инструмент должен иметь:
Предполагаете, что внешние вызовы иногда будут падать, и проектируйте вокруг этого.
Ключевые паттерны:
Разделяйте краткосрочное состояние и долгосрочную память, а сам LLM держите без состояния.
Думайте об агентной системе как о распределённой системе под нагрузкой, даже если отдельный поток выглядит последовательным.
Чтобы оставаться надёжным:
Вам нужно уметь ответить «что сделал агент?» и «почему он это сделал?» для любой задачи.
Практические требования:
Обращайтесь с агентами как с развивающимися сервисами, а не как со статичными подсказками, и управляйте ими так же строго, как другие продакшен‑системы.
Рекомендуемые практики:
Это позволит объяснять, тестировать и отлаживать поведение пошагово, вместо охоты за непрозрачными «мыслями агента».
PLAN — интерпретировать запрос и составить пошаговый планCALL_TOOL — вызвать конкретный инструмент или батч инструментовVERIFY — проверить выводы инструментов по простым инвариантам или дополнительными проверками моделиRECOVER — обработать ошибки через повторные попытки, запасные варианты или эскалациюDONE / FAILED — терминальные исходыСобытия (например, ToolCallSucceeded, TimeoutExceeded) вместе с текущим состоянием определяют следующий переход. Это делает повторные попытки, таймауты и обработку ошибок явными, а не разбросанными по подсказкам или вспомогательному коду.
InvalidInput, NotFound, RateLimited, TransientFailureВалидируйте входные данные перед вызовом инструмента и проверяйте выход после. Версионируйте контракты инструментов и фиксируйте агентов на конкретных версиях, чтобы изменения схем не ломали потоки незаметно.
request_id или бизнес-ключ и возвращают тот же результат при повторном вызове.Это сохраняет надёжность без бесконтрольных петель, дублирующих сайд‑эффектов и взрывного роста стоимости.
Избегайте использования необработанных логов или полной истории разговоров в качестве «памяти» — вместо этого извлекайте компактные, структурированные записи с понятными правилами хранения и конфиденциальности.
Мониторьте глубину очередей, перцентиль задержки и количество 429/503 ошибок, чтобы обнаружить перегрузку до возникновения инцидента.
С этим набором триаж инцидентов преобразуется из «агент ведёт себя ненадёжно» в точное указание состояния, инструмента и изменения, вызвавшего регрессию.
Это позволит непрерывно улучшать агентов, сохраняя сбои локализованными, диагностируемыми и откатываемыми.