CodeGym /Kursy /ChatGPT Apps /Produkcja i bezpieczeństwo: uprawnienia, sandbox, sekrety...

Produkcja i bezpieczeństwo: uprawnienia, sandbox, sekrety, monitoring

ChatGPT Apps
Poziom 12 , Lekcja 4
Dostępny

1. Po co agentowi „myślenie produkcyjne”

Gdy piszesz zwykły backend, sama idea „wejścia na produkcję” automatycznie włącza tryb paranoi: autoryzacja, logowanie, obsługa błędów, limity, sekrety w .env, a nie w kodzie.

Z agentem trzeba włączyć ten sam tryb, tylko jeszcze bardziej rygorystyczny. Powód jest prosty: zwykły backend wykonuje dokładnie to, co napisałeś, a agent — to, co model sam postanowi zrobić w ramach nadanych mu narzędzi i instrukcji. Iluzja kontroli jest tu silniejsza niż w klasycznym kodzie: wydaje się, że prompt wszystko opisuje, ale w rzeczywistości kontrolujesz tylko środowisko i dostępne działania, a nie wszystkie „myśli” modelu.

Dlatego w tym wykładzie będziemy stopniowo otaczać naszego agenta „warstwami ochrony”:

  • najpierw ograniczymy, co dokładnie może robić (uprawnienia narzędzi i rozdzielenie agentów),
  • potem odizolujemy środowisko wykonania (sandbox i limity),
  • uporządkujemy kwestię sekretów i PII,
  • i wreszcie włączymy obserwowalność: logi, metryki i podstawowy tracing.

Dla konkretu będziemy kontynuować historię z naszym GiftGenius: agentem, który pomaga dobrać prezent i lekko zahacza o świat commerce (przez zamówienie i checkout, ale bez szczegółów ACP — to będzie później).

2. Uprawnienia: agent nie potrzebuje „wszystkich przycisków świata”

Zasada najmniejszych uprawnień (Least Privilege)

Pierwsza zasada: agent nie musi umieć wszystkiego. Im więcej ma narzędzi, tym większa szansa, że wywoła „nie tę” funkcję w „nie tym” momencie. Zamiast jednego monstrualnego manageEverything(), który czyta i pisze cokolwiek, projektujemy małe, precyzyjne funkcje, rozdzielone co najmniej na odczyt i zapis.

Dla GiftGenius jest to szczególnie jasne: co innego — czytać listę prezentów i preferencje użytkownika, a co innego — tworzyć lub potwierdzać zamówienie (tam już są pieniądze). Dlatego zazwyczaj robimy:

  • zestaw bezpiecznych narzędzi „read‑only” (wyszukiwanie prezentów, podgląd szczegółów),
  • oddzielne narzędzia „write” (tworzenie szkicu zamówienia, anulowanie zamówienia),
  • oraz, jeśli trzeba, jeszcze jeden poziom dla szczególnie niebezpiecznych operacji (potwierdzenie płatności, zmiany masowe).

Różni agenci do różnych zadań

Jeszcze jeden mocny zabieg — rozdzielać agentów według stref odpowiedzialności. Jeden agent — „dobór prezentów”, drugi — „zarządzanie zamówieniami”. Wtedy nawet jeśli model w agencie gift trochę się „wymknie spod kontroli”, fizycznie nie będzie mógł wywołać narzędzia płatniczego, bo po prostu nie ma go w jego konfiguracji.

Wyobraźmy sobie minimalistyczny typ konfiguracji agenta i narzędzi:


// Uproszczone typy do wyjaśnienia idei
type ToolName = 'suggest_gifts' | 'get_gift_details' |
  'create_order_draft' | 'confirm_order';

type AgentConfig = {
  id: string;
  allowedTools: ToolName[];
  maxSteps: number;
};

Teraz opiszmy dwóch agentów GiftGenius:

export const giftPlannerAgent: AgentConfig = {
  id: 'gift-planner',
  allowedTools: ['suggest_gifts', 'get_gift_details'],
  maxSteps: 6,
};

export const orderAgent: AgentConfig = {
  id: 'order-manager',
  allowedTools: ['create_order_draft', 'confirm_order'],
  maxSteps: 4,
};

Tak, to na razie abstrakcja, ale sedno jest proste: nawet jeśli w kodzie są wszystkie cztery narzędzia, konkretny agent dostaje tylko potrzebny podzbiór.

Powiązanie uprawnień z użytkownikiem i rolami

Ważne pamiętać, że mamy dwie różne jednostki:

  • użytkownik i jego uprawnienia (czy ten user_id może w ogóle coś kupować, anulować, widzieć historię),
  • agent i jego dozwolone narzędzia.

W idealnym przypadku każde wywołanie narzędzia powinno przechodzić dwa sprawdzenia: „czy agent ma do tego prawo?” oraz „czy użytkownik też ma do tego prawo?”.

Umownie:

type UserRole = 'guest' | 'customer' | 'admin';

function canUserCallTool(role: UserRole, tool: ToolName): boolean {
  if (tool === 'confirm_order') {
    return role === 'customer' || role === 'admin';
  }
  if (tool === 'create_order_draft') {
    return role !== 'guest';
  }
  return true; // odczyt dozwolony dla wszystkich
}

Po stronie MCP/backendu przy obsłudze wywołania narzędzia możemy robić podwójne sprawdzenie:

function assertToolAllowed(
  agent: AgentConfig,
  userRole: UserRole,
  tool: ToolName,
) {
  if (!agent.allowedTools.includes(tool)) {
    throw new Error(`Narzędzie ${tool} jest zabronione dla agenta ${agent.id}`);
  }
  if (!canUserCallTool(userRole, tool)) {
    throw new Error(`Użytkownik z rolą ${userRole} nie może wywołać ${tool}`);
  }
}

W efekcie nawet jeśli model nagle zdecyduje się wywołać confirm_order z niewłaściwego agenta albo w imieniu gościa — wywołanie rozbije się o to sprawdzenie i zamieni się w kontrolowany błąd, a nie w nieplanowaną płatność.

Różne konfiguracje w zależności od środowiska

W środowiskach dev i staging często chcesz dać agentowi więcej swobody: testowe narzędzia, fałszywe serwisy płatnicze, funkcje eksperymentalne. W produkcji odwrotnie — konfiguracja jest maksymalnie rygorystyczna: część narzędzi wyłączona, endpoints tylko produkcyjne, tokeny wyłącznie prawdziwe.

Najprostszy schemat:

type Env = 'dev' | 'staging' | 'production';

const env = (process.env.APP_ENV as Env) ?? 'dev';

const orderAgentByEnv: Record<Env, AgentConfig> = {
  dev: {
    id: 'order-manager-dev',
    allowedTools: ['create_order_draft', 'confirm_order'],
    maxSteps: 8,
  },
  staging: {
    id: 'order-manager-staging',
    allowedTools: ['create_order_draft', 'confirm_order'],
    maxSteps: 6,
  },
  production: {
    id: 'order-manager-prod',
    allowedTools: ['create_order_draft'], // confirm tylko przez osobną ścieżkę
    maxSteps: 4,
  },
};

export const currentOrderAgent = orderAgentByEnv[env];

Na produkcji confirm_order może być w ogóle wyniesiony do osobnego „niebezpiecznego” agenta, którego wywołujesz dopiero po jawnym kliknięciu „Potwierdź zamówienie” w widżecie i dodatkowych weryfikacjach.

3. Sandbox: agent nie potrzebuje dostępu root do waszego świata

Poziomy izolacji

Po tym, jak nadaliśmy uprawnienia agentom i użytkownikom, przechodzimy do następnego poziomu ochrony — sandbox i izolacji środowiska wykonania.

Sandbox dla agenta i jego narzędzi można umownie podzielić na kilka poziomów:

  1. Poziom kodu narzędzi. Ograniczamy dostęp do systemu plików, sieci i zasobów procesu: nie pozwalamy pisać gdzie popadnie, chodzić do dowolnych domen, bez końca mielić CPU ani zjadać gigabajtów pamięci.
  2. Poziom Agents SDK. Ustalamy limity na kroki cyklu run, liczbę wywołań narzędzi i rozmiar kontekstu (limit tokenów). Model nie może w nieskończoność „myśleć” i produkować tool‑calls — w pewnym momencie run zakończy się błędem „limit kroków” lub „limit czasu”.

To wszystko składa się na klasyczną „architekturę obronną”, którą wygodnie przedstawić schematem.

graph TD
    A[Prompt / instrukcje systemowe] --> B[JSON Schema narzędzi]
    B --> C[Uprawnienia agenta i użytkownika]
    C --> D[Sandbox infrastruktury]
    D --> E[Zewnętrzne serwisy / baza danych]

    subgraph Agent
      A
      B
      C
    end

    subgraph Infrastruktura
      D
    end

Prompt to najsłabsza ochrona; prawdziwa siła zaczyna się tam, gdzie fizycznie ograniczasz, co może zrobić twój kod i jakie API są dostępne.

Limity cyklu run: kroki, czas, wywołania narzędzi

Część sandboxa można wyrazić bezpośrednio w konfiguracji agenta: maksymalna liczba kroków, łączny czas wykonania, limit wywołań narzędzi. To nie tylko ochrona przed runaway‑pętlami, ale też kontrola kosztów.

Przykład abstrakcyjnej konfiguracji opcji run:

type RunLimits = {
  maxSteps: number;
  maxToolCalls: number;
  timeoutMs: number;
};

const defaultLimits: RunLimits = {
  maxSteps: 8,
  maxToolCalls: 10,
  timeoutMs: 30_000,
};

Takie limity przekazujesz potem do „obudowy”, która uruchamia agenta. Jeśli model postanowi wywołać narzędzie po raz 11 — przerywasz uruchomienie agenta i szczerze mówisz użytkownikowi, że zadanie jest zbyt złożone, zamiast pozwalać agentowi bez kontroli przepalać budżet.

Izolacja kodu i sieci

Na poziomie kontenera/procesu typowe praktyki są następujące:

Kod serwera MCP i/lub serwisu agenta uruchamiany jest w kontenerze z systemem plików tylko do odczytu (poza specjalnie wydzielonym katalogiem roboczym) i ograniczonymi zasobami (CPU, RAM). Sieć jest skonfigurowana w trybie allow‑list: można łączyć się tylko z potrzebnymi zewnętrznymi serwisami (twój backend commerce, płatności, kilka zewnętrznych API), a nie z dowolnym internetem.

Dla scenariuszy agentowych to szczególnie krytyczne: model może próbować wyjść do jakiegoś „lewego” API albo odczytać nieoczekiwane pliki, i dobrze, jeśli nawet przy takich próbach fizycznie nie będzie mieć praw, by dostać się do zbędnych zasobów.

W kodzie zwykle nie wygląda to jak „magiczna linijka TypeScriptu”, tylko jak ustawienia twojego orkiestratora (Docker Compose, Kubernetes, Vercel, Fly.io itd.). Ale docelowo warto myśleć o tym już na etapie projektowania:

  • narzędzie, które uruchamia obcy kod (np. generowanie raportu z komendami shell), powinno działać w osobnym, mocno izolowanym środowisku;
  • narzędzia nie powinny mieć możliwości czytania cudzych plików, sekretów, konfiguracji;
  • dostęp sieciowy lepiej jawnie ograniczać po domenach lub IP.

4. Sekrety i dane poufne: czego agent nie musi wiedzieć

Gdzie powinny żyć sekrety, a gdzie nie

Podstawowa zasada: żadne sekrety — klucze API, hasła, tokeny dostępu — nie powinny trafiać do promptu modelu, do widżetu, do logów ani do repozytorium. One żyją:

  • w zmiennych środowiskowych (process.env.SOMETHING),
  • w menedżerze sekretów (AWS Secrets Manager, GCP Secret Manager, Vault itp.),
  • w osobnych zaszyfrowanych magazynach, do których dostęp jest ściśle kontrolowany.

W naszym GiftGenius, na przykład, jest klucz do commerce‑API sklepu. Potrzebujemy, aby agent mógł tworzyć szkic zamówienia przez narzędzie MCP, ale sam klucz nie powinien być widoczny dla modelu.

// mcp/tools/createOrderDraft.ts
const COMMERCE_API_KEY = process.env.COMMERCE_API_KEY!;

export async function createOrderDraft(args: {
  userId: string;
  giftId: string;
  quantity: number;
}) {
  // Model nigdy nie zobaczy COMMERCE_API_KEY — jest tylko tutaj, po stronie serwera
  const res = await fetch(`${process.env.COMMERCE_API_URL}/orders/draft`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${COMMERCE_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(args),
  });

  if (!res.ok) {
    throw new Error(`Commerce API returned ${res.status}`);
  }

  return res.json(); // W odpowiedzi agentowi zwrócimy już bezpieczny obiekt
}

Ważne: w odpowiedzi narzędzia nie powinieneś „przemycać” kluczy ani innych wrażliwych szczegółów. Agentowi wystarczy znać draftOrderId, listę pozycji i, ewentualnie, status.

PII i minimalizacja danych w kontekście

Oprócz sekretów jest jeszcze kategoria PII (dane osobowe użytkowników): imiona, telefony, adresy dostawy, e‑maile itp. Agentowi często nie jest potrzebny cały ten „surowy” tekst. Wystarczy ustrukturyzowany profil: „lubi gry planszowe”, „wiek 30–35”, „orientacyjny budżet 50–70$”.

Zamiast wrzucać do promptu pełną historię zamówień użytkownika, można zrobić narzędzie get_user_profile_summary, które zwróci już zredukowany i zanonimizowany profil.

type ProfileSummary = {
  ageRange: '18-25' | '26-35' | '36-50' | '50+';
  interests: string[];
  preferredBudget: { min: number; max: number };
};

export async function getUserProfileSummary(userId: string): Promise<ProfileSummary> {
  // Tutaj sięgasz do bazy danych, ale na zewnątrz oddajesz tylko zagregowane informacje
  return {
    ageRange: '26-35',
    interests: ['gry planszowe', 'gadżety'],
    preferredBudget: { min: 30, max: 80 },
  };
}

Model widzi dokładnie tyle, ile potrzeba do doboru prezentu — i ani trochę więcej.

Scrubbing logów

Logi to naturalne miejsce, gdzie przypadkiem wypływają sekrety i PII. Zwłaszcza gdy napisać „wygodny” logger w stylu console.log(...) i drukować „wszystko jak leci”.

Dobrym podejściem jest posiadanie centralnego loggera, który przed wydrukiem przechodzi po ładunku i maskuje pola wrażliwe.

type LogPayload = Record<string, unknown>;

const SENSITIVE_KEYS = ['email', 'phone', 'cardNumber', 'token'];

function scrub(payload: LogPayload): LogPayload {
  const result: LogPayload = {};
  for (const [key, value] of Object.entries(payload)) {
    if (SENSITIVE_KEYS.includes(key)) {
      result[key] = '***redacted***';
    } else {
      result[key] = value;
    }
  }
  return result;
}

export function logEvent(event: string, payload: LogPayload) {
  const safe = scrub(payload);
  console.log(JSON.stringify({ event, ...safe }));
}

Zamiast kiedyś złapać incydent produkcyjny „logujemy telefony i tokeny klientów od pół roku”, budujesz system tak, że to po prostu niemożliwe. To nie tylko kwestia staranności, ale i przyszłych wymogów compliance (GDPR i lokalne przepisy): im mniej PII w logach, tym łatwiej żyje się produktowi.

5. Monitoring i obserwowalność agenta

Co dokładnie trzeba widzieć

Ograniczyliśmy, co agent może robić, jakie dane widzi i co trafia do logów. Kolejne pytanie — jak zrozumieć, czy w całym tym „zoo” agent na produkcji zachowuje się tak, jak zamierzaliśmy?

Zwykły monitoring „serwis żyje / nie żyje” dla agenta jest prawie bezużyteczny. Ważne jest nie tylko wiedzieć, że proces działa, ale rozumieć jego zachowanie: jakie kroki wykonuje, jakie narzędzia wywołuje, gdzie popełnia błędy, gdzie się zapętla.

Minimalny zestaw danych dla każdego run:

  • agent_run_id — unikalny identyfikator uruchomienia;
  • anonimowy user_id lub ID sesji;
  • nazwa agenta i środowisko;
  • lista wywołanych narzędzi: nazwa, liczba, łączny czas;
  • kroki workflow i na którym kroku się zatrzymaliśmy;
  • status końcowy: success, partial_success, failed, canceled, timeout, limits_exceeded.

Można to ująć jako strukturę:

type RunStatus =
  | 'success'
  | 'partial_success'
  | 'failed'
  | 'canceled'
  | 'timeout'
  | 'limits_exceeded';

type ToolCallLog = {
  name: ToolName;
  durationMs: number;
  success: boolean;
};

type AgentRunLog = {
  runId: string;
  agentId: string;
  userId: string;
  env: Env;
  startedAt: string;
  finishedAt: string;
  status: RunStatus;
  toolCalls: ToolCallLog[];
  errorMessage?: string;
};

Przykład „wrappera” wokół uruchomienia agenta

Załóżmy, że masz funkcję runAgent, która enkapsuluje realne wywołanie Agents SDK. Owińmy ją monitoringiem:

async function runAgentWithLogging(
  agent: AgentConfig,
  input: string,
  userId: string,
): Promise<string> {
  const runId = crypto.randomUUID();
  const startedAt = new Date();

  const toolCalls: ToolCallLog[] = [];

  try {
    const result = await runAgent(agent, input, {
      userId,
      limits: defaultLimits,
      onToolCall: (name, durationMs, success) => {
        toolCalls.push({ name, durationMs, success });
      },
    });

    const finishedAt = new Date();

    const log: AgentRunLog = {
      runId,
      agentId: agent.id,
      userId,
      env,
      startedAt: startedAt.toISOString(),
      finishedAt: finishedAt.toISOString(),
      status: 'success',
      toolCalls,
    };

    logEvent('agent_run', log);
    return result;
  } catch (err) {
    const finishedAt = new Date();
    const log: AgentRunLog = {
      runId,
      agentId: agent.id,
      userId,
      env,
      startedAt: startedAt.toISOString(),
      finishedAt: finishedAt.toISOString(),
      status: 'failed',
      toolCalls,
      errorMessage: (err as Error).message,
    };
    logEvent('agent_run', log);
    throw err;
  }
}

Tutaj runAgent to czarna skrzynka, która może być zaimplementowana przez prawdziwe Agents SDK; my pokazujemy, jak dodać obserwowalność, nie wiążąc się z konkretnym API.

Logi vs metryki vs tracing

Wygodnie rozróżniać trzy poziomy obserwowalności:

Poziom Co to jest Przykład dla agenta GiftGenius
Logi „Historie” o konkretnych uruchomieniach (runs) Szczegółowy AgentRunLog z krokami i narzędziami
Metryki Zagregowane wskaźniki liczbowe p95 czas trwania run, średnia liczba tool‑calls, error‑rate
Tracing Drzewo/graf żądań i podżądań Run → kroki → tool‑calls → wywołania zewnętrznych API (commerce, baza danych itp.)

Metryki są potrzebne, by odpowiedzieć na pytanie „czy ogólnie jest dobrze?” (np. error‑rate z ostatniej godziny). Logi i tracing — by zrozumieć „dlaczego źle akurat tutaj?” i odtworzyć konkretne problematyczne uruchomienie.

Przykład zalążka metryk można zrealizować ponad logami: zadanie cykliczne agreguje zdarzenia agent_run i liczy p95 czasu trwania, liczbę błędów itp.

6. Jak to wygląda w GiftGenius w całości

Żeby to nie brzmiało jak zestaw abstrakcji, złóżmy obrazek dla naszej aplikacji szkoleniowej.

Agent gift-planner w środowisku produkcyjnym ma tylko bezpieczne narzędzia: dobór prezentów i pobieranie szczegółów. Nie widzi ani płatności, ani zarządzania zamówieniami. Jego instrukcje systemowe mówią, że nie powinien obiecywać użytkownikowi „zapłacę za ciebie”, a co najwyżej — przygotować rekomendacje i, ewentualnie, szkic listy prezentów.

Agent order-manager istnieje osobno i umie tylko pracować z zamówieniami. Na produkcji może tworzyć jedynie szkic zamówienia (create_order_draft), a potwierdzenie zamówienia (confirm_order) albo wykonuje człowiek poprzez jawny wyzwalacz UI w widżecie, albo jest dostępne tylko w dev/staging. Jego narzędzia używają sekretów (klucze API sklepu) wyłącznie po stronie backendu, a w odpowiedzi przekazują tylko potrzebne pola.

Obaj agenci są uruchamiani przez obudowę runAgentWithLogging, która nakłada limity i zapisuje logi z agent_run_id, userId, środowiskiem i listą narzędzi. W logach nie ma e‑maili ani telefonów; te pola są wcześniej czyszczone scrubberem. Profil użytkownika jest używany w formie zanonimizowanej: zakres wiekowy, zainteresowania, budżet, ale nie pełny tekst historii zakupów.

Infrastruktura, w której żyje serwer MCP i serwis agenta, jest odizolowana: kontenery z systemem plików tylko do odczytu (poza /tmp lub specjalnie wydzielonym katalogiem), ograniczeniem CPU/RAM, siecią po allow‑list domen. Jeśli agent spróbuje wywołać „coś lewego”, po prostu fizycznie nie będzie mógł się do tego dostać.

Jeśli w pewnym momencie widzisz skok metryki „odsetek runów ze statusem limits_exceeded” albo „średnia liczba tool‑calls > 10”, rozumiesz, że albo prompt stał się zbyt gadatliwy, albo któreś narzędzie się psuje i zmusza agenta do powtarzania kroków.

To już zachowanie dorosłego serwisu, a nie eksperymentalnego agenta „byle działało”.

7. Typowe błędy przy wyprowadzaniu agentów na produkcję

Wszystko, o czym mówiliśmy wyżej, to „prawidłowy” obraz agenta produkcyjnego. W praktyce jednak najczęściej spotyka się typowe miny. Zbierzmy je w jedną listę: jeśli unikniesz choćby tych błędów, wejście na produkcję przebiegnie znacznie spokojniej.

Błąd nr 1: agentowi „pozwolono na wszystko”.
Częsty scenariusz: opisaliście mnóstwo narzędzi MCP (wyszukiwanie, modyfikacja, usuwanie, płatności), a przy tworzeniu agenta po prostu podaliście mu całą listę. W efekcie model może przypadkowo wywołać usunięcie albo płatność tam, gdzie chcieliście tylko odczyt. Leczy się to rozdzieleniem narzędzi według ról i stworzeniem kilku węższych agentów, z których każdy ma własne allowedTools.

Błąd nr 2: weryfikacja uprawnień tylko w promcie.
Czasem deweloperzy piszą w instrukcjach systemowych: „nigdy nic nie kupuj bez potwierdzenia użytkownika” i na tym poprzestają. Ale prompt to słaba ochrona, a jail‑breaki i zwykłe błędy nikt nie odwołał. Potrzebne są realne sprawdzenia na poziomie backendu: „agent ma dozwolone to narzędzie” i „użytkownik ma dozwolone to narzędzie”, inaczej jedna nieostrożna generacja może doprowadzić do działań, których nikt nie planował.

Błąd nr 3: sekrety w promptach i logach.
Czasem kusi, żeby „przyspieszyć integrację” i po prostu włożyć klucz API do system‑promptu lub przekazać go w argumentach narzędzia, by agent sam chodził do zewnętrznego API. W rezultacie klucz trafia i do logów modelu, i potencjalnie do systemów zewnętrznych. To prosta droga do wycieków i bana w Store. Sekrety muszą żyć tylko po stronie serwera, w zmiennych środowiskowych lub menedżerze sekretów, i nigdy nie trafiać do kontekstu modelu.

Błąd nr 4: „surowe” logi bez scrubowania.
Podczas debugowania wygodnie pisać console.log(...) i o tym zapomnieć. Po paru miesiącach okazuje się, że w logach leżą adresy użytkowników, telefony, numery zamówień z PII. Szczególnie nieprzyjemne w świecie GDPR i innych regulacji. Lepiej od razu wprowadzić centralny logger i automatyczne maskowanie pól wrażliwych, nawet jeśli wydaje się, że „logujemy tylko na dev”.

Błąd nr 5: brak limitów na zachowanie agenta.
Bez ograniczeń na kroki, czas i liczbę wywołań narzędzi agent może się zapętlić: wielokrotnie wołać to samo narzędzie, próbować bez końca poprawiać ten sam błąd, zużywać mnóstwo tokenów i obciążać zewnętrzne API. W najlepszym razie dostaniesz gigantyczne rachunki za modele, w najgorszym — położysz backend i zezłościsz wszystkich użytkowników. Limity cyklu run i rozsądne domyślne wartości timeoutów są obowiązkową częścią konfiguracji.

Błąd nr 6: mieszanie operacji read i write w jednym narzędziu.
Czasem powstają „wygodne” metody w stylu getOrCreateOrder, które przy braku zamówienia tworzą nowe. Dla klasycznego backendu to dopuszczalny wzorzec, ale w świecie agentów może prowadzić do nieoczekiwanych skutków ubocznych: model chciał tylko poznać stan, a narzędzie coś utworzyło. Dużo bezpieczniej oddzielać get_order_details i create_order_draft, wtedy nawet przy powtórnych wywołaniach konsekwencje są bardziej kontrolowalne.

Błąd nr 7: ignorowanie obserwowalności.
Wielu zaczyna od „później dołożymy logi i metryki, teraz najważniejsze, żeby działało”. Agenci bez monitoringu to czarna skrzynka: nie wiesz, jakie narzędzia wywołują, ile kroków robią, gdzie popełniają błędy. Każda skarga użytkownika zamienia się w dochodzenie w ciemnym pokoju. Dużo łatwiej od razu założyć strukturę logów (agent_run_id, narzędzia, status) i podstawowe metryki, niż później próbować dobudować to ponad chaotycznym kodem.

1
Ankieta/quiz
Orkiestracja agentów, poziom 12, lekcja 4
Niedostępny
Orkiestracja agentów
Orkiestracja agentów z Agents SDK
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION