Ідея за 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 та інших платформ.
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_p95 | 2.4s | +38% за 30 хв | warning: вище SLO |
| tool_error_rate | 7.2% | +4.1pp за 15 хв | critical: alert |
| token_usage_per_run | 8.9k | +22% після релізу | warning: аномалія |
| success_rate | 91.4% | -5.3pp за 1 год | warning: падіння |
Для error_class краще використовувати нормалізований словник значень, щоб не створювати зайву кардинальність.
Наприклад, один рядок метрики може виглядати так:
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.
Пов'язані сторінки
Далі за темою:
- Observability для AI-агентів — загальна модель трейсингу, логування і метрик.
- Логування агентів — які події фіксувати в runtime.
- Трейсинг агента — як бачити шлях одного run по кроках.
- Семантичне логування агентів — як уніфікувати події для аналітики.
- Моніторинг витрат AI-агентів — як контролювати cost у production.