1. Навіщо взагалі потрібні метрики та SLO у ChatGPT App
Уявіть два стани команди.
У першому всі живуть за принципом «здається, працює». Поки користувачі не скаржаться до підтримки й не пишуть гнівні твіти — усе гаразд. Час від часу хтось заходить у логи, гортає суцільне «полотно» рядків, мовчки погоджується й закриває вкладку.
У другому в команди є кілька простих панелей:
- p95 затримки основного MCP‑інструмента.
- error‑rate для MCP і для checkout.
- доступність вебхуків.
- конверсія в оплату за воронкою.
І є 3–5 SLO: «95% викликів інструмента recommend_gifts — швидше ніж за 2 секунди», «частка помилок у MCP‑tools < 1 %», «доступність checkout ≥ 99,5 %», «конверсія від віджета до успішної оплати ≥ 10 %».
У другому варіанті ви:
- швидко розумієте, що із застосунком щось не так, ще до скарг;
- можете виміряти ефект будь-якої зміни (новий алгоритм рекомендацій, міграція SDK, новий тариф провайдера);
- і до моменту, коли ви дійдете до модуля про Store і ревʼю, зможете чесно сказати: «у нас є критерії якості, і ми знаємо, наскільки їм відповідаємо».
Ми говоритимемо про доволі очевидні речі. Що таке p95 і чим він кращий за середнє значення. Як обчислювати error‑rate і availability. Які метрики важливі для комерційної частини з вебхуками. І як із усього цього скласти прості, але корисні SLO. Усе розбиратимемо на прикладі нашого навчального застосунку GiftGenius — комерційного сценарію з MCP‑інструментом рекомендацій, checkout і вебхуками оплати.
2. Базові метрики для ChatGPT App: що саме вимірювати
Затримка: p50/p95/p99 замість «середнього по лікарні»
Затримка (latency) — це час від початку операції до її логічного завершення. У нашому стеку таких операцій кілька:
- виклик MCP‑інструмента recommend_gifts (підбір подарунків);
- виклик інструмента, що створює checkout intent в ACP;
- обробка вебхука про успішну оплату.
Важливо розуміти: загальну затримку, яку бачить користувач у ChatGPT, ми контролюємо лише частково. Є внесок платформи (інференс моделі, мережеві затримки OpenAI). Є й наш внесок: інструменти, бекенд, база даних, платіжний сервіс. Для SLI (Service Level Indicator — вимірюваний показник якості сервісу) щодо затримки зазвичай вимірюють саме нашу частину: від входу запиту в App/MCP до готової відповіді нашого сервера.
Середній час відповіді тут оманливий. Якщо 90 % запитів приходять за 100 мс, а 10 % — за 5 секунд, середнє буде близько пів секунди, і графік начебто «зелений». Але кожен десятий користувач бачить підвисання на 5 секунд — UX відчутно страждає.
Тому зазвичай використовують перцентилі: p50 (медіана), p95, p99. p95 — це така межа, нижче якої вкладаються 95 % запитів. Як підкреслюють настанови SRE, перцентилі «не дають викидам сховатися в середньому значенні» та роблять видимим «життя» тих самих 5–10 % користувачів, які постійно потрапляють у хвіст розподілу.
Простіше думати так: p50 показує, як «живе» типовий користувач. А p95/p99 — наскільки важко тим терплячим людям, у яких постійно «щось гальмує».
Error‑rate: частка неуспішних запитів
Error‑rate — це відношення кількості неуспішних запитів до загальної кількості запитів за період. Основним джерелом даних зазвичай є або структуровані логи, або метрики з мітками status="success" | "error".
У нашому стеку є кілька природних error‑rate:
- на рівні MCP‑tools: частка викликів recommend_gifts, що завершилися помилкою (виняток, HTTP 5xx зовнішнього API, тайм-аут);
- на рівні ACP/checkout: частка неуспішних спроб оформлення замовлення (помилка API, недоступність платіжного сервісу);
- на рівні webhook‑обробника: частка вебхуків, для яких обробка завершилася неуспішно.
Тонкий момент: у ChatGPT App бувають «тихі» помилки. Наприклад, MCP‑інструмент повернув помилку, модель вибачилася й продовжила діалог, не передаючи технічну помилку в заголовок. Формально ChatGPT показує користувачеві «людську» відповідь. Але з погляду надійності ваш стек дав збій. Тому для error‑rate важливо рахувати саме інженерні статуси, а не «кінцеву UX‑відповідь GPT».
Availability: доступність сервісів
Availability — це відсоток запитів, успішно обслугованих за період. Концептуально це той самий error‑rate, тільки з протилежним знаком: «скільки успішних», а не «скільки неуспішних». Класичний приклад: availability = successful_requests / total_requests * 100 %.
Якщо застосувати це до нашого стека:
- доступність MCP‑сервера: скільки JSON‑RPC викликів від ChatGPT завершилося коректною відповіддю за останню годину;
- доступність checkout‑API: яка частка звернень до /api/checkout закінчилася HTTP 2xx;
- доступність webhook‑ендпойнта: яка частка вхідних вебхуків від платіжного сервісу отримала від нас коректну відповідь (зазвичай HTTP 200).
Якщо в документації платіжного провайдера обіцяно, скажімо, доступність 99,9 %, а у вас — 97 %, то проблема, найімовірніше, не в ньому.
Комерційні метрики: конверсія і воронка
GiftGenius — комерційний сценарій. Тут важлива не лише технічна частина (відповіді швидкі, помилок мало), а й бізнес‑результат: як часто рекомендації перетворюються на оплачені замовлення.
Тут у гру вступає конверсійна воронка:
- користувачі, яким показано віджет (view);
- користувачі, які обрали подарунок (selection);
- користувачі, які натиснули «оформити замовлення» (checkout started);
- користувачі, у яких статус замовлення став «оплачено» (paid).
Із цієї воронки можна визначити конверсію «від віджета до оплати». І це теж SLI — вимірюваний показник якості сервісу. Наприклад: «конверсія успішних оплат / користувачі, що побачили віджет за добу, = 7 %». Якщо раптом затримка зросла або checkout іноді падає з тайм-аутом, воронка почне звужуватися в несподіваних місцях.
Якщо ви використовуєте Instant Checkout
Усі описані комерційні метрики так само застосовні й до Instant Checkout на базі Agentic Commerce Protocol. У цьому випадку замість «власного» /api/checkout у вас є стандартизований Agentic Checkout API: ChatGPT викликає ваші REST‑ендпойнти POST /checkout_sessions, POST /checkout_sessions/{id}, POST /checkout_sessions/{id}/complete (а за потреби — cancel і GET), щоразу отримуючи від вас фактичний стан кошика й чекауту.
Для метрик це нічого не змінює: p95 затримки, error-rate і availability ви рахуватимете не для довільного /api/checkout, а для цих стандартних ендпойнтів. SLO на кшталт «p95 затримки checkout < 3 секунди, error-rate < 2 %» просто застосовуються до викликів checkout_sessions, а не до саморобного API.
Окремий світ: метрики вебхуків
Вебхуки — це асинхронні події (наприклад, payment_succeeded від платіжного сервісу), що рухають замовлення його життєвим циклом. Якщо вебхуки обробляються погано, застосунок може добре рекомендувати подарунки, але замовлення «зависатимуть» зі статусом «очікування оплати» або в якомусь невизначеному стані.
Якщо ви інтегруєтеся не напряму зі Stripe/платіжним сервісом, а через Instant Checkout, роль «вебхуків платіжного сервісу» беруть на себе order events з Agentic Checkout: ваш бекенд надсилає в OpenAI події на кшталт order.created і order.updated на виділений webhook‑URL. Це той самий клас сутностей: асинхронні події, від яких залежить фінальний статус замовлення. Просто адресатом є не ваш фронтенд, а ChatGPT.
Відповідно, ті самі метрики — success rate, latency, error-rate — ви рахуватимете вже не для Stripe‑webhookʼів, а для своїх order‑event вебхуків у бік OpenAI. У SLO можна прямо написати: «щонайменше 99 % order.* подій доставляються та успішно обробляються за 7 днів, p95 затримки обробки < 500 мс» — і це буде коректним SLI/SLO саме для Instant Checkout.
Для вебхуків зазвичай дивляться:
- webhook success rate — частка вебхуків, для яких бізнес‑логіка завершилася успішно (включно з повторними спробами);
- webhook latency — час від входу вебхука до завершення обробки;
- webhook error rate — частка випадків, коли обробка вебхука закінчилася помилкою (валідація не пройшла, БД недоступна, тайм-аут тощо).
Наприклад, можна задати SLO: «щонайменше 99 % вебхуків обробляються успішно за 7 днів» і «p95 затримки обробки вебхука < 500 мс».
3. Як технічно вимірювати метрики в GiftGenius
Переходимо від теорії до коду. Нам потрібно зрозуміти, куди вбудовувати вимірювання, щоб потім агрегувати дані в p95, error‑rate та інші метрики.
Щоб не тягнути в лекцію конкретний Prometheus чи Datadog, вважатимемо, що в нас є проста функція logMetric або логування JSON‑подій. А вже поверх цього будь‑яка система спостережуваності збере потрібні графіки.
Вимірюємо затримку MCP‑інструмента
Припустімо, у нас є MCP‑сервер GiftGenius на TypeScript та інструмент recommend_gifts. Огортаємо бізнес‑логіку таймером:
// mcp/tools/recommendGifts.ts
import { logMetric } from "../observability/metrics"; // умовний помічник
export async function recommendGiftsTool(input: RecommendInput) {
const startedAt = performance.now(); // час початку
try {
const result = await recommendGifts(input); // бізнес-логіка
const duration = performance.now() - startedAt;
logMetric("tool_latency_ms", duration, {
tool: "recommend_gifts",
status: "success",
});
return result;
} catch (error) {
const duration = performance.now() - startedAt;
logMetric("tool_latency_ms", duration, {
tool: "recommend_gifts",
status: "error",
error_type: "exception",
});
throw error;
}
}
Тут logMetric може просто писати JSON‑лог у stdout:
// observability/metrics.ts
export function logMetric(
name: string,
value: number,
labels: Record<string, string | number>
) {
// У реальності тут буде клієнт Prometheus/DataDog
console.log(
JSON.stringify({
type: "metric",
name,
value,
labels,
timestamp: new Date().toISOString(),
})
);
}
З таким підходом ви отримуєте потік подій tool_latency_ms, де вже за мітками tool і status можна рахувати p95 лише для успішних викликів або, навпаки, дивитися, як довго тривають запити, що закінчуються помилкою.
Підраховуємо error‑rate
Схожим чином можна логувати окрему метрику помилок:
// всередині того ж обробника інструмента
logMetric("tool_error_total", 1, {
tool: "recommend_gifts",
error_type: "external_api_timeout",
});
А для успішних запитів:
logMetric("tool_success_total", 1, {
tool: "recommend_gifts",
});
Далі в системі метрик ви будуєте error_rate = tool_error_total / (tool_error_total + tool_success_total) за період. Це вже агрегується на стороні системи метрик; у застосунку важливо лише акуратно викидати події.
Якщо хочеться зовсім мінімалізму, можна навіть обійтися логами без окремої error_total. Тоді error‑rate рахується за полем status.
Метрики checkout/ACP
Для checkout‑ендпойнта на Next.js логіка та сама: огортаємо обробник таймером і рахуємо статуси.
// app/api/checkout/route.ts
import { NextRequest, NextResponse } from "next/server";
import { logMetric } from "@/observability/metrics";
export async function POST(req: NextRequest) {
const startedAt = performance.now();
try {
const body = await req.json();
const result = await createCheckoutSession(body); // виклик ACP/Stripe
const duration = performance.now() - startedAt;
logMetric("checkout_latency_ms", duration, { status: "success" });
logMetric("checkout_total", 1, { status: "success" });
return NextResponse.json(result, { status: 200 });
} catch (error) {
const duration = performance.now() - startedAt;
logMetric("checkout_latency_ms", duration, { status: "error" });
logMetric("checkout_total", 1, { status: "error" });
return NextResponse.json(
{ error: "Checkout failed" },
{ status: 500 }
);
}
}
Тепер уже можна дивитися p95 для checkout_latency_ms і error‑rate для checkout_total. На основі цих SLI легко задати SLO на кшталт «p95 < 3 секунди, error‑rate < 2 %».
Метрики вебхуків
І, звісно, вебхуки. Ми вже обговорили, які метрики нам важливі (див. розділ 2). Тепер подивімося, як саме їх знімати в коді. Тут особливо важливі не лише час, а й сам факт успішної обробки. Інакше користувач заплатив, а замовлення так і не перейшло в «paid».
// app/api/webhooks/payment/route.ts
import { NextRequest, NextResponse } from "next/server";
import { logMetric } from "@/observability/metrics";
export async function POST(req: NextRequest) {
const startedAt = performance.now();
try {
const payload = await req.text(); // сирі дані
const sig = req.headers.get("stripe-signature") || "";
const event = verifyStripeSignature(payload, sig); // валідація
await handlePaymentEvent(event); // оновлюємо замовлення
const duration = performance.now() - startedAt;
logMetric("webhook_latency_ms", duration, {
type: event.type,
status: "success",
});
logMetric("webhook_total", 1, {
type: event.type,
status: "success",
});
return new NextResponse("ok", { status: 200 });
} catch (error) {
const duration = performance.now() - startedAt;
logMetric("webhook_latency_ms", duration, {
type: "unknown",
status: "error",
});
logMetric("webhook_total", 1, {
type: "unknown",
status: "error",
});
return new NextResponse("error", { status: 500 });
}
}
На базі цих подій можна задати SLI:
- webhook success rate = success / (success + error);
- p95 webhook_latency_ms для payment_succeeded.
І потім прописати для них SLO, наприклад: «99 % вебхуків успішно обробляються за 7 днів, p95 < 500 мс».
4. Перцентильна статистика: трохи глибше
Ми вже кілька разів спиралися на p50/p95/p99, тож тепер давайте трохи формальніше подивімося, що саме під ними мається на увазі. У реальному житті перцентилі обчислюватиме система метрик, але корисно розуміти, що там узагалі відбувається.
Перцентиль pX — це значення, нижче якого лежить X відсотків вимірювань. Якщо ви відсортуєте масив затримок за зростанням, p95 буде десь у «хвості», ближче до максимальних значень. У коді (наприклад, на Node, якщо раптом вам захотілося локально порахувати p95 для набору значень) це може виглядати приблизно так:
// проста функція обчислення перцентиля
export function percentile(values: number[], p: number): number {
if (values.length === 0) return 0;
const sorted = [...values].sort((a, b) => a - b);
const index = Math.ceil((p / 100) * sorted.length) - 1;
return sorted[Math.max(0, Math.min(index, sorted.length - 1))];
}
Таку функцію можна використовувати в тестах або в простому скрипті, який пограється з даними й покаже, наскільки p95 відрізняється від середнього. У продакшн‑середовищі ж цей функціонал візьмуть на себе Prometheus, Datadog, New Relic та інші зрілі інструменти.
5. SLI, SLO і SLA: людською мовою
Три літери, які здаються страшними, але насправді все дуже просто.
SLI — Service Level Indicator
SLI — це конкретний вимірюваний показник, формула. Наприклад:
- p95(latency) інструмента recommend_gifts за останні 24 години;
- error‑rate MCP‑інструментів за останні 7 днів;
- конверсія віджет → успішна оплата за тиждень.
SLI — це не мета й не обіцянка. Це просто «термометр».
SLO — Service Level Objective
SLO — це ціль для SLI, по суті умова «здоровʼя» сервісу. Наприклад:
- «p95(latency recommend_gifts) < 2 секунди за 7‑денне вікно»;
- «error‑rate усіх MCP‑tools < 1 % за 30‑денне вікно»;
- «доступність checkout API ≥ 99,5 % за квартал»;
- «конверсія від віджета до успішної оплати ≥ 15 % за місяць».
Хороша практика — спочатку виміряти поточний рівень і поставити SLO трохи суворіше, ніж те, що ви вже вмієте витримувати. Інакше це перетвориться або на «місію нездійсненну», або на «і так нормально».
SLA — Service Level Agreement
SLA — це вже не внутрішня ціль, а зовнішній договір із користувачами або партнерами. У SLA прописують зобовʼязання (наприклад, «99,9 % uptime») і наслідки порушення (компенсація, штрафи, продовження підписки тощо). SLO часто суворіше за SLA, щоб був простір для «бюджету помилок».
У межах навчального GiftGenius вам SLA, найімовірніше, не потрібен. Але важливо розуміти цю драбинку:
SLI → SLO → SLA
метрика → ціль → зовнішня обіцянка
Error budget (бюджет помилок)
Error budget — це, спрощено, допустимий обсяг «неідеальності». Якщо у вас SLO «доступність 99,9 % за 30 днів», то 0,1 % — це бюджет помилок, який можна «проїсти» на:
- планові релізи з даунтаймом;
- експерименти;
- непередбачені інциденти.
Коли бюджет «згорів» (наприклад, за підсумком місяця доступність вийшла 99,5 %), час пригальмувати з новими функціями й налагоджувати стабільність.
6. Особливі метрики для GiftGenius: комерційна частина + webhooks
Зберімо все разом і подивімося на GiftGenius як на цілісний продукт.
Воронка і конверсія
Уявімо простий шлях користувача:
flowchart TD A[Відкрив App із віджетом GiftGenius] --> B[Отримав рекомендації] B --> C[Обрав подарунок] C --> D[Натиснув 'перейти до оплати'] D --> E[Успішний платіж]
Для кожного кроку ми можемо логувати подію:
logMetric("funnel_step_total", 1, {
step: "widget_view",
});
logMetric("funnel_step_total", 1, {
step: "gift_selected",
});
logMetric("funnel_step_total", 1, {
step: "checkout_started",
});
logMetric("funnel_step_total", 1, {
step: "checkout_paid",
});
Далі, знаючи кількість подій на кроці widget_view і checkout_paid, ми можемо порахувати конверсію: paid / view * 100 %. І поставити SLO: «конверсія ≥ 10 %». Якщо раптом конверсія впала до 3 %, а технічні SLI (latency/error‑rate) виглядають нормально, можливо, справа в UX, інструкції для моделі або якості продуктового фіда, а не в інфраструктурі.
Метрики вебхуків як частина конверсії
Ми вже говорили про технічні метрики вебхуків та їх реалізацію. Тепер важливо повʼязати їх із конверсією: для комерційних сценаріїв вебхуки критичні. Користувач бачить «оплату проведено», але фінальний статус замовлення залежить від того, як швидко ваш бекенд прийме й обробить payment_succeeded.
SLI для вебхуків:
- webhook success rate;
- webhook latency p95;
- базова доступність webhook_endpoint_availability.
Якщо success rate для вебхуків падає, ви буквально втрачаєте замовлення.
7. Прості алерти поверх SLO
Повноцінна система алертингу — тема ближча до операційних модулів, але вже зараз можна намітити основу.
Ідея в тому, щоб надсилати алерти не на кожну окрему помилку, а саме на порушення SLO. Це те, що часто називають symptom‑based alerts — алерти, завʼязані на симптоми деградації сервісу: вас цікавить не «сталася помилка», а «сервіс почав системно працювати гірше, ніж обіцяно».
Типові приклади:
- error‑rate MCP‑tools за останні 5 хвилин > 2 % при SLO < 1 %;
- p95(latency recommend_gifts) за 10 хвилин > 3 секунди при SLO 2 секунди;
- за останні 15 хвилин немає жодного успішного checkout_paid — підозра на падіння інтеграції;
- webhook success rate за 10 хвилин < 95 %.
Текст алерта має бути людським, а не «Alert: metric 123 > 456». Приклад «сповіщення в Slack» для GiftGenius:
[ALERT][GiftGenius] High error rate on MCP tools:
error_rate = 3.2% (>1% SLO) for last 5 minutes.
Impact: part of users cannot receive gift recommendations.
Actions: check MCP logs and external gift API health.
Такі повідомлення привʼязані до SLO і допомагають черговому розробнику швидко зрозуміти масштаб проблеми.
8. Міні‑практика: вигадуємо SLO для GiftGenius
Спробуймо сформулювати набір SLO для нашого застосунку. Це можна зробити навіть на папері — без розгорнутої системи метрик.
Приклад «старту»:
- Для основного MCP‑інструмента recommend_gifts:
- SLI: p95(latency) успішних викликів за останні 7 днів.
- SLO: p95(latency) < 2 секунди.
- Для помилок MCP‑інструментів:
- SLI: error‑rate = errors / (errors + successes) за 30 днів.
- SLO: error‑rate < 1 %.
- Для checkout API:
- SLI: availability = частка HTTP 2xx від усіх запитів.
- SLO: availability ≥ 99,5 % за місяць.
- Для вебхуків оплати:
- SLI: webhook success rate.
- SLO: щонайменше 99 % успішної обробки webhooks за 7 днів; p95 затримки обробки < 500 мс.
- Для бізнес‑результату:
- SLI: конверсія віджет → оплачене замовлення за місяць.
- SLO: конверсія ≥ 10–15 % (залежно від ніші).
Навіть такий невеликий набір уже дає «каркас» для ухвалення рішень: якщо ви розгортаєте нову версію моделі, змінюєте промпт або алгоритм рекомендацій і бачите, що p95 і error‑rate залишилися в межах SLO, а конверсія зросла — імовірно, експеримент вдався.
Якщо замість власного checkout‑ендпойнта ви використовуєте Instant Checkout, то «checkout API» — це фактично виклики Agentic Checkout (/checkout_sessions create/update/complete), а «вебхуки оплати» — ваші order events (order.created, order.updated) у бік OpenAI. Метрики й формулювання SLO залишаються тими самими — змінюється лише конкретний протокол.
9. Типові помилки під час роботи з метриками та SLO
Помилка №1: вимірювати лише середній час відповіді.
Середнє значення зручне, але небезпечне. Воно легко маскує «хвіст» із повільних запитів. У ChatGPT App це критично: навіть якщо більшість користувачів усе отримують швидко, регулярні 5–10‑секундні лаги для невеликого відсотка людей відчуватимуться як «застосунок увесь час гальмує». Тому для затримки завжди використовуйте принаймні p50 і p95, а не тільки mean.
Помилка №2: плутати SLI, SLO і SLA в голові та в документації.
Іноді розробники пишуть: «наш SLA — p95 < 2 секунд», хоча це взагалі ніде не зафіксовано для зовнішніх користувачів і не супроводжується жодними зобовʼязаннями. Насправді це SLO. SLA — це вже договір із клієнтами, де є наслідки порушень. Якщо все змішати, буде незрозуміло, що ви насправді й кому обіцяєте.
Помилка №3: відсутність звʼязку між алертами та SLO.
Класична пастка — налаштувати алерти «на кожну помилку», на кожен тайм-аут, на будь‑яке відхилення метрики на 0,01. У результаті черговий інженер живе в пеклі сповіщень і перестає на них зважати. Набагато корисніше будувати алерти на порушенні SLO: якщо error‑rate суттєво виріс понад цільовий рівень або p95 «вивалився» за межі — тоді вже є сенс будити людей.
Помилка №4: ігнорування метрик вебхуків і асинхронних частин.
Окремо винесена, але вже знайома нам проблема — ігнорування метрик вебхуків і асинхронних частин. Багато команд стежать тільки за HTTP‑відповідями основних API, але не дивляться на вебхуки й фонову обробку. У комерційних сценаріях саме там ховаються найнеприємніші баги: оплата пройшла, вебхук не обробився, замовлення «застрягло». Без метрик success rate і latency для вебхуків можна довго думати, що «все працює», аж поки цифри в бухгалтерії та в базі не почнуть помітно розходитися.
Помилка №5: нереалістичні SLO або їх повна відсутність.
Іноді SLO формулюють у стилі «100 % uptime» або «жодних помилок узагалі». На практиці це нездійсненно й демотивує команду. Друга крайність — не формулювати SLO взагалі й жити в режимі «ніби нормально». Золота середина — почати з вимірювання поточних SLI, поставити трохи суворішу, але досяжну ціль і поступово посилювати її в міру дорослішання системи.
Помилка №6: метрики лише «для галочки», без привʼязки до продукту.
Буває і так: у системі метрик повно графіків, p95, p99, десятки дашбордів, але ніхто не може відповісти на просте питання: «Якщо ця лінія підскочить — що постраждає? Користувач? Гроші? Репутація?» Особливо в LLM‑продуктах важливо повʼязати технічні показники (latency, error‑rate) із бізнес‑метриками (конверсія, успішні оплати). Інакше спостережуваність перетворюється на красивий, але марний телевізор.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ