Source: https://aiprimetech.io/ru/blog/agent-cost-control/ 25 июня, 2026 · 6 мин · Агенты # Как не дать циклам агентов израсходовать ваш бюджет на API A Автор: [AI Prime Tech](https://aiprimetech.io/ru/) · При участии ИИ Я сразу подготовлю статью с конкретными инженерными приёмами, сравнительной таблицей и практическими примерами. В её основе будут реальное поведение циклов и методы контроля затрат, а в конце обязательно будут рекомендации, которые можно применить на практике.## Самый быстрый способ сжечь бюджет на API Однажды я наблюдал, как «простой» агент для программирования менее чем за две минуты превратил пользовательский запрос на 400 токенов в строку счёта на 28,000 токенов. Сценарий был до боли знакомым: агент вызвал инструмент поиска, кратко изложил результаты, перечитал собственное резюме, снова обратился к тому же инструменту с немного другой формулировкой, а затем расширял контекст, пока каждый ход не стал включать весь разговор, половину репозитория и череду неудачных планов. С моделью всё было в порядке. Проблема была в устройстве цикла. В этом и заключается основная проблема агентных систем: затраты растут не с количеством пользовательских запросов, а в зависимости от поведения цикла. Один неудачно спроектированный цикл может многократно увеличить: - число входных токенов из-за постоянно растущего контекста, - число выходных токенов из-за многословных «размышлений», - затраты на инструменты из-за повторных вызовов, - и задержку из-за последовательных повторных попыток. Если вы создаёте агентов для промышленной эксплуатации, настоящая задача — не «сделать модель умнее», а «сделать цикл дешевле, короче и менее склонным выходить из-под контроля». ## Почему циклы агента приводят к резкому росту затрат Обычный ход в чате состоит из одного входа и одного выхода. Цикл агента выглядит примерно так: - пользователь задаёт вопрос, - модель строит план, - модель вызывает инструмент, - инструмент возвращает данные, - модель читает данные, - модель определяет следующий шаг, - цикл повторяется. Каждая итерация добавляет новые токены в запрос следующей итерации. Если постоянно добавлять полные результаты работы инструментов и предыдущие размышления, объём запроса растёт примерно как ступенчатый график. На практике это создаёт три фактора, многократно увеличивающих затраты: - Накопление контекста: каждый шаг включает в себя предыдущие шаги. - Избыточные обращения к инструментам: один и тот же инструмент вызывается с немного разными аргументами. - Неконтролируемые повторные попытки: модель продолжает «пробовать что-нибудь ещё», хотя уже сделала достаточно. Распространённая ловушка: перерасход токенов обычно незаметен, пока вы не изучите трассировки отдельных запусков. Итоговый ответ может быть кратким, но скрытая цепочка может быть в 10x больше результата, видимого пользователю. ### Простая модель затрат Если цикл выполняет N шагов, каждый из которых содержит: - C токенов переносимого контекста, - I токенов результатов работы инструментов, добавляемых обратно в контекст, - O токенов ответа модели, то приблизительный итог составит: ``` total_tokens ≈ N * (C + I + O) ``` Это оптимистичная оценка, поскольку C обычно растёт по мере выполнения цикла. У реального агента более поздние ходы часто оказываются самыми дорогими. ## Рычаги управления, которые действительно важны Есть несколько точек, на которые можно воздействовать. Главное — сочетать эти меры. | Мера контроля | Что сокращает | Где лучше всего применять | Компромисс | Ограничение числа шагов | Неконтролируемые циклы | Любой агент с инструментами | Может преждевременно остановить выполнение сложных задач | Сокращение контекста | Число входных токенов | Задачи с длительным горизонтом выполнения | Риск потери полезной истории | Более дешёвые субагенты | Затраты на рутинную работу | Поиск, извлечение данных, классификация | Накладные расходы на координацию | Кэширование результатов работы инструментов | Затраты на повторные обращения к инструментам | Стабильные источники, повторяющиеся запросы | Устаревание данных и инвалидация кэша | Наблюдаемость затрат | Неожиданные счета | Системы в промышленной эксплуатации | Требует внедрения средств сбора метрик и трассировок | Условия остановки цикла | Бесконечные или почти бесконечные циклы | Ненадёжные агенты | Нужно определить, что считать «достаточно хорошим» В наиболее эффективных системах промышленной эксплуатации используются все эти меры. ## 1) Жёстко ограничивайте число шагов Это самое простое и самое недооценённое решение: если вы не задаёте жёсткий максимум, то полагаетесь на то, что модель остановится сама. Обычно я начинаю с таких ограничений: - 3–5 шагов для узких задач, - 6–8 шагов для задач средней сложности, - больше — только при наличии веской причины. Хорошее ограничение — не просто предохранитель. Оно заставляет лучше разбивать задачу на части. Если у агента есть всего четыре хода, он обычно: - раньше обращается к нужному инструменту, - избегает исследовательских отступлений, - и быстрее приходит к пригодному для использования ответу. ### Пример контроллера цикла ``` MAX_STEPS = 5 for step in range(MAX_STEPS): action = model.next_action(state) if action.type == "final": return action.final_answer observation = run_tool(action.tool_name, action.arguments) state = update_state(state, action, observation) raise RuntimeError("Agent stopped after step cap") ``` Распространённая ловушка: ограничение числа шагов должно применяться ко всему плану, а не отдельно к каждой подпрограмме. Иначе вы просто переносите взрывной рост затрат из агента верхнего уровня во вложенные вспомогательные компоненты. ## 2) Сокращайте контекст на каждом ходу Большинство агентов сохраняют слишком много истории. Им не нужен каждый предыдущий ответ инструмента, особенно если ответ был большим, а значение имела только одна деталь. На практике лучшие результаты у меня получались с многоуровневой политикой памяти: - Сохраняйте дословно: запрос пользователя, текущую цель, окончательные решения, ещё не разрешённые ограничения. - Кратко излагайте: предыдущие рассуждения, попытки по разным ветвям, длинные результаты работы инструментов. - Удаляйте: тупиковые гипотезы, дублирующиеся результаты работы инструментов, старые промежуточные черновики. ### Что сохранять Компактный объект состояния работает лучше, чем повторная передача всей необработанной истории диалога: ``` { "goal": "Find the failing endpoint and propose a fix", "constraints": [ "No schema changes", "Must preserve backward compatibility" ], "known_facts": [ "500s started after deploy 1842", "Only /v1/search is affected" ], "open_questions": [ "Is the new cache layer returning stale payloads?" ], "step": 3 } ``` Это намного дешевле, чем передавать модели 20 ходов с «размышлениями вслух». ### Практическая схема сокращения контекста Используйте три буфера: - working_set: текущий ход, результаты инструментов, активный план. - summary_memory: сжатая история. - archive: всё, что может понадобиться для аудита, но не для контекста запроса. Агент видит только working_set + summary_memory. Когда working_set становится слишком большим, сожмите его в краткую сводку и поместите в summary_memory. Подводный камень здесь — смысловой дрейф сводки. Если при сжатии теряется информация, могут исчезнуть ограничения, из-за чего агент начнёт повторять ошибки. Я предпочитаю хранить структурированные факты отдельно от сводок в свободной форме, чтобы модели не приходилось извлекать их из текста. ## 3) Используйте более дешёвых субагентов для простых задач Не каждый шаг требует вашей лучшей модели. Если задача заключается в том, чтобы: - извлечь поля из JSON, - классифицировать обращение, - проверить, изменился ли файл, - или сформировать короткий поисковый запрос, то зачастую достаточно небольшой и более дешёвой модели. Приберегите дорогую модель для планирования, обобщения и работы с неоднозначностью. ### Типичное распределение ролей - Основной агент: анализирует задачу и решает, когда остановиться. - Субагент: выполняет узкие задачи, например поиск, извлечение или нормализацию. - Проверяющий агент: проверяет результаты на соответствие формату, полноту и явные ошибки. Практическая архитектура: ``` User -> Orchestrator (strong model) -> Search agent (cheap model) -> Extract agent (cheap model) -> Final synthesis (strong model) ``` ### Почему это работает Основная модель сосредоточена на управлении ходом выполнения, вместо того чтобы делать всё самостоятельно. Для субагента можно задать строгие ограничения в запросе и жёсткий лимит токенов. Пример запроса для поискового субагента: ``` Return only: 1. the best search query, 2. 3 likely source categories, 3. no explanation. ``` Такой узкий контракт делает результаты короткими и предсказуемыми. Если ваша платформа предоставляет доступ к нескольким моделям по доступной цене, именно здесь это окупается: одна мощная модель для оркестрации, одна более дешёвая модель для повторяющихся подзадач — и никакой необходимости тратить дорогие токены на рутинный разбор данных. ## 4) Подойдите к кэшированию результатов инструментов всерьёз В циклах работы агента за одну и ту же информацию часто платят несколько раз: - повторное чтение файлов, - повторные запросы к базе данных, - повторные поиски в интернете, - повторные вызовы для получения эмбеддингов или поиска данных. Кэширование результатов инструментов — одно из изменений с наибольшей отдачей, которые вы можете внести. ### Кэшируйте по смысловому ключу, а не по исходному тексту Кэш исходных запросов не сработает, если формулировка немного изменится. Лучше строить ключи на основе нормализованного намерения: ``` def cache_key(tool_name, args): normalized = normalize_args(args) return f"{tool_name}:{hash(normalized)}" ``` Пример записи в кэше: ``` { "tool": "repo_search", "query": "auth middleware rate limit bug", "result": { "files": ["src/middleware/auth.py", "src/api/routes.py"], "ts": 1710001123 } } ``` ### Когда кэширование помогает больше всего - Агенты для анализа репозиториев, - поиск по документам, - стабильные базы знаний, - детерминированные преобразования, - повторный поиск данных в рабочих процессах с несколькими ходами. ### Когда кэширование может навредить - быстро меняющиеся данные, - результаты, зависящие от конкретного пользователя, - всё, для чего актуальность данных критична. Распространённая ошибка — кэшировать результаты инструментов, но не сообщать агенту об их актуальности. Если модель считает, что получила текущие данные, хотя на самом деле они взяты из кэша, она может принимать неверные решения. Добавляйте временные метки или сроки актуальности в метаданные инструментов. ## 5) Выведите расходы на панель мониторинга Если отслеживать только задержку и долю успешных выполнений, можно упустить главную проблему: слишком дорогой успех. Для каждого запуска нужно отслеживать: - входные токены, - выходные токены, - вызовы инструментов, - задержку инструментов, - общие расходы, - количество шагов, - причину остановки. ### Минимальная схема события ``` { "run_id": "r_8f2c", "step": 4, "model": "gpt-5.5", "input_tokens": 1842, "output_tokens": 221, "tool_name": "repo_search", "tool_latency_ms": 312, "estimated_cost_usd": 0.087, "stop_reason": "step_cap" } ``` ### За чем следить на практике - Токены на успешно выполненную задачу: самый прямой показатель затрат. - Токены на шаг: помогает выявить разрастание запросов. - Повторные вызовы инструментов: обычно указывают на ошибку планирования. - Распределение причин остановки: если многие запуски завершаются из-за достижения лимита, ваш агент недостаточно хорошо планирует. Один из самых полезных графиков — стоимость по типам задач. Некоторые категории закономерно будут дорогими. Это нормально. Выявлять нужно задачи, которые должны быть дешёвыми, но оказываются дорогими. ## 6) Останавливайте неуправляемые циклы заранее У цикла должны быть и другие причины остановки, кроме «достигнут лимит шагов». Полезные условия остановки: - модель выдала окончательный ответ, - один и тот же вызов инструмента повторяется без получения новой информации, - прирост уверенности или полезности ниже порогового значения, - проверяющий агент подтвердил, что задача решена, - выполнение плана перестало продвигаться. ### Пример прерывания цикла ``` seen_calls = set() for step in range(MAX_STEPS): action = model.next_action(state) signature = (action.tool_name, canonicalize(action.arguments)) if signature in seen_calls: return fail("repeated tool call with no new information") seen_calls.add(signature) observation = run_tool(action.tool_name, action.arguments) state = update_state(state, action, observation) if verifier_says_done(state): return finalize(state) ``` Это позволяет выявить очень распространённую проблему: модель «хочет быть полезной» и просто переформулирует один и тот же запрос. ### Ещё один полезный защитный механизм: проверка полезности Если шаг недостаточно меняет состояние, не продолжайте. Условия могут быть совсем простыми: - новые файлы не найдены, - новые сущности не извлечены, - уверенность в ответе не повысилась, - новые подтверждающие данные не добавлены. Конкретная метрика зависит от задачи, но оценивать дополнительную пользу каждого шага необходимо. ## Что на самом деле происходит при правильной настройке Когда вы налаживаете цикл, агент обычно становится лучше в аспектах, которые поначалу легко не заметить: - Он становится менее многословным и более решительным. - Он раньше обращается к нужному инструменту. - У него становится меньше циклов «я всё ещё разбираюсь». - Он становится дешевле и быстрее, потому что меньшее число ходов означает меньше контекста и меньшую задержку. Важный компромисс заключается в том, что жёсткие лимиты и агрессивное сокращение контекста могут сделать систему ненадёжной на действительно сложных задачах. Поэтому не делайте вид, что агент «решает» проблемы, если на самом деле он просто не успевает выполнить их за отведённое время. Честно отражайте это в логах и сообщениях для пользователей. Моё практическое правило: стремитесь к компетентности в заданных пределах вместо бесконечных блужданий. ## Практичная конфигурация для рабочей среды Если бы я запускал агента завтра, я бы начал со следующего: - MAX_STEPS = 5 - структурированное состояние вместо повторной передачи всей истории диалога - память в виде краткого резюме после каждого хода - недорогие субагенты для поиска и извлечения данных - кэш результатов инструментов с метаданными об актуальности - учёт токенов и расходов для каждого запуска - детектор повторных вызовов - явные причины остановки В такой конфигурации нет ничего эффектного, но именно она не даёт счёту расти быстрее продукта. ## Практические рекомендации - Сначала ограничьте число шагов: это самая дешёвая защита от неконтролируемых циклов. - Активно сокращайте контекст и храните структурированное состояние отдельно от кратких резюме. - Используйте более дешёвых субагентов для узких задач вместо того, чтобы повсюду тратить дорогие токены. - Кэшируйте воспроизводимые результаты инструментов, но указывайте их актуальность, чтобы модель не доверяла устаревшим данным вслепую. - Регистрируйте расходы для каждого запуска, шага и вызова инструмента, чтобы рост затрат был сразу заметен. - Останавливайте циклы при повторениях, отсутствии прогресса или подтверждённом завершении задачи, а не только при исчерпании ресурсов. - Проектируйте систему с расчётом на компетентность в заданных пределах: достаточно хорошее, быстрое и предсказуемое решение всегда лучше хитроумного, но дорогого. [Агенты](/ru/blog/tag/agents/)[Инженерия](/ru/blog/tag/engineering/)[API](/ru/blog/tag/api/) A [AI Prime Tech](https://aiprimetech.io/ru/) Этот блог публикует AI Prime Tech. Статьи готовятся с помощью ИИ; прежде чем полагаться на сведения, зависящие от версии, такие как идентификаторы моделей, цены и лимиты, сверяйте их с официальной документацией. Получите более дешёвый доступ к API Claude Один ключ API для Claude Opus 5.5, Sonnet 5, Haiku 4.5 и Fable 5.1, а также GPT-6 моделей. Оплата по мере использования, без подписки. [Получить ключ API →](/register) AI Prime Tech — независимый сторонний API-шлюз. Claude™ и Anthropic® — товарные знаки Anthropic, PBC. Связь с этой компанией или одобрение с её стороны не подразумеваются.