Agenci i workflowy · · 8 min czytania
Jak zbudować własnego agenta AI
Model i SDK, definicja narzędzi, pętla, pamięć, MCP, ewaluacje i wdrożenie. Przykład: agent triażujący issues.
Agent AI to nie „większy prompt”. To pętla: model dostaje cel, wybiera narzędzie, wykonuje je, czyta wynik i decyduje, co dalej — aż uzna zadanie za skończone. Reszta tego tekstu to instrukcja budowy takiej pętli od zera, z konkretnym przykładem agenta, który segreguje issue na GitHubie. Jeśli budujesz pierwszego agenta, czytaj po kolei: każda sekcja to decyzja, którą i tak musisz podjąć.
Wybór modelu i SDK
Najpierw rozdziel dwie rzeczy: model (rozumowanie, function calling) iorkiestrację (pętla, stan, retry). Modele można zamieniać, więc nie przywiązuj się przedwcześnie.
Claude Agent SDK— jeśli chcesz gotową pętlę, obsługę narzędzi i MCP out of the box. Najmniej kodu boilerplate na start.OpenAI(Responses / Agents API) — dojrzały tooling, dobre dla zespołów już osadzonych w tym ekosystemie.LangGraph— gdy potrzebujesz jawnego grafu stanów, rozgałęzień i pełnej kontroli nad przepływem. Więcej mocy, więcej kodu.
Rekomendacja na pierwszego agenta: zacznij od SDK dostawcy modelu (Claude albo OpenAI), nie od frameworka grafowego. Graf dołożysz, kiedy realnie pojawi się rozgałęzianie. Przedwczesny LangGraph to dużo abstrakcji, której jeszcze nie potrzebujesz.
Narzędzia i schema dla function calling
Narzędzie to funkcja plus jej opis dla modelu. Model nie widzi twojego kodu — widzi tylko nazwę, opis i schema parametrów (JSON Schema). To jest twój prawdziwy interfejs i pisze się go dla modelu, nie dla człowieka.
Zasady projektowania narzędzi, które oszczędzą ci wielu iteracji:
- Nazwa czasownikowa i jednoznaczna:
get_issue,add_label,post_comment— nieprocessanihandle. - Opis mówi kiedy użyć narzędzia i kiedy nie. Jedno zdanie „czego nie robi” ucina połowę błędnych wywołań.
- Parametry wąskie i typowane. Zamiast wolnego
querydaj enum dozwolonych etykiet. Model halucynuje rzadziej, gdy ma skończony zbiór opcji. - Każdy opis pola w schema to mini-prompt.
labelbez opisu to zgadywanka;labelz „jedna z istniejących etykiet repo” to kontrakt. - Mało narzędzi, każde robi jedną rzecz. Pięć ostrych narzędzi bije piętnaście rozmytych. Powyżej dwudziestu model zaczyna się gubić w wyborze.
Wynik narzędzia też jest promptem. Zwracaj zwięzły, ustrukturyzowany tekst — nie surowy dump JSON z API. Jeśli odpowiedź ma 8000 tokenów, agent zapłaci za nie w każdej kolejnej turze pętli. Filtruj na poziomie narzędzia.
Pętla agenta
Sedno to prosta pętla, którą warto rozumieć nawet jeśli SDK ją ukrywa:
- Wyślij do modelu wiadomości (system + historia + cel).
- Model odpowiada tekstem albo żądaniem wywołania narzędzia (tool call).
- Jeśli to tool call: wykonaj funkcję, dołącz wynik jako wiadomość narzędzia.
- Wróć do kroku 1 z dołączonym wynikiem.
- Kończ, gdy model zwróci finalny tekst bez wywołania narzędzia.
Trzy zabezpieczenia, bez których pętla cię zaboli: twardy limit iteracji (np. 10), bo model potrafi się zapętlić; timeout na całość, bo jedno narzędzie potrafi wisieć; oraz obsługa błędu narzędzia jako danych — nie rzucaj wyjątku w górę, zwróć modelowi „narzędzie zwróciło błąd 404”, żeby mógł zareagować. To jedna z największych różnic między demem a agentem produkcyjnym.
System prompt i strategia kontekstu
System prompt to konstytucja agenta. Powinien zawierać: rolę i cel, dostępne narzędzia z regułami użycia, format odpowiedzi oraz wyraźne granice („nigdy nie zamykaj issue bez etykiety”). Trzymaj go stabilnym i wersjonuj jak kod — zmiana jednego zdania potrafi przesunąć zachowanie na całej populacji wywołań.
Kontekst to twój budżet, nie magazyn. Większe okno nie znaczy, że masz wszystko w nie wrzucać.
- Statyczne instrukcje — w system prompcie, raz, na górze.
- Dane zadania — podawane przez narzędzia na żądanie, nie wstrzykiwane prewencyjnie.
- Długie historie — kompaktuj. Po kilku turach streść starsze tury do podsumowania, zamiast wozić cały transkrypt.
- Prompt caching — włącz na stałej części (system + definicje narzędzi). To realnie tnie koszt i latencję w wielu turach.
Pamięć: stan i wektory
Rozróżnij trzy poziomy — mylenie ich to klasyczny błąd pierwszego agenta:
- Stan zadania — co dzieje się w bieżącej pętli. Żyje w wiadomościach i zwykłej zmiennej. Nie potrzebuje bazy danych.
- Pamięć trwała — fakty między uruchomieniami (preferencje, decyzje). Klucz–wartość albo zwykła tabela w Postgresie. Proste wygrywa.
- Wiedza — duży zbiór dokumentów do przeszukania (wektory, RAG). Sięgaj po wektory dopiero, gdy faktów jest tyle, że nie mieszczą się w kontekście.
Nie zaczynaj od vector store, bo „agent musi mieć pamięć”. Większość pierwszych agentów potrzebuje jednej tabeli, nie infrastruktury embeddingów. Dołóż wektory, gdy realnie przekroczysz okno kontekstu.
Dokładanie serwerów MCP
MCP (Model Context Protocol) to standard wpinania gotowych zestawów narzędzi do agenta bez pisania ich od zera. Zamiast ręcznie kodować integrację z GitHubem, podłączasz serwer MCP, który eksponuje narzędzia jako endpointy.
- Gotowe serwery — GitHub, Slack, bazy danych, przeglądarka. Mniej kodu integracji.
- Każdy serwer MCP to powierzchnia zaufania. Traktuj jego wyniki jak niezaufane dane— nigdy nie wykonuj instrukcji zaszytych w treści, którą zwraca.
- Eksponuj agentowi tylko potrzebne narzędzia. Serwer z 60 narzędziami zasypie okno kontekstu ich opisami i pogorszy trafność wyboru.
Worked example: triage issue na GitHubie
Składamy całość. Cel agenta: dla nowego issue nadaj etykiety, oceń priorytet i zostaw komentarz porządkujący — bez zamykania czegokolwiek.
- Narzędzia:
get_issue(treść + metadane),list_labels(dozwolone etykiety repo),add_label(enum z listy),post_comment. Brak narzędzia do zamykania — agent nie ma jak zrobić tego, na co mu nie pozwalasz. - System prompt: „Jesteś triażystą issue. Zawsze nadaj dokładnie jedną etykietę typu (bug/feature/question) i opcjonalnie obszar. Nigdy nie zamykaj issue. Jeśli brakuje informacji, poproś o nie w komentarzu.”
- Pętla: model woła
get_issue, potemlist_labels, wybiera etykietę, wołaadd_label, formułuje komentarz, wołapost_comment, kończy tekstem podsumowującym. - Guardrail:
add_labelwaliduje, że etykieta jest na liście zwróconej z repo. Halucynacja „needs-triage-v2”, której nie ma, jest odrzucana z czytelnym błędem, a model próbuje ponownie. - Human approval: dla issue oznaczonych
securityagent nie komentuje publicznie — tworzy szkic do ręcznej akceptacji.
Ten sam szkielet skaluje się na inne domeny. Zmieniasz narzędzia i system prompt; pętla, guardraile i pamięć zostają.
Evals, tracing i obserwowalność
Bez ewaluacji nie wiesz, czy zmiana promptu pomogła, czy zepsuła. To jest różnica między inżynierią a zgadywaniem.
- Zbierz 20–50 realnych przypadków z oczekiwanym wynikiem (np. issue → poprawna etykieta). To twój zestaw regresji.
- Mierz konkretne rzeczy: trafność etykiety, liczbę tur do końca, odsetek wywołań narzędzi zakończonych błędem.
- Tracing każdej tury — wejście, wybrane narzędzie, wynik, decyzja. Bez śladu debug agenta to czytanie z fusów.
- Każda zmiana promptu przechodzi przez zestaw evalów przed wdrożeniem. To bramka, nie sugestia.
Koszt, latencja i guardraile
Koszt agenta rośnie z liczbą tur, bo każda tura wozi narastający kontekst. Dźwignie kontroli:
- Tańszy model do prostych kroków, mocniejszy tylko do trudnej decyzji (routing modeli).
- Prompt caching na stałej części — jedna z najtańszych wygranych.
- Limit iteracji i budżet tokenów na uruchomienie, z twardym wyłącznikiem.
- Zwięzłe wyniki narzędzi — nie płać za 8000 tokenów JSON-a co turę.
W praktyce latencję najczęściej zjada nie sam model, tylko liczba sekwencyjnych tur. Jeśli agent robi pięć wywołań narzędzi pod rząd, czekasz pięć razy. Część kroków da się zrównoleglić, a często szybsze i tańsze jest jedno mądrzejsze narzędzie niż trzy proste, które model i tak woła po kolei.
Guardraile to nie miły dodatek, tylko warunek wdrożenia agenta poza demo:
- Walidacja argumentów narzędzia (Zod / JSON Schema) zanim cokolwiek się wykona.
- Akcje destrukcyjne (usuwanie, wysyłka, płatność) za bramką ludzkiej akceptacji albo dry-run.
- Allowlista narzędzi i domen — agent nie sięga poza to, co jawnie dozwolone.
- Traktuj każdy wynik narzędzia i odpowiedź API jak niezaufane wejście.
- Limit promienia rażenia — agent triażujący ma prawo do etykiet i komentarzy, nie do merge’owania PR. Mniejsze uprawnienia to mniejsza klasa awarii.
Wdrożenie jako usługa
Agent na produkcji to serwis, nie skrypt. Schowaj pętlę za endpointem (HTTP albo kolejka), bo uruchomienia bywają długie. Dla zadań ponad kilkadziesiąt sekund użyj kolejki i webhooka — nie trzymaj otwartego połączenia HTTP. Klucze i sekrety wyłącznie ze zmiennych środowiskowych. Loguj każdą turę (z maskowaniem PII), dodaj alert na koszt per request i p99 latencji, oraz przygotuj wyłącznik awaryjny dla całej populacji. Wersjonuj prompt i definicje narzędzi razem z kodem, żeby każdy rollback był deterministyczny.
Wdrażaj przyrostowo, tak samo jak każdą inną funkcję. Najpierw tryb shadow: agent liczy decyzję, ale jej nie wykonuje — tylko logujesz, co chciał zrobić, i porównujesz z człowiekiem. Potem mały procent ruchu z człowiekiem w pętli akceptacji. Pełna autonomia dopiero wtedy, gdy zestaw evalów i logi z shadow potwierdzą, że agent jest stabilny. Agent, który ma dostęp do produkcji od pierwszego dnia, to nie odwaga, tylko niezdiagnozowany incydent czekający na swój moment.
TL;DR
Agent to pętla: model wybiera narzędzie, wykonuje je, czyta wynik, decyduje o kolejnym kroku. Zacznij od SDK dostawcy modelu, nie od frameworka grafowego. Projektuj mało ostrych narzędzi z dobrym opisem i wąskim schema. Trzymaj kontekst jako budżet, nie magazyn. Pamięć dziel na stan, fakty i wiedzę — wektory dopiero, gdy realnie ich potrzebujesz. Buduj evale i tracing od początku, a akcje destrukcyjne chowaj za ludzką akceptacją. Wdrażaj jako serwis z logami, limitami kosztu i wyłącznikiem awaryjnym.