CodeGym /Kursy /ChatGPT Apps /Lekkie testy obciążeniowe i jakość danych feedu

Lekkie testy obciążeniowe i jakość danych feedu

ChatGPT Apps
Poziom 17 , Lekcja 4
Dostępny

1. Po co ChatGPT App w ogóle testy obciążeniowe?

W klasycznym webie testy obciążeniowe często kojarzą się z obrazkiem „miliony RPS, gigantyczny klaster, pizza dla SRE”. Dla ChatGPT App i serwerów MCP rzeczywistość jest prostsza i, na szczęście, tańsza. Prawdopodobnie znasz już SLO, ale spójrzmy, jak SLO/observability i jakość feedu współdziałają pod obciążeniem.

Główna cecha: ChatGPT czeka na zakończenie tool call, aby kontynuować generowanie odpowiedzi. Użytkownik widzi ładny strumień tokenów, ale gdy tylko model decyduje się wywołać narzędzie, magia strumienia się kończy – do czasu, aż backend odpowie. Jeśli Twój serwer MCP lub ACP czasem odpowiada 8–10 sekund zamiast docelowych 2–4, UX zmienia się z „magicznego asystenta” w „kolejną powolną stronę”.

Jest też twardy budżet czasowy: dla wywołań narzędzi OpenAI trzyma górną granicę rzędu kilkudziesięciu sekund (dokładne wartości zależą od trybu, ale należy myśleć o 30–60 sekundach, a z perspektywy UX – raczej o 5–10 sekundach). Jeśli przy piku obciążenia Twoje tool calls zaczynają mieścić się w 25–30 sekundach, formalnie nadal mieścisz się w limicie, ale z punktu widzenia użytkownika już „się zepsuło”.

Druga kwestia: ważniejsza jest nie tyle abstrakcyjna RPS, ile współbieżność. Dla aplikacji ze Store całkiem realistyczne jest 50–100 jednocześnie aktywnych użytkowników; to właśnie chcemy sprawdzić, a nie „czy wytrzyma 50k RPS syntetycznego GET /health”.

I wreszcie, ChatGPT App to stack:

flowchart LR
  User --> ChatGPT
  ChatGPT -->|tools/call| MCP["Serwer MCP GiftGenius"]
  MCP --> DB["Baza z feedem prezentów"]
  MCP --> ACP["Checkout / backend ACP"]
  ACP --> PSP["Płatności / Stripe"]

Jeśli nie sprawdzimy, jak ten stack zachowuje się pod niewielkim, ale realistycznym obciążeniem, to każda kampania promocyjna albo trafienie do selekcji w Store może szybko zamienić go w slajd „jak nie robić produktów LLM”.

W tym wykładzie pod „lekkimi testami obciążeniowymi” będziemy rozumieć krótkie przebiegi (zwykle 1–10 minut), które sprawdzają:

  • czy system wytrzymuje oczekiwany szczytowy online;
  • czy p95/p99 latencji nie wyjeżdżają ponad SLO;
  • czy nie sypią się błędy, timeouty i rate‑limity z zewnętrznych API.

Równolegle spojrzymy na drugą stronę jakości – dane feedu produktowego (product feed, dalej po prostu „feed”), bez których żaden GiftGenius nie będzie ani „Gift”, ani „Genius”.

Najpierw zajmiemy się lekkimi testami obciążeniowymi dla MCP/ACP (co dokładnie i jak obciążać, jakie metryki obserwować), potem osadzimy to w observability (latencja, błędy, zasoby, webhooks i logi), a w drugiej części porozmawiamy o jakości feedu i o tym, jak potrafi ona pod obciążeniem zaskakująco „strzelać”.

2. Co dokładnie obciążać: nie ChatGPT, lecz własne API

Ważne jest utrwalić jedną myśl, żeby nie mieszać jej później: testy obciążeniowe prowadzimy bezpośrednio na nasz backend – serwer MCP, endpointy ACP, webhooks – a nie przez UI ChatGPT.

Powodów jest kilka.

  • Po pierwsze, oszczędność. Jeśli puszczać realne tool calls przez ChatGPT, będziesz płacić za tokeny i jednocześnie uderzać w limity ChatGPT, choć testujesz własny kod.
  • Po drugie, przewidywalność. Przy bezpośrednich wywołaniach /mcp lub /api/checkout kontrolujesz scenariusz, nie zależąc od decyzji modelu, czy akurat wywoła narzędzie, czy nie.
  • Po trzecie, przejrzystość. Pod obciążeniem chcesz widzieć jasno: oto 2000 żądań do MCP w 5 minut, oto rozkład latencji, oto wykres CPU. Jeśli puszczać obciążenie przez ChatGPT, dodatkowa warstwa szumów i ograniczeń tylko skomplikuje obraz.

Typowy zestaw endpointów dla testu obciążeniowego GiftGenius:

  • endpoint serwera MCP, który realizuje narzędzia JSON‑RPC (/mcp lub analogiczny);
  • jeden–dwa endpointy ACP do tworzenia i finalizacji checkout (w trybie sandbox bramki płatniczej);
  • ewentualnie – endpoint, który obsługuje webhooks z bramki płatności, aby zobaczyć, jak zachowuje się przy piku zdarzeń.

Załóżmy, że mamy backend Next.js 16, na którym żyje serwer MCP dostępny pod /api/mcp, oraz serwer ACP z endpointem /api/checkout/create.

3. Mini‑scenariusz smoke‑load dla GiftGenius

Wyobraźmy sobie, że nasi product managerowie wierzą w świetlaną przyszłość i mówią: „Realistyczny szczyt – 50 równoczesnych użytkowników; każdy wchodzi, wybiera prezent i czasem dochodzi do płatności”.

Do lekkiego testu obciążeniowego wystarczy zamodelować, powiedzmy, 30–50 „wirtualnych użytkowników” (VU), z których każdy wykonuje sekwencję:

  1. Wywołanie narzędzia giftgenius.search_gifts (wyszukiwanie prezentów wg profilu i budżetu).
  2. Wywołanie giftgenius.get_gift_details dla pary produktów z wyniku.
  3. (Czasami) wywołanie endpointu ACP create_checkout_session dla jednego produktu.

Wszystko to bezpośrednio przez HTTP do naszego MCP/ACP, bez ChatGPT.

Wywołanie JSON‑RPC do MCP

Przykład ciała żądania do MCP (uproszczony):

const body = {
  jsonrpc: "2.0",
  id: "test-" + Math.random(),
  method: "tools/call",
  params: {
    toolName: "giftgenius.search_gifts",
    arguments: {
      occasion: "birthday",
      budget: 50,
      interests: ["sport", "books"],
    },
  },
};

W realnym projekcie struktura może się nieco różnić, ale zasada jest ta sama: jedna metoda JSON‑RPC, w środku – narzędzie i argumenty.

4. Piszemy prosty skrypt obciążeniowy w TypeScript

Jako pierwszy krok zaimplementujemy najprostszy fragment naszego scenariusza – wywołanie giftgenius.search_gifts do MCP. Najpierw zrobimy minimalny skrypt Node.js w TypeScript, który będzie wysyłał takie żądania na /api/mcp i mierzył latencję, a później dodamy checkout i bardziej złożone ścieżki.

Bazowy klient HTTP

Załóżmy, że mamy .env z MCP_URL=http://localhost:3000/api/mcp.

// scripts/loadTest.ts
import "dotenv/config";

const MCP_URL = process.env.MCP_URL!;

async function callSearchGifts() {
  const body = {
    jsonrpc: "2.0",
    id: `search-${Date.now()}-${Math.random()}`,
    method: "tools/call",
    params: {
      toolName: "giftgenius.search_gifts",
      arguments: { occasion: "birthday", budget: 50 },
    },
  };

  const started = Date.now();
  const res = await fetch(MCP_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  const latencyMs = Date.now() - started;
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return latencyMs;
}

Tutaj można dodać najprostsze parsowanie odpowiedzi JSON, ale na potrzeby latencji/error rate to wystarczy.

Równoległe uruchamianie wielu żądań

Musimy kontrolować liczbę równoczesnych żądań. Dla prostoty weźmiemy stałą liczbę „wirtualnych użytkowników” i poprosimy każdego, aby wykonał N żądań pod rząd.

async function runVirtualUser(iterations: number) {
  const latencies: number[] = [];
  for (let i = 0; i < iterations; i++) {
    try {
      const ms = await callSearchGifts();
      latencies.push(ms);
    } catch (e) {
      console.error("Error in VU:", e);
      latencies.push(-1); // oznaczmy błąd
    }
  }
  return latencies;
}

Teraz możemy uruchomić, powiedzmy, 20 takich wirtualnych użytkowników:

async function main() {
  const users = 20;
  const iterations = 10;

  const tasks = Array.from({ length: users }, () =>
    runVirtualUser(iterations),
  );

  const results = await Promise.all(tasks);
  const all = results.flat();
  // ...podliczanie metryk
}

main().catch((e) => console.error(e));

To zapewni już około 200 wywołań MCP, z których część będzie wykonywać się równolegle, czyli ze sporą współbieżnością.

Obliczanie p95 i error rate

Dodajmy mały pomocnik do wyliczania percentyla i błędów. Przypomnijmy: p95 to wartość, poniżej której mieści się 95% żądań.

function percentile(values: number[], p: number) {
  const sorted = values.filter(v => v >= 0).sort((a, b) => a - b);
  if (!sorted.length) return 0;
  const idx = Math.floor((p / 100) * (sorted.length - 1));
  return sorted[idx];
}

function errorRate(values: number[]) {
  const total = values.length;
  const errors = values.filter(v => v < 0).length;
  return (errors / total) * 100;
}

I w main dodamy wypisanie:

const p95 = percentile(all, 95);
const p99 = percentile(all, 99);
const errRate = errorRate(all);

console.log(`Total: ${all.length}`);
console.log(`p95: ${p95} ms, p99: ${p99} ms`);
console.log(`Error rate: ${errRate.toFixed(2)}%`);

Masz teraz minimalny skrypt smoke‑load, który można uruchamiać lokalnie lub na stagingu przed wydaniem. Przy tym w ogóle nie dotykasz ChatGPT, nie palisz tokenów, a całe skupienie jest na Twoim MCP.

Co zrobić z ACP i checkout

Podobnie można dodać kolejny helper callCreateCheckoutSession, który będzie uderzał w endpoint ACP. Ważne jest używać trybu testowego/sandbox płatności, aby nie nabijąć realnych zamówień. Typowe wywołanie będzie wyglądać jak zwykły POST z JSON:

async function callCreateCheckoutSession(productId: string) {
  const started = Date.now();
  const res = await fetch("http://localhost:3000/api/checkout/create", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ productId, test: true }),
  });
  const latencyMs = Date.now() - started;
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return latencyMs;
}

Następnie możesz w runVirtualUser zastosować wzorzec: 3 razy search → 1 raz checkout, aby zasymulować lejek „wyszukiwań więcej niż zakupów”.

5. Poważniejsze narzędzia: k6 (ale po prostu)

Skrypt w Node jest dobry jako „minimalny start”, ale czasem wygodnie użyć narzędzia specjalizowanego, jak k6, gdzie scenariusze pisze się w JavaScript, a sam runtime to Go (czyli szybki).

Przykładowy mały skrypt k6 dla MCP:

// loadtest-mcp.js
import http from "k6/http";
import { check, sleep } from "k6";

export const options = {
  stages: [
    { duration: "30s", target: 30 },
    { duration: "2m", target: 30 },
  ],
};

export default function () {
  const payload = JSON.stringify({
    jsonrpc: "2.0",
    id: `search-${Math.random()}`,
    method: "tools/call",
    params: {
      toolName: "giftgenius.search_gifts",
      arguments: { occasion: "birthday", budget: 50 },
    },
  });

  const res = http.post(__ENV.MCP_URL, payload, {
    headers: { "Content-Type": "application/json" },
  });

  check(res, { "status is 200": (r) => r.status === 200 });
  sleep(1);
}

Polecenie uruchomienia:

MCP_URL=http://localhost:3000/api/mcp k6 run loadtest-mcp.js

k6 sam policzy p95/p99 i error rate, narysuje ładne raporty – a dalej możesz je eksportować do Grafany i innych systemów.

Ważne: nawet z takimi narzędziami nasz cel pozostaje ten sam – nie wytrzymać miliona RPS, lecz upewnić się, że przy 5–10× oczekiwanego piku system się nie rozsypuje, a p95 pozostaje w granicach SLO.

6. Na co patrzeć podczas (i po) przebiegu obciążeniowego

Omawialiśmy już metryki i SLO, teraz po prostu „uziemimy” je w kontekście obciążenia.

Po pierwsze: latencja. Dla narzędzi MCP typu search_gifts wcześniej ustawiałeś sobie cel w stylu „p95 < 2–3 sekundy”. Podczas smoke‑load patrzysz: czy p95/p99 nie poszły w górę 2–3×. Ważne jest porównanie z baseline: jeśli przed zmianą kodu p95 wynosiła 400 ms, a po – 1500 ms, to nawet jeśli formalnie nadal mieścisz się w SLO, jest to powód do namysłu.

Po drugie: error rate. Pod obciążeniem często wychodzą niespodzianki: wyczerpana pula połączeń do bazy, nieoczekiwane 429 z zewnętrznego API, timeouty przy odwołaniach do bramki płatności. Przy normalnym obciążeniu error rate powinien być bliski zeru; w smoke‑load dopuszczalne są pojedyncze potknięcia, ale na pewno nie 5–10%.

Po trzecie: metryki zasobów: CPU, pamięć, czasem – liczba otwartych deskryptorów plików i połączeń. Zależą one od Twojej infrastruktury, ale kluczowa idea jest prosta: nie chcesz widzieć, jak przy 30 VU CPU ma 100%, a GC zjada połowę czasu.

Po czwarte: webhooks. Jeśli masz scenariusz commerce, to finalny punkt zamówienia często zależy od pomyślnej obsługi webhooka z systemu płatności. Ważne jest patrzeć nie tylko na szybkość żądania w ACP, ale i na opóźnienie „przyszedł webhook → pomyślnie go obsłużyliśmy”.

I wreszcie: logi. Strukturalne logi z trace_id/checkout_session_id pozwalają po przebiegu obciążeniowym wziąć parę najwolniejszych lub wywalonych żądań i przejść łańcuchem: MCP → zewnętrzne API → ACP → webhook. To szczególnie przydatne, jeśli pod obciążeniem widzisz dziwne ogony p99.

7. Jakość danych feedu: od struktury do sensu

Zobaczyliśmy, jak pod obciążeniem zachowują się latencja, błędy i zasoby. Ale nawet jeśli pod tymi SLO mieścisz się w celach, doświadczenie użytkownika nadal może się „sypać” przez złe dane.

Przechodzimy do drugiego dużego tematu: dane. W aplikacji commerce jak GiftGenius product feed (feed produktów) to nie „coś na dysku”, tylko dosłownie paliwo dla LLM i agentów. Jeśli w feedzie jest śmietnik, model nie „wymyśli” za Ciebie ceny ani dostępności.

Wygodnie jest myśleć o jakości feedu w trzech warstwach.

Poziom strukturalny

To podstawowa poprawność danych:

  • JSON parsuje się poprawnie.
  • Wszystkie pola obowiązkowe są obecne: id, name, price, currency, imageUrl, availability itd.
  • Typy wartości odpowiadają oczekiwaniom: cena – liczba, availability – enum, categories – tablica stringów.
  • Brak duplikatów id.

Część tego pokryłeś już testami kontraktowymi, gdy opisywałeś schemę JSON/Zod dla feedu. Teraz trzeba stosować te schemy do realnych wolumenów danych.

Przykład prostej schemy Zod dla elementu feedu GiftGenius:

import { z } from "zod";

export const giftItemSchema = z.object({
  id: z.string().min(1),
  name: z.string().min(3),
  description: z.string().optional(),
  price: z.number().positive(),
  currency: z.enum(["USD", "EUR", "GBP"]),
  imageUrl: z.string().url(),
  inStock: z.boolean(),
  tags: z.array(z.string()).default([]),
});

A schema całego feedu to po prostu z.array(giftItemSchema).

Poziom biznesowy (semantyka)

Strukturalnie produkt może być poprawny, ale z biznesowego punktu widzenia – absurdalny:

  • Cena 0 lub 0,01 dla drogiego produktu.
  • Waluta nie odpowiada rynkowi (USD dla produktów sprzedawanych wyłącznie w EUR).
  • inStock = true, ale data ostatniej aktualizacji sprzed pół roku.
  • Kategorie z 1000 wariantów bez unifikacji.

Na tym poziomie warto dodać dodatkowe weryfikacje i „reguły zdrowego rozsądku”. Na przykład:

const businessRules = (item: GiftItem) => {
  const problems: string[] = [];

  if (item.price > 10000) {
    problems.push("podejrzanie wysoka cena");
  }
  if (!item.inStock && item.tags.includes("bestseller")) {
    problems.push("bestseller, ale niedostępny");
  }
  return problems;
};

Te weryfikacje można uruchamiać jako część nightly job albo podczas generowania nowego feedu.

Poziom LLM

Model jest bardzo sprytny, ale ma swoje „wady”:

  • Opis zapchany HTML, zbędnymi tagami i tekstem technicznym.
  • Wymieszane języki (pół feedu po rosyjsku, pół po angielsku) bez wskazanego locale.
  • Bardzo długie „SEO‑nazwy” w stylu „Kup najlepszy super hiper prezent natychmiast tanio”.

Na tym poziomie ważne jest doprowadzić dane do przyjaznego formatu:

  • Usunąć tagi HTML lub sprowadzić je do plaintext.
  • Znormalizować język opisów (albo przynajmniej jawnie wskazywać locale).
  • Przyciąć nadmiernie długie nazwy i zdublowane informacje.

Te zadania można częściowo zautomatyzować (np. skryptami pre‑processing), a częściowo – dogadać się z zespołem, który uzupełnia feed.

8. Praktyka: walidator feedu dla GiftGenius

Dodajmy do naszego projektu prosty skrypt validateFeed.ts, który będzie czytał JSON z feedem, walidował go przez Zod i liczył podstawowe metryki jakości.

// scripts/validateFeed.ts
import { readFile } from "fs/promises";
import { giftItemSchema } from "../src/schema/giftItem";

async function main() {
  const raw = await readFile("data/gift-feed.json", "utf-8");
  const data = JSON.parse(raw);

  const items = giftItemSchema.array().parse(data);
  console.log(`Łączna liczba produktów: ${items.length}`);

  const missingImages = items.filter(i => !i.imageUrl).length;
  console.log(`Bez zdjęć: ${missingImages}`);
}

main().catch((e) => {
  console.error("Feed validation failed:", e);
  process.exit(1);
});

Tutaj używamy tego samego kontraktu, co serwer MCP, czyli testy kontraktowe i weryfikacja feedu używają jednej schemy – to znacząco zmniejsza ryzyko rozbieżności.

Dalej można dodać sprawdzanie reguł biznesowych i metryki typu:

  • odsetek produktów bez opisu;
  • odsetek produktów z podejrzanie niską/wysoką ceną;
  • liczba duplikatów id lub powtarzających się name + price.

Te liczby można już wysyłać do systemu metryk (Prometheus, Datadog itp.) i mieć pod nimi osobne SLO na jakość danych – tak jak definiujesz SLO dla kodu.

9. Jak obciążenie i feed są ze sobą powiązane

Czasem wydaje się, że „wydajność” i „jakość danych” to dwa niezbyt powiązane tematy. W praktyce są dość mocno splecione.

Przykłady powiązań:

  • Pod obciążeniem część żądań zaczyna iść „rzadkimi” ścieżkami logiki, które wcześniej prawie nie występowały. Na przykład, produkty ze specjalnymi typami rabatów albo niestandardowym shipping. Jeśli feed w tych miejscach jest brudny, możesz dostać i błędy, i poważną degradację wydajności (mnóstwo walidacji, wyjątków, logiki fallback).
  • Jeśli feed jest mocno hałaśliwy (ogromne opisy z HTML, bezsensowne tagi), serwer MCP musi pobierać i serializować więcej danych, co bezpośrednio wpływa na czas przetwarzania tool-call i rozmiar odpowiedzi.
  • W części commerce zły feed może prowadzić do dużej liczby „pustych” prób checkout, gdy użytkownik wybiera produkt, który nagle jest out of stock. Uderza to i w UX, i w metryki ACP (wzrost nieudanych intentów).

Wygodnie patrzeć na to jak na macierz:

Problem feedu Objaw pod obciążeniem Gdzie patrzeć
Niespójne ceny/waluty Błędy w ACP, odrzucone płatności Logi ACP + SLO checkout
Duplikaty produktów Dziwne wyniki rekomendacji, zbędne wywołania Logi MCP, metryki UX
Brak zdjęć/opisów Model daje „płaskie” rekomendacje Logi aplikacji + opinie UX
HTML/śmieci w opisach Powolne serializacje, duże payloady Latencja MCP

Przebieg obciążeniowy pełni tu rolę latarki: pomaga podświetlić te fragmenty feedu, które w zwykłym życiu rzadko były dotykane, ale przy aktywnym ruchu zaczynają „strzelać”.

10. Wpinamy to w proces release GiftGenius

Z punktu widzenia procesu wszystko powyższe nie powinno być „raz przed pierwszą prod‑em”. W planie kursu modułów 16 („Production, sieć i skalowanie”) i 17 („Obserwowalność i jakość”) podejście to jest wpisane jako część regularnego release‑checklistu: przed wydaniem nie tylko uruchamiasz unit/contract/E2E, ale też krótki smoke‑load plus weryfikację feedu.

Rozsądny minimalny pipeline przed wdrożeniem nowej wersji:

  1. Unit + contract + testy integracyjne na zielono.
  2. Krótki smoke‑load przeciw MCP/ACP na stagingu, jeśli zmieniał się krytyczny kod (logika wyszukiwania, praca z bazą, checkout).
  3. Walidator feedu działa bez błędów, podstawowe metryki feedu (liczba uszkodzonych rekordów, odsetek bez zdjęć itp.) w dopuszczalnych granicach.
  4. Dashboardy i alerty zaktualizowane z uwzględnieniem nowych endpointów i SLO.
  5. Na wypadek porażki przygotowany plan rollbacku: albo wyłączenie funkcji flagą, albo cofnięcie builda.

Dzięki temu GiftGenius przestaje być „demkiem na DevDay” i staje się usługą gotową do życia w Store i do skoków ruchu.

11. Typowe błędy przy testach obciążeniowych i weryfikacji feedu

Błąd nr 1: test obciążeniowy „przez ChatGPT”, a nie wobec własnego backendu.
Czasem ktoś próbuje „przetestować wszystko jak w rzeczywistości” i uruchamia skrypty, które chodzą właśnie przez UI ChatGPT. W rezultacie wpada w limity OpenAI, pali tokeny i dostaje niezwykle szumne wyniki. Tymczasem problemy MCP/ACP można było wychwycić sto razy taniej, strzelając prosto w /mcp i /api/checkout.

Błąd nr 2: skupienie wyłącznie na średnim czasie odpowiedzi.
„Mamy średnią latencję 500 ms, jest świetnie” – a to, że p95 przy tym wynosi 5 sekund, jakoś się zapomina. Omawialiśmy już przy SLO, że to ogon rozkładu (p95/p99) determinuje realny UX. Pod obciążeniem średnia często pozostaje przyzwoita, a ogon rośnie dwukrotnie lub trzykrotnie.

Błąd nr 3: próba zrobienia „enterprise’owego obciążenia” zamiast praktycznego smoke‑load.
Miesiącami rozwijać skomplikowane środowisko imitujące dziesiątki tysięcy użytkowników dla ChatGPT App klasy GiftGenius – to prawie zawsze zbędne. Znacznie lepiej mieć prosty, ale regularnie odpalany smoke‑load na 50–100 VU z czytelnymi metrykami.

Błąd nr 4: nierealistyczny scenariusz obciążenia.
Skrypt wysyła jedno i to samo żądanie, bez wariantów użytkownika, języka, typu produktu, a przy tym nie dotyka ACP i webhooków. W rezultacie testujesz jedną „gorącą” ścieżkę happy‑path, a realne „narożniki” systemu pozostają w cieniu. Lepiej modelować choćby uproszczony, ale wiarygodny flow: różne budżety, różne zainteresowania, część użytkowników dochodzi do checkout, część – nie.

Błąd nr 5: sprawdzanie feedu tylko „na oko” albo w produkcji.
Feed zebrano, wrzucono na produkcję, model zaczął dawać dziwne rekomendacje i zaczyna się drapanie po głowie. Tymczasem prosty skrypt na Zod/JSON Schema mógłby w minutę pokazać, że 10% produktów nie ma zdjęć, 5% ma cenę 0, a 3% walutę XXX. Brak automatycznej walidacji feedu to jedno z najczęstszych źródeł wstydu w aplikacjach commerce.

Błąd nr 6: nadzieja, że LLM „samo wszystko zrozumie” przy złym feedzie.
Tak, model potrafi wiele, ale nie wymyśli poprawnej ceny ani dostępności. Jeśli ten sam produkt w feedzie występuje z różnymi cenami albo równocześnie jako „dostępny”/„niedostępny”, agent może dać i halucynacje, i niespójne doświadczenie użytkownika. Odpowiedzialność za czystość danych jest po Twojej stronie, a nie modelu.

Błąd nr 7: brak powiązania między metrykami feedu a ogólnymi SLO.
Można mieć idealnie szybkie MCP i ACP, ale jeśli 30% produktów w feedzie jest „uszkodzonych”, doświadczenie użytkownika i tak będzie kiepskie. Często zespoły śledzą tylko techniczne SLO (latencja, error rate) i ignorują SLO jakości danych (minimalny procent poprawnych SKU, maksymalna liczba duplikatów itd.). W rezultacie „na liczbach wszystko dobrze”, a w odczuciu – nie.

Błąd nr 8: uruchamianie testów obciążeniowych bezpośrednio na produkcji bez przygotowania.
Czasem ktoś w piątek wieczorem decyduje „szybko puścić k6 na produkcyjnym MCP”, nie uprzedzając nikogo. W najlepszym razie rozstroisz realne metryki i zdezorientujesz on‑call inżyniera skokiem ruchu, w najgorszym – wpadniesz w rate‑limity zewnętrznego API lub systemu płatności. Zawsze uruchamiaj pierwsze scenariusze na stagingu, a jeśli potrzebny jest test na produkcji – rób to świadomie, z oknami i powiadomieniami.

1
Ankieta/quiz
Obserwowalność i jakość, poziom 17, lekcja 4
Niedostępny
Obserwowalność i jakość
Obserwowalność i jakość
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION