Метрики агентів

Метрики показують успішність, tool calls і кроки виконання агента.
На цій сторінці
  1. Ідея за 30 секунд
  2. Основна проблема
  3. Як це працює
  4. Типові production-метрики для агентів
  5. Коли використовувати
  6. Приклад реалізації
  7. Типові помилки
  8. Лише середні значення, без p95/p99
  9. Висококардинальні labels
  10. Немає метрик по stop_reason
  11. Немає алертів на ключові аномалії
  12. Самоперевірка
  13. FAQ
  14. Пов'язані сторінки

Ідея за 30 секунд

Метрики агентів показують стан системи на рівні багатьох run, а не одного випадку.

Вони відповідають на питання: чи система стабільна, чи ростуть витрати, і де починається деградація.

Без метрик проблеми зазвичай видно запізно: після скарг користувачів або перевитрати бюджету.

Основна проблема

Логи й трейсинг добре пояснюють один конкретний інцидент.

Але в production важливо бачити тренди: що відбувається з latency, token usage, error rate і tool calls між релізами. Без метрик система може деградувати поступово, і це довго залишається непомітним.

У production це зазвичай виглядає так:

  • середній час відповіді ніби нормальний, але p95 уже росте;
  • витрати токенів підскакують хвилями після релізу;
  • зростає кількість tool calls на один run;
  • команда дізнається про проблему тільки після інциденту.

Саме тому метрики потрібні як окремий сигнал observability: вони дозволяють вчасно помічати аномалії й реагувати до великих збоїв.

Як це працює

Метрики — це агреговані числові сигнали, які показують поведінку системи в часі.

Зазвичай у системі є три рівні метрик:

  • run-рівень (run_count, success_rate, stop_reason);
  • кроки й інструменти (tool_calls_per_run, tool_error_rate, step_count);
  • вартість і швидкість (token_usage, cost_per_run (розраховується з token_usage на рівні дашборду або запитів до метрик), latency_p50/p95).

Метрики дають ранній сигнал, коли система починає деградувати. Логи відповідають на «що сталося», а трейсинг — «як саме це сталося в конкретному run».

Типові production-метрики для агентів

МетрикаЩо показуєНавіщо потрібна
run_countкількість run за періодконтроль навантаження та обсягу трафіку
success_rateчастка успішних runшвидка оцінка стабільності
latency_p50 / latency_p95типова й «довга» затримкавиявлення деградації швидкодії
token_usage_per_runскільки токенів витрачає runконтроль витрат на LLM
cost_per_runприблизна вартість одного runконтроль бюджету та прогноз витрат
tool_calls_per_runскільки разів run викликає toolsвиявлення зайвих або циклічних викликів
tool_error_rateчастота помилок інструментівраннє виявлення нестабільних залежностей
stop_reason_distributionрозподіл причин завершення runконтроль обмежень і типових збоїв

Щоб метрики були корисними, їх зазвичай розбивають за release, model або tool.

Важливо: не додавай у мітки (labels) висококардинальні поля (run_id, request_id, user_id), інакше сховище метрик швидко перевантажиться.

Коли використовувати

Широкий набір метрик не завжди потрібен.

Для раннього прототипу інколи достатньо базових лічильників run і помилок.

Але метрики стають критичними, коли:

  • агентна система вже в production;
  • є SLO по latency або якості;
  • потрібно контролювати витрати токенів і tool calls;
  • релізи виходять часто й треба бачити регресії до інцидентів.

Приклад реалізації

Нижче — спрощений приклад runtime-інструментації метрик у стилі Prometheus. У реальних системах ті самі принципи працюють і для Datadog, Grafana Cloud, CloudWatch та інших платформ.

PYTHON
import time
from prometheus_client import Counter, Histogram

RUN_TOTAL = Counter(
    "agent_run_total",
    "Total number of agent runs",
    ["status", "stop_reason", "release"],
)
# success_rate = RUN_TOTAL{status="ok"} / RUN_TOTAL

RUN_LATENCY_MS = Histogram(
    "agent_run_latency_ms",
    "Run latency in milliseconds",
    ["release"],
    buckets=(100, 250, 500, 1000, 2000, 5000, 10000),
)

STEP_COUNT = Histogram(
    "agent_steps_per_run",
    "Number of steps per run",
    ["release"],
    buckets=(1, 2, 4, 8, 12, 16, 24, 32),
)

TOOL_CALL_TOTAL = Counter(
    "agent_tool_call_total",
    "Total tool calls",
    ["tool", "status", "release"],
)

TOOL_ERROR_TOTAL = Counter(
    "agent_tool_error_total",
    "Total tool errors by class",
    ["tool", "error_class", "release"],
)

LLM_ERROR_TOTAL = Counter(
    "agent_llm_error_total",
    "Total LLM step errors by model and class",
    ["model", "error_class", "release"],
)

TOOL_LATENCY_MS = Histogram(
    "agent_tool_latency_ms",
    "Tool call latency in milliseconds",
    ["tool", "release"],
    buckets=(20, 50, 100, 250, 500, 1000, 2000, 5000),
)

TOKEN_USAGE_TOTAL = Counter(
    "agent_token_usage_total",
    "Total LLM tokens",
    ["model", "token_type", "release"],
)


def observe_llm_usage(model, token_usage, release):
    # більшість LLM-провайдерів повертають token usage у відповіді
    if not token_usage:
        return
    TOKEN_USAGE_TOTAL.labels(model=model, token_type="prompt", release=release).inc(
        token_usage.get("prompt_tokens", 0)
    )
    TOKEN_USAGE_TOTAL.labels(model=model, token_type="completion", release=release).inc(
        token_usage.get("completion_tokens", 0)
    )


def run_agent(agent, task, release="2026-03-21"):
    started_at = time.time()
    steps = 0
    stop_reason = "max_steps"
    run_status = "ok"

    try:
        for step in agent.iter(task):
            steps += 1
            step_type = step.type
            result = None  # може залишитись None для невідомих типів кроків (захищено перевіркою нижче)

            if step_type == "tool_call":
                tool_name = getattr(step, "tool_name", "unknown")
                tool_started_at = time.time()
                try:
                    result = step.execute()
                    TOOL_CALL_TOTAL.labels(tool=tool_name, status="ok", release=release).inc()
                    TOOL_LATENCY_MS.labels(tool=tool_name, release=release).observe(
                        (time.time() - tool_started_at) * 1000
                    )
                except Exception as error:
                    TOOL_CALL_TOTAL.labels(tool=tool_name, status="error", release=release).inc()
                    TOOL_ERROR_TOTAL.labels(
                        tool=tool_name,
                        error_class=type(error).__name__,
                        release=release,
                    ).inc()
                    TOOL_LATENCY_MS.labels(tool=tool_name, release=release).observe(
                        (time.time() - tool_started_at) * 1000
                    )
                    run_status = "error"
                    stop_reason = "tool_error"
                    raise
            else:
                try:
                    result = step.execute()
                    observe_llm_usage(
                        model=getattr(step, "model", "unknown"),
                        token_usage=getattr(result, "token_usage", None),
                        release=release,
                    )
                except Exception as error:
                    LLM_ERROR_TOTAL.labels(
                        model=getattr(step, "model", "unknown"),
                        error_class=type(error).__name__,
                        release=release,
                    ).inc()
                    run_status = "error"
                    stop_reason = "step_error"
                    raise

            if result and result.is_final:
                stop_reason = "completed"
                break

    finally:
        RUN_TOTAL.labels(status=run_status, stop_reason=stop_reason, release=release).inc()
        RUN_LATENCY_MS.labels(release=release).observe((time.time() - started_at) * 1000)
        STEP_COUNT.labels(release=release).observe(steps)

У production ці метрики зазвичай йдуть у дашборди та алерти.

Ось як ці метрики виглядають разом у реальному дашборді:

МетрикаПоточне значенняТрендСтатус
latency_p952.4s+38% за 30 хвwarning: вище SLO
tool_error_rate7.2%+4.1pp за 15 хвcritical: alert
token_usage_per_run8.9k+22% після релізуwarning: аномалія
success_rate91.4%-5.3pp за 1 годwarning: падіння

Для error_class краще використовувати нормалізований словник значень, щоб не створювати зайву кардинальність.

Наприклад, один рядок метрики може виглядати так:

TEXT
agent_tool_call_total{tool="search_docs",status="error",release="2026-03-21"} 47

Типові помилки

Навіть коли метрики вже додано, вони часто не дають користі через типові помилки нижче.

Лише середні значення, без p95/p99

Середнє значення приховує «довгий хвіст» повільних run. Тому для production мінімум — p50 і p95.

Висококардинальні labels

Мітки на кшталт run_id або user_id різко збільшують навантаження на бекенд метрик. Краще сегментувати за release, model, tool.

Немає метрик по stop_reason

Без розподілу stop_reason складно зрозуміти, чому run завершуються через max_steps або tool_error. Це часто маскує збій інструмента і перші сигнали вибуху бюджету.

Немає алертів на ключові аномалії

Метрики без алертів перетворюються на пасивні графіки. Без цього легко пропустити спам інструментами або різке падіння успішності після релізу.

Самоперевірка

Нижче — короткий checklist базових метрик агентів перед релізом.

Прогрес: 0/9

⚠ Бракує базової observability

Систему буде складно дебажити в production. Почніть з run_id, structured logs і tracing tool calls.

FAQ

Q: Чим метрики відрізняються від логів і трейсингу?
A: Метрики показують тренди й стан системи в часі. Логи пояснюють події, а трейсинг показує шлях конкретного run.

Q: Який мінімум метрик потрібен для першого production-релізу?
A: Почни з run_count, success_rate, latency_p95, tool_error_rate, token_usage_per_run і stop_reason_distribution.

Q: Чому недостатньо тільки середнього latency?
A: Середнє приховує довгі повільні run. Саме p95 швидше показує реальну деградацію для користувачів.

Q: Які labels найчастіше ламають сховище метрик?
A: Усе, що має високу кардинальність: run_id, request_id, user_id, повні prompts або raw args.

Пов'язані сторінки

Далі за темою:

⏱️ 6 хв читанняОновлено 21 березня 2026 р.Складність: ★★★
Інтегровано: продакшен-контрольOnceOnly
Додай guardrails до агентів з tool-calling
Зашип цей патерн з governance:
  • Бюджетами (кроки / ліміти витрат)
  • Дозволами на інструменти (allowlist / blocklist)
  • Kill switch та аварійна зупинка
  • Ідемпотентність і dedupe
  • Audit logs та трасування
Інтегрована згадка: OnceOnly — контрольний шар для продакшен агент-систем.

Автор

Микола — інженер, який будує інфраструктуру для продакшн AI-агентів.

Фокус: патерни агентів, режими відмов, контроль рантайму та надійність систем.

🔗 GitHub: https://github.com/mykolademyanov


Редакційна примітка

Ця документація підготовлена з допомогою AI, із людською редакторською відповідальністю за точність, ясність і продакшн-релевантність.

Контент базується на реальних відмовах, постмортемах та операційних інцидентах у розгорнутих AI-агентних системах.