Главная / Статьи

Работа агентов AWS AI в масштабе: токены, расходы, лимиты и автономность

Подсчёт токенов, мониторинг расходов, восстановление после превышения лимита контекста и надёжные автономные рабочие процессы для агентов AWS, каждый из которых объясняется с помощью паттернов, знакомых разработчикам full-stack JavaScript.

Издатель: REACTAPP.TOP8 мин чтения1929 слов

Источники: 🏗️ Building Production-Ready Enterprise AI Agents on AWS — Part 3: Tokenization, Monitoring, Context Limits & Autonomous Workflows

Агент, чьи параметры разработаны разумно и который подкреплен механизмами проверки и извлечения данных, всё равно должен выдержать первый месяц работы в производственной среде. Определяющими факторами здесь являются в основном не самые привлекательные аспекты: способ подсчёта токенов, мониторинг расходов ресурсов, что происходит, когда заполняется окно контекста, и то, построен ли длительный автономный цикл как настоящий движок рабочих процессов. Если вы работали с полноценным JavaScript на AWS, каждый из этих аспектов легко соотносится с чем-то, что вам уже знакомо, и в данном обзоре постоянно используются эти аналогии.

Токенизация — это слой сериализации

Веб-приложения сериализуют данные перед их передачей по сети с помощью JSON.stringify или protobuf. Языковая модель выполняет аналогичную функцию с текстом: прежде чем она начнёт обрабатывать информацию, текст разбивается на токены — фрагменты между словами, а не отдельные символы или целые слова.

"Enterprise agents scale."
   → ["Enterprise", " agents", " scale", "."]   (≈4 tokens, illustrative)

Причины, по которым инженеру важно обращать на это внимание, сводятся к трём моментам.

  • Токены — это единица измерения, по которой взимается плата; они сопоставимы с единицей GB-секунд в Lambda. И входные, и выходные данные стоят денег, и поскольку цены в Bedrock различаются в зависимости от модели, количество токенов напрямую влияет на счёт от AWS.
  • Токены также определяют максимальный размер данных, передаваемых в запросе, подобно лимиту для тела запроса, как в примере express.json({ limit: '5mb' }). В данном случае таким лимитом является окно контекста, которое у современных моделей может достигать сотен тысяч токенов и продолжает расти; поэтому уточните реальный лимит для вашей модели, а не делайте предположения.
  • Процесс токенизации неодинаков для разных языков. Для текста на языках, отличных от английского, часто требуется больше токенов на одно слово, что имеет значение для продуктов, работающих в нескольких регионах.

В Node.js подсчёт количества токенов перед отправкой запроса похож на измерение размера данных перед вызовом функции fetch:

// Node.js: counting tokens before sending a request
// (conceptually like calculating payload size before a fetch call)
import { encoding_for_model } from "tiktoken";

const enc = encoding_for_model("gpt-4o");
const tokens = enc.encode(userPrompt);
console.log(`Payload size: ${tokens.length} tokens`); // like Content-Length
enc.free();

В Bedrock можно обойтись без стороннего токенизатора. API CountTokens подсчитывает количество токенов с серверной стороны для поддерживаемых моделей Claude, использует ту же структуру сообщений, что и API Converse, и позволяет заранее проверить количество токенов в запросе без необходимости оплачивать обработку:

# Python (boto3): pre-flight token count via Bedrock, no separate tokenizer library needed
response = bedrock_runtime.count_tokens(
    modelId="anthropic.claude-3-5-sonnet-20241022-v2:0",
    input={"converse": {"messages": [{"role": "user", "content": [{"text": userPrompt}]}]}}
)
print(f"Input tokens: {response['inputTokens']}")

Урок: измеряйте количество токенов так же, как вы измеряете размер HTTP-полезной нагрузки, до отправки запроса, а не после того, как вам выставлен счёт или запрос отклонён.

Контроль расхода токенов как CloudWatch для счётов за LLM

В предыдущем разделе рассматривался один запрос. В общем виде показывается, сколько токенов потратил каждый агент за день для каждого арендатора, и вам нужно узнать это раньше, чем сделают финансовый отдел. Никто бы не запустил Express API на AWS без панелей управления и сигнализации, и мониторинг токенов заслуживает такого же подхода.

Ключевые показатели для агента:

  • Количество токенов на запрос, аналогично размеру входящей полезной нагрузки.
  • Количество токенов на завершение запроса, аналогично размеру ответа.
  • Общее количество токенов за сессию, что соответствует объёму памяти сессии.
  • Стоимость за агента, пользователя или арендатора — тот же показатель, что и для использования базы данных в мультиарендаторских SaaS-решениях.
  • Задержка вызовов агента на уровне p95 и p99 должна соответствовать вашим обычным показателям SLO. Четко указывайте эти показатели и переходите на кэшированный или оптимальный по эффективности путь при их нарушении.
  • Скорость расхода токенов в минуту, сопоставимая с ограничением количества запросов в секунду.
  • Конкретные шаги в AWS:

    • Отправляйте пользовательские метрики CloudWatch для токенов ввода, токенов завершения и стоимости в долларах (prompt_tokens, completion_tokens, cost_usd), с метками имени агента и тенанта.
    • Включите кэширование вводных данных. Bedrock может повторно использовать длинные, стабильные префиксы, такие как системные запросы, определения инструментов и примеры работы с небольшим количеством данных, что позволяет значительно снизить затраты и уменьшить задержку. Размещайте неизменяемый контент в первую очередь, иначе вы будете платить полную стоимость за одинаковые токены при каждом вызове.
    • Создайте сигнализации CloudWatch, которые уведомляют через SNS при превышении ежедневных расходов установленного порога, точно как сигнализации биллинга AWS.
    • Записывайте каждый вызов агента в S3 с помощью Kinesis Firehose или напрямую, чтобы сохранять историю операций. Это потребуется со стороны проверяющих SOC 2 и крупных клиентов.
    • Для оценки достаточно простого расчёта, аналогичного тому, что используется для Lambda: добавьте токены начала и завершения обработки, умножьте на стоимость модели, затем добавьте расходы на получение данных и инфраструктуру за сессию.

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

    Ошибки из-за ограничения объёма данных: защита от сбоев и пагинация

    У каждого окна контекста есть предел. Длинные разговоры, обширные результаты работы инструментов и перегруженные фрагменты данных могут превысить этот лимит. Это аналог ошибки 413 Payload Too Large в мире агентов, только хуже, поскольку она может возникнуть на любом этапе многошаговой задачи.

    Стратегии восстановления заимствованы непосредственно из инженерии бэкенда:

    1. Суммаризация и сжатие. Периодически компрессировать старые сообщения, как при ротации журналов логов.
    2. Использование скользящего окна или обрезка. Удалять самые старые сообщения, подобно тому как при просмотре большого набора результатов запроса не загружаются все из них сразу.
    3. Перенос данных на внешние системы. Хранить материалы вне запроса и загружать их по мере необходимости, следуя принципу отложенной загрузки.
    4. Разделение на подагентов. Разбивать одну огромную задачу на мелкие этапы с новым контекстом, как превращение монолитной задачи в очередь небольших операций.
  • Плавное снижение качества. Если невозможно восстановиться в процессе выполнения задачи, необходимо совершить сбой с использованием структурированной точки контроля, позволяющей возобновить работу, а никогда не допускать появления незаметных искажений.
  • Вот пример шаблона обработки ошибок и сжатия в коде, аналогичного тому, что используется для обработки превышения времени ожидания в базе данных:

    # Conceptual: catching a context-limit error, like catching a DB timeout
    try:
        result = agent.run(task, conversation_history)
    except AgentError as err:
        if err.code == "context_length_exceeded":
            # Keep the system prompt intact — compact the *conversation*, never the identity.
            # Summarizing everything (including system instructions) is how agents quietly
            # "forget" their role mid-task, the same way you'd never let a cache-eviction
            # policy drop your app's config alongside its session data.
            compacted = summarize(conversation_history, preserve="system_prompt")
            result = agent.run(task, compacted)
        else:
            raise  # don't swallow unrelated errors
    

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

    Автономные рабочие процессы как машины состояний

    Автономный агент не действует один раз — он планирует, выполняет действия, наблюдает за ситуацией и повторяет этот цикл до достижения цели. Любой, кто создавал машину состояний XState, схему Redux saga или поток AWS Step Functions для многоэтапной процедуры оплаты, уже знаком с такой структурой. Это знакомый цикл в стиле ReAct, состоящий из планирования, действия, наблюдения и оценки, с возможностями завершения работы или передачи задачи человеку, и его следует проектировать как надёжный движок рабочих процессов.

    • Идемпотентные шаги. Повторная обработка шага не должна дважды взимать плату с клиента или отправлять дублирующиеся электронные письма.
  • Четкие условия остановки. Ограничьте количество итераций, затраты и время выполнения или же избегайте создания бесконечных циклов while(true), связанных с использованием кредитной карты.
  • Передача задачи человеку. Как и в случае с компенсационными транзакциями, при низкой степени уверенности или высоких рисках задача должна быть передана человеку, а не выполняться автоматически.
  • Постоянное состояние. Создавайте точки контроля на каждом этапе в DynamoDB или состоянии Step Functions, чтобы при сбое в процессе выполнения можно было возобновить работу без потери транзакции. Установите временной интервал истечения для элементов сессии в DynamoDB, чтобы устаревшие данные исчезали автоматически, как это делается с ключами сессий в Redis.
  • Модели резервного варианта. Если основная модель, например Claude на Bedrock, теряет связь или работает с ограничениями по скорости, механизм защиты должен переключиться на резервную модель, такую как Amazon Nova, и не показывать пользователю ошибку. Это аналогично тому, как HTTP-клиент переключается на резервный регион.
  • Управление моделями. Замена модели должна обрабатываться так же, как любая другая развертывание в производстве: сначала её следует протестировать на части трафика, получить одобрение и предусмотреть возможность возврата к предыдущей версии. Фраза «Мы просто изменили модель» не должна означать незаверенное изменение.
  • Кнопка остановки. Необходимо иметь способ — вручную или по достижении определённого порога — мгновенно остановить всю флотилию агентов, подобно кнопке остановки функционального флага для предотвращения неконтролируемого выпуска обновлений.
  • Чекпоинт — это просто ещё одна запись о задании, которое можно возобновить, в DynamoDB; Step Functions считывает её при перезапуске, чтобы продолжить выполнение с последнего успешного шага:

    {
      "pk": "session#8f2c1a",
      "sk": "checkpoint#2026-08-23T10:15:00Z",
      "step": "plan",
      "hop_count": 2,
      "cost_so_far_usd": 0.014,
      "state": { "goal": "resolve billing dispute", "last_action": "lookup_charge" },
      "ttl": 1788084900
    }
    

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

    Над всем этим существует еще один уровень: процедуры оценки. Правила безопасности, качество получаемой информации и ее соответствие реальности требуют тестов на регрессию так же, как и код приложения. Набор инструментов для оценки воспроизводит фиксированный набор входных данных для агента и оценивает результаты по сравнению с ожидаемым поведением, так что изменение промпта или замена модели не могут тайно ухудшить работу системы. Ragas оценивает показатели, связанные с получением информации, такие как точность и релевантность контекста, в то время как Promptfoo и Braintrust обеспечивают более широкую проверку промптов и результатов. Подключите выбранный инструмент в процесс CI так же, как вы это делаете с pytest, чтобы сбой в оценке блокировал развертывание, и никому не пришлось бы вручную проверять результаты.

    Чек-лист готовности к производственному использованию

    • У каждого агента есть одна проверяемая обязанность и узко определённые разрешения IAM.
    • Передача данных осуществляется с использованием структурированного контекста через надёжный канал (SQS или EventBridge) и защищена от циклов.
    • Механизмы контроля входных и выходных данных являются отдельными, комбинируемыми элементами и имеют собственные версии.
    • Качество получаемых данных оценивается с помощью отдельных показателей точности и полноты, их не сводят к формулировке «ИИ ошибается».
    • Все критически важные элементы проходят проверки на галлюцинации: проверка на основе реальных данных, оценка уровня уверенности или участие человека.
    • Использование токенов измеряется до отправки запроса, а не только после его выполнения.
    • Панели управления CloudWatch показывают расходы по каждому арендатору и каждому агенту, с алертами о достижении лимитов бюджета через SNS.
    • Существует чёткий план восстановления при ошибках, связанных с ограничением объёма контекста: сжатие, обрезка или перенос данных.
  • У автономных циклов существуют строгие ограничения по количеству итераций, времени выполнения и затрачиваемым ресурсам.
  • Невозвратные действия, такие как возврат средств, отправка электронных писем и удаление данных, требуют утверждения человеком.
  • Решения агентов полностью записываются в S3 или CloudWatch для обеспечения соответствия требованиям.
  • Автоматизированные тесты выполняются в CI перед развертыванием любых изменений в запросах или моделях — этот этап никогда не пропускается при работе с кодом приложений.
  • Этап тестирования в промежуточной среде проходил с имитацией сбоев моделей, поврежденных результатов поиска и циклов передачи данных, так что пути восстановления проверялись до того, как они стали использоваться в производственной среде.
  • Что это дает в итоге

    Каждая концепция агента имеет свой аналог в мире полноценных фреймворков, с которым вы знакомы. Агенты — это микросервисы с способностью к принятию решений, а передача данных осуществляется с использованием надежных, отслеживаемых маршрутов с ограничением по количеству этапов. Механизмы контроля представляют собой слои промежуточного программирования, процесс извлечения информации осуществляется с помощью усовершенствованного слоя запросов, а галлюцинации выявляются путем связывания с реальностью и проверки, а не простым вежливым запросом. Токены одновременно служат мерой размера данных и единицей оплаты. Мониторинг расходов — это аналог CloudWatch для отслеживания использования моделей. Полное окно контекста представляет собой фактор, приводящий к предсказуемым сбоям, требующим плана восстановления, а автономия — это надежная машина состояний, совершенно не похожая на простой цикл while.

    Энтерпрайз-агенты редко терпят неудачу в производственной среде из-за недостаточной умности модели. Они терпят неудачу потому, что команды игнорируют основные принципы работы распределенных систем, которые они никогда бы не проигнорировали при работе с обычным API: валидацию, возможность наблюдения за работой системы, повторные попытки выполнения операций, идемпотентность, а также механизмы плавного снижения нагрузки. Если вы уже развернули устойчивое JavaScript-приложение на AWS, у вас уже есть соответствующие навыки. Остается лишь направить эти навыки на неизвестную и непредсказуемую зависимость, которая отвечает в виде текстовых сообщений.