Wskazówki praktyczne: Naprawa błędów niedeterministycznego odtwarzania w pętlach agentów sztucznej inteligencji
Krok po kroku przewodnik po praktycznych wskazówkach: naprawa błędów niedeterministycznego odtwarzania w pętlach agentów AI: kontrakty, sprawdzanie oraz gotowe miejsca na kod dla zespołów wdrażających ten wzorzec.
Następujące notatki przedstawiają praktyczny plan działania dotyczący „Poprawy błędów niedeterministycznej odtwarzalności w pętlach agentów sztucznej inteligencji”. Nacisk kładziony jest na umowy, sprawdzania oraz miejsca zastępcze dla kodu, a nie na motywacyjne aspekty. Podczas przechodzenia przez etap przeglądu najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zapisz czas wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy przechodzi się od demonstracji do wspólnych środowisk.
Błąd, zanim pojawią się nazwy
Błąd występujący przed konkretnym etapem funkcjonuje najlepiej, gdy traktowany jest jako mierzalna powierzchnia do analizy. Zapisz jeden przykład udanego działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres badania. Przechowuj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny poufnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, aby operatorzy mogli je sprawdzić bez konieczności przeglądania całej struktury. Utrzymuj stan struktury w formie prostych, spójnie zdefiniowanych elementów. Wложone struktury utrudniają określenie, który węzeł zapisał dane w danym polu, i powodują przerwy w kontynuacji działania po interwencjach.
# ❌ What I almost wrote — LLM call INSIDE the workflow.
@workflow.defn
class SourcingWorkflow:
@workflow.run
async def run(self, brief: SourcingBriefInput) -> SourcingResult:
suppliers = await workflow.execute_activity(research_activity, brief)
scored = await workflow.execute_activity(score_activity, suppliers)
# The LLM call — right here, in the workflow. THIS IS THE BUG.
# If worker dies here, replay will re-call the LLM and mutate state/history.
response = await llm_client.complete(
messages=build_decide_prompt(scored),
temperature=0.0,
)
selected = parse_llm_decision(response.content, scored)
approval = await workflow.wait_condition(...)
# ...create PO, initiate payment
# ✅ The fix — LLM call moved into an activity.
@activity.defn
async def decide_activity(scored: list[dict], brief: SourcingBriefInput) -> dict:
"""The LLM call lives here — in the activity, not the workflow."""
from app.agentmesh.llm import get_llm_client
client = get_llm_client()
response = await client.complete(
messages=build_decide_prompt(scored),
temperature=0.0,
)
selected, rationale = parse_llm_decision(response.content, scored)
return {"selected_supplier": selected, "decision_reason": rationale}
@workflow.defn
class SourcingWorkflow:
@workflow.run
async def run(self, brief: SourcingBriefInput) -> SourcingResult:
suppliers = await workflow.execute_activity(research_activity, brief)
scored = await workflow.execute_activity(score_activity, suppliers)
# The LLM call is NOWHERE in the workflow.
# The activity result is recorded. On replay, it's injected.
decision = await workflow.execute_activity(
decide_activity,
args=(scored, brief),
)
Zasada: ponowne odtworzenie musi dać ten sam wynik
Proces ponownego uruchomienia zgodnie z prawem funkcjonuje najlepiej, gdy traktowany jest jako mierzalna powierzchnia. Zapisz jeden udany przypadek, jeden przypadek awarii oraz notatkę o cofnięciu zmian, zanim rozszerzysz zakres działania. Zdokumentuj zarówno ścieżkę prawidłowego funkcjonowania, jak i ścieżkę przywracania do stanu poprzedniego. Próby ponownych działań, kontrolne punkty ludzkie oraz obsługa wiadomości nieodebranych stanowią część produktu, a nie elementy dodawane później. Utrzymuj stan grafu w formie prostych, spójnych struktur. Wplecione elementy ukrywają informację o tym, który węzeł zapisał dane w danym polu, co powoduje przerwanie kontynuacji po interwencjach.
Wykroczenie: co się dzieje, gdy LLM znajduje się w procesie pracy
Faza analizy naruszeń funkcjonuje najlepiej, gdy traktowana jest jako mierzalna powierzchnia do badania. Zapisz jeden idealny zapis rozmowy, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres badania. Wolij małe, testowalne jednostki nad rozbudowane skrypty. Gdy jakiś krok zawiedzie, awaria powinna wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji. Określ budżet tokenów na jeden ruch i jedną sesję. Narzędzia typu agentic intensywnie rozszerzają kontekst; sztywne limity zapobiegają pojawieniu się nieoczekiwanych rachunków. Faza analizy naruszeń funkcjonuje najlepiej, gdy traktowana jest jako mierzalna powierzchnia do badania. Zapisz jeden idealny zapis rozmowy, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres badania. Zapisuj czasy wykonywania zadań oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy przechodzi się od demonstracji do środowisk współdzielonych.
Dlaczego „temperature=0.0” cię nie uratuje
Dla etapu temperatury Why 0 0 należy zdefiniować dane wejściowe, osobę odpowiedzialną za ten krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić ten krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Wprowadź ludzką aprobatę dla operacji, które powodują wydatki lub zmieniają dane produkcyjne. Połączenia ustalone w czasie kompilacji nie równają się pełności biznesowej.
Granica: aktywność stanowi błonę nieokreśloności
Aby określić etap działania w ramach granic, zdefiniuj wprowadzenia, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Zdokumentuj zarówno ścieżkę prawidłowego działania, jak i ścieżkę naprawczą. Próby ponownych działań, kontrolne punkty ludzkie oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodawane później. Wymagaj zatwierdzenia przez człowieka w przypadkach, gdy dochodzi do wydawania pieniędzy lub zmiany danych produkcyjnych. Podłączenia realizowane w czasie kompilacji nie równają się kompletności rozwiązania biznesowego.
Kod: jak to faktycznie wygląda w AgentMesh
Dla tego etapu w kodzie należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed dokonaniem zmian w kodzie. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy preferować małe, łatwe do przetestowania jednostki zamiast rozbudowanych skryptów. Gdy dany krok zawiedzie, powinien wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji. Konieczne jest ludzkie zatwierdzenie w przypadkach, gdy dochodzi do wydawania pieniędzy lub modyfikacji danych produkcyjnych. Połączenia realizowane w czasie kompilacji nie równają się pełnej kompletności rozwiązania biznesowego. Dla tego etapu w kodzie należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed dokonaniem zmian w kodzie. Operatorzy powinni móc ponownie uruchomić dany krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Obok wyników funkcjonalnych należy rejestrować czas wykonywania oraz koszt tokenów lub zapytań. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do wspólnych środowisk.
Temporal Workflow (deterministic — replayed)
└── Activity: run_graph_until_interrupt (non-deterministic — recorded once)
└── LangGraph StateGraph
└── decide_node (async function)
└── get_llm_client().complete() ← the LLM call
Przepływ pracy — czysta orkiestracja (workflow.py)
Gdy przechodzisz przez etap czystej orkiestracji przepływu pracy, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Przechowuj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całego grafu. Ustaw punkty kontrolne po kosztownych krokach. Funkcja wznowienia nie powinna ponownie naliczać opłat za tę samą wywołanie LLM, gdy operator próbuje ponownie uruchomić późniejszy węzeł.
@workflow.defn
class SourcingWorkflow:
@workflow.run
async def run(self, brief: SourcingBriefInput) -> SourcingResult:
# ↓ This is the boundary. The activity runs once. Result is recorded.
graph_result = await workflow.execute_activity(run_graph_until_interrupt, brief, ...)
# Wait for human signal — a Temporal primitive, not an LLM call.
# On replay, the signal is injected from history.
await workflow.wait_condition(lambda: self._approval_received, timeout=timedelta(hours=24))
# ↓ Another boundary. Resume activity runs once. Result is recorded.
resume_result = await workflow.execute_activity(resume_graph, self._approval_data, ...)
# ↓ Side effects — each is its own activity with its own retry policy.
po_result = await workflow.execute_activity(create_po_activity, args=(...), ...)
Aktywność — gdzie występuje niedeterministyczność (activity.py)
Gdy pracujesz nad aktywnością obejmującą poszczególne etapy, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zdokumentuj zarówno ścieżkę prawidłowego działania, jak i ścieżkę naprawczą. Próby ponowne, kontrola przez człowieka oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodawane później. Ustal punkty kontrolne po kosztownych krokach. Narzędzie do kontynuacji pracy nie powinno ponownie naliczać opłat za tę samą wywołanie LLM, gdy operator próbuje ponownie uruchomić późniejszy węzeł.
@activity.defn
async def run_graph_until_interrupt(brief: SourcingBriefInput) -> dict:
graph = build_sourcing_graph(checkpointer=await get_checkpointer())
config = {"configurable": {"thread_id": activity.info().workflow_id}}
# ↓ Everything inside this call is non-deterministic. It runs ONCE.
# The result dict is recorded in the event history. On replay, it's injected.
final_state = await graph.ainvoke({"brief": brief, "suppliers": [], "attempts": 0, ...}, config)
return {"paused": True, "selected_supplier": selected, "suppliers": final_state.get("suppliers", [])}
Węzeł grafu — miejsce, w którym faktycznie uruchamia się LLM (graph.py)
Gdy pracujesz nad węzłem grafu odpowiadającym określonemu etapowi, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Wolno preferować małe, testowalne jednostki zamiast rozbudowanych skryptów. Gdy jakiś krok się nie powiedzie, błąd powinien wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji. Zachowuj w pamięci tymczasowej stabilne instrukcje systemowe oraz schematy narzędzi. Ponowne wysyłanie identycznego prefiksu to częsty powód marnotrawstwa zasobów. Gdy pracujesz nad węzłem grafu odpowiadającym określonemu etapowi, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zapisuj czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega niespodziewanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do współdzielonych środowisk.
async def decide_node(state: AgentState) -> dict:
scored = state.get("scored_suppliers", [])
messages = build_decide_prompt(brief.item, brief.quantity, brief.budget, scored, past_decisions)
# ↓ THE LLM CALL. This is the non-determinism that must never be in the workflow.
response = await get_llm_client().complete(messages=messages, temperature=0.0, max_tokens=1000)
selected, rationale = parse_llm_decision(response.content, scored)
return {"selected_supplier": selected, "decision_reason": rationale, "cost_incurred": response.cost_usd} # ↓ THE LLM CALL. This is the non-determinism that must never be in the workflow.
response = await get_llm_client().complete(messages=messages, temperature=0.0, max_tokens=1000)
Gdy granice stają się niewyraźne
Najlepiej funkcjonuje, gdy granica jest traktowana jako mierzalna powierzchnia. Zapisz jeden idealny przypadek, jeden przypadek awarii oraz notatkę o cofnięciu zmian, zanim rozszerzysz zakres. Trzymaj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Utrzymuj stan struktury w prostym formacie i z określonym typem. Wplecione bloki ukrywają informację o tym, który węzeł zapisał dane w danym polu, co utrudnia kontynuację pracy po przerwach.
Temporal event history LangGraph checkpoint (Postgres)
└── ActivityCompleted(result) └── graph state at interrupt()
paused: True node: "approve"
selected: SupplierB selected: SupplierB
suppliers: [A, B, C] suppliers: [A, B, C]
Wzorzec izolacji — ta sama zasada wszędzie
Wzorzec izolacji działa najlepiej na tym samym etapie, gdy traktuje się go jako mierzalną powierzchnię. Zapisz jeden idealny przypadek działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres. Zdokumentuj zarówno pomyślny, jak i naprawczy ścieżkę działania produktu. Próby ponownych działań, kontrolne punkty ludzkie oraz obsługa wiadomości nieodebranych stanowią część produktu, a nie elementy dodawane później. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wplecione struktury ukrywają informację o tym, który węzeł zapisał dane do którego pola, co utrudnia kontynuację działania po przerwach.
To samo ograniczenie w innych architekturach agentów
To samo ograniczenie w fazie realizacji działa najlepiej, gdy traktuje się je jako mierzalną powierzchnię. Zapisz jeden idealny przepis działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres projektu. Wolno preferować małe, testowalne jednostki zamiast rozbudowanych skryptów. Gdy jakiś krok zawiedzie, awaria powinna wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wtórne struktury ukrywają informację o tym, który węzeł zapisał dane do którego pola, co utrudnia kontynuację pracy po przerwach. To samo ograniczenie w fazie realizacji działa najlepiej, gdy traktuje się je jako mierzalną powierzchnię. Zapisz jeden idealny przepis działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres projektu. Zapisuj czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do współdzielonych środowisk.
Koszt: co tracisz w imię determinizmu
Aby określić koszt realizacji zadania, należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany etap oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany etap na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny poufnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Wprowadź procedurę ludzkiej aprobaty dla operacji, które powodują wydatki lub zmieniają dane produkcyjne. Połączenia realizowane w czasie kompilacji nie gwarantują pełnej kompletności biznesowej.
Przypadek krawędziowy 1: Długotrwałe operacje i problem „serca”
Dla przypadku krawędziowego 1 – etapu trwającego długo – należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy udokumentować zarówno normalny przebieg procesu, jak i ścieżkę naprawczą. Próby ponownych działań, kontrola przez ludzi oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodawane później. Konieczna jest ludzka akceptacja w przypadkach, gdy dochodzi do wydawania pieniędzy lub zmiany danych produkcyjnych. Połączenia skompilowane w czasie kompilacji nie równają się kompletności rozwiązania biznesowego.
@activity.defn
async def run_graph_until_interrupt(brief: SourcingBriefInput) -> dict:
# Long-running: graph may run 5+ minutes with multiple LLM calls
for node in graph.stream(initial_state, config):
activity.heartbeat() # ← "I'm alive, don't timeout me"
# ... process node output
Przypadek krawędziowy 2: Przesyłanie tokenów LLM przez Temporal
Dla etapu Edge case 2 Streaming należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy preferować małe, łatwe do przetestowania jednostki nad rozbudowanymi skryptami. Gdy dany krok zawiedzie, powinien wskazywać na konkretną przyczynę, a nie na skomplikowaną strukturę całego procesu. W przypadku, gdy następnym krokiem jest kod lub wywołanie narzędzia, lepsze są ustrukturyzowane wyniki z walidacją schematu niż swobodny tekst. Zapisywanie czasu wykonywania oraz kosztów tokenów lub zapytań obok wyników funkcjonalnych pomaga uniknąć niespodziewanych rachunków po przeniesieniu procesu z środowiska demonstracyjnego do wspólnych środowisk.
Przypadek krawędziowy 3: Polityki ponawiania działań — nie wszystkie działania powinny być ponawiane w ten sam sposób
Gdy pracujesz nad etapem działań w Przypadku krawędziowym 3, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Przechowuj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Ustaw punkty kontrolne po kosztownych krokach. Mechanizm kontynuacji nie powinien ponownie naliczać opłat za tę samą wywołanie LLM, gdy operator ponawia działanie późniejszego węzła.
# Side effect — strict, non-retryable. Idempotency key handles safety.
po_result = await workflow.execute_activity(
create_po_activity,
args=(supplier, item, quantity, price),
retry_policy=workflow.RetryPolicy(
initial_interval=timedelta(seconds=1),
maximum_attempts=1, # ← don't retry. Idempotency key prevents duplicates.
non_retryable_error_types=["DuplicatePOError"],
),
)
# Verification — aggressive retry, but with backoff for eventual consistency
po_verification = await workflow.execute_activity(
verify_po_exists,
args=(po_result["po_id"],),
retry_policy=workflow.RetryPolicy(
initial_interval=timedelta(seconds=2), # ← give the DB time to sync
maximum_attempts=5,
),
)
Model mentalny
Gdy przechodzisz przez etap modelu mentalnego, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zdokumentuj zarówno ścieżkę prawidłowego działania, jak i ścieżkę naprawczą. Próby ponowne, kontrolne punkty ludzkie oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodawane później w celu udoskonalenia. Zachowuj w pamięci podręcznej stabilne instrukcje systemu oraz schematy narzędzi. Ponowne wysyłanie identycznego preambułu jest częstą przyczyną marnotrawstwa zasobów.
Co będzie w następnym wpisie
Gdy przechodzisz przez etap „Co przychodzi”, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Wolę małe, testowalne jednostki niż rozbudowane skrypty. Gdy jakiś krok się nie powiedzie, błąd powinien wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji. Ustalaj punkty kontrolne po kosztownych krokach. System powinien unikać ponownego naliczania opłat za tę samą funkcję LLM, gdy operator próbuje ponownie uruchomić późniejszy element. Gdy przechodzisz przez etap „Co przychodzi”, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zapisuj czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega niespodziewanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do wspólnych środowisk.
Odnośniki
Etap referencji działa najlepiej, gdy traktuje się go jako mierzalną powierzchnię. Zapisz jeden idealny przepis działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Trzymaj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności przeglądania całej struktury. Utrzymuj stan struktury w prostym formacie i z określonym typem danych. Wtórne struktury danych ukrywają informację o tym, który węzeł zapisał dane w danym polu, co powoduje przerwanie kontynuacji po przerwach.
Listwa kontrolna operacyjna
Dla etapu listwy kontrolnej operacyjnej zdefiniuj dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie wykonać dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Traktuj ten etap jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i odrzucaj ciche, częściowe ukończenie zadań.
Należy uzyskać zatwierdzenie człowieka dla procesów, które wydają pieniądze lub zmieniają dane produkcyjne. Połączenia skompilowane w czasie kompilacji nie gwarantują kompletności rozwiązania biznesowego.
Napisz krótki przewodnik: jak rotować klucze, jak opróżniać kolejki z zadań, jak cofnąć ostatni proces importu.
Zapisz czasy wykonywania zadań oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega niespodziewanym rachunkom, gdy ścieżka przechodzi z środowiska demonstracyjnego do współdzielonych środowisk.
Należy uzyskać zatwierdzenie człowieka dla procesów, które wydają pieniądze lub zmieniają dane produkcyjne. Połączenia skompilowane w czasie kompilacji nie gwarantują kompletności rozwiązania biznesowego.
Zanim wdrożysz całą architekturę, zamroź wersje oprogramowania, utwórz dokładny zapis procesów dla kluczowych ścieżek oraz potwierdź kroki konieczne do cofnięcia zmian. Współdzielone środowiska wymagają ograniczeń szybkości, weryfikacji uprawnień użytkowników oraz jasno określonego osoby odpowiedzialnej za rotację kluczy. Lepiej wybrać nudną, ale niezawodną rozwiązanie niż pomysłowe, jednorazowe demonstracje.
Uwaga dotycząca partii 11b06b04feeb: unikaj przechowywania kluczy dostawcy w repozytorium, ustaw ograniczenie liczby tokenów na sesję oraz przechowuj transkrypcje obok narzędzi do oceny, aby późniejsze zmiany modeli pozostały porównywalne.
Podczas pracy nad etapem 0 notatki dotyczącej wzmocnienia bezpieczeństwa, najpierw zapisz specyfikację: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Dokumentuj zarówno prawidłowy przebieg działania, jak i ścieżkę naprawczą. Próby ponowne, kontrola przez ludzi oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodatkowej optymalizacji.
Szczegół wzmocnienia bezpieczeństwa 0/898: zmierz czas wykonywania, klasę błędu oraz zużycie tokenów dla tej notatki, a następnie zdecyduj, czy zachować zmianę na podstawie ustalonego zestawu pytań, a nie jedynie informacji anegdotycznych.
Etap 1 notatki dotyczącej wzmocnienia bezpieczeństwa działa najlepiej, gdy traktuje się go jako mierzalną powierzchnię. Zapisz jeden idealny przykład działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmiany, zanim rozszerzysz zakres prac. Traktuj ten etap jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i odrzuć ciche, częściowe ukończenie zadań.
Szczegóły wzmocnienia bezpieczeństwa 1/898: zmierz czas wykonywania operacji, klasę błędu oraz zużycie tokenów dla tej notatki, a następnie zdecyduj, czy zachować zmianę, opierając się na ustalonej serii pytań, a nie na osobistych obserwacjach.
Dla drugiego etapu ulepszeń związanych z zabezpieczeniami należy określić dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie wykonać ten krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić, nie musząc czytać całej struktury aplikacji.
Szczegóły ulepszenia nr 2/898: należy zmierzyć czas wykonywania, klasę błędów oraz zużycie tokenów dla tego etapu, a następnie zdecydować o utrzymaniu zmiany na podstawie ustalonego zestawu kryteriów, a nie opisów przypadkowych.
Gdy przechodzisz przez trzeci etap notatki dotyczącej wzmocnienia bezpieczeństwa, najpierw zapisz warunki kontraktu: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego awarii. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Wolno preferować małe, testowalne jednostki zamiast rozbudowanych skryptów. Gdy jakiś krok zawiedzie, awaria powinna wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji.
Szczegół nr 3/898 dotyczący wzmocnienia bezpieczeństwa: zmierz czas wykonywania, klasę błędu oraz zużycie tokenów dla tej notatki, a następnie zdecyduj, czy zachować zmianę na podstawie ustalonego zestawu pytań, a nie jedynie informacji anegdotycznych.
Trzeci etap notatki dotyczącej wzmocnienia bezpieczeństwa działa najlepiej, gdy jest traktowany jako mierzalna powierzchnia do analizy. Zapisz jeden idealny przykład działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres prac. Zapisuj czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do wspólnych środowisk.
Szczegół wzmocnienia 4/898: zmierz czas wykonywania, klasę błędu oraz zużycie tokenów dla tej notatki, a następnie zdecyduj, czy zachować zmianę na podstawie ustalonego zestawu pytań, a nie jedynie anegdoty.
W fazie 5 notatki dotyczącej wzmocnienia określ dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie wykonać ten krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Zdokumentuj zarówno prawidłowy przebieg działania, jak i ścieżkę naprawczą. Próby ponowne, kontrolne punkty ludzkie oraz obsługa wiadomości błędnych stanowią część produktu, a nie element późniejszej dopracowywania.
Szczegół wzmocnienia 5/898: zmierz czas wykonywania, klasę błędu oraz zużycie tokenów dla tej notatki, a następnie zdecyduj, czy zachować zmianę na podstawie ustalonego zestawu pytań, a nie jedynie anegdoty.
Notatka dotycząca wzmocnienia bezpieczeństwa na etapie 0 działa najlepiej, gdy traktuje się ją jako mierzalną powierzchnię do analizy. Zapisz jeden idealny przypadek działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres prac. Zapisuj czasy wykonywania operacji oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega niespodziewanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do współdzielonych środowisk.
Szczegóły wzmocnienia bezpieczeństwa 0/917: zmierz czas wykonywania operacji, klasę błędu oraz zużycie tokenów dla tej notatki, a następnie zdecyduj, czy zachować zmianę na podstawie ustalonego zestawu pytań, a nie jedynie informacji anegdotycznych.
Dla etapu 1 notatki dotyczącej wzmocnienia bezpieczeństwa zdefiniuj dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie wykonać ten krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu systemu. Zdokumentuj zarówno prawidłowy przebieg procesu, jak i ścieżkę naprawczą. Próby ponownych działań, mechanizmy kontroli ludzkiej oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodawane później.
Szczegóły wzmocnienia 1/917: zmierz czas działania ściany, klasę błędu oraz zużycie tokenów dla tej notatki, a następnie zdecyduj, czy zachować zmianę na podstawie ustalonego zestawu pytań, a nie jedynie anegdoty.