Strona główna / Artykuły / Bramy zatwierdzenia w LangGraph.js: Wstrzymywanie agentów za pomocą interrupt() i Command

Bramy zatwierdzenia w LangGraph.js: Wstrzymywanie agentów za pomocą interrupt() i Command

Stwórz minimalną bramkę zatwierdzeń LangGraph.js, która zawiesza działanie przed wystąpieniem skutku ubocznego, zbiera decyzję człowieka w terminalu i bezpiecznie kontynuuje pracę od punktu kontrolnego.

3146 słów

Część działań agenta ma zbyt duże konsekwencje, by mogły być wykonywane bez nadzoru: wysyłanie e-maila w imieniu kogoś, usuwanie rekordu, zatwierdzanie płatności. W takich przypadkach chcemy, aby agent zaproponował działanie, zatrzymał się i czekał, aż osoba odpowie „tak” lub „nie”. Ten przewodnik pokazuje, jak w LangGraph.js można zaimplementować takie zachowanie przy użyciu jak najmniejszego grafu, aby można było dokładnie zobaczyć, w jaki sposób interrupt(), checkpointer, thread_id oraz Command({ resume }) współpracują, by wstrzymać wykonywanie i wznowić je później.

Zamierzenie polega na tym, by tutaj nie było ani orkiestracji wielu agentów, ani wywołań LLM. Cały schemat mieści się w jednym zdaniu: wstrzymać, pozwolić człowiekowi podjąć decyzję, wznowić.

Kiedy agent nie powinien mieć ostatecznego słowa

Autonomia jest cenna, gdy możemy spokojnie pozwolić agentowi działać na podstawie własnego osądu. Wiele działań nie spełnia tego wymogu, więc krok rewizji przez człowieka jest warte tych dodatkowych wysiłków. Typowe przykłady to:

  • wysyłanie e-maila lub wiadomości w czacie w imieniu użytkownika
  • modyfikacja lub usuwanie rekordu w bazie danych
  • zatwierdzanie płatności
  • wdrażanie kodu
  • znikanie zasobów chmurowych
  • przekierowanie zgłoszenia obsługi klienta na wyższy poziom
  • publikowanie treści stworzonych przez AI

W każdym przypadku cel jest taki sam. Agent nadal myśli i przygotowuje działanie, ale przed podjęciem jakiegokolwiek nieodwracalnego kroku prezentuje swój zamiar i przekazuje ostateczną decyzję osobie. To właśnie oznacza w praktyce podejście Human-in-the-Loop (HITL).

Jak współpracują funkcje interrupt() i Command

W swojej istocie HITL w LangGraph działa w ten sposób: graf zatrzymuje się w trakcie wykonywania, czeka na dane z zewnątrz, a następnie kontynuuje pracę przy użyciu tych danych.

Zatrzymanie jest realizowane za pomocą funkcji interrupt(). Gdy ją wywoła węzeł, LangGraph zatrzymuje bieżące uruchomienie i zapisuje stan grafu za pomocą skonfigurowanego checkpointera, aby to samo uruchomienie można było później kontynuować. Twoja aplikacja otrzymuje wartość przekazaną do interrupt(), pokazuje ją osobie, zbiera odpowiedź, a następnie wznowia pracę grafu, wywołując go za pomocą obiektu Command, który zawiera tę odpowiedź.

Ogólna sekwencja wygląda następująco:

Graph starts
    ↓
Agent decides to send email
    ↓
⏸ interrupt()
    ↓
Human reviews the action
    ↓
Approve / Reject
    ↓
Command({ resume: ... })
    ↓
Graph continues

Pamiętaj o tej strukturze; każdy fragment kodu poniżej odpowiada jednej strzałce w niej.

Sytuacja: e-mail wymagający zatwierdzenia

Przykład dotyczy e-maila. Agent decyduje, że chce wysłać tę wiadomość:

Meeting at 5 PM with Aman

Zanim ta wiadomość trafi gdziekolwiek, ktoś powinien ją przejrzeć i albo zatwierdzić, albo odrzucić. Struktura rozgałęzień wygląda w ten sposób:

User
  ↓
Agent decides to send email
  ↓
⏸ Human approval
  ↓
┌───────────────┐
│ Approve       │ → Send email
│ Reject        │ → Stop
└───────────────┘

Aby skupić się na mechanizmie pauzowania i kontynuowania, nie wykorzystuje się żadnego rzeczywistego dostawcy e-maili. Węzeł, który „wyśyła” e-mail, po prostu wyświetla go w terminalu. Zastąpienie go rzeczywistym API później nic nie zmienia w przepływie sterowania.

Budowanie grafu krok po kroku

Zainstaluj LangGraph i przygotuj importy

Zacznij od pustego projektu Node.js i dodaj LangGraph:

npm install @langchain/langgraph

w zależności od wersji, którą zainstalujesz, LangGraph.js może również wymagać @langchain/core jako zależności równorzędnej; jeśli npm wyda ostrzeżenie w tej sprawie lub importy się nie udają, dodaj również ten pakiet i sprawdź aktualne dokumentacje instalacyjne.

Dane wprowadzane przez człowieka będą pochodzić z terminala za pośrednictwem wbudowanego modułu Node readline/promises, więc nie jest potrzebny żaden dodatkowy pakiet. Importy ładują narzędzie do budowania grafu, pomocnik do adnotacji stanu, sentyneli START i END, funkcję interrupt, mechanizm sprawdzania stanu w pamięci oraz klasę Command, a także elementy związane z interfejsem readline:

import {
  StateGraph,
  Annotation,
  START,
  END,
  interrupt,
  MemorySaver,
  Command,
} from "@langchain/langgraph";

import readline from "node:readline/promises";
import {
  stdin as input,
  stdout as output,
} from "node:process";

Plik używa składni modułów ES (import), więc należy albo nadać mu rozszerzenie .mjs, albo ustawić "type": "module" w pliku package.json, a także użyć wersji Node obsługującej await na poziomie najwyższym, ponieważ później kod będzie używał await na poziomie modułu.

Zdefiniuj stan, który przenosi graf

Stan w LangGraph to wspólny obiekt, który przepływa przez graf. Każdy węzeł odczytuje z niego dane i zwraca częściowe aktualizacje. Ten graf wymaga zaledwie dwóch pól:

  • message – tekst e-maila proponowanego przez agenta
  • decision – odpowiedź udzielona przez człowieka
const StateAnnotation = Annotation.Root({
  message: Annotation,
  decision: Annotation,
});

Annotation.Root() określa strukturę stanu. Przy braku wskazanego reduktora każde pole po prostu przyjmuje najnowszą wartość, która została do niego zapisana. Węzeł agenta wypełni pole message; węzeł zatwierdzenia wypełni pole decision, gdy człowiek udzieli odpowiedzi.

Napisz akcję, którą chcesz zabezpieczyć

Następnie przychodzi węzeł reprezentujący ryzykowną operację. W środowisku produkcyjnym powinien on wywołać API do wysyłania e-maili. Tutaj jedynie loguje dane:

function sendEmail(state) {
  console.log(`\n📧 Email sent: "${state.message}"`);

  return {};
}

To, co robi ta funkcja, jest prawie nieistotne. Ważne jest kiedy ona działa: nigdy przed tym, jak osoba ludzka wyda zatwierdzenie. Reszta struktury służy do zapewnienia takiego porządku. Zauważ, że zwraca pusty obiekt, co oznacza, iż nie zmienia stanu.

Zastąpienie decyzji agenta

W rzeczywistym systemie to tutaj model językowy AI odczytałby prośbę użytkownika i zdecydował, że potrzebna jest e-mail, prawdopodobnie poprzez wywołanie narzędzia. Dodanie modelu w tym miejscu tylko odwróciłoby uwagę od mechanizmów HITL, dlatego zwykła funkcja pełni rolę agenta i zwraca wiadomość, którą „wybrał”:

function agent() {
  return {
    message: "Meeting at 5 PM with Aman",
  };
}

Traktuj ten węzeł jako komunikat agenta informujący o jego zamiarze: to jest e-mail, który chce wysłać. Jeśli później zastąpisz go modelem językowym AI oraz logiką wywoływania narzędzi, mechanizmy zatwierdzania wokół niego pozostaną w zasadzie takie same.

Pauza dla osoby ludzkiej za pomocą interrupt()

To jest serce wzorca. Węzeł zatwierdzenia wywołuje interrupt() z treścią opisującą, co wymaga decyzji, i zwraca to, co wraca jako nowa decyzja:

function humanApproval(state) {
  const decision = interrupt({
    message: state.message,
    question: "Do you want to send this email?",
  });

  return {
    decision,
  };
}

Jeszcze przed osiągnięciem tego wywołania

interrupt(...)

graf się zatrzymuje. Przekazany obiekt staje się treścią przerwy, którą aplikacja wywołująca może odczytać. W tym przypadku jest to:

{
  message: "Meeting at 5 PM with Aman",
  question: "Do you want to send this email?"
}

Aplikacja pokazuje tę treść osobie, czeka na odpowiedź i wznowia działanie grafu. Kluczowym elementem jest to, że wartość podana przy wznowieniu staje się wartością zwracaną przez interrupt(). Dlatego ta linia

const decision = interrupt(...);

efektywnie przekształca się w następującą po zatwierdzeniu przez osobę:

const decision = "approve";

Ta wartość jest zapisywana w stanie jako decyzja. Funkcja routingu następnie wybiera na jej podstawie kolejny krok:

function routeAfterApproval(state) {
  if (state.decision === "approve") {
    return "sendEmail";
  }

  return END;
}

Odpowiedź "approve" powoduje wywołanie funkcji sendEmail; wszystko inne kończy działanie. Traktowanie każdej innej odpowiedzi niż zatwierdzenie jako sygnału do zatrzymania jest rozsądnym standardem dla mechanizmów bezpieczeństwa: jeśli pojawi się nieoczekiwana wartość, proces zostanie przerwany zamiast wykonać odpowiednią akcję.

Dodaj checkpointer i połącz graf

Zanim przystąpi się do kompilacji, potrzebny jest jeszcze jeden element: checkpointer. Ponieważ proces ma zostać zatrzymany, a później wznowiony, LangGraph musi zachować stan wykonywania w momencie przerwy. Bez checkpointera nie ma czego przywrócić. Do demonstracji wystarczy implementacja w pamięci operacyjnej:

const checkpointer = new MemorySaver();

Następnie zarejestruj trzy węzły, połącz START z agentem oraz agenta ze szczeblem zatwierdzenia, dodaj warunkową krawędź od szczebla zatwierdzenia sterowaną przez routeAfterApproval, a następnie skompiluj z checkpointerem:

const graph = new StateGraph(StateAnnotation)
  .addNode("agent", agent)
  .addNode("humanApproval", humanApproval)
  .addNode("sendEmail", sendEmail)

  .addEdge(START, "agent")
  .addEdge("agent", "humanApproval")

  .addConditionalEdges(
    "humanApproval",
    routeAfterApproval,
    {
      sendEmail: "sendEmail",
      [END]: END,
    }
  )

  .compile({
    checkpointer,
  });

Trzeci argument funkcji addConditionalEdges mapuje każdą wartość, którą może zwrócić router, na odpowiedni węzeł docelowy, co umożliwia również LangGraphowi poprawne narysowanie grafu. Otrzymana topologia:

START
  ↓
agent
  ↓
humanApproval
  ↓
 ┌──────────────┐
 │              │
approve       reject
 │              │
 ↓              ↓
sendEmail      END
 │
 ↓
END

MemorySaver przechowuje punkty kontrolne w pamięci procesu, co jest idealne do eksperymentów, ale bezużyteczne po zakończeniu procesu. Do rzeczywistych implementacji należy używać trwałego punktu kontrolnego wspieranego bazą danych, aby zatrzymana sesja przetrwała restarty i mogła być kontynuowana z innego procesu lub serwera – co jest standardową sytuacją, gdy zatwierdzenie przychodzi kilka godzin później przez interfejs webowy. Aby lepiej zrozumieć, jak wewnętrznie przechowywane są punkty kontrolne, zapoznaj się z artykułem opisującym, w jaki sposób mechanizm przechowywania w pamięci LangGraph organizuje punkty kontrolne i dokonuje zapisów.

Drugim elementem zapewniającym trwałość są thread_id. Określają one, o której zatrzymanej sesji mowa. Aby ją zatrzymać lub wznowić, konieczne jest użycie tego samego thread_id; w przeciwnym razie LangGraph nie ma sposobu na odnalezienie zapisanej ekzekucji.

Ruch procesu z terminala

Rozpocznij wykonywanie i zidentyfikuj przerwę

Interfejs readline przekształca terminal w narzędzie do sprawdzania przez człowieka:

const rl = readline.createInterface({
  input,
  output,
});

Obiekt konfiguracji zawiera thread_id w polu configurable. Wszystko, co dotyczy tego procesu, zarówno początkowe wywołanie, jak i wznowienie, musi używać tego samego obiektu (lub przynajmniej tej samej identyfikatora):

const config = {
  configurable: {
    thread_id: "thread-1",
  },
};

Rozpocznij działanie grafu od stanu początkowego. Węzeł agenta nadpisze pusty element message:

const stream = await graph.stream(
  {
    message: "",
  },
  config
);

Wykonywanie przechodzi przez agent do humanApproval, gdzie funkcja interrupt() je zatrzymuje. Następnie strumień dostarcza fragment zawierający klucz __interrupt__. Jego pierwsza wartość to dane przekazane do interrupt(), które pętla wyświetla osobie sprawdzającej:

for await (const chunk of stream) {
  if (chunk.__interrupt__) {
    const interruptValue =
      chunk.__interrupt__[0].value;

    console.log(
      "\n⏸ Waiting for human approval...\n"
    );

    console.log(
      "The agent wants to send this email:"
    );

    console.log(`"${interruptValue.message}"`);

    console.log(
      `\n${interruptValue.question}`
    );
  }
}

Wynik w terminalu wygląda następująco. Pierwszy wiersz przedstawia oryginalną prośbę użytkownika w kontekście zadania; kod pokazany powyżej jej nie wyświetla:

User: Send an email to Aman about the 5 PM meeting

⏸ Waiting for human approval...

The agent wants to send this email:
"Meeting at 5 PM with Aman"

Do you want to send this email?

W tym momencie graf jest wstrzymany i żaden e-mail nie został wysłany. Proces czeka na sygnał z mechanizmu kontrolnego.

Zapytaj o decyzję i ją zweryfikuj

Następnie zapytaj osobę sprawdzającą. Pętla będzie zadawać pytania, aż otrzyma jedną z dwóch akceptowalnych odpowiedzi, uprzednio normalizując spacje i wielkość liter:

let humanAnswer;

while (true) {
  humanAnswer = (
    await rl.question("\nApprove or reject: ")
  )
    .trim()
    .toLowerCase();

  if (
    humanAnswer === "approve" ||
    humanAnswer === "reject"
  ) {
    break;
  }

  console.log(
    'Please type "approve" or "reject".'
  );
}

W terminalu wyświetla się Zatwierdź lub odrzuć: oraz blok. Wpisanie approve zakończa pętlę i umożliwia wznowienie pracy grafu. Weryfikacja danych wejściowych przed wznowieniem jest warta tych kilku dodatkowych linii kodu – wartość, którą przekazujesz dalej, jest dokładnie tą samą, którą zobaczy twoja logika routingu.

Wznowienie działania za pomocą polecenia

Kontynuacja oznacza ponowne uruchomienie grafu, ale zamiast nowych danych wejściowych przekazuje się Command, którego pole resume zawiera odpowiedź człowieka:

await graph.invoke(
  new Command({
    resume: humanAnswer,
  }),
  config
);

Wykorzystywany jest ten sam config, co oznacza tę samą wartość thread_id, i w ten sposób LangGraph lokalizuje przerwany proces. Wartość resume jest zwracana jako wartość zwrotna metody interrupt(). Gdy wpisano approve, wywołanie wewnątrz humanApproval

const decision = interrupt(...);

teraz zwraca approve. Węzeł zwraca to jako decision, a router uruchamia:

function routeAfterApproval(state) {
  if (state.decision === "approve") {
    return "sendEmail";
  }

  return END;
}

Ponieważ decision jest równe "approve", kontrola przechodzi do sendEmail, a terminal wyświetla:

📧 Email sent: "Meeting at 5 PM with Aman"

E-mail został „wyśłany” dopiero po wyraźnej zgodzie. Gdy skończysz, wezwij rl.close(), aby interfejs readline zwolnił stdin i proces mógł się zakończyć.

Jak wygląda odrzucenie

Zrób ponownie skrypt. Ponieważ MemorySaver znajduje się w pamięci, nowy proces rozpoczyna się z pustym magazynem punktów kontrolnych; jeśli uruchomisz go ponownie w tym samym procesie, użyj nowego thread_id, aby nie wznowić działania już zakończonego procesu. Gdy pojawi się komenda,

Approve or reject:

odpowiedź:

reject

Grafik jest kontynuowany z tą wartością. Poniższy fragment przedstawia ją dosłownie dla większej klarowności; w skrypcie jest to po prostu humanAnswer:

await graph.invoke(
  new Command({
    resume: "reject",
  }),
  config
);

Tym razem state.decision zawiera wartość "reject", więc router zwraca END i funkcja sendEmail nigdy nie jest wykonywana:

Agent wants to send email
        ↓
   ⏸ Paused
        ↓
Human: reject
        ↓
      END

Różnica ta ma znaczenie. Graf nie tylko generuje inną wiadomość w przypadku odrzucenia; węzeł odpowiedzialny za efekt uboczny w ogóle się nie wykonuje. To właśnie sprawia, że krok zatwierdzenia stanowi rzeczywistą ochronę, a nie tylko element dekoracyjny.

Pełny obraz

Gdy połączymy wszystko razem, pełny graf wygląda w ten sposób:

                 ┌─────────────┐
                 │    START    │
                 └──────┬──────┘
                        ↓
                 ┌─────────────┐
                 │    Agent    │
                 └──────┬──────┘
                        ↓
              ┌───────────────────┐
              │  Human Approval   │
              │                   │
              │   ⏸ interrupt()   │
              └─────────┬─────────┘
                        ↓
                 Human decides
                   /       \
                  /         \
             approve       reject
                ↓             ↓
         ┌────────────┐      END
         │ sendEmail  │
         └──────┬─────┘
                ↓
               END

W jednym zdaniu: agent podejmuje decyzję, graf się wstrzymuje, człowiek sprawdza sytuację, graf wznowia działanie, a dopiero wtedy wykonuje się odpowiednia akcja.

Pułapka ponownego wykonywania: zachowanie efektów ubocznych po przerwie

Jedno z zachowań funkcji interrupt() dziwi wielu osób. Krótko mówiąc, LangGraph nie kontynuuje działania od wiersza po interrupt() – ponownie uruchamia węzeł zawierający ten wywołanie od jego pierwszego wiersza. Różnica przy drugim przetworzeniu polega na tym, że wywołanie interrupt() natychmiast zwraca wartość kontynuacji, zamiast wstrzymywać działanie.

To ma bezpośrednie konsekwencje dla efektów ubocznych. Wszystko, co znajduje się przed interrupt() w tym samym węźle, jest wykonywane raz, gdy graf się wstrzymuje, a ponownie, gdy kontynuuje działanie. To właśnie tego należy unikać:

function humanApproval(state) {
  saveSomethingToDatabase();

  const decision = interrupt("Approve?");

  return { decision };
}

Tutaj saveSomethingToDatabase() zostałby wykonywany dwa razy przy jednej akceptacji. Rozwiązanie jest strukturalne: należy utrzymać węzeł akceptacji wolny od efektów ubocznych i umieścić każdą rzeczywistą akcję w późniejszym węźle, który zostanie uruchomiony dopiero po odpowiedzi użytkownika. W ten sposób jest zorganizowany przykład:

humanApproval
      ↓
interrupt()
      ↓
human response
      ↓
sendEmail

Jeśli naprawdę musisz wykonać jakąś czynność przed przerwaniem w tym samym węźle, spraw, aby była ona idempotentna (bezpieczna do powtórzenia, na przykład operacja upsert z kluczem stanowiącym stabilny identyfikator) lub przenieś ją do osobnego, poprzedzającego węzła, którego wynik został już zapisany jako punkt kontrolny i nie zostanie ponownie wykonywany.

Co dzieje się w tle, krok po kroku

Gdy kod nie stanowi już problemu, cykl życia jest krótki:

  1. Graf zaczyna się wykonywać.
  2. Węzeł agenta decyduje, że chce wysłać e-mail.
  3. Wykonanie dociera do funkcji interrupt().
  4. LangGraph zatrzymuje działanie.
  5. Bieżący stan jest zapisywany przez mechanizm kontrolny.
  6. Aplikacja otrzymuje treść przerwania.
  7. Ktoś sprawdza proponowaną akcję.
  8. Ta osoba podejmuje decyzję.
  9. Aplikacja wznowia działanie grafu za pomocą polecenia Command, używając tego samego thread_id.
  • interrupt() zwraca odpowiedź osoby.
  • Graf kontynuuje się wzdłuż gałęzi wybranej przez tę odpowiedź.
  • Zawołania API to część prosta; kluczową koncepcją, którą warto opanować, jest wzorzec pauzy i kontynuacji:

            Graph
              │
              ▼
        Agent decision
              │
              ▼
          interrupt()
              │
              │
          ┌───┴───┐
          │ Human │
          └───┬───┘
              │
         approve/reject
              │
              ▼
           resume
              │
              ▼
          Continue
    

    Gdy ten proces stanie się jasny, HITL przestaje wydawać się tajemniczy. To po prostu pauza z kontrolnymi punktami kończąca się wpisaną odpowiedzią.

    Kontrola własnej implementacji

    Zanim polegniesz na mechanizmie zatwierdzenia, przeprowadź kilka szybkich testów:

    • Zatwierdź raz i upewnij się, że operacja zostanie wykonaana dokładnie jeden raz.
    • Odrzuć i upewnij się, że węzeł operacji nigdy nie zostanie wykonywany, a nie tylko że wynik będzie inny.
    • Wpisz nieważną odpowiedź i upewnij się, że pytanie zostanie powtórzone, zamiast kontynuować z błędnymi danymi.
    • Kontynuuj działanie z innym thread_id i sprawdź, czy pierwotne wykonywanie nie uległo zmianie.
  • Z stałym wskaźnikiem stanu uruchomienia, ponownie rozpocznij proces pomiędzy pauzą a kontynuacją i upewnij się, że działanie nadal zostanie zakończone.
  • Gdzie stosuje się ta sama brama

    E-mail jest jedynie wygodną demonstracją. Ta identyczna struktura pasuje do każdej czynności wymagającej nadzoru ludzkiego: komunikacji, aktualizacji lub usuwania rekordów, zatwierdzania płatności, wdrażania rozwiązań, dezinstalacji zasobów chmurowych, publikowania utworzonego treści lub eskalacji zapytań o wsparcie. Zmienia się węzeł czynności; brama przed nim pozostaje niezmienna. Jeśli chcesz zobaczyć kroki zatwierdzania obok innych wzorców orkiestracji, takich jak routowanie i rozprzestrzenianie, ten przegląd pięciu wzorców LangGraph przedstawia je obok siebie.

    Główne wnioski

    • interrupt() wstrzymuje wykonywanie grafu i przekazuje dane do Twojej aplikacji; wartość, z którą kontynuujesz działanie, staje się jej wartością zwrotną.
    • Command({ resume: ... }) zwraca odpowiedź użytkownika do wstrzymanego procesu.
    • Dla wstrzymywania działania konieczny jest wskaźnik stanu; w środowisku produkcyjnym należy używać takiego, który zachowuje dane między restartami, aby akceptacje mogły dotrzeć po ponownym uruchomieniu.
    • Aby wstrzymać i wznowić dany proces, musi być użyty ten sam thread_id.
    • Gdy kontynuuje się działanie, węzeł zawierający interrupt() uruchamia się ponownie od początku, dlatego efekty uboczne należy umieszczać w późniejszych węzłach lub uczynić je idempotentnymi.
    • Każdą informację inną niż wyraźna akceptacja należy kierować do punktu zatrzymania, aby brama została zamknięta.

    Nie potrzebujesz złożonego procesu pracy, aby umożliwić człowiekowi kontrolę nad agentem. Jedna dobrze umieszczona pauza przed kluczowym krokiem pozwala agentowi wykonać większość pracy, podczas gdy osoba decyduje o ostatecznym rozstrzygnięciu.

    Powiązane materiały

  • Specjalistyczni agenci, szybki marszrutyzator i interupcja: Coach LangGraph — Zaprojektuj asystenta LangGraph z dwoma specjalistami, wykorzystującego deterministyczny marszrutyzator, wspólny stan przetrzymywany podczas przenoszenia zadań oraz punkt kontrolny ludzki oparty na interupcji i poleceniach.
  • Weryfikacja działań agentów AI: uprawnienia, bramy zatwierdzenia i poziomy ryzyka — Dowiedz się, dlaczego agenci działający samodzielnie potrzebują nastawienia na weryfikację, oraz jak minimalne uprawnienia, zatwierdzenie przez człowieka i autonomia oparta na ryzyku pomagają ograniczyć ich błędy.
  • Pauzowanie i wznowienie agentów LangGraph za pomocą interrupt() i Command — Jak funkcje interrupt() i Command w LangGraph wykorzystują punkty kontrolne oraz wątki, aby zapauzować agenta w celu uzyskania zatwierdzenia, edycji lub wprowadzenia danych przez człowieka, a następnie bezpiecznie go wznowić później.
  • Obcinanie stringów bez uszkodzenia emoji w JavaScript za pomocą Intl.Segmenter — Dlaczego metody slice() i obcinanie oparte na rozszerzaniu uszkadzają emoji oraz tekst z akcentami, jak to wpłynęło na rzeczywiste produkty oraz jak zamiast tego obcinać stringi przy granicach grafemów.