Saltar al contenido principal
Adjuntas una política de firewall a una clave, el modelo transmite una llamada a herramienta de vuelta, y la etapa response la quita o reescribe antes de que tu agente actúe sobre ella. La decisión de aplicación es idéntica en cada proveedor — mismas reglas, mismos veredictos, mismos eventos. Lo que difiere es la forma de cable que tu cliente ve una vez que el firewall ha actuado sobre una llamada a herramienta transmitida, porque OpenAI chat, la OpenAI Responses API y el /v1/messages nativo de Claude enmarcan cada uno las llamadas a herramienta de forma diferente. Esta página es la nota enfocada a esas diferencias observables por el cliente. No re-documenta el lenguaje de reglas — ver Reglas del Firewall — ni el modelo de etapas, cubierto en Etapas. Para el mecanismo interno de retener-y-reensamblar compartido por los tres, lee Internos de streaming.

1. Por qué el streaming del proveedor en el firewall difiere por cable

En una respuesta no transmitida el firewall ve toda la respuesta de una vez y decide. En un stream, la llamada a herramienta del modelo llega como una secuencia de fragmentos — un nombre en un frame, el JSON de argumentos goteado a lo largo de muchos más. Un veredicto necesita la llamada completa (nombre y argumentos completos), y un fragmento de llamada a herramienta, una vez reenviado, no se puede retractar. Así que en cada proveedor el gateway hace lo mismo: deja que el contenido ordinario se transmita en vivo, y retiene los frames de llamada a herramienta hasta que la llamada esté completamente ensamblada. Al final del stream evalúa cada llamada ensamblada y emite solo las supervivientes — en la forma de evento propia de ese proveedor.
Tu texto nunca se detiene. Solo los frames de llamada a herramienta se retienen. El contenido del asistente, el razonamiento y los frames de role se transmiten en vivo y sin cambios. La retención aplica desde el primer fragmento de llamada a herramienta hasta el final de ese turno — así que una respuesta de solo chat se transmite exactamente como si no hubiera firewall adjunto.

2. OpenAI chat completions

En /v1/chat/completions, las llamadas a herramienta se transmiten como fragmentos delta.tool_calls con clave por índice. La compuerta retiene esos (y la forma legada delta.function_call) y, en el frame de cierre, emite las llamadas supervivientes reindexadas desde cero, seguidas de un frame de finalización:
ResultadoQué recibe tu cliente
allowLos frames retenidos originales, byte a byte — paso verdadero.
sanitizeUn delta tool_calls con argumentos reescritos, luego finish_reason: "tool_calls".
deny (algunas llamadas)Solo las llamadas supervivientes, luego finish_reason: "tool_calls".
deny (todas las llamadas)Ninguna llamada a herramienta, luego finish_reason: "stop" — el turno parece como si el modelo eligiera responder en texto.
Esa última fila es la señal contra la que probar: cuando un firewall quita cada llamada a herramienta de un turno de OpenAI chat, tu agente ve un finish_reason: "stop" limpio, no un frame de error. Construye tu bucle para tratar “ninguna llamada a herramienta este turno” como un resultado válido.

3. OpenAI Responses API

El stream nativo /v1/responses tiene su propio modelo de eventos — una llamada a herramienta es un ítem function_call que abre con response.output_item.added, transmite fragmentos response.function_call_arguments.delta, y se completa en response.output_item.done. El firewall evalúa en done, el primer punto en que la llamada está completa:
Los eventos added / delta-de-argumentos / done del ítem se emiten sin cambios una vez que la llamada se libera.
La cáscara added se transmite, luego un done cuyos argumentos son la versión redactada — los fragmentos delta-de-argumentos originales se descartan para que el valor sin redactar nunca te llegue.
Los eventos en buffer se descartan, y el ítem denegado también se filtra fuera del objeto terminal response.completed desde el que tu cliente construye su estado final — sin referencia colgante a una llamada que nunca se ejecutó.
Los deltas de texto y razonamiento se transmiten en vivo todo el tiempo, exactamente como en chat completions.

4. Claude nativo /v1/messages

Un stream nativo de Anthropic es una bestia diferente: el contenido llega como bloques indexadoscontent_block_startcontent_block_delta (fragmentos input_json_delta) → content_block_stop — cerrados por un message_delta que lleva stop_reason. El firewall retiene desde el primer bloque tool_use, evalúa cada uno, y reconstruye los bloques supervivientes con índices contiguos para que un bloque quitado no deje hueco de índice. La señal específica de Claude es stop_reason. Si cada bloque tool_use se deniega, un stop_reason de tool_use le prometería a tu cliente una llamada a herramienta que nunca llega — así que el gateway lo reescribe a 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"}
Un quitado parcial conserva los bloques tool_use supervivientes, reindexados contiguamente, y deja stop_reason: "tool_use" intacto.
Esto aplica a los streams nativos de Claude. Un modelo Claude llamado a través de endpoints en formato OpenAI se aplica en el cable de OpenAI chat en su lugar (§2), así que muestra finish_reason: "stop", no stop_reason: "end_turn". Empareja tu manejo de fin-de-turno con el formato de cable que llamaste, no con el modelo subyacente.

5. Un ejemplo concreto

La misma regla produce la misma decisión en cada proveedor — solo difiere la forma de cable que tu cliente lee. Autórala una vez, en la etapa response:
{
  "stage": "response",
  "tool_name_glob": "shell.exec",
  "verdict": "deny",
  "args_match_json": "{\"clauses\":[{\"path\":\"$.command\",\"op\":\"regex\",\"value\":\"rm -rf|mkfs\"}]}"
}
Transmite el mismo prompt de tres formas y el firewall deniega la llamada rm -rf cada vez. Lo que tu cliente observa:
CableSeñal terminal después de un quitado completo
OpenAI chatfinish_reason: "stop"
OpenAI Responsesítem ausente de response.completed
Claude nativostop_reason: "end_turn"
La llamada coincidida-y-denegada aparece de forma idéntica en los eventos del firewall sin importar el cable, así que tu observabilidad es agnóstica del proveedor aunque el stream no lo sea.

6. Qué permanece constante a través de los proveedores

El cable difiere; el contrato no:
  • Los veredictos y reglas son agnósticos del cable. allow / audit / deny / sanitize significan lo mismo en cada proveedor. Ver Veredictos.
  • Sanitize toca solo los argumentos de la llamada a herramienta, nunca el contenido que una herramienta devuelve — en cada cable. Ver Sanear respuestas.
  • Allow es paso verdadero. Cuando el firewall no toma ninguna acción, los frames retenidos se reproducen como los bytes upstream exactos — sin reagrupar, sin campos específicos del proveedor perdidos.
  • El modo shadow aplica en todas partes. Actívalo y las llamadas a herramienta retenidas siempre sobreviven (degradadas a audit) para que puedas medir el impacto de una política a través de proveedores antes de que cambie el tráfico. Ver Modo shadow.

7. Dónde encaja esto

Internos de streaming

El mecanismo de retener-ensamblar-reensamblar que cada proveedor comparte.

Etapas

Por qué la aplicación de llamadas a herramienta transmitidas vive en la superficie response.

Veredictos

Las decisiones agnósticas del proveedor a las que resuelve una llamada transmitida.

Filtrado de respuesta

Gobernar las llamadas a herramienta que un modelo emite, en stream o no.
Para las amenazas que estas verificaciones transmitidas abordan, ver Llamadas a herramienta peligrosas y Exfiltración de datos; para dónde se sitúa la aplicación de stream en la ruta de la solicitud, ver Latencia de la ruta de aplicación.