Dodatek: AI Testing Skills - ewolucja poza RAG i MCP
Ten dodatek rozwija temat skilli z lekcji 11. Możesz potraktować go jako szerszy kontekst: skąd wzięły się skills, czym różnią się od RAG, MCP i tool callingu oraz jak projektować je jako powtarzalne playbooki dla agentów testowych.
W ciągu ostatnich trzech lat LLM-y przestały być "autouzupełnianiem, które mówi", a stały się czymś znacznie bliższym członkowi zespołu: agentami AI, które potrafią eksplorować repozytorium, uruchamiać komendy, analizować dane i iterować, aż coś zacznie działać. To przesunięcie umożliwiło Agentic Testing. Opisuję je jako testowanie wykonywane przez agentów AI - nie przez skrypty, nie przez ludzi klikających po aplikacji, ale przez agentów, którzy potrafią rozumować, działać, weryfikować i raportować.
Ten postęp następował etapami. Function calling dał modelom "ręce". RAG dał im "fakty". MCP obiecał uniwersalny konektor do narzędzi. Ale w realnym użyciu ta wygoda potrafi mieć koszt: definicje narzędzi i schematy rozdymają prompty, wracają w kolejnych turach i zużywają tokeny zanim agent wykona naprawdę użyteczną pracę.
Skills są kolejnym krokiem: praktyczną odpowiedzią na marnowanie tokenów, brak powtarzalnych playbooków i pokusę upychania procedur w RAG-u (który lepiej traktować jako dokumentację, a nie instrukcję wykonania). Skills formalizują pamięć proceduralną dzięki progressive disclosure: na początku ładowane są tylko krótkie metadane, a szczegółowe instrukcje są dociągane dopiero wtedy, gdy faktycznie są potrzebne.
W tym poście przejdziemy tę ścieżkę (function calling → RAG → MCP → skills), potem głębiej omówimy skills (struktura + użycie w Claude i Codex), a na końcu omówimy dwa konkretne przykłady: open-source'owy skill webapp-testing od Anthropic oraz zupełnie nowy skill do testowania API zbudowany od zera.
Kamienie milowe agentów
Wyobraź sobie wczesną erę LLM-ów jako zatrudnienie genialnego teoretyka... i zamknięcie go w pokoju, w którym ma tylko klawiaturę.
Taki model potrafił mówić, szkicować plany, wymyślać API, tłumaczyć architekturę, a nawet pisać przekonujący pseudokod. Ale nie potrafił niczego dotknąć. Brak systemu plików. Brak przeglądarki. Brak bazy danych. Brak "uruchommy i zobaczmy".
Jeżeli więc chciałeś mieć coś przypominającego workflow - analiza → decyzja → działanie → weryfikacja → raport - musiałeś udawać go przez prompt chaining: rozbić pracę na kroki, wielokrotnie wywoływać model i podawać wynik jednego promptu jako wejście do kolejnego. Czasem działało, ale było kruche dokładnie tak, jak kruche bywają wieloetapowe pipeline'y:
- jedno błędne założenie na początku łańcucha rozsypywało dalsze kroki (klasyczna propagacja błędu)
- trzeba było dodawać "bramki" (programistyczne walidacje) między krokami, żeby utrzymać łańcuch na torach
- latencja i koszt rosły, bo każdy "krok" był kolejnym pełnym wywołaniem modelu
- debugowanie oznaczało ustalanie, które ogniwo łańcucha zgubiło ograniczenie albo po cichu wymyśliło szczegół
Poniższe kamienie milowe są w gruncie rzeczy historią przejścia od tego kruchego świata prompt chainingu do agentów, które potrafią wiarygodnie robić rzeczy - nie tylko je opisywać. Pierwszą poprawką było danie teoretykowi pary rąk.
Function calling: uczenie języka działania
Function Calling (który szczegółowo opisałem w poście How does Playwright MCP work) to moment, w którym przestaliśmy traktować model jak powieściopisarza, a zaczęliśmy traktować go jak koordynatora.
Zamiast prosić model, żeby opisał działanie tekstem ("teraz wywołaj API z tymi parametrami"), udostępniasz mu zestaw narzędzi, a on odpowiada ustrukturyzowanym wywołaniem: wywołaj X z argumentami Y. Twoja aplikacja wykonuje to deterministycznie, a potem zwraca wynik, żeby agent mógł zdecydować, co dalej.
Koncepcyjnie jest to przejście od:
- nietypowanego tekstowego I/O (niejednoznacznego i trudnego do walidacji) do
- typowanej intencji (argumentów JSON ograniczonych schematem), z wykonaniem poza modelem
Ale jest też ukryty koszt, który stanie się ważny później: żeby model dobrze wybierał narzędzia, musi widzieć ich nazwy, opisy i często schematy parametrów - a te definicje zużywają kontekst zawsze wtedy, gdy są do niego dołączone.
RAG: uczenie języka wiedzy
Użycie narzędzi dało agentowi "ręce", ale nie rozwiązało większej prawdy: model może być elokwentny i jednocześnie się mylić.
RAG (Retrieval-Augmented Generation) rozwiązuje praktyczną wersję tego problemu przez wyszukiwanie odpowiednich dokumentów i opieranie odpowiedzi na tym materiale. Sens jest prosty: model może odwołać się do informacji spoza swoich danych treningowych w czasie inferencji.
Koncepcyjnie RAG daje agentowi kartę biblioteczną:
- może sprawdzać rzeczy (dokumentację, tickety, polityki, specyfikacje, repozytoria)
- a potem mówić z podaniem źródeł.
Różnica, która będzie ważna później: RAG jest mechanizmem dokumentacyjnym. Jest zbudowany do wyszukiwania prawdy, a nie do kodowania procedur.
MCP: uczenie systemów podłączania się do agentów
Kiedy użycie narzędzi stało się normalne, następnym wąskim gardłem stał się rozrost integracji: każdy agent + każda usługa oznaczały własny klej.
MCP (Model Context Protocol) jest "momentem USB-C": otwartym standardem, który ma sprawić, że integracje narzędzi i kontekstu będą reużywalne między ekosystemami. Klienci mogą łączyć się z dowolnymi serwerami, a te serwery wystawiają narzędzia, zasoby i workflow przez spójny interfejs, niezależnie od tego, kto je zbudował.
MCP vs CLI: podatek od efektywności tokenów
To jest fragment, który błyszczące dema często pomijają: uniwersalność potrafi być droga.
Jeżeli model ma wybierać między dziesiątkami narzędzi, potrzebuje w kontekście wystarczająco szczegółowych definicji tych narzędzi. Te definicje (i wyniki) zajmują miejsce, a w wieloturowym użyciu narzędzi potrafią stać się istotną częścią budżetu promptu. Anthropic pisze o tym wprost: bezpośrednie tool calle zużywają kontekst dla każdej definicji i każdego wyniku, co staje się problemem skalowania, gdy dodajesz więcej narzędzi i wykonujesz więcej kroków.
Nie chodzi o to, że MCP musi za każdym razem wysyłać "cały schemat". Chodzi o to, że naiwni klienci często trzymają w kontekście duży rejestr narzędzi (albo wielokrotnie dołączają wiele opisów narzędzi), więc płacisz za schematyczne, ciężkie opisy w kolejnych turach.
Dygresja: dlaczego CLI często wydaje się "lżejsze"?
Bo CLI ma małą, stabilną powierzchnię. Odkrywanie zwykle dzieje się na żądanie ("help", strony man, listy plików), zamiast preładowania katalogu schematów JSON po to, żeby model mógł zdecydować. Innymi słowy: CLI jest interfejsem zaprojektowanym do zapamiętania, a rejestry narzędzi MCP często są zaprojektowane do opisania.
To nie znaczy, że MCP jest złe. To znaczy tylko, że naiwny wzorzec użycia może stać się piecem na tokeny.
Skills: uczenie agentów, jak pamiętać procedury
Skills są kolejnym krokiem, bo odpowiadają na problemy, których poprzednie kamienie milowe nie rozwiązały:
- Function calling pomaga agentowi
działać(wykonać zadanie), ale nie daje mu reużywalnego playbooka. - RAG pomaga agentowi
wiedzieć(wyszukiwać informacje), ale nie jest dobrym miejscem na przechowywanie procedur. - MCP pomaga agentowi
korzystaćz potrzebnych narzędzi, ale przy skali może stać się ciężkie kontekstowo.
Skill jest w praktyce spakowanym playbookiem: katalogiem zawierającym plik SKILL.md (z wymaganymi metadanymi nazwy i opisu) oraz opcjonalnymi skryptami i zasobami.
Kluczową ideą jest progressive disclosure:
- na starcie agent ładuje tylko nazwę i opis każdego skilla
- pełne instrukcje są ładowane dopiero wtedy, gdy skill jest faktycznie istotny
To jest architektoniczna odpowiedź na historię podatku tokenowego: discovery ma być tanie; szczegóły mają być lazy-loaded.
Mapa kamieni milowych wygląda więc tak:
- Function calling = ręce
- RAG = karta biblioteczna
- MCP = port USB-C
- Skills = pamięć mięśniowa / playbooki
I to prowadzi do realnego pytania w kolejnej sekcji: jak wygląda dobry playbook, kiedy musi działać w "głowie" agenta - i w skończonym oknie kontekstu?
Głębsze spojrzenie na skills
Skills rozwiązują bardzo konkretny problem skalowania agentów. Chcesz mieć powtarzalne playbooki (wiedzę proceduralną), nie chcesz płacić za nie tokenami, jeżeli nie są akurat potrzebne, i chcesz móc je współdzielić (per użytkownik, per repozytorium, per zespół).
Podstawową zasadą projektową jest progressive disclosure: załaduj na początku odrobinę metadanych, a cięższe instrukcje i pliki dociągnij dopiero wtedy, gdy agent wybierze dany skill. Anthropic opisuje to jako wstępne ładowanie name + description dla każdego zainstalowanego skilla, a następnie czytanie SKILL.md (i powiązanych plików) tylko wtedy, gdy są istotne.
Codex robi to samo: na starcie ładuje tylko nazwę i opis, a ciało instrukcji trzyma na dysku aż do aktywacji skilla.
Zasada stojąca za tym podejściem jest prosta: fakty należą do RAG-u, możliwości należą do narzędzi, procedury należą do skills.
Struktura skilla
W najprostszej wersji skill jest po prostu katalogiem z jednym plikiem:
my-skill/
SKILL.md
Ten SKILL.md ma dwie części:
- YAML frontmatter (warstwę odkrywania - fragment, który jest preładowany)
---
name: api-testing
description: >
Generate and run API checks for REST endpoints. Use when the user mentions
HTTP, REST, OpenAPI, swagger, endpoints, contract tests, or postman
collections.
---
- ciało Markdown (playbook - ładowany dopiero wtedy, gdy skill się aktywuje)
# API Testing
## Instructions
1. Identify the endpoints, auth, environments, and data setup.
2. Decide the checks: contract, functional, negative, idempotency, rate limits, etc.
3. Implement the checks using the project’s tooling (or propose the
lightest viable harness).
...
Gdy skills rosną, warto utrzymywać SKILL.md jako lekki plik przypominający spis treści, a szczegóły przenosić do linkowanych plików (trzeci poziom disclosure). Anthropic wprost rekomenduje ten wzorzec, kiedy pojedynczy SKILL.md stałby się zbyt duży albo zbyt szeroki.
Codex dokumentuje dokładnie taki kształt (scripts/, references/, assets/) jako standardową konwencję.
my-skill/
SKILL.md # required
references/ # optional docs (standards,
# runbooks, ADRs)
scripts/ # optional executable helpers
assets/ # optional templates/schemas
Dokumentacja Claude Code pokazuje tę samą ideę (dodatkowy Markdown + scripts/ + templates/) i podkreśla, że pliki są czytane tylko wtedy, gdy są potrzebne.
my-skill/
├── SKILL.md (required)
├── reference.md (optional documentation)
├── examples.md (optional examples)
├── scripts/
│ └── helper.py (optional utility)
└── templates/
└── template.txt (optional template)
Claude vs Codex Skills
Oba rozwiązania mają ten sam podstawowy kształt (katalog + SKILL.md), ale codzienne użycie różni się w sposób, który ma praktyczne znaczenie.
Gdzie mieszkają skills
Claude Code:
- User skills:
~/.claude/skills/ - Repo skills:
.claude/skills/(trzymane w repozytorium)
Claude wspiera też skills dostarczane przez pluginy.
Codex:
- User skills:
~/.codex/skills/ - Repo skills:
.codex/skills/(trzymane w repozytorium)
Praktyczna różnica: oba systemy wspierają zakres "osobisty vs zespołowy", ale nazwy katalogów są inne (~/.claude/skills/ vs ~/.codex/skills/).
Jak skills się uruchamiają
Claude Code:
- Skills są model-invoked, czyli wywoływane przez model: Claude decyduje, czy użyć skilla, na podstawie Twojej prośby i opisu skilla.
- To sprawia, że pole
descriptionstaje się prawdziwą powierzchnią wyzwalania - uczysz Claude'a, kiedy ma po dany skill sięgnąć.
Codex:
- Wspiera oba tryby:
- implicit invocation (Codex decyduje na podstawie opisu, podobnie jak Claude Code)
- explicit invocation (możesz wybrać/wspomnieć skills przez mechanizmy UI, takie jak
/skills, albo wpisując$, żeby odwołać się do skilla).
Praktyczna różnica: Codex daje ścieżkę "manual override", kiedy chcesz deterministycznego zachowania ("użyj teraz tego playbooka"), podczas gdy Claude Code mocniej opiera się na autonomicznym wyborze.
Co ładuje runtime (efektywność tokenów z założenia)
Oba systemy implementują progressive disclosure:
- preładowują tylko
name+descriptiondo promptu / kontekstu systemowego - ładują pełne ciało
SKILL.mdtylko wtedy, gdy skill jest faktycznie używany - ładują dodatkowe pliki tylko wtedy, gdy skill na nie wskazuje i są potrzebne
Codex opisuje to szczególnie wprost: wymagane klucze to name i description, dodatkowe klucze YAML są ignorowane, a ciało Markdown zostaje na dysku i nie jest wstrzykiwane, dopóki skill nie zostanie wywołany.
To jest konkretna wersja historii "anti-MCP-token-waste": discovery jest tanie, szczegóły są lazy-loaded.
Guardrails i uprawnienia
Claude Code wspiera pole frontmatter allowed-tools, które ogranicza narzędzia dostępne w czasie działania skilla (tylko Claude Code).
To ma znaczenie w workflow testowych: możesz tworzyć skills w trybie "read-only" albo "safe mode", które nie mogą mutować repozytoriów lub środowisk.
Publiczna dokumentacja Codex skupia się bardziej na formacie, scaffoldingu oraz konwencji scripts/ i zasobów; Codex dostarcza też wbudowane skills, takie jak $skill-creator i $skill-installer, żeby spójnie tworzyć i dystrybuować skills.
Przykłady
Najszybszym sposobem zrozumienia skills jest przeanalizowanie dobrze zaprojektowanego przykładu. Przyjrzyjmy się oficjalnemu przykładowi Anthropic z GitHuba (webapp-testing), a potem zbudujmy podobny skill do testowania API dla mojego projektu awesome-localstack.
Anthropic webapp-testing skill
Skill webapp-testing od Anthropic jest dobrym przykładem referencyjnej implementacji, bo jasno opisuje zakres i mechanikę. Nie wymyśla nowej, "agent-native" abstrakcji przeglądarki. Standaryzuje znany runtime i sprawia, że agent działa przez niego:
To test local web applications, write native Python Playwright scripts.
Od tego miejsca skill robi trzy praktyczne rzeczy: uczy agenta wyboru podejścia, stabilizowania dynamicznego UI przed działaniem oraz kontrolowania użycia kontekstu. To ważne, bo pokazuje progressive disclosure w praktyce: procedury mieszkają w skryptach i przykładach, więc model nie musi "nosić" ich w przestrzeni promptu.
1) Koduje strategię wyboru (nie tylko poradę).
SKILL.md zawiera konkretne drzewo decyzyjne: zacznij od odróżnienia statycznego HTML-a od dynamicznej aplikacji webowej; potem, dla dynamicznych aplikacji, sprawdź, czy serwer już działa; jeżeli nie, "poprawnym" ruchem jest użycie helpera zamiast improwizacji.
To jest wartościowe, bo zamienia niejednoznaczny prompt ("przetestuj tę aplikację") w powtarzalną procedurę z rozgałęzieniami.
2) Formalizuje operacyjną pętlę dla dynamicznych stron.
Dla gałęzi "serwer już działa" skill zaleca pętlę reconnaissance-first: przejdź na stronę, poczekaj aż aplikacja się ustabilizuje, zbierz dowody, wyprowadź selektory z wyrenderowanego stanu, a dopiero potem działaj. W przykładach i wskazówkach networkidle jest traktowane jako kluczowy krok stabilizacji dla aplikacji mocno opartych o JavaScript.
3) Traktuje tokeny i kontekst jako ograniczenie pierwszej klasy.
Skill jest wyjątkowo bezpośredni w temacie unikania rozdęcia kontekstu. Mówi agentowi, żeby traktował skrypty pomocnicze jak czarne skrzynki i nie wczytywał ich, o ile nie jest to konieczne:
Always run scripts with --help first…
Ostrzega też, że czytanie dużych skryptów może "pollute your context window". To dokładnie filozofia progressive disclosure zastosowana na poziomie mikro: uruchamiaj narzędzia; nie wklejaj narzędzi.
Helper scripts/with_server.py
Skill dostarcza jeden skrypt pomocniczy, scripts/with_server.py, i mocno opiera się na nim w gałęzi "server not running". Zamierzony wzorzec użycia jest taki: podaj jedną lub więcej komend startujących serwery wraz z portami, potem separator --, a potem komendę, która uruchamia automatyzację Playwright. SKILL.md zawiera przykłady zarówno dla jednego, jak i wielu serwerów (np. backend + frontend).
Funkcjonalnie with_server.py robi to, co inaczej szybko zacząłbyś implementować ad hoc w promptach: startuje procesy, czeka aż porty będą osiągalne i pilnuje sprzątania nawet wtedy, gdy test się nie powiedzie.
Najważniejszy dla tego posta jest punkt architektoniczny: to jest ekstrakcja właściwa dla skilla. Orkiestracja serwerów jest proceduralna i deterministyczna, więc należy do wykonywalnego helpera, a nie do powtarzanego tekstu promptu typu "wykonaj te kroki".
Katalog examples/
Repozytorium zawiera też katalog examples/. Te pliki nie są "dokumentacją"; są zwięzłymi demonstracjami wzorców, które agent może skopiować z minimalną adaptacją.
examples/console_logging.pypokazuje, jak przechwytywać output konsoli przeglądarki przez zarejestrowanie handlera zdarzeń konsoli Playwright przed nawigacją i zbieranie komunikatów do późniejszej analizy. Przykład zapisuje typ i treść komunikatu oraz wypisuje je w czasie rzeczywistym.
To ma znaczenie, bo logi konsoli są jednym z najszybszych sposobów zamiany niestabilnego "nie działa" w konkretny sygnał awarii (błędy JS, ostrzeżenia, zablokowane requesty).
- Ten sam zestaw przykładów obejmuje też dwa inne praktyczne przypadki: testowanie statycznego HTML-a przez URL-e
file://oraz podstawowe odkrywanie elementów (przyciski/linki/inputy) jako krok rozpoznawczy.
Razem struktura tego skilla jest bardzo świadoma: polityka + procedura w SKILL.md, deterministyczna orkiestracja w scripts/with_server.py i minimalne wykonywalne wzorce w examples/. Ta kombinacja sprawia, że działa dobrze jako showcase skills: pokazuje, jak utrzymać zachowanie agenta w ryzach bez pompowania każdego promptu szczegółami operacyjnymi.
Skill do testowania API dla projektu awesome-localstack
Teraz zastosujmy to w praktyce i stwórzmy podobny skill do testowania API w moim projekcie awesome-localstack.

Oto wynikowy SKILL.md:
---
name: playwright-api-testing
description: >
Create, update, and run Playwright API tests in this repository. Use when
the user asks to add or modify API tests, validate API behavior, work with
HTTP clients in `http/`, or follow the repo's API testing rules and fixtures.
---
# Playwright API Testing
## Overview
Use this skill to design and implement API tests with Playwright that follow
the repo's conventions: shared fixtures, HTTP client wrappers, and API test
rules. Validate behavior against `api-docs.json` or live docs at
`http://localhost:4001/v3/api-docs`.
## Workflow
1. Read rules in `.cursor/rules/api-test-rules.mdc` and follow them exactly
(naming, ordering by status code, limited 400s, required lint/test runs).
2. Confirm API schema via `api-docs.json` or
`curl http://localhost:4001/v3/api-docs`.
3. Identify auth needs and obtain tokens using the existing fixtures or login flow.
4. Add or update HTTP client wrappers in `http/` (one method per file, one endpoint per file).
5. Write tests in `tests/api/` using `// given`, `// when`, `// then` comments and shared helpers.
6. Run `npm run test:api` and `npm run lint`, then report failures or regressions.
## Auth and Fixtures
- Use `fixtures/apiAuthFixture.ts` for API tests that need authenticated users.
- `createAuthFixture` signs up a generated user and logs in to return
`{ token, userData }`.
- Bearer tokens come from `/users/signin` responses (`loginResponse.token`).
- Pass tokens via `Authorization: Bearer <token>` in HTTP client wrappers.
## HTTP Client Pattern
- Keep HTTP logic in `http/**` request helpers (one HTTP method per file).
- Use `API_BASE_URL` from `config/constants`.
- Add optional token handling in headers when needed.
- Keep tests thin: tests call helpers; helpers call `http/` clients.
## Test Structure and Data
- Follow `.cursor/rules/api-test-rules.mdc` for naming and test ordering.
- Use Faker-based generators from `generators/` for randomized data.
- Use shared helpers from `tests/helpers/` for products, carts, and orders.
- Avoid duplicating setup logic; extend helpers if needed.
## Validation
- Run `npm run test:api` after finishing API test changes.
- Run `npm run lint` after each incremental change and before reporting results.
- If failures look like app/API behavior changes, report them with the failing
test names and expected vs actual status codes.
## Quick References
- API rules: `.cursor/rules/api-test-rules.mdc`
- API docs: `api-docs.json` or `curl http://localhost:4001/v3/api-docs`
- Fixtures: `fixtures/apiAuthFixture.ts`, `fixtures/createAuthFixture.ts`
- HTTP clients: `http/`
- Example tests: `tests/api/`
Żeby go użyć, możesz odwołać się do niego jawnie przez prefiks $:

Albo polegać na implicit invocation na podstawie opisu:

Oba podejścia działają.
Podsumowanie
Przesunięcie w stronę agentic AI następowało etapami: tool calling zmienił modele z "pisarzy" w systemy, które potrafią działać; RAG pomógł im trzymać się rzeczywistości; MCP ułatwił podłączanie agentów do narzędzi. Każdy krok usuwał część tarcia, ale też odsłaniał nowe ograniczenia - szczególnie wokół rozmiaru kontekstu, kosztu tokenów i braku czystego miejsca na powtarzalne procedury.
Właśnie tu pasują skills. Pakują "jak robimy rzeczy" w lekkie, ładowane na żądanie playbooki, które można współdzielić i wersjonować, bez upychania każdego workflow w promptach i bez wymuszania, żeby wszystko trafiało do RAG-u. W praktyce skills stają się sposobem na to, żeby agenci AI byli bardziej powtarzalni, bardziej efektywni tokenowo i łatwiejsi do skalowania, gdy ekosystem będzie się dalej zmieniał.
