Od „Rozpocznij” do zakończenia: stan, zatwierdzenie i idempotencja dla agentów działających
Dowiedz się, jak wyraźne propozycje, ograniczone zatwierdzenia, ponowna walidacja, klucze idempotencji oraz weryfikacja wyników przekształcają proces zwrotu pieniędzy z udziałem wielu agentów w coś, czemu można zaufać.
Demo z udziałem wielu agentów zazwyczaj kończy się przekonującą odpowiedzią. System produkcyjny rozpoczyna swoją najtrudniejszą pracę zaraz po tym, gdy użytkownik odpisze „Świetnie, kontynuuj”, a oprogramowanie musi przekształcić sugestię z rozmowy w rzeczywistą zmianę w innym systemie. Ten artykuł śledzi pojedynczą prośbę o zwrot pieniędzy przez koordynatora, specjalistycznego agenta, przechowywane propozycje, zatwierdzenie i realizację przez człowieka, pokazując, jakie obowiązki należą do modelu, a które muszą pozostać w kodzie aplikacji. Na końcu otrzymasz konkretną listę kontrolną, która pomoże ocenić, czy workflow oparty na agentach jest gotowy do działania w rzeczywistym świecie.
Co się zmienia, gdy asystent musi działać
Aż do momentu zatwierdzenia asystent musiał jedynie zinterpretować prośbę i udzielić pomocnej odpowiedzi. Gdy użytkownik przytakuje, pojawia się zupełnie inny zestaw obowiązków. System musi dokładnie przypomnieć sobie, co zaproponował, znaleźć odpowiedni rekord, wybrać komponent odpowiedzialny za zadanie, potwierdzić, że dana akcja nadal ma sens, sprawdzić, czy dany użytkownik może ją zatwierdzić, skorzystać z usługi zewnętrznej, poradzić sobie z awariami, a na koniec ustalić coś, co brzmi prosto, ale rzadko tak jest: czy akcja rzeczywiście miała miejsce.
Znana architektura demonstracyjna obejmuje tylko pierwszą część. Orkiestrator otrzymuje żądanie, przekazuje je specjalistom, ci z kolei uruchamiają narzędzia, a następnie przychodzi odpowiedź. Ta struktura jest cenna, lecz w praktyce stanowi jedynie punkt wejścia. Bezpieczne działanie wymaga wyraźnego określenia stanu, uprawnień, procesów roboczych odpornych na czekanie i ponowną weryfikację, właściwości idempotencji, możliwości odzyskania po niejednoznacznych awariach oraz dowodów na zakończenie zadania. W tym momencie projektowanie agenta staje się problemem architektonicznym, a nie problemem sterowania.
Śledzenie pojedynczego żądania zwrotu pieniędzy
Weźmy przykład żądania, które klient może wysłać do asystenta obsługi: sprawdź zamówienie nr 4821, określ, czy kwalifikuje się ono do zwrotu, a jeśli tak, przygotuj projekt zwrotu w celu zatwierdzenia. Człowiek-agent podzieliłby to na około dziesięć kroków:
- Określ, o co prosi klient.
- Pobierz informacje o zamówieniu.
W oprogramowaniu każda z tych transycji wymaga osoby odpowiedzialnej oraz miejsca realizacji. Skuteczna sekwencja przekazuje żądanie od użytkownika do orkiestratora, następnie do specjalisty, który zbiera dowody i sporządza propozycję, która jest zatwierdzana, realizowana i ostatecznie weryfikowana.
- Rozkierowanie żądania to zadanie orkiestratora.
- Specjalista przeprowadza badania w danej dziedzinie.
- Narzędzia łączą system z dokumentacją oraz aktualnymi danymi.
- Jasno określony stan odzwierciedla proponowaną akcję.
- Użytkownik zatwierdza konkretną propozycję.
Jedna zasada łączy te elementy:
Model może decydować, co powinno się wydarzyć; aplikacja decyduje, co jest dozwolone.
Poniższe fragmenty to szkice architektury w Pythonie wykorzystującym LlamaIndex. Konfiguracja modelu, integracje z systemami przechowywania danych oraz usług pozostawiono bez opisu, a nazwy takie jak llm i policy_retriever oznaczają komponenty, które powinna dostarczyć aplikacja. Niektóre fragmenty mają łączone linie (na przykład instrukcje umieszczone na jednej linii), więc należy je traktować jako zarysy, a nie kod gotowy do skopiowania.
Rozdzielenie routingu od zadań specjalistycznych
Naj szybszym sposobem na stworzenie agenta jest dostarczenie mu od razu wszystkich narzędzi: wyszukiwania dokumentacji, sprawdzania zamówień, weryfikacji płatności, zwrotów pieniędzy, korespondencji e-mail oraz generowania raportów. Dla małej aplikacji jest to zupełnie rozsądne. Gdy jednak liczba obowiązków rośnie, zadanie agenta staje się trudne do opisania w jednym zdaniu, a kwestie związane z czasem realizacji muszą dzielić „zatłoczoną skrzynkę narzędzi” z operacjami przekazywania pieniędzy.
Czystsze rozdzielenie obowiązków obejmuje dwie kwestie: decydowanie o tym, do której grupy należy dane żądanie, oraz wykonywanie specjalistycznych działań w jego ramach. Dla asystenta obsługi klienta oznacza to koordynatora, który odpowiada na ogólne pytania i przekazuje żądania związane ze zwrotami pieniędzy dedykowanemu specjaliście. Specjalista ten jest tworzony przy użyciu FunctionAgent z LlamaIndex:
from llama_index.core.agent.workflow import FunctionAgent
Jego definicja przewiduje wąski zakres zadań, trzy narzędzia oraz uprawnienie do przekazania kontroli koordynatorowi. Zauważ, że prośba systemu wymaga od niego wyjaśnienia dostępnych dowodów przed przedstawieniem jakichkolwiek propozycji:
refund_specialist = FunctionAgent(
name="refund_specialist",
description="Checks refund eligibility and prepares proposals.",
system_prompt=(
"Check the order and the applicable policy. "
"Explain your evidence before proposing a refund. "
"Hand back unrelated requests to the coordinator."
),
tools=[get_order, search_policy, prepare_refund],
llm=llm,
can_handoff_to=["coordinator"],
)
Pole description nie służy jedynie dekoracji; stanowi część architektury. Etykieta taka jak „przydatny asystent” nie przekazuje żadnych informacji. Opis „Sprawdza możliwość zwrotu pieniędzy i przygotowuje propozycje” określa obowiązek, który można przetestować i zaudytować. W LlamaIndex klasa FunctionAgent oraz AgentWorkflow są zaprojektowane właśnie do takiego wyraźnego przekazywania zadań. Jeśli nadal wybierasz framework, porównanie LangChain i LlamaIndex omawia szersze aspekty tej decyzji.
Dodawanie agentów nie zawsze oznacza poprawę. Każdy specjalista wprowadza dodatkowe obciążenia związane z koordynacją, więc każdy z nich musi uzasadnić swoje istnienie. W tym przypadku para agentów – koordynator oraz specjalista od zwrotów pieniędzy – tworzy sensowną granicę bez dodatkowych kosztów.
Łączenie koordynatora z specjalistą
Koordynator oraz proces pracy łączący obu agentów pochodzą z tego samego modułu:
from llama_index.core.agent.workflow import AgentWorkflow
Koordynator ma dostęp do funkcji wyszukiwania zasad dotyczących ogólnych pytań i może przekazać je specjalistowi od zwrotów pieniędzy. AgentWorkflow rejestruje obu agentów i czyni z koordynatora element korzeniowy, który otrzymuje każde żądanie. W tym fragmencie zamknięcie nawiasu koordynatora oraz przypisanie procesu pracy znalazły się na jednej linii; są to dwa oddzielne instrukcje:
coordinator = FunctionAgent(
name="coordinator",
description="Answers general questions and routes refund requests.",
system_prompt=(
"Use documentation for general questions. "
"Hand refund requests to the refund specialist."
),
tools=[search_policy],
llm=llm,
can_handoff_to=["refund_specialist"],
)workflow = AgentWorkflow(
agents=[coordinator, refund_specialist],
root_agent="coordinator",
)
Dzięki temu żądanie ma określoną ścieżkę. Koordynator rozpoznaje pytanie dotyczące zwrotu pieniędzy i przekazuje je dalej; specjalista pobiera zamówienie, sprawdza zasady firmy, odnotowuje, co jeszcze brakuje, a gdy dowody są wystarczające, opracowuje propozycję.
Trasa rozmowy może się różnić. Jeden klient podaje datę dostawy od razu; inny najpierw pyta o zasady zwrotów, a o zamówieniu wspomina dopiero po trzech wiadomościach. Pracownicy potrafią dostosować się do tych różnic, podczas gdy aplikacja nadal określa dostępne funkcje oraz ograniczenia ich używania.
Rozmowy mogą pozostać elastyczne, podczas gdy kluczowe operacje są kontrolowane.
Dowody przed sprawdzeniem warunków
Przekazanie żądania właściwemu pracownikowi nie gwarantuje, że jego wniosek będzie prawidłowy. Specjalista nadal musi zebrać dowody.
Załóżmy, że zasady pozwalają na zwrot nierozpakowanych produktów w ciągu 30 dni, podczas gdy dane zamówień pokazują, że zamówienie nr 4821 dotarło 12 dni temu. Te informacje pochodzą z dwóch różnych systemów i oba są niezbędne: zasady określają reguły, a zamówienie opisuje sytuację; dopiero po ich połączeniu można określić, czy dany produkt spełnia warunki zwrotu. Dlatego jego odzyskanie staje się etapem w procesie pracy. Specjalista przeszukuje dokumenty zasad, jednocześnie ładując oddzielnie aktualne dane zamówienia.
Najprostsze narzędzie do wyszukiwania zasad zaczyna się od asynchronicznego podpisu oraz dokumentacji, która informuje użytkownika o jego przeznaczeniu:
async def search_policy(question: str) -> list[dict]:
"""Find policy passages relevant to a customer request."""
Jego treść pobiera odpowiadające fragmenty i zwraca każdy z nich wraz z tytułem źródłowym i sekcją, do której należy. Podobnie jak w poprzednim fragmencie, wywołanie aretrieve oraz instrukcja return muszą znajdować się na osobnych liniach:
matches = await policy_retriever.aretrieve(question) return [
{
"text": match.node.get_content(),
"source": match.node.metadata.get("title"),
"section": match.node.metadata.get("section"),
}
for match in matches
]
Ważne jest to, co towarzyszy tekstu: jego pochodzenie. Jeśli asystent później twierdzi, że zamówienie mieści się w oknie zwrotu pieniędzy, wyjaśnienie powinno umożliwić odwołanie się do fragmentu polityki określającego to okno. Pochodzenie ma jednak swoje ograniczenia:
Cytat nie może udowodnić, że interpretacja jest prawidłowa; jego zadaniem jest umożliwienie komuś jej sprawdzenie.
Sytuacja, gdy paczka nie została otwarta, ujawnia lukę: w systemie zamówień nie ma żadnych informacji o tym, czy paczka została otwarta. Prawidłowym rozwiązaniem nie jest zgadywanie, lecz zapytanie klienta. Solidny projekt wyraźnie pokazuje brakujące informacje, zamiast pozwalać niepewności przerodzić się w pewną rekomendację.
Historia rozmów to nie stan aplikacji
Załóżmy, że dochodzenie zostało zakończone. Specjalista donosi, że zamówienie nr 4821 kwalifikuje się do zwrotu w wysokości 79 euro, i pyta, czy kontynuować. Użytkownik odpowiada twierdząco.
Prześlijcie to dalej – procedura zadziałała, dochodzenie zostało przeprowadzone, a narzędzia dostarczyły swoje dane, jednak wciąż istnieje luka. System nie może dokładnie określić, co zostało zatwierdzone: zamówienie, kwota i waluta oraz wersja oferty są jedynie domyślane, a nie zapisane. Jeśli rozmowa dotyczyła dwóch zamówień lub kwota została przeliczona po przedstawieniu oferty, odpowiedź „tak” staje się niejednoznaczna.
Dlatego działania wynikowe nie mogą istnieć wyłącznie w transkrypcji rozmowy. Aplikacja wymaga wyraźnego zapisu oczekującej akcji, takiej jak ta:
pending_action = {
"proposal_id": "refund-proposal-17",
"order_id": "4821",
"amount_minor": 7900,
"currency": "EUR",
"status": "awaiting_approval",
"evidence": [
"returns-policy:section-3",
"order:4821",
],
}
Pole status jest przydatne, ale to proposal_id ma największe znaczenie. Zatwierdzenie powinno odnosić się do dokładnej propozycji, którą widział użytkownik. Jeśli kwota się zmienia, to jest to nowa propozycja. Jeśli zmienia się kolejność, to też jest to nowa propozycja. Jeśli nowe dowody zmieniają rekomendację, to jest to nowa propozycja. Pieniądze są przechowywane jako amount_minor w postaci całkowitych centów, co unika niespodzianek związanych z zaokrąglaniem liczb zmiennoprzecinkowych, a lista evidence zachowuje informację o tym, która sekcja polityki i który rekord kolejności uzasadniały daną ofertę.
Zapis rozmowy służy do zrozumienia sytuacji; ustrukturyzowany stan służy do podjęcia działań.
Rozmowa pomaga modelowi zrozumieć, co oznacza „proszę kontynuować”. Przechowywana propozycja daje aplikacji coś jednoznacznego do wykonania.
Zatwierdzenie jako dane, a nie słowa
Naiwny sposób rejestrowania zgody to jeden prosty fakt:
user said yes
Bardziej bezpieczny model łączy ze sobą tożsamość, propozycję, działanie i kontekst:
user X approved proposal Y,
containing action Z,
under the current conditions.
To rozróżnienie staje się decydujące w momencie, gdy agent może wpływać na zewnętrzne systemy. Zgoda powinna łączyć konkretną osobę z określoną proponowaną akcją, a nie z czymś, co model odtwarza później. Dlatego propozycja musi zawierać wszystkie istotne szczegóły operacyjne:
- rekord docelowy
- kwotę
- walutę
- dowody potwierdzające
- bieżący stan
- jedyny identyfikator propozycji
Język naturalny wyjaśnia działanie użytkownikowi; strukturyzowana propozycja definiuje je dla systemu.
Czekanie jest częścią procesu pracy
Autorizacja przez człowieka również wprowadza element czasu. Użytkownik może odpowiedzieć natychmiast, po obiedzie lub następnego dnia po zamknięciu przeglądarki. Gdy proces pracy zależy od osoby, pauzowanie nie jest wyjątkiem, lecz normalną fazą, dlatego prace w oczekiwaniu muszą zostać zapisane i wznowione później.
LlamaIndex dostarcza obiektów kontekstowych procesu pracy oraz sposobów na pauzowanie w oczekiwaniu na wprowadzenie danych przez człowieka, co nadaje tej interakcji pewną strukturę. Jednak trwałe przechowywanie, uprawnienia, zasady wygaśnięcia oraz cały cykl życia aplikacji pozostają twoją odpowiedzialnością.
W przypadku tak istotnej kwestii jak zwrot pieniędzy, rozsądny podział zadań polega na tym, aby agent przygotował propozycję, a zwykły kod aplikacji wykonał zatwierdzoną operację. Procesor autoryzacji zaczyna od załadowania zapisanej propozycji:
async def approve_refund(proposal_id, user):
proposal = await proposals.load(proposal_id)
Następnie sprawdza uprawnienia, potwierdza, że propozycja jest nadal w oczekiwaniu, ponownie ją waliduje, wywołuje usługę płatności używając ID propozycji jako klucza idempotencji oraz zapisuje potwierdzenie transakcji. W tym fragmencie te wywołania asynchroniczne są realizowane jednocześnie na tych samych wierszach kodu; każde await stanowi odrębną instrukcję:
await permissions.require(
user,
"approve_refund",
proposal,
) await proposals.require_pending(proposal) await refunds.revalidate(proposal) receipt = await payments.refund(
order_id=proposal.order_id,
amount_minor=proposal.amount_minor,
idempotency_key=proposal.id,
) await proposals.mark_completed(
proposal.id,
receipt,
) return receipt
W tej krótkiej funkcji ukryte są kilka istotnych elementów:
- Propozycja pochodzi ze zapisanego stanu, a nie z rozmowy.
- Uprawnienia są sprawdzane poza modelami biznesowymi.
- Kod potwierdza, że propozycja wciąż czeka na zatwierdzenie, co zapobiega jej ponownej obsłudze tą samą ścieżką (przy równoczesnym wykonywaniu zadań ta weryfikacja powinna być atomowa względem aktualizacji statusu).
- Eligibilitet jest ponownie sprawdzany tuż przed podjęciem działań.
- Wywołanie do dokonania płatności zawiera stabilny klucz idempotencji.
- Wynik operacji jest zapisywany trwale.
Sekwencja ta obejmuje od zapisanej propozycji, przez autoryzowaną zatwierdzenie, nową weryfikację i samą realizację, aż po zapisany wynik. Ta sekwencja ma znacznie większe znaczenie niż to, czy agent sformułował swoją wiadomość perfekcyjnie.
Ponownie zweryfikuj przed podjęciem działań
Dlaczego sprawdzać ponownie, skoro użytkownik już się zgodził? Ponieważ świat nadal się porusza, podczas gdy system czeka. Zamówienie mogło zostać zwrócone przez inny kanał obsługi klienta, status płatności mógł się zmienić, ktoś mógł edytować zamówienie lub równoległy proces pracy mógł już wykonać tę operację.
Propozycja to zrzut ekranu tego, co wydawało się poprawne w momencie jej opracowania. Realizacja następuje później, czasem znacznie później. Przed podjęciem istotnej operacji aplikacja powinna potwierdzić, że założenia leżące u podstaw propozycji nadal są aktualne.
Tak oznacza zgodę na podjęcie działań; nie powstrzymuje to zmian w świecie.
Im dłużej propozycja pozostaje w oczekiwaniu, tym ważniejsze staje się to kwestie. Połączenie ponownej weryfikacji z terminem ważności propozycji pozwala ograniczyć okres obowiązywania przestarzałych założeń.
Ponawiania nie mogą powtarzać tej samej czynności
Rozważmy inny przypadek niepowodzenia. Użytkownik wyraża zgodę, aplikacja wysyła zwrot pieniędzy, dostawca płatności go przetwarza, ale sieć przerywa połączenie zanim nadejdzie odpowiedź. Klient próbuje ponownie. Czy klient powinien otrzymać drugie 79 euro? Oczywiście, że nie.
Idempotencja zapobiega temu. Stabilny identyfikator operacji pozwala każdej zewnętrznej usłudze, która obsługuje idempotencję, rozpoznać dwa żądania jako jedną logiczną operację. Zamiast traktować ponowną próbę jako „wydanie kolejnego zwrotu pieniędzy”, usługa traktuje ją jako żądanie statusu lub wyniku operacji już powiązanej z propozycją nr 17. Użycie identyfikatora propozycji jako klucza jest skuteczne, ponieważ każda zatwierdzona propozycja powinna generować co najwyżej jeden zwrot pieniędzy. Mechanizmy po stronie serwera są omówione bardziej szczegółowo w rozumieniu kluczy idempotencji w punktach końcowych Node.js POST.
Rzadko pojawia się to w dopracowanych demonstracjach z udziałem wielu agentów. Jednak gdy tylko system może przenosić pieniądze, wysyłać wiadomości, zmieniać dane, otwierać zgłoszenia lub wywoływać jakikolwiek inny efekt w rzeczywistym świecie, ponowne próby stanowią część architektury.
"Nieznany" to prawomocny wynik
A teraz najbardziej kłopotliwy przypadek: próba dokonania płatności kończy się wygaśnięciem czasu oczekiwania. Być może nic się nie stało, albo pieniądze zostały przelane chwilę przed przerwaniem połączenia. Dla klienta różnica wynosi 79 euro.
Powiadomienie użytkownika, że zwrot pieniędzy nie powiódł się i będzie próbowany ponownie, byłoby błędne, ponieważ nic nie potwierdza takiego twierdzenia. System posiada jedynie brak potwierdzenia, co to zupełnie inna kwestia. Niezawodny proces pracy sprawia, że ta niepewność pozostaje widoczna: zamiast powtarzać operację, system sprawdza stan istniejącej transakcji i tymczasem informuje użytkownika o dokładnej sytuacji, na przykład że nie udało się potwierdzić zakończenia transakcji i sprawdza się jej status.
Taka dyscyplina obowiązuje na każdym etapie, nie tylko podczas dokonywania płatności:
- Gdy wyszukiwanie zamówienia zwraca błąd, istnienie zamówienia jest nieznane, a nie udowodnione jako nieistniejące.
Zaufane systemy traktują je jako odrębne stany, zamiast łączyć je w kategorię „nieudane” lub „nie znaleziono”.
Zwrócone wezwanie do narzędzia nie oznacza zakończenia
Tutaj architektura wykracza poza prostą orkiestrację. Zwrócone wezwanie do narzędzia nie jest równoznaczne z ukończeniem pracy. Odpowiedź o błędzie oznacza, że praca nie została ukończona. Czas wygaśnięcia również oznacza, że praca nie została ukończona. Jeśli aplikacja nie może stwierdzić, czy doszło do zwrotu pieniędzy, z pewnością nie jest jeszcze ukończona.
Mocniejszą definicją zakończenia jest to, że system może przedstawić dowody na to, iż zamierzony rezultat rzeczywiście nastąpił. To zmienia charakter umowy. Celem nie jest to:
call refund()
Celem jest to:
establish that refund proposal #17
for order #4821
was successfully processed exactly once
To są zupełnie różne gwarancje, i to właśnie ta różnica sprawia, że orkiestratorzy oraz specjaliści stanowią jedynie początek projektu.
Dziewięć pytań przed uruchomieniem systemu wieloagentowego
Zanim połączysz proces pracy z AI z operacjami o rzeczywistych konsekwencjach, przemyśl następujące pytania:
- Czy każdy agent ma określoną odpowiedzialność, którą można sformułować w jednym zdaniu?
- Czy możesz prześledzić ważną decyzję do dokumentów i zapisów, które ją uzasadniają?
- Czy brakujące informacje mogą pozostać wyraźnie nieznane, czy też niepewność potajemnie przeradza się w pewną rekomendację?
- Czy proponowana akcja jest przechowywana jako ustrukturyzowany stan, a nie istnieje tylko w ramach rozmowy?
- Czy użytkownik zatwierdza konkretną propozycję, tak aby „tak” oznaczało zatwierdzenie czegoś konkretu?
Jeśli na niektóre z tych pytań nie ma jasnej odpowiedzi, możesz nadal mieć imponującą demonstrację, ale prawdopodobnie nie jest to jeszcze system, któremu można zaufać w działaniu.
Zakończenie
Ponownie przeanalizuj proszę pierwotną prośbę: sprawdź zamówienie nr 4821 i przygotuj zwrot pieniędzy w celu zatwierdzenia. Każda jego część ma teraz swoje miejsce. Koordynator kieruje zadania, specjalista zbiera dowody, narzędzia trafiają do dokumentacji i żywych zapisów, specjalny system przechowuje propozycję, osoba podpisuje się pod konkretną akcją, kod aplikacji sprawdza uprawnienia i je ponownie waliduje, system płatności wykonuje transakcję, a wynik jest przechowywany i potwierdzany. W tym momencie asystent może udzielić przyjemnie monotonnej odpowiedzi: zwrot 79 euro za zamówienie nr 4821 został przetworzony, dołączono potwierdzenie. To stwierdzenie jest wiarygodne dzięki wszystkiemu, co za nim stoi.
Gdy system rośnie, te same pytania wciąż okazują się kluczowe. Czy każda funkcjonalność należy do kogoś konkretnego? Czy można przeanalizować dowody leżące u podstaw decyzji? Czy proces pracy zachowuje niepewność i potrafi się odzyskać w przypadku awarii zależności? Czy osoba może dokładnie zobaczyć to, co aprobuje, a system może udowodnić, że osiągnięto zamierzony rezultat? Odpowiedzi pokazują, gdzie dodatkowy specjalistyczny agent wnosi wartość, gdzie wystarczy zwykła funkcja oraz gdzie potrzebne są wyraźniejsze granice. Najlepsze doświadczenie z agentami może zakończyć się pojedynczym, zwyczajnym słowem „Gotowe”, a architektura sprawia, że jest to wiarygodne.
Literatura pokrewna
- Agentic AI wyjaśnione: od modeli językowych do autonomicznych agentów — Strukturalny przewodnik po tym, jak modele językowe ewoluują w systemy agentic poprzez narzędzia, pamięć, planowanie, architektury wielu agentów oraz integrację MCP.
- Routing, Fan-Out, ReAct, krytyka i zatwierdzenie: pięć wzorów LangGraph — Poznaj pięć wzorów pracowniczych w LangGraph, od routerów i pętli ReAct po bramy ewaluacyjne i zatwierdzenie przez człowieka, wraz z zasadami bezpieczeństwa niezbędnymi przy ich stosowaniu w produkcji.