Przejdź do głównej treści
Przypinasz politykę firewalla do klucza, model strumieniuje wywołanie narzędzia z powrotem, a etap response usuwa je lub przepisuje, zanim twój agent na nim zadziała. Decyzja o egzekwowaniu jest identyczna na każdym dostawcy — te same reguły, te same werdykty, te same zdarzenia. Różni się kształt drutu, który widzi twój klient, gdy firewall zadziała na strumieniowanym wywołaniu narzędzia, ponieważ OpenAI chat, API OpenAI Responses i natywny Claude /v1/messages każdy obramowuje wywołania narzędzi inaczej. Ta strona to skupiona notatka o tych obserwowalnych dla klienta różnicach. Nie dokumentuje ponownie języka reguł — zobacz Reguły Firewall — ani modelu etapów, omówionego w Etapy. Dla wewnętrznego mechanizmu wstrzymaj-i-złóż-ponownie, współdzielonego przez wszystkie trzy, przeczytaj Wewnętrzności strumieniowania.

1. Dlaczego strumieniowanie dostawców firewalla różni się drutem

W odpowiedzi nie-strumieniowej firewall widzi całą odpowiedź naraz i decyduje. Na strumieniu wywołanie narzędzia modelu przybywa jako sekwencja fragmentów — nazwa w jednej ramce, JSON argumentów rozsączony przez wiele kolejnych. Werdykt potrzebuje kompletnego wywołania (nazwa i pełne argumenty), a fragment wywołania narzędzia, raz przesłany, nie da się wycofać. Więc na każdym dostawcy brama robi to samo: pozwala zwykłej treści strumieniować na żywo i wstrzymuje ramki wywołań narzędzi, dopóki wywołanie nie jest w pełni złożone. Na końcu strumienia ewaluuje każde złożone wywołanie i emituje tylko ocalałe — w kształcie zdarzenia tego dostawcy.
Twój tekst nigdy się nie zatrzymuje. Tylko ramki wywołań narzędzi są wstrzymywane. Treść asystenta, rozumowanie i ramki roli strumieniują na żywo i niezmienione. Wstrzymanie dotyczy od pierwszego fragmentu wywołania narzędzia do końca tej tury — więc odpowiedź czysto-czatowa strumieniuje dokładnie tak, jakby żaden firewall nie był przypięty.

2. OpenAI chat completions

Na /v1/chat/completions wywołania narzędzi strumieniują jako fragmenty delta.tool_calls kluczowane indeksem. Brama wstrzymuje je (oraz starszy kształt delta.function_call) i, na ramce zamykającej, emituje ocalałe wywołania reindeksowane od zera, po których następuje ramka zakończenia:
WynikCo otrzymuje twój klient
allowOryginalne wstrzymane ramki, bajt-po-bajcie — prawdziwa przepustowość.
sanitizeJedna delta tool_calls z przepisanymi argumentami, potem finish_reason: "tool_calls".
deny (niektóre wywołania)Tylko ocalałe wywołania, potem finish_reason: "tool_calls".
deny (wszystkie wywołania)Brak wywołania narzędzia, potem finish_reason: "stop" — tura wygląda, jakby model zdecydował odpowiedzieć tekstem.
Ten ostatni wiersz to sygnał do przetestowania: gdy firewall usuwa każde wywołanie narzędzia z tury OpenAI chat, twój agent widzi czyste finish_reason: "stop", nie ramkę błędu. Zbuduj swoją pętlę, by traktowała „brak wywołania narzędzia w tej turze” jako poprawny wynik.

3. API OpenAI Responses

Natywny strumień /v1/responses ma własny model zdarzeń — wywołanie narzędzia to element function_call, który otwiera się response.output_item.added, strumieniuje fragmenty response.function_call_arguments.delta i kończy się response.output_item.done. Firewall ewaluuje przy done, pierwszym punkcie, gdy wywołanie jest całe:
Zdarzenia added / delta-argumentu / done elementu są emitowane niezmienione, gdy wywołanie przejdzie.
Powłoka added strumieniuje, potem done, którego argumenty to wersja zredagowana — oryginalne fragmenty delta-argumentu są odrzucane, więc niezredagowana wartość nigdy do ciebie nie dociera.
Buforowane zdarzenia są odrzucane, a odmówiony element jest też odfiltrowany z terminalnego obiektu response.completed, z którego twój klient buduje swój stan końcowy — bez wiszącego odniesienia do wywołania, które nigdy się nie uruchomiło.
Delty tekstu i rozumowania strumieniują na żywo przez cały czas, dokładnie jak w chat completions.

4. Natywny Claude /v1/messages

Natywny strumień Anthropic to inna bestia: treść przybywa jako indeksowane blokicontent_block_startcontent_block_delta (fragmenty input_json_delta) → content_block_stop — zamknięte przez message_delta niosący stop_reason. Firewall wstrzymuje od pierwszego bloku tool_use, ewaluuje każdy i rekonstruuje ocalałe bloki z ciągłymi indeksami, więc usunięty blok nie zostawia luki w indeksach. Sygnałem charakterystycznym dla Claude jest stop_reason. Jeśli każdy blok tool_use jest odmówiony, stop_reason o wartości tool_use obiecywałby twojemu klientowi wywołanie narzędzia, które nigdy nie przybywa — więc brama przepisuje go na end_turn:
upstream:  content_block_start (tool_use) … message_delta {stop_reason: "tool_use"}
            ↓ firewall denies the only tool_use
client:    (no tool_use block)            … message_delta {stop_reason: "end_turn"}
Częściowe usunięcie zachowuje ocalałe bloki tool_use, reindeksowane ciągłe, i zostawia stop_reason: "tool_use" nietknięty.
To dotyczy natywnych strumieni Claude. Model Claude wywołany przez endpointy w formacie OpenAI jest egzekwowany na drucie OpenAI chat zamiast tego (§2), więc pokazuje finish_reason: "stop", nie stop_reason: "end_turn". Dopasuj swoją obsługę końca tury do formatu drutu, który wywołałeś, nie modelu podstawowego.

5. Jeden konkretny przykład

Ta sama reguła produkuje tę samą decyzję na każdym dostawcy — różni się tylko kształt drutu, który czyta twój klient. Napisz ją raz, na etapie response:
{
  "stage": "response",
  "tool_name_glob": "shell.exec",
  "verdict": "deny",
  "args_match_json": "{\"clauses\":[{\"path\":\"$.command\",\"op\":\"regex\",\"value\":\"rm -rf|mkfs\"}]}"
}
Strumieniuj ten sam prompt na trzy sposoby, a firewall odmawia wywołania rm -rf za każdym razem. Co obserwuje twój klient:
DrutSygnał terminalny po pełnym usunięciu
OpenAI chatfinish_reason: "stop"
OpenAI Responseselement nieobecny w response.completed
Natywny Claudestop_reason: "end_turn"
Dopasowane-i-odmówione wywołanie pojawia się identycznie w zdarzeniach firewalla niezależnie od drutu, więc twoja obserwowalność jest niezależna od dostawcy, mimo że strumień nie jest.

6. Co pozostaje stałe między dostawcami

Drut się różni; kontrakt nie:
  • Werdykty i reguły są niezależne od drutu. allow / audit / deny / sanitize znaczą to samo na każdym dostawcy. Zobacz Werdykty.
  • Sanitize dotyka wyłącznie argumentów wywołania narzędzia, nigdy treści, którą narzędzie zwraca — na każdym drucie. Zobacz Sanityzacja odpowiedzi.
  • Allow to prawdziwa przepustowość. Gdy firewall nie podejmuje akcji, wstrzymane ramki są odtwarzane jako dokładne bajty nadrzędnego — bez ponownego batchowania, bez utraconych pól specyficznych dla dostawcy.
  • Tryb cienia stosuje się wszędzie. Włącz go, a wstrzymane wywołania narzędzi zawsze ocaleją (zdegradowane do audit), więc możesz zmierzyć wpływ polityki między dostawcami, zanim zmieni ruch. Zobacz Tryb cienia.

7. Gdzie to pasuje

Wewnętrzności strumieniowania

Mechanizm wstrzymaj-złóż-złóż-ponownie, który dzieli każdy dostawca.

Etapy

Dlaczego egzekwowanie strumieniowanych wywołań narzędzi żyje na powierzchni response.

Werdykty

Niezależne od dostawcy decyzje, do których rozwiązuje się strumieniowane wywołanie.

Filtrowanie odpowiedzi

Bramkowanie wywołań narzędzi, które model emituje, strumień czy nie.
Dla zagrożeń, którym zaradzają te strumieniowane sprawdzenia, zobacz Niebezpieczne wywołania narzędzi i Eksfiltrację danych; gdzie egzekwowanie strumienia siedzi na ścieżce żądania, zobacz Opóźnienie ścieżki egzekwowania.