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:
| Wynik | Co otrzymuje twój klient |
|---|---|
| allow | Oryginalne wstrzymane ramki, bajt-po-bajcie — prawdziwa przepustowość. |
| sanitize | Jedna 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. |
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:
allow → buforowane zdarzenia wypłukane dosłownie
allow → buforowane zdarzenia wypłukane dosłownie
Zdarzenia
added / delta-argumentu / done elementu są emitowane
niezmienione, gdy wywołanie przejdzie.sanitize → powłoka elementu + przepisane done
sanitize → powłoka elementu + przepisane done
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.deny → element usunięty wszędzie
deny → element usunięty wszędzie
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.4. Natywny Claude /v1/messages
Natywny strumień Anthropic to inna bestia: treść przybywa jako indeksowane
bloki — content_block_start → content_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:
tool_use, reindeksowane
ciągłe, i zostawia stop_reason: "tool_use" nietknięty.
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 etapieresponse:
rm -rf za każdym razem. Co obserwuje twój klient:
| Drut | Sygnał terminalny po pełnym usunięciu |
|---|---|
| OpenAI chat | finish_reason: "stop" |
| OpenAI Responses | element nieobecny w response.completed |
| Natywny Claude | stop_reason: "end_turn" |
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/sanitizeznaczą 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.
