Anti-Slop Guardrails: jak oduczyłem agenta AI oszukiwania w kodzie TypeScript
Kiedy dajesz modelom sztucznej inteligencji wolną rękę przy pisaniu kodu frontendowego, szybko pojawia się zjawisko określane jako AI Slop - kod, który na pierwszy rzut oka wygląda profesjonalnie, ale w środku jest pełen pułapek i kruchych założeń.
Problem: Kiedy sztuczna inteligencja udaje, że kod działa
Najczęstszym grzechem modeli LLM w języku TypeScript jest tzw. Compiler Bluffing (oszukiwanie kompilatora). Można to porównać do mechanika samochodowego, który zamiast sprawdzić, dlaczego stuka w silniku, po prostu zakleja czerwoną kontrolkę na desce rozdzielczej czarną taśmą izolacyjną.
W kodzie objawia się to następująco:
- Podwójne rzutowania (
as unknown as T): Gdy model gubi się w typach, na siłę wmawia TypeScriptowi: „Uwierz mi na słowo, to na pewno jest profil użytkownika”. - Worek na śmieci (
anyiRecord<string, any>): Wyłączenie sprawdzania typów dla wygody, co niszczy całe bezpieczeństwo języka. - Brak weryfikacji danych z sieci: Model zakłada, że serwer ZAWSZE zwróci idealne dane dokładnie w takim formacie, jakiego oczekuje.
Skutek w praktyce? Aplikacja kompiluje się bez ani jednego ostrzeżenia, ale gdy żywy użytkownik wejdzie na stronę i serwer zwróci pusty wynik lub błąd, przeglądarka wyświetla biały ekran (TypeError: Cannot read properties of undefined).
Co zrobiłem: Twarde barierki na autostradzie (Guardrails)
Zamiast prosić model w promptach o „staranne pisanie kodu”, w projekcie Agent Operations Center (AOC) wdrożyłem zestaw twardych reguł inżynierskich (.agents/rules/anti-slop.md), których żaden agent kodujący nie ma prawa złamać.
1. Bezwzględny zakaz rzutowań na skróty
Agent ma zakaz stosowania konstrukcji as unknown as T. Jeśli w newralgicznym miejscu musi użyć asercji typu, jest zobowiązany dodać komentarz // SAFETY: z logicznym uzasadnieniem. Jeśli uzasadnienia brak - kod jest automatycznie odrzucany w fazie review.
2. Strażnik graniczny na wejściu (Biblioteka Zod)
Dane przychodzące z sieci nie mają prawa trafić do wnętrza aplikacji na słowo honoru. Agent musi przepuścić surowy JSON przez schemat walidacyjny w bibliotece Zod, który weryfikuje każdy parametr w czasie rzeczywistym.
// ❌ Przed: Typowy kod pisany przez AI (pozorne bezpieczeństwo)
const fetchProfile = async (id: string) => {
const res = await fetch(`/api/users/${id}`);
// Oszukanie kompilatora - zero gwarancji, co naprawdę zwrócił serwer:
const data = (await res.json()) as unknown as UserProfile;
return { ...(data.isAdmin ? { role: 'admin' } : {}) };
};
// ✅ Po: Standard Anti-Slop wymuszony na agencie
const fetchProfile = async (id: string): Promise<UserRoleProps> => {
const res = await fetch(`/api/users/${id}`);
const raw = await res.json();
// Bezpieczna bramka: Zod weryfikuje dane w runtime i rzuca czytelny błąd, jeśli format jest zły
const data = UserProfileSchema.parse(raw);
return {
role: data.isAdmin ? 'admin' : undefined
};
};
3. Precyzyjne stany zamiast niejednoznaczności
Zamiast wrzucać do komponentu 5 osobnych flag typu isLoading, isError, isSuccess (co prowadzi do sytuacji, gdzie isLoading i isError są jednocześnie prawdziwe), agent musi stosować jawne stany rozłączne (tzw. unie dyskryminowane): stan to albo loading, albo error, albo ready.
Dlaczego to ważne (Use Case)
Dlaczego warto narzucać tak surowy rygor na modele AI?
- Stabilność na produkcji: Gdy agent buduje nowy moduł w panelu AOC, mamy 100% pewności, że niespodziewana zmiana w API czy chwilowa awaria bazy nie wysypie całego interfejsu.
- Koniec z długiem technologicznym: Kod produkowany przez bota nie wymaga wielogodzinnego poprawiania przez programistę. Spełnia standardy kodu pisanego przez Senior Developera od pierwszej minuty.
- Szybkie wykrywanie błędów: Jeśli backend zmieni strukturę danych, błąd wychodzi natychmiast na poziomie schematu Zod z precyzyjną informacją: „Brakujące pole: email w obiekcie użytkownika”, zamiast losowego błędu gdzieś głęboko w widoku.
Czego się nauczyłem
- AI potrzebuje ograniczeń, a nie wolności: Rola inżyniera pracującego z agentami to nie bycie pisarzem promptów, lecz architektem reguł i ograniczeń (Constraint Architect).
- Kompilator to nie wróg, którego trzeba oszukać: Jeśli kompilator zgłasza błąd typów, to znak, że w logice biznesowej brakuje obsługi jakiegoś przypadku brzegowego.
- Zasada ograniczonego zaufania (Zero Trust) do danych z sieci: Niezależnie od tego, czy kod pisze człowiek, czy maszyna, walidacja na granicach systemu to fundament stabilnego oprogramowania.