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ę:
- Wywołanie narzędzia giftgenius.search_gifts (wyszukiwanie prezentów wg profilu i budżetu).
- Wywołanie giftgenius.get_gift_details dla pary produktów z wyniku.
- (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:
- Unit + contract + testy integracyjne na zielono.
- Krótki smoke‑load przeciw MCP/ACP na stagingu, jeśli zmieniał się krytyczny kod (logika wyszukiwania, praca z bazą, checkout).
- Walidator feedu działa bez błędów, podstawowe metryki feedu (liczba uszkodzonych rekordów, odsetek bez zdjęć itp.) w dopuszczalnych granicach.
- Dashboardy i alerty zaktualizowane z uwzględnieniem nowych endpointów i SLO.
- 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.
GO TO FULL VERSION