Prompty to sugestie. Hooki to twarde blokady.

Przez miesiące traktowałem Claude Code jak junior developera, który po prostu potrzebuje jasnych zasad. Moje instrukcje projektowe były jednoznaczne: nigdy nie używaj force-push, nigdy nie usuwaj gałęzi, nigdy nie uruchamiaj niszczycielskich komend. Większość wieczorów tak właśnie wyglądała. Agent pisał testy, refaktoryzował funkcje i nie ingerował w historię git. Aż pewnego razu jeden rebase poszedł nie tak.

Okno kontekstowe wypełniło się błędami git. Znaczniki konfliktów, komunikaty o detached HEAD i ostrzeżenia o rozbieżności gałęzi piętrzyły się, token po tokenie. Pod tym szumem ukryta była moja uprzejma instrukcja, by unikać force-pushingu. Dla modelu najbardziej aktualnym i istotnym tekstem w wątku był strumień błędów. Statystyczna uwaga przeważyła nad polityką działania. Agent wykonał komendę, która wymazała dwie godziny niezatwierdzonych lokalnych zmian. To nie była złośliwość; agent był po prostu rozproszony. To rozróżnienie ma znaczenie. LLM nie łamie zasad ze złośliwości. Łamie je, ponieważ silniejszy wzorzec w oknie kontekstowym tymczasowo nadpisuje wcześniejszą instrukcję.

To zdarzenie zmieniło moje podejście do bezpieczeństwa agentów. Bariera (guardrail), która działa w dziewięćdziesięciu dziewięciu procentach przypadków, jest obciążeniem. Jeśli tryb awarii kosztuje Cię czas, pieniądze lub dane produkcyjne, nie możesz zostawić go w samym prompcie. Potrzebujesz egzekwowania zasad poza pętlą rozumowania modelu.

Hooki w Claude Code rozwiązują właśnie ten problem. Są to małe skrypty, które przechwytują wywołania narzędzi w trzech konkretnych momentach: przed wykonaniem narzędzia (PreToolUse), po zakończeniu działania narzędzia (PostToolUse) oraz gdy agent decyduje, że skończył pracę (Stop). Ponieważ działają jako kod zewnętrzny, nie zależą od pamięci, nastroju czy presji kontekstu modelu. Model może zapomnieć każdą instrukcję, jaką mu kiedykolwiek podałeś; hook i tak powie „nie”.

Oto mechanizm, który zbudowałem po tamtym straconym wieczorze.

Hook ochronny: Przechwyć, zanim dojdzie do szkód

Mój hook PreToolUse sprawdza każdą komendę Bash, zanim zostanie ona przetworzona przez powłokę. Prowadzę ścisłą czarną listę (denylist) niszczycielskich wzorców. Jeśli ciąg znaków komendy pasuje do czegoś niebezpiecznego, hook przerywa wykonanie i zwraca błąd bezpośrednio do agenta.

Blokowane przeze mnie wzorce są proste i jednoznaczne:

  • git push --force lub jakakolwiek wariacja force-with-lease, której jeszcze nie ufam
  • git reset --hard
  • rm -rf

To nie jest zaawansowane badanie bezpieczeństwa. To pas bezpieczeństwa. Ale kluczowym szczegółem jest to, co dzieje się po zablokowaniu.

Nigdy nie zwracam suchego „Zablokowano”. Stanowcza odmowa dezorientuje agenta i może uwięzić go w pętli, w której próbuje różnych wariacji tej samej niszczycielskiej komendy. Zamiast tego komunikat o błędzie zawiera drogę wyjścia. Gdy hook wykryje hard reset, mówi agentowi: „Ta komenda została zablokowana, aby chronić niezatwierdzone zmiany. Najpierw utrwal postęp (commit), a następnie spróbuj ponownie”. To jedno dodatkowe zdanie całkowicie zmienia zachowanie agenta. Zamiast próbować ratować sytuację po szkodzie, zaczyna dbać o bezpieczeństwo. Hook to nie tylko ściana; to kontrola ruchu.

Wybrałem również czarną listę (denylist) zamiast białej listy (allowlist) dla komend powłoki. Początkowo rozważałem dopuszczenie tylko ściśle określonego zestawu bezpiecznych podkomend git. To szybko okazało się porażką. Agenci są kreatywnie dosłowni. Uruchamiają poprawne, ale nieoczekiwane komendy, takie jak git stash push -m "wip" lub git branch --show-current, aby sprawdzić stan. Biała lista przerywa normalny przepływ pracy w momencie, gdy model wymyśli poprawną, ale niewymienioną komendę. Krótka, starannie dobrana czarna lista prawdziwie niszczycielskich wzorców daje agentowi swobodę działania, jednocześnie chroniąc granice.

Hook formatujący: Automatyzacja żmudnej pracy

Kiedyś marnowałem tokeny w prompcie, mówiąc agentowi, aby „zawsze uruchamiał formatter po edycji pliku”. Zapominał o tym przez połowę czasu. W drugiej połowie czasu pauzował i pytał, czy sformatować plik, marnując wywołanie narzędzia na decyzję, która miała tylko jedną właściwą odpowiedź.

Teraz zajmuję się tym za pomocą hooka PostToolUse. Po edycji pliku przez agenta, hook sprawdza jego rozszerzenie. Jeśli to Python, uruchamia Ruff. Jeśli to JavaScript lub TypeScript, uruchamia Prettier. Jeśli to Go, uruchamia gofmt. Agent nie wie nawet o istnieniu formattera. Nie musi tego wiedzieć.

Przeniesienie tego poza prompt przyniosło dwa efekty. Po pierwsze, kod jest konsekwentnie czysty, bez obciążania modelu poznawczo. Po drugie, instrukcje projektu stały się krótsze. Każde „zawsze” i „nigdy”, które usuniesz z promptu, to token, który model może przeznaczyć na rzeczywiste rozwiązywanie problemów. Hook odpowiada za niezmienniki; prompt za intencję.

Brama jakości: Redefinicja pojęcia „Gotowe”

Hook Stop uruchamia się, gdy agent uzna, że zakończył zadanie i spróbuje zakończyć sesję. Nie pozwalam na to. Zamiast tego, hook uruchamia pełny zestaw testów. Jeśli jakikolwiek test zakończy się niepowodzeniem, hook blokuje polecenie zatrzymania i zwraca agentowi wynik błędu.

To zmienia definicję ukończenia zadania. „Gotowe” nie jest już odczuciem modelu. To mierzalna brama. Agent może zakończyć pracę dopiero wtedy, gdy harness potwierdzi, że kod działa. W praktyce tworzy to szczelną pętlę zwrotną. Agent pisze kod, uważa, że skończył, naciska przycisk stop i natychmiast widzi traceback z pytest. Następnie dokonuje autokorekty, naprawia błąd importu lub błędną asercję i ponownie próbuje się zatrzymać. Obserwowałem, jak agenci iterują w tej pętli trzy lub cztery razy bez ingerencji człowieka. Harness wymusza jakość; model dostarcza poprawki.

Czego to uczy w kontekście inżynierii agentów

Budowanie niezawodnych systemów autonomicznych wymaga zmiany podejścia. Przechodzisz od pisania coraz dłuższych promptów do budowania coraz szczelniejszych harnessów.

Używaj hooków do egzekwowania zasad, a promptów do definiowania polityki. Jeśli reguła musi być przestrzegana w stu procentach, powinna znaleźć się w kodzie, a nie w języku naturalnym. Prompty świetnie radzą sobie z niejednoznacznością, gustem i architekturą. Są jednak fatalne w utrzymywaniu niezmienników. Jeśli błąd kosztowałby Cię popołudnie na naprawę lub, co gorsza, czas dostępności produkcji, napisz hooka.

Krótsze prompty dają lepsze rezultaty. Gdy przenosisz mechaniczne reguły do skryptów, model ma mniej do zapamiętania i mniej okazji do sprzeczności. Okno kontekstowe agenta jest zasobem deficytowym. Nie wypełniaj go przypomnieniami o formatowaniu.

W końcu zaakceptuj fakt, że Twoja rola się zmienia. W miarę jak agenci zyskują autonomię, praca człowieka przesuwa się z generowania treści na projektowanie barier ochronnych (guardrails). Budujesz harness, który decyduje, czego model może dotknąć, kiedy może skończyć i jak musi się zachować, gdy coś pójdzie nie tak. To jest inżynieria, a nie prompting.

Źródło, które zainspirowało to podejście, oraz dodatkowe szczegóły implementacji można znaleźć tutaj.

Jeśli budujesz rozwiązania oparte na agentach AI i chcesz wymienić się doświadczeniami z innymi praktykami, społeczność edukacyjną GyaanSetu znajdziesz tutaj.