JavaRush /Курсы /ChatGPT Apps /Метрики и SLO: p95, error‑rate, webhooks, availability

Метрики и SLO: p95, error‑rate, webhooks, availability

ChatGPT Apps
17 уровень , 1 лекция
Открыта

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, какие метрики важны для commerce‑части с вебхуками и как из всего этого собрать простые, но полезные SLO. Разбирать всё это будем на примере нашего учебного приложения GiftGenius — commerce‑сценария с MCP‑инструментом рекомендаций, checkout’ом и вебхуками оплаты.

2. Базовые метрики для ChatGPT App: что именно мерять

Латентность: p50/p95/p99 вместо «среднего по больнице»

Латентность — это время от начала операции до её логического конца. В нашем стеке таких операций несколько:

  • вызов 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‑шные гайды, перцентили «не дают выбросам спрятаться в среднем значении» и делают видимой жизнь тех самых несчастных 510% пользователей, которые всё время попадают в хвост распределения.

Проще всего думать так: p50 говорит, как живёт «средний» пользователь, а p95/p99 — насколько больно приходится терпеливым людям, у которых всё время «что‑то тормозит».

Error‑rate: доля неуспешных запросов

Error‑rate — это отношение количества неуспешных запросов к общему числу запросов за период. Основным источником данных обычно являются либо структурированные логи, либо метрики с label’ами status="success" | "error".

В нашем стеке есть несколько естественных error‑rate:

  • на уровне MCP‑tools: доля вызовов recommend_gifts, закончившихся ошибкой (исключение, HTTP 5xx внешнего API, timeout);
  • на уровне 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%, проблема, скорее всего, не у них.

Commerce‑метрики: конверсия и воронка

GiftGenius — commerce‑сценарий. Тут важна не только техничка (ответы быстрые, ошибок мало), но и бизнес‑результат — насколько часто рекомендации превращаются в оплаченные заказы.

Здесь в игру вступает конверсионная воронка:

  • пользователи, у которых показан виджет (view);
  • пользователи, которые выбрали подарок (selection);
  • пользователи, которые нажали на «оформить заказ» (checkout started);
  • пользователи, у которых статус заказа стал «оплачен» (paid).

Из этой воронки можно определить конверсию «от виджета до оплаты», и это тоже SLI — измеримый показатель качества сервиса. Например, «конверсия успешных оплат / пользователи, увидевшие виджет за сутки, = 7%». Если внезапно latency вырос, или checkout иногда падает по таймауту, воронка начнёт сужаться в неожиданных местах.

Если вы используете Instant Checkout

Все описанные commerce‑метрики так же применимы и к 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, а по этим стандартным endpoint’ам. 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"; // условный helper

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, где уже по label’ам 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: commerce + 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, инструкции для модели или в качестве продуктового фида, но не в инфраструктуре.

Метрики вебхуков как часть конверсии

Мы уже говорили про технические метрики вебхуков и их реализацию. Теперь важно связать их с конверсией: для commerce‑сценариев вебхуки критичны. Пользователь видит «оплата прошла», но финальный статус заказа зависит от того, как быстро ваш backend примет и обработает 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 и помогают on‑call разработчику быстро понять масштаб бедствия.

8. Мини‑практика: придумываем SLO для GiftGenius

Попробуем сформулировать набор SLO для нашего приложения. Это можно сделать даже на бумаге, без развёрнутой метрик‑системы.

Пример «старта»:

  1. Для основного MCP‑инструмента recommend_gifts:
    • SLI: p95(latency) успешных вызовов за последние 7 дней.
    • SLO: p95(latency) < 2 секунд.
  2. Для ошибок MCP‑инструментов:
    • SLI: error‑rate = errors / (errors + successes) за 30 дней.
    • SLO: error‑rate < 1%.
  3. Для checkout API:
    • SLI: availability = доля HTTP 2xx от всех запросов.
    • SLO: availability99.5% за месяц.
  4. Для вебхуков оплаты:
    • SLI: webhook success rate.
    • SLO: не менее 99% успешной обработки webhooks за 7 дней; p95 задержки обработки < 500 мс.
  5. Для бизнес‑результата:
    • SLI: конверсия виджет → оплаченный заказ за месяц.
    • SLO: конверсия ≥ 1015% (в зависимости от ниши).

Даже такой небольшой набор уже даёт «каркас» для принятия решений: если вы выкатываете новую версию модели, меняете промпт или алгоритм рекомендаций и видите, что 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. В результате on‑call инженер живёт в аду нотификаций и перестаёт на них смотреть. Гораздо полезнее строить алерты на нарушении SLO: если error‑rate вырос существенно сверх целевого уровня или p95 «вывалился» за границы, вот тогда уже есть смысл будить людей.

Ошибка №4: игнорирование метрик вебхуков и асинхронных частей.
Отдельно вынесенная, но уже знакомая нам проблема — игнорирование метрик вебхуков и асинхронных частей. Многие команды следят только за HTTP‑ответами основных API, но не смотрят на вебхуки и фоновую обработку. В commerce‑сценариях именно там прячутся самые неприятные баги: оплата прошла, вебхук не обработался, заказ «застрял». Без метрик success rate и latency по вебхукам можно долго думать, что «всё работает», пока цифры в бухгалтерии и в базе не начнут сильно расходиться.

Ошибка №5: нереалистичные SLO или их полное отсутствие.
Иногда SLO формулируют в стиле «100% uptime» или «никаких ошибок вообще». На практике это невыполнимо и демотивирует команду. Вторая крайность — не формулировать SLO вообще и жить в режиме «вроде норм». Золотая середина — начать с измерения текущих SLI, поставить чуть более строгую, но достижимую цель и постепенно ужесточать её по мере взросления системы.

Ошибка №6: метрики только ради галочки, без привязки к продукту.
Бывает и так: в системе метрик полно графиков, p95, p99, десятки дашбордов, но никто не может ответить на простой вопрос: «Если эта линия подскочит — что пострадает? Пользователь? Деньги? Репутация?» Особенно в LLM‑продуктах важно связать технические показатели (latency, error‑rate) с бизнес‑метриками (конверсия, успешные оплаты). Иначе наблюдаемость превращается в красивый, но бесполезный телевизор.

1
Задача
ChatGPT Apps, 17 уровень, 1 лекция
Недоступна
Мини-калькулятор перцентилей (mean vs p95)
Мини-калькулятор перцентилей (mean vs p95)
1
Задача
ChatGPT Apps, 17 уровень, 1 лекция
Недоступна
Логирование метрик для «инструмента» (latency + error-rate)
Логирование метрик для «инструмента» (latency + error-rate)
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ