Практическое руководство

Учёт стоимости AI-агента по бизнес-операциям, а не по токенам

Суммарный расход токенов отвечает на вопрос «сколько потрачено», но не объясняет, какая операция создала расход, завершилась ли она и сколько стоили инструменты, ожидание, повторные попытки и инфраструктура. Ниже — схема учёта, которая связывает затраты с бизнес-исходами.

Уровень: продвинутый Чтение: до 10 минут Результат: стоимость завершённых и сорванных операций

Почему токенов недостаточно

Для начала определим бизнес-операцию как единицу полезной работы с явным началом, ожидаемым исходом и конечным статусом. Это может быть обработка возврата, сверка счёта, подготовка ответа или обновление записи — при условии, что границы операции заданы системой, а не восстановлены постфактум из отдельных вызовов модели.

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

Практическая единица анализа выглядит так:

business_operation
├── operation_id
├── operation_type
├── attempts[]
│   ├── model_calls[]
│   ├── tool_calls[]
│   └── infrastructure_usage[]
└── outcome

Главный принцип: сначала создаётся идентификатор операции, затем все расходы записываются с этим идентификатором. Распределять обезличенный месячный счёт между операциями значительно менее надёжно.

1. Зафиксируйте границы и исход операции

Не используйте свободный текст в качестве типа или статуса. Задайте ограниченный словарь:

{
  "operation_id": "op_01...",
  "operation_type": "invoice_reconciliation",
  "started_at": "2026-07-29T08:00:00Z",
  "finished_at": "2026-07-29T08:00:14Z",
  "status": "completed",
  "outcome_code": "matched",
  "attempt_count": 2
}

Это пример структуры, а не описание конкретной системы. Полезный минимальный набор статусов:

  • completed — проверяемый бизнес-результат достигнут;
  • failed — операция завершилась ошибкой;
  • abandoned — исчерпан лимит времени, попыток или бюджета;
  • escalated — работа передана человеку или другой системе;
  • cancelled — инициатор отменил ещё актуальную операцию.

Не приравнивайте успешный HTTP-ответ или корректный JSON к завершённой операции. Технический шаг может пройти успешно, а бизнес-результат — не состояться.

2. Введите единый конверт атрибуции

Каждое затратное событие должно содержать ключи, позволяющие пройти от счёта поставщика к бизнес-исходу:

{
  "event_id": "evt_01...",
  "operation_id": "op_01...",
  "operation_type": "invoice_reconciliation",
  "attempt_id": "att_02",
  "run_id": "run_08...",
  "component": "model",
  "resource": "model_alias",
  "quantity": {
    "input_tokens": 4200,
    "output_tokens": 380
  },
  "cost": {
    "amount": "0.000000",
    "currency": "USD",
    "pricing_version": "internal-example-v1"
  },
  "occurred_at": "2026-07-29T08:00:10Z"
}

Значение стоимости здесь намеренно условное. Фактическую цену следует вычислять по вашим договорам, счетам и версиям тарифов. Храните денежные значения как decimal или целое число минимальных расчётных единиц, а не как число с плавающей точкой.

Передавайте operation_id, attempt_id и run_id через контекст выполнения. Не включайте в них персональные данные, содержимое запроса или секреты.

3. Записывайте расходы как отдельные события

Унифицируйте модельные и немодельные расходы, но не смешивайте исходные единицы измерения.

Компонент Количество Денежная стоимость
Модель Входные, выходные и кэшированные токены По версии тарифа и классу модели
Инструмент Вызовы, страницы, документы, запросы По факту или внутренней ставке
Вычисления CPU/GPU-секунды, память, длительность По измеренной ставке инфраструктуры
Хранение и передача Байты, операции чтения/записи По счёту или ставке платформы
Человек Минуты проверки или исправления Отдельная управленческая ставка

Для каждого события храните две величины:

  • измеренное потребление — токены, секунды, вызовы или байты;
  • оценённую стоимость — результат применения конкретной версии тарифа.

Так историю можно пересчитать при исправлении тарифной таблицы, не меняя первичные данные.

4. Не скрывайте стоимость повторных попыток

Повторная попытка должна получать новый attempt_id, сохраняя прежний operation_id. Записывайте причину повтора ограниченным кодом:

retry_reason:
  - model_timeout
  - tool_timeout
  - rate_limited
  - invalid_model_output
  - tool_result_rejected
  - policy_recheck
  - optimistic_lock_conflict
  - operator_requested

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

Полезные производные метрики:

operation_cost =
  sum(model_cost + tool_cost + infrastructure_cost)

retry_cost =
  sum(cost where attempt_number > 1)

waste_cost =
  sum(cost where status in ("failed", "abandoned", "cancelled"))

cost_per_completed_operation =
  sum(operation_cost) / count(status = "completed")

waste_cost — управленческая классификация, а не бухгалтерский термин. Например, диагностика неуспешной операции может быть полезна для качества, хотя относится к затратам без завершённого результата.

5. Настройте безопасный бюджет выполнения

Одной аналитики недостаточно: агенту нужны ограничения на уровне операции. Пример конфигурации:

operation_budgets:
  invoice_reconciliation:
    max_attempts: 3
    max_model_calls: 8
    max_tool_calls: 12
    max_elapsed_seconds: 90
    max_estimated_cost_minor: 250
    on_limit: escalate

attribution:
  require_operation_id: true
  reject_unknown_operation_type: true
  pricing_version_required: true
  redact_payloads: true

Числа в примере демонстрационные. Подберите их по собственному распределению стоимости и допустимому риску. Денежный лимит лучше хранить в минимальных расчётных единицах вместе с валютой.

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

6. Постройте итоговую витрину

Ниже — безопасный пример запроса только на чтение. Названия таблиц условны:

SELECT
  o.operation_type,
  o.status,
  COUNT(*) AS operations,
  SUM(c.amount_minor) AS total_cost_minor,
  SUM(CASE WHEN c.attempt_number > 1
           THEN c.amount_minor ELSE 0 END) AS retry_cost_minor,
  AVG(c.operation_cost_minor) AS avg_operation_cost_minor
FROM business_operations AS o
JOIN (
  SELECT
    operation_id,
    MAX(attempt_number) AS attempt_number,
    SUM(amount_minor) AS amount_minor,
    SUM(amount_minor) AS operation_cost_minor
  FROM cost_events
  GROUP BY operation_id
) AS c USING (operation_id)
WHERE o.started_at >= :period_start
  AND o.started_at < :period_end
GROUP BY o.operation_type, o.status
ORDER BY total_cost_minor DESC;

Перед выполнением адаптируйте запрос к своей СУБД и схеме. Параметры периода передавайте через механизм параметризации драйвера. Не подставляйте пользовательский ввод конкатенацией строк.

Для управленческого отчёта достаточно пяти срезов:

  • стоимость по типу и статусу операции;
  • доля повторных попыток в стоимости;
  • стоимость по инструменту и коду ошибки;
  • медиана и верхние перцентили стоимости завершённой операции;
  • сумма затрат на сорванные и эскалированные операции.

Проверка результата

Проверяйте не только наличие дашборда, но и сохранность денег при агрегации.

  1. Полнота. У каждого затратного события есть operation_id. Неатрибутированные события вынесены в отдельную очередь контроля, а не удалены.
  2. Уникальность. Повторная доставка события не удваивает стоимость. Для этого применяется уникальный event_id или идемпотентная запись.
  3. Сверка. Сумма событий за расчётный период сопоставима со счётом поставщика с учётом налогов, скидок, округления и временных границ.
  4. Терминальность. У старых операций нет бесконечного статуса running; зависшие операции переводятся в явный исход по правилу.
  5. Повторы. Искусственно созданная в тестовой среде повторная попытка попадает в ту же операцию, но в другой attempt_id.
  6. Инвариант. Сумма стоимости по статусам и типам равна общей сумме атрибутированных событий за тот же период.

Для каждой операции полезно уметь открыть цепочку: бизнес-исход → попытки → вызовы → потребление → тариф → стоимость. Если переход невозможен, отчёт нельзя надёжно использовать для оптимизации.

Типовые ошибки

Агрегация только по модели

Она показывает дорогие модели, но скрывает сценарий, который вызывает модель десять раз. Основным измерением должен быть тип операции, а модель — дополнительным срезом.

Смешение запуска и операции

Перезапуск процесса создаёт новый run_id, но не обязательно новую бизнес-операцию. Иначе повторы искусственно увеличат число операций и занизят стоимость единицы результата.

Учёт только успешных операций

Так исчезают расходы на тайм-ауты, невалидный вывод и откаты. Все начатые операции должны получить конечный статус либо попасть в контроль зависших операций.

Перезапись цены задним числом

Сохраняйте версию тарифа и первичное потребление. Иначе исторический отчёт изменится после обновления модели или контракта без объяснимой причины.

Стоимость инструментов считается нулевой

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

Высокая кардинальность в метриках

Не помещайте уникальный operation_id в labels временных рядов. Храните его в трассах или аналитическом журнале, а в метриках оставляйте ограниченные поля: тип, статус, компонент и код ошибки.

Логи содержат данные запроса

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

Ограничения подхода

  • Общие расходы — простаивающие вычисления, базовые лицензии и наблюдаемость — нельзя объективно приписать одной операции. Выберите и документируйте правило распределения либо показывайте их отдельной строкой.
  • Стоимость не измеряет ценность. Дорогая операция может приносить больше результата, чем дешёвая; для решения нужна отдельная метрика бизнес-эффекта.
  • Косвенные последствия ошибки, например задержка следующего процесса, редко видны в телеметрии агента.
  • Предварительная оценка во время выполнения может отличаться от итогового счёта из-за скидок, пакетных тарифов, кэширования и округления.
  • Сравнение типов операций корректно только при стабильных правилах определения завершения и сопоставимой сложности входов.

Практический критерий готовности

Система учёта готова к использованию, если для любого заметного всплеска затрат можно без чтения содержимого запросов ответить:

  1. какие типы операций создали расход;
  2. сколько операций завершилось, сорвалось или было эскалировано;
  3. какие модели, инструменты и инфраструктурные ресурсы дали основной вклад;
  4. какая доля расходов пришлась на повторные попытки;
  5. каким лимитом или изменением сценария можно управлять затратами.

Токены после этого не исчезают из отчётности. Они занимают правильное место: становятся одним из видов потребления внутри операции, а не заменяют собой экономику агентной системы.