Naprawa błędów w agentach AI warstwa po warstwie: prompt, kontekst, harness czy pętla
Warstwowy model awarii agentów AI: w jaki sposób różnią się prompt, kontekst, harness i inżynieria pętli oraz rutyna oparta na śledzeniu do ustalenia, która warstwa uległa awarii.
Gdy agent AI zachowuje się niewłaściwie w środowisku produkcyjnym, zespoły mają tendencję do kłótni o słownictwo zamiast o dowody: jeden inżynier chce lepszego promptu, inny obwinia kontekst, a trzeci wskazuje na mechanizmy obsługi agenta. Inżynieria promptów, kontekstu, mechanizmów obsługi oraz pętli nie są konkurencyjnymi szkołami myślenia; stanowią one cztery warstwy ułożone jedna na drugiej, przy czym każda z nich zawodzi w swoisty, rozpoznawalny sposób. Ten przewodnik definiuje każdą warstwę na przykładzie małego agenta programistycznego, a następnie przedstawia procedurę opartą na analizie śladów działania, aby ustalić, która warstwa faktycznie zawiodła, zanim zmieni się jakikolwiek kod.
Wersja skrócona
- Inżynieria promptów kształtuje instrukcje przekazywane modelowi.
- Inżynieria kontekstu decyduje o tym, co model może zobaczyć przed udzieleniem odpowiedzi.
- Inżynieria mechanizmów obsługi tworzy środowisko, w którym działa model: narzędzia, pamięć, pliki, uprawnienia oraz mechanizmy odzyskiwania.
Najdroższe incydenty związane z agentami wynikają z naprawiania niewłaściwego warstwy w tym układzie.
Gdy demo działa, a produkcja nie
To schemat dobrze nam znany. Agent wygląda bez zarzutu w demonstracji. Kilka dni po uruchomieniu zaczyna się pętlić, zużywać tokeny, traci informacje o czymś, co zostało mu powiedziane godzinę wcześniej, lub zawodzi przy pierwszym przypadku, gdy narzędzie zwraca nieoczekiwany payload.
Zespół dzieli się na frakcje. Ktoś proponuje przepisanie instrukcji, ktoś inny uważa to za problem z kontekstem, a jeszcze inna osoba podejrzewa sam mechanizm obsługi. Bez wspólnego sposobu na zlokalizowanie błędu naprawa trwająca dwa dni zamienia się w przepisywanie trwające dwa tygodnie, ponieważ każda poprawka wprowadzana jest do warstwy, która wcześniej funkcjonowała poprawnie.
To jest praktyczny powód, dla którego należy trzymać te cztery pojęcia oddzielnie. Każde z nich odnosi się do innego miejsca, w którym mogą wystąpić problemy, a gdy potrafimy je rozróżnić, debugowanie staje się procesem zamiast grą w zgadywanki.
PACT: mnemoniczne skrót dla czterech warstw
Kompaktowy sposób na zapamiętanie tych warstw podczas incydentu to PACT: Prompt, Awareness, Control, Trajectory. Każde słowo odpowiada jednemu pytaniu:
- Prompt: czy zadanie zostało jasno określone?
- Awareness: czy model otrzymał informacje niezbędne do wykonania tego kroku?
- Control: czy środowisko wykonawcze może bezpiecznie i niezawodnie wykonać to, o co prosi model?
- Trajectory: czy powtarzany proces zmierza w kierunku zakończenia, które można zweryfikować?
Celem nie jest dodanie kolejnej warstwy żargonu. Chodzi o to, aby granice między typami awarii były na tyle łatwe do zapamiętania, że faktycznie można by z nich skorzystać, gdy coś się pali.
Jak pojawiły się te warstwy i dlaczego ważny jest ich porządek
Pojęcia te pojawiały się mniej więcej w kolejności, w miarę jak narzędzia zyskiwały nowe możliwości:
- Inżynieria promptów (mniej więcej od 2022 do 2024 roku) była pierwszą umiejętnością: sformułowywanie zapytań, przykłady, ograniczenia oraz wzorce typu few-shot skierowane na jedną próbę wywołania modelu.
Te daty opisują momenty, w których terminy te zaczęły być używane, a nie formalne kamienie milowe; pojęcia te są nadal w fazie kształtowania się w momencie pisania tego tekstu. Ważne jest to, że nic nie zostało zastąpione – każda warstwa została dodana na wierzch poprzedniej, ponieważ każdy wzrost autonomii wymagał własnego mechanizmu sterowania.
Inżynieria promptów: lokalna umowa
Inżynieria promptów to sztuka sformułowywania, strukturyzowania i ilustrowania instrukcji w taki sposób, aby odpowiedź była bardziej przewidywalna. Bez niej mamy do czynienia z niejasnymi poleceniami, niestabilnymi formatami wyjściowymi oraz modelem zmuszonym do zgadywania, co miałeśmy na myśli.
W agencie do programowania prompt do przeglądu kodu może wyglądać w ten sposób:
SYSTEM: You are a code reviewer.
Given a diff, output ONLY valid JSON:
{"issues": [{"line": int,
"severity": "low|medium|high", "note": str}]}
No prose. No markdown fences.
If there are no issues, return {"issues": []}.
Ten prompt ustala lokalną umowę. Przypisuje rolę, opisuje transformację (różnice wejściowe, lista problemów wyjściowych), określa dokładny kształt wyniku, w tym nazwy pól i dozwolone wartości stopnia poważności, a nawet definiuje przypadek pustego wyniku, dzięki czemu późniejsze parsowanie nie musi radzić sobie z tekstem opisowym lub brakującymi kluczami.
Powszechnie twierdzi się, że inżynieria promptów jest przestarzała. To nieprawda; stanowi ona najgłębszą warstwę. Każdy proces przetwarzania kontekstu ostatecznie przekazuje modelowi instrukcję, a słaba instrukcja w doskonałym „opakowaniu” nadal daje słabe wyniki, tylko teraz w otoczeniu imponującej infrastruktury.
Inżynieria kontekstu: selekcja w ograniczonym budżecie
Inżynieria kontekstu polega na wyborze, dla każdego zapytania, jakich dokumentów, historii, definicji narzędzi i informacji należy uwzględnić, a równie starannie decyduje się, które z nich pominiąć. Gdy jest zaniedbywana, model odpowiada na podstawie przestarzałych informacji, jest rozpraszany przez nieistotne fragmenty tekstu lub traci koncentrację, ponieważ okno z informacjami jest przepełnione materiałem dodanym na wszelki wypadek.
Budowniczy kontekstu dla agenta programistycznego może wyszukać odpowiednie kandydaty, przeliczyć ich rangę i skompresować historię:
def build_context(query, full_history, kb):
relevant = retrieve(query, kb, top_k=8)
reranked = rerank(relevant, query)[:3]
summary = summarize_if_long(
full_history, max_tokens=800
)
return {
"docs": reranked,
"history": summary,
"query": query,
}
Należy to postrzegać jak lejek. Proces pozyskiwania danych rozszerza zakres o osiem kandydatów, a ponowna klasyfikacja pozostawia trzy najbardziej istotne. Historia rozmów jest podsumowywana do około 800 tokenów tylko wtedy, gdy staje się zbyt długa. Funkcja zwraca mały, ustrukturyzowany pakiet danych, a nie surowy materiał. Kluczowymi umiejętnościami są selekcja, ranking i kompresja, a nie ilość danych.
Dlatego najczęstsze nieporozumienie, jakoby inżynieria kontekstu oznaczała dostarczanie modelowi większej ilości informacji, jest zazwyczaj błędne. Większy kontekst często zawiera więcej szumów: przestarzałe instrukcje, powtarzane fakty i sprzeczne dane. Duża część pracy polega na decydowaniu, co pominąć.
Inżynieria kontekstu jest szersza niż RAG
Generowanie wzbogacone pozyskiwaniem danych to jedna z technik stosowanych w inżynierii kontekstu, obejmująca etap pozyskiwania informacji. Proces RAG może pobrać osiem fragmentów danych i przeklasfikować je do trzech, jak opisano powyżej. Inżynieria kontekstu obejmuje również kompresję historii, formatowanie definicji narzędzi, utrzymywanie aktualnego stanu kluczowych zadań, przekazywanie wyników weryfikacji z powrotem do modelu oraz decydowanie o tym, co należy pominąć.
Agent kodujący bez żadnego magazynu wektorowego nadal ma poważny problem z kontekstem do rozwiązania. Stan pliku Git, otwarte pliki, błędy kompilatora, wyniki testów, obecny plan oraz zapis poprzednich działań muszą dotrzeć do modelu w formie użytecznej.
Inżynieria wykorzystania zasobów: granica między intencją a efektem
Pasy jest niewiążącą się od modelu częścią systemu: narzędzia i dostęp do plików, pamięć trwała, zasady uprawnień, środowiska testowe, śledzenie działań oraz wszystko to, co dzieje się w przypadku błędu. Bez dobrego rozwiązania otrzymujemy agenta, który dobrze rozumuje, ale nie może na tej wiedzy działać, albo takiego, który działa, ale nie ma niezawodnego sposobu na odzyskanie stanu w przypadku błędu przy wywoływaniu narzędzia.
Przykład obudowy wywołania narzędzia ilustruje tę koncepcję:
def call_tool(tool_name, args, retries=2):
for attempt in range(retries + 1):
try:
result = TOOLS[tool_name](**args)
log_trace(tool_name, args, result, status="ok")
return result
except ToolError as e:
log_trace(tool_name, args, str(e), status="failed")
if attempt == retries:
return {"error": str(e), "recoverable": False}
args = repair_args(args, e)
Obudowa nie podejmuje żadnych prób rozwiązania zadania. Określa granicę pomiędzy tym, o co prosi model, a tym, co robi rzeczywisty system. Każda próba jest śledzona wraz ze swoimi argumentami, wynikiem i stanem. Błąd powoduje ograniczoną liczbę prób ponownych wywołań z poprawionymi argumentami, a po wyczerpaniu tych prób zwraca ustrukturyzowany błąd oznaczony jako nieodwracalny, dzięki czemu wywołujący otrzymuje dane, na których może się oprzeć w swoich rozważaniach, zamiast wyjątku, który przerywa działanie programu.
Jest kuszące utożsamianie harnessa z dowolną ramą agenta, którą zainstalowałeś. Rama dostarcza podstawy; harness to zbiór konkretnych decyzji, które podejmujesz na jej podstawie: co jest rejestrowane, co robi nieudana próba połączenia, jaki stan przetrwa awarię, jakie polecenia są dozwolone oraz w jaki sposób wyniki wykonywania są przekazywane z powrotem do modelu.
Inżynieria pętli: umowa zakończenia
Inżynieria pętli określa zadania każdej iteracji, sposób, w jaki agent ocenia, czy robi postępy, oraz – co najważniejsze – warunki zatrzymania. Jej typowym błędem jest nieskończona pętla: agent, który ciągle wywołuje narzędzia i zużywa tokeny, ponieważ nic mu nie mówi, że skończył lub utknął.
Minimalny kontroler wygląda tak:
def run_loop(task, harness, max_iters=15, stall_limit=3):
state = init_state(task)
stalls = 0
for i in range(max_iters):
action = plan_next_step(state)
result = harness.call_tool(action.tool, action.args)
state = update_state(state, result)
if is_goal_met(state):
return state, "success"
if made_no_progress(state):
stalls += 1
if stalls >= stall_limit:
return state, "stalled — escalate"
else:
stalls = 0
return state, "max iterations reached"
Tutaj zastosowano kilka ograniczeń. max_iters określa maksymalną liczbę iteracji, is_goal_met umożliwia wyjście w przypadku osiągnięcia celu, a licznik zatorów śledzi kolejne iteracje bez postępów, resetując się po wznowieniu pracy. Po stall_limit takich krokach pętla zwraca specjalny stan wskazujący na konieczność interwencji, zamiast kontynuować pracę w tle. Każda ścieżka wyjścia zwraca jasny powód, co ułatwia późniejszą klasyfikację wykonań.
Główną ideą jest kontrakt zakończenia: jasny cel, wskaźniki postępów, które można zmierzyć, ograniczona liczba prób, budżety na czas i tokeny, krok weryfikacji oraz określona polityka postępowania, gdy postęp się zatrzymuje. Aby zapoznać się z implementacjami tych samych koncepcji w TypeScript, sprawdź ograniczone pętle agentowe do wykorzystania w narzędziach LLM.
Pętle i mechanizmy inżynieryjne są często mylone. Mechanizm ten określa, do czego może mieć dostęp agent oraz jak radzić sobie z błędami na poziomie poszczególnych narzędzi. Pętla znajduje się na wyższym poziomie i decyduje o zachowaniu w poszczególnych krokach, w tym o momencie zakończenia całego procesu.
Cztery warstwy obok siebie
- Prompt (P): odpowiada za instrukcję. Typowe problemy: model błędnie interpretuje intencję lub zwraca dane w niewłaściwym formacie. Pierwsze miejsce do sprawdzenia: sam wynik.
- Context (A): odpowiada za stan gotowy do użycia przez model. Typowe problemy: model działa na podstawie przestarzałych, brakujących lub rozpraszających informacji. Pierwsze miejsce do sprawdzenia: zrzuty stanu kontekstu po każdej turze.
- Harness (C): odpowiada za wykonywanie zadań i przywracanie normalnego funkcjonowania. Typowe problemy: błędy podczas wywoływania narzędzi, próby ponownych działań bez analizy lub niemożność przywrócenia stanu. Pierwsze miejsce do sprawdzenia: nieudane operacje w śledzeniu działań narzędzi.
- Loop (T): odpowiada za postęp i zatrzymanie procesu. Typowe problemy: agent nigdy nie osiąga celu ani się nie zatrzymuje. Pierwsze miejsce do sprawdzenia: powtarzające się udane wywołania z niemal identycznymi argumentami.
Rozróżnienie błędu w komponencie harness od błędu w pętli
Największą oszczędnością czasu jest fakt, że dwa różne błędy wyglądają identycznie z zewnątrz. Agent uwięziony w pętli z powodu nieodpowiedniego kontrolera oraz agent uwięziony z powodu awarii obsługi narzędzi wykazują ten sam objaw: nadal działają, rachunek stale rośnie, a nikt nie wie dlaczego. Krótka lista kontrolna pomaga odróżnić je od innych problemów.
1. Sprawdź ślad wywołań narzędzi
Wywołania, które wszystkie kończą się powodzeniem z rozsądnymi wynikami, podczas gdy agent ciągle używa tego samego narzędzia z niemal niezmienionymi argumentami, wskazują na pętlę.
2. Szukaj powtarzających się awarii narzędzi
Jeśli jakieś narzędzie ciągle się psuje, a agent próbuje ponownie bez zmiany swojego podejścia, należy podejrzewać system połączeń.
3. Przyjrzyj się temu, co model mógł zobaczyć
Jeśli model wydaje się zapominać jakiegoś faktu w trakcie wykonywania zadania, sprawdź budowę kontekstu: czy dochodzi do jego skracania, zastępowania, kompresji lub jak stan jest przenoszony pomiędzy kolejnymi krokami.
4. Sprawdź wynik na końcu
Jeśli narzędzia funkcjonowały prawidłowo, kontekst był dokładny, a pętla zakończyła się bez problemów, ale odpowiedź nadal jest błędna, przyjrzyj się promptowi.
Zasada ogólna
Błędy w pętlach wynikają z decyzji o tym, co ma nastąpić dalej. Błędy związane z obsługą błędów pojawiają się, gdy coś idzie nie tak. Błędy kontekstowe dotyczą tego, co model może zobaczyć. Błędy promptu wynikają z tego, o co poprosiłeś.
Odpowiedz najpierw na pytanie, które z czterech się odnosi, zanim cokolwiek edytujesz.
Przykład z praktyki: recenzent pull-requesta, który nie chce przestać
Załóżmy, że zespół używa agenta do kodowania, który przegląda prośby o łączenie zmian. Testy przebiegają pomyślnie. W środowisku produkcyjnym niektóre przeglądy trwają ponad 40 minut na jedną prośbę o łączenie zmian i powodują nieoczekiwane rachunki.
Pierwsza teoria zakłada, że instrukcja jest zbyt niejasna i agent zbyt długo się nad tym zastanawia. Instrukcja zostaje przepisana, ale nic się nie zmienia. Druga teoria dotyczy kontekstu: być może agent na każdym kroku ponownie czyta całe repozytorium. Dane z śledzenia mówią coś innego – pobieranie danych odbywa się we właściwym zakresie, a łączone są tylko te pliki, które są istotne.
Następnie ktoś uważnie analizuje ślad wywołań narzędzi. Agent wielokrotnie uruchamia swoje narzędzie test-runner, a każde z tych wywołań kończy się bez błędów. Pakiet testowy rzeczywiście zawodzi z powodu niestabilnego testu integracyjnego, który nie ma związku z prośbą o połączenie zmian. Agent nadal próbuje naprawić ten błąd poprzez niepowiązane zmiany w kodzie i ponowne uruchamianie testów, ponieważ nic nie wskazuje mu, że wykonał już wystarczającą liczbę prób w ramach tego podcelu i powinien przestać oraz zgłosić problem na wyższy poziom.
To nie jest problem z promptem, nie jest to problem z kontekstem ani problem z narzędziem do zarządzania; narzędzia zachowywały się dokładnie tak, jak zostały zaprojektowane. To czysto błąd pętli. Krok po kroku analizując sytuację, można dojść do takiego wniosku.
Krok 1: potwierdzenie poprawnego działania narzędzi
Sukcesywne wykonania w śladzie wykluczają najprostsze błędy narzędzi do zarządzania, takie jak polecenie, które nigdy się nie uruchamia, lub adapter, który ciągle wywołuje błędy.
Krok 2: porównanie stanu pomiędzy iteracjami
Wynik testu znajduje się w kontekście agenta, a proces wyodrębniania informacji jest prawidłowo skonfigurowany, więc model nie pomija żadnych dowodów.
Krok 3: sprawdzenie zbieżności
Zmiany w repozytorium występują przy każdej iteracji, ale sygnał weryfikacyjny, który ma znaczenie, nigdy się nie poprawia. Brakuje detektora zatrzymania oraz ograniczenia liczby prób realizacji tego samego podcelu.
Krok 4: nauczenie kontrolera wykrywania stagnacji
Rozwiązaniem jest niewielka część logiki kontrolera, która porównuje „podpis postępu” pomiędzy poszczególnymi krokami:
if progress_signature == previous_signature:
stagnant_steps += 1
else:
stagnant_steps = 0
if stagnant_steps >= 3:
escalate("no measurable progress")
stop()
progress_signature może być czymkolwiek, co odzwierciedla istotny postęp, na przykład zbiorem nieudanych testów wraz z ich komunikatami o błędach. Jeśli nie ulega zmianie, zwiększa się licznik stagnacji; po trzech takich krokach kontroler eskaluje sytuację z uzasadnieniem i zatrzymuje proces. Każda rzeczywista zmiana resetuje ten licznik.
Można posunąć się dalej i ograniczyć liczbę prób dla każdego konkretnego błędu, a nie tylko całkowitą liczbę iteracji. Ta różnica jest ważna: piętnaście skutecznych iteracji może być w pełni akceptowalne, podczas gdy piętnaście prób naprawy jednego nie do usunięcia błędu testu to czysta strata czasu. Prawdziwym rozwiązaniem nie jest uczynienie modelu mniej upartym, lecz dostarczenie kontrolerowi jasnej definicji stanu stagnacji.
Przypadek studialny: ograniczenie, które znika z kontekstu
Załóżmy teraz agenta, który na początku migracji ustala, że nic nie może uszkodzić istniejących klientów. Czterdzieści tur później rozmowa zostaje skompresowana, a to ograniczenie ginie w streszczeniu. Wtedy agent proponuje zmianę schematu, która może spowodować problemy.
To wygląda na błędne rozumowanie, ale przyczyna leży gdzie indziej. Porównaj kontekst, który model faktycznie otrzymał w różnych turach. Jeśli ograniczenie było obecne w piątej turze, a brakowało go w czterdziestej, rozwiązanie leży w inżynierii kontekstu: przechowuj kluczowe niezmienniki oddzielnie od rozmowy, sprawiaj, by streszczenia przenosiły wyraźne ograniczenia, i przestań traktować surową historię jako jedyny źródło prawdy.
„Model zapomniał” to nigdy nie jest kompletna diagnoza. Ważne pytanie brzmi, co faktycznie dano modelowi.
Kiły, a nie zamienniki
Każda nowsza dziedzina opiera się na starszych, zamiast je odrzucać. Agent produkcyjny otacza jasny prompt starannie wybranym kontekstem, uruchamia go w niezawodnym środowisku i kieruje całym procesem za pomocą celowego pętli. Sekwencja tych elementów odzwierciedla to, co zespoły musiały stopniowo dodawać: najpierw jaśniejsze instrukcje, potem lepiej dobrane informacje, następnie środowisko wykonawcze dla modelu, a na końcu kontroler, który utrzymuje to środowisko w działaniu bez nadzoru. Jeśli zastanawiasz się, czy Twój własny silnik wykonawczy, czy framework powinien zarządzać tą zewnętrzną pętlą, porównanie silników wykonawczych i frameworków złożonych przedstawia wszystkie możliwe kompromisy.
Sformułowane za pomocą PACT:
- Prompt: określenie instrukcji.
Następnie trzeba dopasować rozwiązanie do problemu. Model, który błędnie interpretuje jasno określone zadanie, wymaga natychmiastowych działań korygujących. Model, który nie ma dostępu do faktów, od których zależy, potrzebuje dodatkowego kontekstu. Rozsądne decyzje, które nie przeradzają się w wiarygodne działania, wymagają prac nad ich realizacją. Działania, które odnoszą sukces, podczas gdy cały proces nigdy się nie zakończy, wymagają poprawek w strukturze pętli.
Częste pytania
Czy inżynieria harness to po prostu inne określenie na framework agentów?
Nie. Framework dostarcza elementów budulcowych. „Szykownica” składa się z decyzji, które podejmujesz podczas ich łączenia: zachowanie w przypadku awarii narzędzia, utrzymywanie stanu, logowanie, uprawnienia oraz sposób powrotu wyników do modelu.
Czy asystent zadający jedno pytanie wymaga projektowania pętli?
Właściwie nie. Projektowanie pętli staje się istotne, gdy agent podejmuje kilka działań w ramach jednego zadania i musi samodzielnie lub za pomocą kodu kontrolera zdecydować, że praca jest zakończona. Bot do pytań i odpowiedzi działający w jednej rundzie ma niewiele lub wcale nie musi projektować pętli.
Którą warstwę powinieneś najpierw poznać?
Zacznij od projektowania promptów i kontekstu, a następnie przejdź do „szykownicy” i pętli. Musisz odróżnić złą instrukcję od braku stanu, zanim będziesz mógł naprawiać błędy w czasie wykonywania i w kodzie kontrolera.
Czy jeden błąd może dotyczyć kilku warstw?
Tak, i to często są najtrudniejsze przypadki. Błąd kontekstowy może ukryć sygnał postępu przed pętlą, przez co wygląda to jak błąd pętli. Należy prześledzić przyczynę awarii na wszystkich poziomach, zamiast naprawiać tylko pierwszy widoczny objaw.
Główne wnioski
- Cztery te terminy opisują elementy sterowania, a nie sprzeczne tendencje: instrukcję, stan gotowy do pracy modelu, wykonywanie i przywracanie oraz powtarzający się postęp z regułą zatrzymania.
- Każde śledztwo należy rozpoczynać od zapisu działań, a nie od prośby: należy zapytać, co widział model, co zrobił system operacyjny, co zmieniło się pomiędzy iteracjami oraz dlaczego mechanizm sterowania zdecydował się kontynuować.
- Sukcesywne wywołania narzędzi z niemal identycznymi argumentami wskazują na pętlę; powtarzające się awarie przy próbach bez żadnych modyfikacji wskazują na problemy z infrastrukturą.
- Należy nadać pętlom wyraźną definicję stanu zatrzymania, najlepiej w zależności od charakterystyki awarii, oraz określić ścieżkę eskalacji.
Literatura pokrewna
- Jak protokół contextu modelu pozwala agentom SI odkrywać i korzystać z narzędzi — Jasne wyjaśnienie MCP: jak hosty, klienci i serwery umożliwiają aplikacjom SI odkrywanie narzędzi, wywoływanie ich za pomocą ustrukturyzowanych danych wejściowych oraz określanie ich ograniczeń.
- CodeBuddy: Zdrowsze zdobywanie contextu dla agentów programistycznych SI — Wyjaśnia, w jaki sposób system zdobywania contextu oparty na grafie zależności pomaga agentom programistycznym SI unikać zarówno niedoboru contextu, jak i jego przeładowania w dużych bazach kodu.
- Od pisania w Kotlin do kierowania agentami: nowa rola inżyniera mobilnego — Jak agenci programistyczne zmieniają pracę inżyniera Android na działania związane z specyfikacjami, kontekstem, ograniczeniami architektury i weryfikacją, oraz które zasadnicze elementy są ważniejsze niż kiedykolwiek.