JavaRush /Курси /ChatGPT Apps /UX‑аналітика workflow: вимірюємо ефективність кроків

UX‑аналітика workflow: вимірюємо ефективність кроків

ChatGPT Apps
Рівень 11 , Лекція 4
Відкрита

1. Навіщо взагалі вимірювати workflow

Коротко: без аналітики ви живете в режимі «мені здається», а не в режимі «я знаю».

У попередніх лекціях цього модуля ми вже розбивали сценарій на кроки, розподіляли ролі GPT, віджета та MCP, обговорювали tool‑gating і збереження стану між кроками. Тепер погляньмо на цю саму конструкцію очима аналітики. Чи справді все працює так, як ми задумували? І де користувачі насправді застрягають?

У звичайному вебі всі давно звикли до лійок: скільки людей прийшло на лендінг, скільки поклало товар у кошик, а скільки дійшло до оплати. У ChatGPT App усе те саме, тільки замість сторінок у вас кроки workflow, а замість «кліку по кнопці „Купити“» — комбінація репліки користувача, виклику інструмента та взаємодії з віджетом.

Коли ви будуєте складний сценарій без метрик, ви не бачите:

  • на якому кроці люди найчастіше «відвалюються»;
  • де вони зависають і читають по хвилині (або просто пішли по чай і не повернулися);
  • який крок узагалі не дає користі й лише дратує;
  • як зміни в промптах або tool‑gating впливають на поведінку.

Мета покрокової аналітики проста: навчитися підвищувати частку завершених сценаріїв, скорочувати час до результату й зменшувати кількість помилок та звернень у підтримку.

Від цього моменту workflow — не лише архітектурний обʼєкт і не просто UX‑квест. Це ще й вимірювана річ — у цифрах.

2. Лійка сценарію в ChatGPT App

У класичному вебі лійка виглядає лінійно: Landing → Product → Cart → Checkout. У ChatGPT App картина трохи «веселіша»: користувач може «перестрибнути» крок словами, модель інколи може пропустити крок, а віджет і текст діалогу можуть розсинхронізуватися.

Втім базова ідея та сама: у нас є послідовність кроків, і на кожному з них частина користувачів іде далі, а частина — ні.

Візьмімо наш GiftGenius:

  1. collect_recipient — ChatGPT і віджет збирають базові дані про отримувача (стать, вік, ким доводиться, інтереси).
  2. collect_budget — уточнюємо бюджет і валюту.
  3. suggest_ideas — MCP / агент підбирає ідеї та повертає картки подарунків у віджет.
  4. review_selection — користувач лайкає / приховує ідеї та обирає 1–2 фаворити.
  5. checkout — створюється commerce intent, оформлюється замовлення.

У вигляді лійки це можна намалювати так:

flowchart TD
    A[Початок workflow] --> B["1\. Отримувач"]
    B --> C["2\. Бюджет"]
    C --> D["3\. Ідеї подарунків"]
    D --> E["4\. Вибір подарунка"]
    E --> F["5\. Checkout"]

Але важливо памʼятати: користувач може написати в чат «Давайте одразу до оплати» або «Покажіть спочатку дорогі варіанти», і модель вирішить перестрибнути частину кроків. Тому покрокова аналітика в ChatGPT App — це не лише про UI‑екрани, а й про поведінку моделі. Важливо розуміти, які кроки реально проходять, у якому порядку, а також хто ініціював перехід — користувач, віджет чи GPT.

3. Базові метрики за кроками

Почнімо з класики продуктової аналітики й трохи адаптуймо її під ChatGPT App.

Для кожного workflow нам потрібні щонайменше чотири базові показники.

Для зручності зведімо їх у таблицю:

Метрика Що означає Типове запитання
Start rate Скільки користувачів узагалі розпочали сценарій Наш застосунок узагалі комусь показується?
Completion rate Скільки користувачів дійшли до фіналу Наскільки сценарій доводить до результату?
Conversion per step Частка користувачів, що перейшли з кроку N на крок N+1 На якому кроці в нас «дірка»?
Drop-off per step Частка користувачів, які пішли на кроці N На якому кроці люди найчастіше здаються?

До них майже завжди додають метрики зусиль:

  • середній час на кроці (де люди «зависають»);
  • кількість взаємодій на кроці (скільки повідомлень / кліків знадобилося);
  • частку кроків, що завершилися помилкою або вимагали повторної спроби.

У контексті LLM‑сценаріїв зʼявляються й специфічніші речі — наприклад, точність вибору інструмента моделлю або частка «галюцинаторних» відповідей на конкретному кроці. Але це вже просунута тема: до неї ми дійдемо у фінальних модулях.

Для commerce‑сценаріїв поверх кроків накладаються бізнес‑метрики:

  • конверсія в оплату від старту workflow;
  • конверсія в оплату від конкретного кроку (наприклад, від «підбору ідей»);
  • середній чек;
  • частка скасувань / повернень.

Важливо: усі ці цифри не існують самі по собі — між ними є причинно‑наслідкові звʼязки. Крок із великим drop‑off не завжди поганий. Можливо, він відсіює невідповідних користувачів, і далі йдуть лише ті, кому сценарій справді корисний. Тому аналітика — це не просто «рахувати відсотки», а вміти розповідати історію на основі даних.

Щоб усі ці відсотки й лійки було з чого рахувати, нам потрібні сирі події: хто, коли й який крок пройшов (або не пройшов). У наступному розділі домовимося про формат таких подій.

4. Як виглядають події аналітики

Перш ніж писати код, потрібно домовитися про формат «події» (event), яку ми надсилатимемо з віджета та бекенду.

Зазвичай подія аналітики містить:

  • хто: ідентифікатор користувача або принаймні сесії;
  • який workflow і яка його версія;
  • який крок;
  • що сталося (тип події);
  • чи було успішно, і скільки це зайняло часу;
  • трохи метаданих (локаль, пристрій тощо).

Спрощену схему подій для workflow можна описати так:

export type WorkflowEventType =
  | "workflow_started"
  | "workflow_finished"
  | "step_started"
  | "step_completed"
  | "step_failed";

export interface WorkflowAnalyticsEvent {
  eventId: string;              // uuid
  timestamp: string;            // ISO-рядок
  userId?: string;              // якщо можна деанонімізувати
  conversationId?: string;      // id діалогу ChatGPT (якщо доступний)
  workflowId: string;           // наш внутрішній ідентифікатор
  workflowType: "gift_selection";
  workflowVersion: string;      // наприклад, "1.2.0" або "1.2.0-A"
  stepName?: string;            // collect_budget, suggest_ideas тощо
  eventType: WorkflowEventType;
  toolName?: string;            // якщо пов’язано з tool-call
  success?: boolean;
  errorCode?: string | null;
  durationMs?: number;
  metadata?: Record<string, unknown>;
}

Кілька нюансів:

По‑перше, workflowVersion дуже важлива, якщо ви збираєтеся робити A/B‑тести: без неї ви ніколи не дізнаєтеся, який саме варіант сценарію дає кращі цифри.

По‑друге, conversationId або інший correlation ID дає змогу повʼязати події: кроки у віджеті, tool‑виклики в MCP і текстовий діалог. У наступних модулях ми ще говоритимемо про трасування та спостережність, але звичка від самого початку думати про «наскрізні» ідентифікатори дуже корисна.

По‑третє, не потрібно тягнути в подію все підряд: повні тексти повідомлень, електронну пошту, адреси та іншу PII краще не збирати або жорстко анонімізувати. Про це ми ще поговоримо ближче до кінця.

5. Інструментація у віджеті (Next.js + Apps SDK)

Тепер найцікавіше: як зробити так, щоб наш віджет GiftGenius сам тихо звітував про кроки, коли користувач проходить сценарій.

Припустімо, у попередніх лекціях ви вже зробили щось на кшталт:

// components/GiftWizard.tsx
type StepId = "recipient" | "budget" | "ideas" | "review" | "checkout";

export function GiftWizard() {
  const [currentStep, setCurrentStep] = useState<StepId>("recipient");
  const [workflowId] = useState(() => crypto.randomUUID());

  // ... тут рендеринг різних кроків
}

Додаймо невеликий «шар аналітики» у вигляді хука.

Хук useWorkflowAnalytics

Зробимо обгортку, яка знає про workflowId, workflowVersion і вміє надсилати подію до нашого Next.js API‑роуту /api/workflow-analytics.

// lib/useWorkflowAnalytics.ts
import { useCallback } from "react";
import type { WorkflowAnalyticsEvent, WorkflowEventType } from "./types";

const WORKFLOW_VERSION = "1.0.0";

export function useWorkflowAnalytics(
  workflowId: string,
  workflowType: WorkflowAnalyticsEvent["workflowType"] = "gift_selection"
) {
  const sendEvent = useCallback(
    async (payload: Omit<WorkflowAnalyticsEvent, "eventId" | "timestamp" | "workflowType" | "workflowVersion" | "workflowId">) => {
      const event: WorkflowAnalyticsEvent = {
        eventId: crypto.randomUUID(),
        timestamp: new Date().toISOString(),
        workflowId,
        workflowType,
        workflowVersion: WORKFLOW_VERSION,
        ...payload,
      };

      // просте надсилання до API; у проді можна додати буфер/дебаунс
      await fetch("/api/workflow-analytics", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(event),
      });
    },
    [workflowId, workflowType]
  );

  const trackStepEvent = useCallback(
    async (stepName: string, eventType: WorkflowEventType, extra?: Partial<WorkflowAnalyticsEvent>) => {
      await sendEvent({ stepName, eventType, ...extra });
    },
    [sendEvent]
  );

  return { sendEvent, trackStepEvent };
}

Тут важливо, що хук ніяк не залежить від конкретного UI‑кроку. Він просто знає, що таке stepName і eventType. А конкретні компоненти казатимуть йому: «ось я почав крок», «ось я його завершив» — і так далі.

Надсилаємо workflow_started і workflow_finished

У компоненті GiftWizard можна в момент монтування та демонтування реєструвати початок і завершення сценарію:

// components/GiftWizard.tsx
export function GiftWizard() {
  const [currentStep, setCurrentStep] = useState<StepId>("recipient");
  const [workflowId] = useState(() => crypto.randomUUID());
  const { sendEvent } = useWorkflowAnalytics(workflowId);

  useEffect(() => {
    void sendEvent({ eventType: "workflow_started" });
    return () => {
      void sendEvent({ eventType: "workflow_finished" });
    };
  }, [sendEvent]);

  // ...
}

Звісно, завершення «під час демонтування» — це грубе наближення: користувач може просто згорнути чат або перейти в інший діалог. Але навіть така базова метрика вже дає відчуття, скільки сценаріїв узагалі «дійшли кудись».

Відстежуємо події за кроками

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

interface StepProps {
  stepId: StepId;
  onNext: () => void;
  trackStepEvent: (stepName: string, eventType: WorkflowEventType, extra?: Partial<WorkflowAnalyticsEvent>) => Promise<void>;
}

function StepRecipient({ stepId, onNext, trackStepEvent }: StepProps) {
  useEffect(() => {
    void trackStepEvent(stepId, "step_started");
  }, [stepId, trackStepEvent]);

  const handleSubmit = async () => {
    // ... валідація, збереження в widgetState
    await trackStepEvent(stepId, "step_completed");
    onNext();
  };

  return (
    <div>
      {/* поля форми отримувача */}
      <button onClick={handleSubmit}>Далі</button>
    </div>
  );
}

У GiftWizard передаємо trackStepEvent:

export function GiftWizard() {
  // ...
  const { trackStepEvent } = useWorkflowAnalytics(workflowId);

  const goToNext = () => {
    setCurrentStep((prev) => NEXT_STEP[prev]);
  };

  if (currentStep === "recipient") {
    return (
      <StepRecipient
        stepId="recipient"
        onNext={goToNext}
        trackStepEvent={trackStepEvent}
      />
    );
  }

  // інші кроки...
}

Аналогічно, у кроках, де можливі помилки (наприклад, запит до зовнішнього API в suggest_ideas), у разі невдачі можна надіслати "step_failed" з errorCode. А після успішного завантаження варіантів — надіслати "step_completed".

Так ми отримуємо:

  • зрозумілий список подій: коли кроки стартують і завершуються;
  • можливість порахувати час кроку: різниця між "step_started" і "step_completed";
  • видимість того, які кроки найчастіше завершуються як "step_failed".

6. Інструментація на бекенді / MCP

Клієнтська аналітика — це добре, але віджет живе в доволі крихкому світі: браузер користувача, iframe, обмеження пісочниці та все, що з цим повʼязано. Тому паралельно варто записувати події й на серверному боці — у MCP‑інструментах або в бекенд‑API вашого застосунку.

Наприклад, у вас є інструмент suggest_gifts, який справді робить важку роботу: звертається до product feed, застосовує фільтри й повертає подарунки. Усередині такого інструмента ви можете логувати і бізнес‑логіку, і аналітичні події.

Умовний обробник MCP‑tool на TypeScript може виглядати так:

// mcp/tools/suggestGifts.ts
import type { SuggestGiftsArgs } from "../schemas";
import { logWorkflowEvent } from "../analytics/log";

export async function handleSuggestGifts(args: SuggestGiftsArgs, context: { workflowId: string; stepName: string }) {
  const startedAt = Date.now();

  try {
    // ... основна логіка підбору ідей

    await logWorkflowEvent({
      workflowId: context.workflowId,
      workflowType: "gift_selection",
      workflowVersion: "1.0.0",
      stepName: context.stepName,
      eventType: "step_completed",
      toolName: "suggest_gifts",
      success: true,
      durationMs: Date.now() - startedAt,
    });

    return {
      content: [{ type: "text", text: "Знайшов 5 ідей подарунків." }],
      _meta: {
        // сирі дані для віджета
      },
    };
  } catch (e) {
    await logWorkflowEvent({
      workflowId: context.workflowId,
      workflowType: "gift_selection",
      workflowVersion: "1.0.0",
      stepName: context.stepName,
      eventType: "step_failed",
      toolName: "suggest_gifts",
      success: false,
      errorCode: "SUGGEST_FAILED",
      durationMs: Date.now() - startedAt,
    });
    throw e;
  }
}

А logWorkflowEvent може писати в ту ж таблицю/сховище, куди складаються події з фронтенду, просто з позначкою "source": "backend".

Чому серверна аналітика надійніша

По‑перше, tool‑виклик або стався, або ні. Це чіткий факт, а не евристика на кшталт «користувач ніби натиснув кнопку».

По‑друге, на сервері простіше агрегувати дані: ви можете рахувати, скільки разів викликано кожен інструмент, який у нього середній durationMs і яка частка викликів завершується помилкою.

По‑третє, так ви бачите різницю між UX‑проблемою (користувач не доходить до кроку, де викликається інструмент) і технічною проблемою (до кроку доходять, але інструмент часто падає).

7. Як читати дані: шукаємо вузькі місця

Припустімо, ви вже реалізували надсилання подій "workflow_started", "step_started", "step_completed" і "step_failed" з віджета та MCP, і в сховищі накопичилося достатньо даних. Уявімо, що ви вже зібрали певний масив даних по GiftGenius і отримали зведену статистику за кроками. У таблиці нижче наведено умовні цифри для 1000 запущених workflow:

Крок Старт кроку Завершено крок Drop‑off на кроці Середній час (с)
recipient 1000 950 5 % 12
budget 950 700 26 % 35
ideas 700 680 3 % 8
review 680 500 26 % 40
checkout 500 420 16 % 20

На що тут варто звернути увагу:

По‑перше, крок budget — очевидне вузьке місце. Високий drop‑off (26 %) і помітно більший середній час. Можливо, ви питаєте надто багато про валюту або податки. Або формулювання незрозумілі. Або в людей просто немає впевненості щодо бюджету. Це хороший кандидат на спрощення кроку, поділ на два підкроки чи зміну формулювань запитань.

По‑друге, review теж дає сильний «відвал». Можливо, UI карток подарунків перевантажений, або користувачеві незрозуміло, що означає «лайкнути» подарунок. Може бути й так, що модель повертає забагато варіантів, і віджет виглядає як нескінченний список. Тут уже варто дивитися не лише на цифри, а й на скриншоти / записи сесій (якщо ви їх ведете) або хоча б вручну пройти сценарій як користувач.

По‑третє, checkout втрачає 16 % — для commerce‑сценарію це багато грошей. Але тут треба зрозуміти, де саме вони губляться: під час оформлення замовлення, через помилки платіжного провайдера чи тому, що користувач просто передумав. Це вже не суто питання UX, а поєднання UX і бізнес‑обмежень.

Важливо вміти розрізняти проблеми UI та моделі.

  • Якщо користувачі часто повертаються до попереднього кроку й змінюють відповіді — це сигнал про незрозуміле або невдало сформульоване запитання.
  • Якщо крок швидко завершується, але tool‑виклик на ньому часто падає — це проблема бекенду/MCP.
  • Якщо крок триває довго і при цьому немає ні помилок, ні повернень, можливо, користувач просто читає довгий текст, який йому не дуже потрібен.

8. Експерименти та A/B‑тести workflow

Цифри самі по собі нічого не покращують. Щоб від аналітики був зиск, потрібно вміти ставити експерименти: змінювати кроки й порівнювати, чи стало краще.

У контексті ChatGPT App типовий експеримент — це порівняння двох версій кроку або послідовності кроків:

  • довгий wizard із кількома простими екранами проти однієї складної форми;
  • різні формулювання запитань;
  • різний порядок кроків (наприклад, бюджет запитати раніше або пізніше);
  • різні стратегії tool‑gating (на першому кроці менше tools, на другому — більше).

Корисна звичка — фіксувати версію сценарію в workflowVersion і додавати туди ідентифікатор експерименту. Наприклад, "1.3.0-A" і "1.3.0-B".

Найпростіший A/B‑розподіл у віджеті

Зрозуміло, у проді ви захочете стабільне призначення варіанта на рівні користувача або сесії (через бекенд), але для навчального прикладу достатньо випадкового вибору варіанта.

// lib/useWorkflowVariant.ts
import { useMemo } from "react";

export type WorkflowVariant = "A" | "B";

export function useWorkflowVariant(): WorkflowVariant {
  return useMemo(() => {
    return Math.random() < 0.5 ? "A" : "B";
  }, []);
}

У GiftWizard визначаємо варіант і передаємо його в аналітику:

export function GiftWizard() {
  const [workflowId] = useState(() => crypto.randomUUID());
  const variant = useWorkflowVariant();
  const { sendEvent, trackStepEvent } = useWorkflowAnalytics(
    workflowId,
    "gift_selection"
  );

  useEffect(() => {
    void sendEvent({
      eventType: "workflow_started",
      metadata: { variant },
    });
  }, [sendEvent, variant]);

  // далі можна змінювати тексти/структуру кроків залежно від variant
}

На сервері можна замінити жорстке "1.0.0" на щось на кшталт "1.1.0-A" і "1.1.0-B". Або просто логувати metadata.variant і вже в аналітиці групувати за ним.

Головний сенс A/B‑тесту — заздалегідь обрати цільову метрику. Наприклад: «ми хочемо підняти completion rate сценарію з 42 % до 50 %» або «зменшити час на кроці budget на 20 %». Без цільової метрики будь‑яка перебудова workflow буде схожа на ремонт за принципом «переставили шафу — ніби стало гарніше».

9. Приватність і етика даних

Раніше ми вже побіжно згадували, що в metadata і події аналітики краще не тягнути PII напряму. Коли ви обговорюєте метрики, легко захопитися й почати логувати все підряд. Але варто памʼятати: ви працюєте всередині ChatGPT, а користувач цілком може обґрунтовано очікувати, що його особисті повідомлення не відлітатимуть у зовнішню аналітику в сирому вигляді.

Кілька простих правил, яких варто дотримуватися вже зараз — ще до модулів про безпеку і Store:

  • По‑перше, не логуйте повні тексти повідомлень користувача. Замість цього можна зберігати довжину повідомлення, тип відповіді (число, «так/ні», вибір зі списку) або анонімізовані ознаки на кшталт «відповідь порожня / неповна / змінена».
  • По‑друге, не логуйте явно ідентифікувальну інформацію (PII), якщо вона вам не потрібна для бізнес‑логіки: електронну пошту, телефони, адреси, повні імена. Якщо без цього ніяк, зберігайте такі дані в іншому, захищеному контурі та жорстко обмежуйте доступ.
  • По‑третє, обережно ставтеся до контексту діалогу. Якщо ви зберігаєте conversationId, переконайтеся, що в аналітиці ви не намагаєтеся «склеювати» окремі діалоги в суперпрофіль без вагомої причини та правової підстави.
  • По‑четверте, зверніть увагу на політики OpenAI і вимоги Store (ми детально дійдемо до цього в модулях про публікацію та безпеку), де прямо сказано, які дані можна виносити з ChatGPT, а які — ні. На етапі проєктування аналітики корисно відразу закласти анонімізацію й мінімізацію даних, щоб потім не переписувати пів системи.

І, нарешті, памʼятайте: UX‑аналітика — це не тотальне стеження і точно не surveillance у дусі Big Brother. Її мета — покращувати сценарії та зменшувати фрустрацію користувача, а не будувати Big Brother‑панель «хто о 2:37 ночі не дійшов до checkout».

10. Типові помилки в UX‑аналітиці workflow

Помилка №1: «Ми ще не в проді, метрики потім».
Дуже часто розробники починають думати про аналітику тоді, коли застосунок уже використовують реальні люди. У результаті події вводяться «заднім числом», дані виходять уривчастими, а порівняти стару й нову версії сценарію майже неможливо. Мінімальну лійку ("workflow_started", "step_started", "step_completed", "workflow_finished") краще закласти одразу, поки код ще відносно простий.

Помилка №2: Логувати лише успіхи та ігнорувати помилки.
Іноді в логах є тільки "step_completed", а от "step_failed" ніхто не пише, бо «ну воно ж не має падати». У результаті ви бачите, що до якогось кроку доходить мало людей, але не розумієте: вони самі пішли чи їх «викинуло» помилками. Завжди логуйте і успішне завершення кроку, і неуспішне — щонайменше з грубим errorCode.

Помилка №3: Повна відсутність привʼязки до версії workflow.
Ви змінюєте тексти, порядок кроків, вводите tool‑gating, а в подіях увесь час фігурує workflowVersion: "1.0.0". За місяць ви дивитеся на графіки й не можете зрозуміти, що було до змін, а що — після. Фіксація версії сценарію і, за потреби, варіанта A/B — обовʼязковий елемент аналітики.

Помилка №4: Надто детальна аналітика без приводу.
Протилежна крайність — одразу будувати «ідеальну» схему подій на 50 полів, логувати кожен клік по пікселю й кожне редагування символу. По‑перше, це може порушувати приватність. По‑друге, такі дані складно аналізувати — ви потонете в шумі. Краще почати з невеликого набору подій і метрик, які справді відповідають на конкретні продуктові запитання, а потім розвивати систему в міру потреби.

Помилка №5: Непогодженість імен кроків і сценаріїв.
Іноді в коді крок називається budget, в аналітиці — collect_budget, а у звіті — «крок із питанням про гроші». За пару тижнів ніхто не памʼятає, що є що. На етапі проєктування workflow корисно домовитися про стабільні ідентифікатори кроків (stepName) і використовувати їх і в UI, і в логах, і у звітах.

Помилка №6: Наявність метрик, якими ніхто не користується.
Найсумніша історія: ви акуратно зібрали масу даних, налаштували надсилання подій із віджета та MCP, але ніхто потім не відкриває панелі й не ухвалює рішень. Аналітика заради аналітики не потрібна. Завжди ставте собі запитання: «Яке рішення я зможу ухвалити на підставі цієї метрики?». Якщо відповіді немає — метрика вам поки що не потрібна.

1
Опитування
Workflow, рівень 11, лекція 4
Недоступний
Workflow
Workflow і поступове розкриття інструментів
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ