response 执行面在你的智能体据此行动之前剥除或重写它。
执行决策在每个提供商上都相同——相同的规则、相同的判定、相同的事件。不同
的是一旦防火墙作用于一个流式工具调用后你的客户端看到的线上形态,因为
OpenAI chat、OpenAI Responses API 和原生 Claude /v1/messages 各自以不同
方式构造工具调用帧。
本页是关于那些客户可观察差异的聚焦说明。它不重新记录规则语言——参见
防火墙规则——也不重新记录执行面模型,那在
执行面 中讲解。关于三者共享的内部
挂起-重组机制,阅读 流式内部机制。
1. 为什么防火墙提供商流式因线而异
在一个非流式响应上,防火墙一次性看到整个回复并决定。在一个流上,模型的 工具调用作为一系列片段到达——一个帧里一个名称,参数 JSON 滴滴答答地跨更多 帧。一个判定需要完整的调用(名称和完整参数),而一个工具调用片段 一旦被转发就无法收回。 所以在每个提供商上网关做同样的事:它让普通内容实时流式通过,并挂起 工具调用帧直到该调用被完整组装。在流结束时它评估每个组装好的调用,并只 发出幸存者——以那个提供商自己的事件形态。你的文本绝不停顿。 只有工具调用帧被挂起。Assistant 内容、推理和角色帧
实时且原封不动地流式传输。挂起从第一个工具调用片段一直应用到那个回合的
结束——所以一个纯聊天响应的流式传输完全就像没有绑定防火墙一样。
2. OpenAI chat completions
在/v1/chat/completions 上,工具调用作为按索引为键的 delta.tool_calls
片段流式传输。这道门挂起那些(以及遗留的 delta.function_call 形态),并在
收尾帧上发出从零重新索引的幸存调用,后跟一个 finish 帧:
| 结果 | 你的客户端收到什么 |
|---|---|
| allow | 原始挂起帧,逐字节——真正的 passthrough。 |
| sanitize | 一个带重写参数的 tool_calls 增量,然后 finish_reason: "tool_calls"。 |
| deny(部分调用) | 只有幸存的调用,然后 finish_reason: "tool_calls"。 |
| deny(全部调用) | 没有工具调用,然后 finish_reason: "stop"——该回合看起来像模型选择用文本回答。 |
3. OpenAI Responses API
原生/v1/responses 流有它自己的事件模型——一个工具调用是一个
function_call 项,以 response.output_item.added 开启、流式传输
response.function_call_arguments.delta 片段、并在 response.output_item.done
完成。防火墙在 done 处评估,即该调用完整的第一个点:
allow → 缓冲的事件逐字刷出
allow → 缓冲的事件逐字刷出
一旦该调用通过,该项的
added / 参数增量 / done 事件被原封不动发出。sanitize → 项外壳 + 重写的 done
sanitize → 项外壳 + 重写的 done
added 外壳流式传输,然后是一个其参数为脱敏版本的 done——原始的
参数增量片段被丢弃,所以未脱敏的值永不到达你。deny → 项在各处被移除
deny → 项在各处被移除
缓冲的事件被丢弃,且被拒绝的项也被从你的客户端据以构建其最终状态的
终端
response.completed 对象中过滤掉——没有指向一个从未运行的调用的
悬空引用。4. 原生 Claude /v1/messages
一个原生 Anthropic 流是另一种东西:内容作为带索引的块到达——
content_block_start → content_block_delta(input_json_delta 片段)→
content_block_stop——由一个携带 stop_reason 的 message_delta 收尾。
防火墙从第一个 tool_use 块开始挂起、评估每一个,并以连续索引重建幸存
的块,这样一个被剥除的块不留下索引缺口。
Claude 特有的迹象是 stop_reason。如果每个 tool_use 块都被拒绝,一个
tool_use 的 stop_reason 会向你的客户端承诺一个永不到达的工具调用——所以
网关把它重写为 end_turn:
tool_use 块(连续重新索引),并让
stop_reason: "tool_use" 保持不变。
5. 一个具体示例
同一条规则在每个提供商上产生同样的决策——只有你的客户端读取的线上形态 不同。在response 执行面上编写它一次:
rm -rf 调用。你的
客户端观察到的:
| 线 | 全部剥除后的终端信号 |
|---|---|
| OpenAI chat | finish_reason: "stop" |
| OpenAI Responses | 项从 response.completed 中缺席 |
| 原生 Claude | stop_reason: "end_turn" |
6. 跨提供商保持不变的东西
线不同;约定不变:- 判定和规则是线无关的。
allow/audit/deny/sanitize在每个 提供商上意思相同。参见 判定。 - Sanitize 只触碰工具调用参数,绝不触碰工具返回的内容——在每条线上。 参见 脱敏响应。
- Allow 是真正的 passthrough。 当防火墙不采取行动时,挂起的帧作为确切 的上游字节被重放——无重新批处理,无丢失提供商特定字段。
- 影子模式在各处都适用。 打开它,挂起的工具调用总会幸存(降级为
audit),这样你能在一条策略改变流量之前跨提供商衡量它的影响。参见 影子模式。
7. 这部分的位置
流式内部机制
每个提供商共享的挂起-组装-重组机制。
执行面
为什么流式工具调用执行存在于
response 执行面上。判定
一个流式调用解析到的提供商无关决策。
Response 过滤
对一个模型发出的工具调用设门,无论是否流式。
