Production-safe агентный цикл: как не дать ИИ сжечь бюджет
Агентные циклы сжигают бюджет не из-за плохих моделей, а из-за нечёткого условия остановки. Разбираем, как собрать production-ready каркас на Python: спецификацию, предохранитель, аудит и человеческое согласование.
Если ваш ИИ-агент работает круглосуточно и не может остановиться — это не автономность, а биллинговая авария, которая уже идёт. В июле 2025 года рекурсивный агентный цикл в Claude Code сжёг от 16 000 до 50 000 долларов за пять часов. Агенты не падают и не выдают ошибку: они делают ровно то, что им сказали, — пока кто-то не скажет остановиться.
Через четыре месяца четырёхагентный пайплайн на LangChain крутился одиннадцать дней и стоил 47 000 долларов. Никто не заметил, пока не пришёл счёт. Тот же паттерн: цикл работал корректно, но у него не было условия выхода.
Проблема не в моделях, а в отсутствии условия остановки. В этой статье разберём, как собрать минимальный, но production-ready каркас агентного цикла: спецификацию до запуска, предохранитель по токенам и ходам, неизменяемый аудит и поверхность для человеческого согласования. Полный код и 80 тестов с 100% покрытием доступны в репозитории автора оригинала.
Ключевые выводы
Агентные циклы сжигают бюджет не из-за плохих моделей, а из-за нечёткого условия остановки.
Спецификация должна отвечать на три вопроса: что делает, что не делает и что значит «готово» — в одном предложении.
Circuit breaker режет цикл по жёстким потолкам: число ходов и суммарные токены. Проверка — до вызова модели, а не после.
Ledger в SQLite фиксирует каждый ход: хеш входа, дельту токенов, время, результат. Это аудит, а не лог для отладки.
Review surface даёт поверхность для обязательной человеческой аттестации и формирует аудиторскую квитанцию frame_hash, которую вызывающий код может использовать как условие передачи результата в прод.
Что такое агентный цикл и почему он уходит в бесконечность
Агентный цикл — это конструкция вида while True, внутри которой языковая модель получает задачу, вызывает инструменты, анализирует результат и решает, продолжать или закончить. Такие циклы лежат в основе оркестраторов вроде LangGraph, CrewAI и AutoGen, а также внутри coding-агентов. Если только начинаете разбираться с LLM, полезно сначала понять, как устроены большие языковые модели, а для практики — заглянуть в основы Python.
Цикл уходит в бесконечность не потому, что модель «глупая», а потому, что никто не определил, что значит «готово». Модель видит неоднозначность и пытается быть полезной: перефразирует вызов инструмента, запускает верифицирующего агента, тот находит «проблему», срабатывает корректирующий агент — и так далее. На дашбордах всё выглядит активно: растёт число вызовов инструментов, completion rate держится высоким, а бюджет течёт в пустоту.
Почему дорожает каждая итерация:
Агент не начинает с чистого листа. Он каждый раз перечитывает всё предыдущее окно контекста — все неудачные попытки, все промежуточные выводы. Итерация 1 стоит 100 токенов, итерация 10 — уже тысячи. Вы платите за каждый провал снова и снова.
Почему компании сначала платят за чатбота, а потом за агентный рабочий процесс
Gartner фиксирует разрыв в потреблении токенов между пилотными чатботами и production-агентными рабочими процессами в 5–30 раз. А отчёт FinOps Foundation за 2026 год говорит, что 73% компаний превысили изначальный бюджет на ИИ. Цифры взяты из оригинального туториала; полные отчёты Gartner и FinOps Foundation доступны по платным подпискам. Причина разрыва — в неправильном масштабировании: команда планировала стоимость чатбота (~0,04 USD за взаимодействие), а в прод ушёл мультиагентный оркестр (~1,20 USD за взаимодействие, до 70x на сложных задачах).
A loop that runs without an exit condition isn't autonomous. It's a billing event waiting to happen.
Пять примитивов, которые ловят большинство отказов
Автор оригинального туториала предлагает не монолитный фреймворк, а пять независимых Python-модулей, которые можно встроить в любой проект. Вместе они покрывают три уровня риска: дисциплину до запуска, принудительную остановку во время работы и доказательства после.
- Spec writer — заставляет ответить на три вопроса до первого вызова модели.
- Circuit breaker — режет цикл, если превышены потолки по ходам или токенам.
- Ledger — ведёт append-only журнал каждого хода в SQLite.
- Agent loop — связывает три компонента в единый цикл.
- Review surface — собирает пятиэлементный фрейм и требует человеческой аттестации перед выдачей результата.
Фаза 1. Определить «готово» до первой строчки кода
Самая дорогая ошибка в разработке агентов — не выбор модели, а начало кодинга до того, как команда может одним предложением описать условие завершения. «Агент проверит сайт» — не подходит. «Агент обходит целевой URL, извлекает все теги <title> и <meta name="description">, помечает отсутствующие или слишком длинные и останавливается» — подходит.
Spec writer интерактивно запрашивает три поля, сохраняет их значения в SQLite и возвращает неизменяемый SpecResult(frozen=True). Полученный session_id связывает спецификацию, строки журнала и итоговый результат в одну трассируемую сессию.
Почему frozen=True:
Спецификация — это обязательство, а не черновик.frozen=Trueзапрещает переприсваивать поля объектаSpecResult, поэтому код цикла не может «подвинуть» условие завершения посреди запуска.
Фаза 2. Принудить «готово» на лету
Circuit breaker задаёт два жёстких потолка: turn_limit — максимальное число обращений к модели, и token_limit — суммарное число токенов за всю сессию. Каждый потолок — «строго больше»: если лимит 5 ходов, пятый ещё разрешён, шестой выбросит исключение.
Ключевое правило: breaker.check() вызывается до запроса к модели, а не после. Постфактум проверка бессмысленна: токены уже сожжены. Исключение, а не код возврата, — чтобы нельзя было промолчать.
Как подобрать лимиты для продакшена
Демонстрационные значения 5 ходов / 15 000 токенов слишком жёсткие для реальных задач. Для продакшена автор предлагает настроить лимиты под свой бюджет; в туториале приведён пример breaker = CircuitBreaker(turn_limit=10, token_limit=50000). Если одна сессия должна стоить не дороже 1 USD, а средний ход — 0,10 USD, получается порядка 10 ходов. Конкретный token_limit выбирается исходя из прайсинга модели и среднего размера контекста: чем длиннее история диалога, тем раньше сработает потолок.
- Стартуйте с жёсткими лимитами и разрешайте рост только по метрикам, не по интуиции.
- Отдельно лимитируйте retry-политику: каждый повторный запрос увеличивает и
turn_count, и объём контекста. - Не смешивайте лимит токенов с лимитом выходных токенов модели; circuit breaker считает сумму input + output.
Фаза 3. Записывать всё, что нельзя подделать
Circuit breaker защищает бюджет. Ledger защищает понимание того, что произошло. Это не лог для отладки, а журнал аудита: каждая строка — один ход, append-only, без обновлений и удалений.
Три решения стоит взять на заметку. Во-первых, вместо исходного текста сохраняется SHA-256 хеш входа: так не утекают персональные данные, а одинаковые входы разных запусков можно сравнивать. Во-вторых, pass_fail хранится как INTEGER (1/0), потому что у SQLite нет булева типа. В-третьих, временная метка — datetime.now(timezone.utc).isoformat(), так как datetime.utcnow() объявлен устаревшим в Python 3.12.
Фаза 4. Цикл, который уважает границы
Agent loop — единственный компонент, который обращается к языковой модели. Всё остальное работает локально: проверка потолков, запись в журнал, оценка условия выхода.
Анатомия одного хода простая и строгая: сначала breaker.check(), потом вызов модели, потом ledger.write(), потом проверка stop_reason. Если модель вернула end_turn — возвращаем результат. Если нет — добавляем сообщение continue и идём на следующий круг.
Этот вариант цикла — минимальный текстовый. Если агент использует инструменты, в Anthropic API stop_reason может быть tool_use: тогда нужно выполнить инструмент, вернуть его результат в messages и только потом решать, продолжать или завершать.
Системный промпт обязательно включает все три поля спецификации, а не только done_looks_like. Модели нужна негативная область — то, что агент делать не должен (what_it_does_not), — не меньше, чем позитивная: иначе она начнёт «добавлять ценность» за рамками задачи.
Фаза 5. Поверхность согласования: цикл бежит к человеку
Circuit breaker и ledger решают технические проблемы, но не отвечают на вопрос: «Соответствует ли результат тому, что обещали?» Именно здесь ошибки проходят в прод: вывод выглядит аккуратным, дашборд зелёный, ревьюер ставит галочку.
Review surface собирает пятиэлементный фрейм из SQLite и требует явной аттестации:
- Исходное обещание — три поля спецификации.
- Критерий приёмки — поле done_looks_like как явный бенчмарк.
- Diff — вход первого хода, выход последнего, число ходов, токены, сработал ли breaker.
- Доказательства — все строки ledger за сессию.
- Неразрешённые допущения — строки с breach_reason и failed-ходами.
После согласования ревьюер вызывает attest(). Функция собирает пятиэлементный фрейм в каноническом порядке и считает от него SHA-256 — получается frame_hash. Это аудиторская квитанция: она доказывает, что ревьюер видел именно этот фрейм, а не краткое резюме.
Практический пример: SEO-аудит по расписанию
Автор приводит пример SEO-аудита. SEO-аудит имеет естественный ритм: обход, выявление проблем, исправление, ожидание переиндексации. Запускать агента 24/7 бессмысленно — он будет сжигать токены в паузах между событиями. Честная архитектура — cron-задача, которая запускает цикл по расписанию.
Пример упрощён:
В production-варианте стоит проверять URL (допустимые схемы и хосты) и оборачиватьrequests.getвtry/except requests.RequestException, чтобы агент не падал при недоступности сайта.
Cron-строка выглядит так:
Агент выполняет работу, записывает ходы в ledger, и если circuit breaker сработал — результат уходит на человеческую проверку, а не в прод.
Провайдер-независимость через адаптер
Цикл работает с любым клиентом, удовлетворяющим протоколу LLMClient. По умолчанию используется Anthropic, но через адаптер можно подключить OpenAI, Gemini, Ollama, локальные модели или собственный сервер. В репозитории автора показан иллюстративный пример адаптера для OpenAI. Главное — привести ответ к форме, которую ожидает AgentLoop: usage.input_tokens, usage.output_tokens, content[0].text, stop_reason.
Часто задаваемые вопросы
Что такое agent loop и чем он отличается от обычного цикла?
Agent loop — это цикл, в котором языковая модель многократно вызывается для решения задачи, анализа результата и принятия решения о продолжении. От обычного цикла он отличается тем, что условие остановки не всегда формализовано: модель сама решает, закончила ли она, что приводит к риску бесконечного выполнения.
Почему circuit breaker проверяет лимиты до вызова модели?
Потому что после вызова токены уже израсходованы. Постфактумная проверка защищает только от следующего хода, но не возвращает уже потраченный бюджет. Pre-flight проверка гарантирует, что запрос к модели вообще не произойдёт, если лимит исчерпан.
Зачем хранить хеш входа в ledger вместо самого текста?
Две причины. Первая — защита персональных данных: сырые промпты не попадают в долгоживущий аудит. Вторая — сравнимость: одинаковые входы на разных запусках дают одинаковый хеш, что помогает отслеживать повторяющиеся сценарии.
Можно ли использовать SQLite-ledger в распределённой системе?
Для одного процесса — да. Как только несколько агентов пишут в общий ledger параллельно, SQLite требует serializable-изоляции, иначе записи могут повреждаться. В таких случаях ledger нужно поднимать до полноценной очереди событий или журнала с криптографической подписью.
Как подобрать начальные лимиты для своего агента?
Начните с бюджета на одну сессию. Разделите его на среднюю стоимость одного хода — получите максимальное число ходов. Установите token_limit исходя из среднего размера контекста и ожидаемой глубины рассуждения. Первые недели собирайте статистику по реальным запускам и сужайте лимиты, а не расширяйте без причин.
Выводы: дисциплина дороже модели
Большинство аварий с агентными циклами предотвращаются не интеллектом модели, а чёткими границами. Спецификация до запуска, жёсткие потолки ресурсов, неизменяемый аудит и человеческое согласование — это минимальный набор примитивов, который отделяет автономного агента от неконтролируемого биллингового события.
Для российских команд это означает, что внедрять LLM-агентов без бюджетных предохранителей — всё равно что запускать бесконечный цикл с доступом к корпоративной карте. Начните с пяти модулей, описанных выше, прогоните их на тестовом дубле модели и только после этого открывайте доступ к реальным API.
Define what done looks like before you start. That's the job, and always has been.
Полный код и 80 тестов с 100% покрытием доступны в репозитории автора оригинала: github.com/dannwaneri/production-safe-agent-loop.
Источник: How to Build a Production-Safe Agent Loop — From Exit Conditions to Audit Trails, freeCodeCamp.