跳到主要内容
版本:1.6.0

安全护栏行为

安全护栏会在 AISIX 转发受支持的网关流量之前、上游返回响应之后,或在这两处添加内容策略检查。其共用运行时控制项决定哪些路由和内容会被检查、匹配是否会阻止流量,以及调用方和操作员能够观测到什么。

内置关键词和 PII 安全护栏在网关内部评估内容。语义筛查会调用配置好的向量嵌入模型,自定义脚本则运行运维自定义的逻辑。服务提供方集成会把提取出的内容发送到审核服务,并应用其决策。选择安全护栏服务提供方比较了这些选项。

所有类别都使用本页所述的行为。适用的检查无法完成时,通常由失败设置决定会发生什么;部分流式和解码路径则有更严格或固定的处理方式。

检查位置​

安全护栏的检查位置决定 AISIX 在哪里执行检查,以及阻断内容时会发生什么:

检查位置AISIX 检查内容被阻断时的效果
inputAISIX 发送到服务提供方之前的调用方请求。不会调用服务提供方。
outputAISIX 返回给调用方之前的响应内容。调用方不会收到被阻断的响应。
both请求和响应内容。在路由支持的两侧都应用同一个安全护栏。

输入安全护栏覆盖标准推理路由中包含文本的部分,包括 Chat Completions、Completions、Responses、Messages、Embeddings、图像生成与编辑、视频生成、音频语音及重排请求。AISIX 还会扫描可选的音频转录和翻译提示词、Realtime 文本帧、MCP tools/call 参数以及 A2A 消息文本。相同的输入检查也覆盖 Batch 和 Fine-tuning 请求载荷,以及透传路由请求体。

输出安全护栏会扫描 Chat Completions、Completions、Responses、Messages、音频转录和翻译返回的文本。它们还会扫描 Realtime 文本帧、MCP tools/call 结果、Batch 和 Fine-tuning 响应载荷,以及透传路由响应体。A2A、Embeddings、图像生成与编辑、视频生成、音频语音、重排和 Count Tokens 只运行输入侧检查。Realtime 二进制帧会直接中继,不执行安全护栏内容检查。

在原生 /v1/responses 上,输出扫描覆盖消息文本,以及每个工具调用的名称、参数和输入。托管工具的结果(例如 mcp_call.output、File Search 结果和 Code Interpreter 日志)不会被扫描或脱敏,refusal 片段和托管工具的代码或操作文本同样不会。

在 /v1/chat/completions、/v1/messages、/v1/messages/count_tokens、/v1/responses 以及承载可识别信封的透传路由上,护栏在输入和输出两个方向都从不扫描工具调用 ID。这里的 ID 指将工具调用与其结果关联起来的 id、call_id、tool_use_id 或 tool_call_id,包括其中携带的 Gemini 思维签名。函数名称、参数和工具结果仍会被扫描。因此,只匹配工具调用 ID 内部文本的规则不会触发。

在透传路由上,安全护栏通过 passthrough_route 挂载作用域生效,并与调用方 API Key、团队和环境作用域并列。扫描文本遵循识别出的正文信封。携带可识别 LLM 信封(Chat、Completions、Responses)的请求会扫描提取出的消息或提示词文本,以及与这些字段一起转发的普通 JSON 字符串。工具调用 ID 以及安全护栏扫描范围列出的其他不透明句柄不会被扫描。提取范围包括工具调用参数以及 Anthropic Messages 流量的内容;各信封读取哪些内容,请参见安全护栏扫描范围。

如果 AISIX 识别出信封但无法解码,AISIX 不会回退到扫描原始正文。这可防止类型化的不透明媒体被重新解释为文本。对于透传路由承载的可识别 Responses 信封,输出扫描仅覆盖可识别的可见文本、refusal 和工具字段。已知媒体、推理和未知输出项仍不透明。格式错误的可识别缓冲输出无法评估,并按已配置的失败策略处理。未知的 Responses 流事件仍不透明。

对于不透明的缓冲流量,如果正文是 JSON,则扫描已解码的 JSON 字符串值;否则扫描文本正文。不透明 SSE 流只能扫描单独的 JSON 字符串。其他承载内容的载荷形态无法评估,因为 AISIX 无法可靠地将其文本与之前的流内容关联。

请求在转发前扫描。阻断判定会返回 422,并且不会接触上游。内置 pii 安全护栏无法把掩码结果写回服务提供方原生正文,因此仅配置 mask 动作时,匹配内容会不经掩码原样转发,并按无法执行脱敏的接入面所述记录。如果匹配内容绝不能到达上游,请使用 block 动作。Presidio、Lakera 等安全护栏类型则会在可掩码的结果没有回写通道时给出阻断判定。有一种情况会覆盖上述全部规则。Anthropic thinking 块内部的掩码命中,对所有安全护栏类型都会原样转发,因为该块是带签名的。参见推理与思考内容。

非流式响应会在中继前完整缓冲,AISIX 会扫描其符合条件的内容。阻断判定会返回 422,并扣下上游响应体。流式响应按护栏链的流式输出策略增量中继。hold-back 护栏会按窗口或全量缓冲帧,只有扫描文本通过后才释放;阻断判定会以一个终止性 SSE error 帧结束流,并丢弃被扣住的帧。各接口面使用各自协议的错误形态,并没有一个统一的帧。Chat Completions 携带 content_filter。/v1/messages 使用嵌套的 Anthropic 形态,错误类型取 Anthropic 合法值,error.code 为 content_filter 或 guardrail_unavailable。透传路由跟随其所中继流的协议:Anthropic Messages 流使用 Anthropic 形态,其他流使用 Chat Completions 形态。/v1/responses 则按上游区分:上游原生提供 Responses API 时,被保留的流会以 422 而不是一个帧被拒绝;AISIX 为不提供该 API 的服务提供方做桥接时,拒绝才是那个扁平的 Responses 错误事件。参见安全护栏拒绝。

无法执行脱敏的接入面​

以下接入面中继的内容,AISIX 没有改写通道:

  • 透传路由的请求和响应。
  • /a2a 消息。
  • Batch 和 Fine-tuning 的请求体,以及为其返回的上游正文。
  • /v1/realtime 双向的文本帧。
  • /v1/videos 的提示词。

pii 脱敏规则在这些接入面上命中时,内容不经修改放行,AISIX 会在用量记录中记录这次命中:

执行模式AISIX 记录的内容
block一条 action 为 mask_unsupported 的执行命中,附带检测器计数。guardrail_blocked 保持为 false;由于没有内容被脱敏,redacted_entity_counts 不计入该命中。
monitor一条 action 为 would_mask_unsupported 的监控命中,取代 would_mask,因为强制执行该规则同样不会脱敏任何内容。

如果匹配内容绝不能通过,请使用 block 动作。Presidio、Lakera、AWS Bedrock、阿里云 AI 安全护栏和自定义脚本不会在这些接入面上放行脱敏结果,而是拒绝该内容。其他运行安全护栏的接入面会改写需要脱敏的内容,本页其他部分所述的例外除外。

Files 路由不做筛查​

安全护栏完全不作用于 Files API。POST /v1/files、GET /v1/files、GET /v1/files/{id}、DELETE /v1/files/{id} 和 GET /v1/files/{id}/content 既不运行输入侧检查也不运行输出侧检查,无论请求作用域内挂了什么安全护栏、fail_open 取什么值。上传的文件会按调用方书写的样子原样离开网关,下载的文件也会按服务提供方存储的样子原样到达调用方。同一段文本放在对话消息里会被关键词、PII 或远程安全护栏拒绝,放在这里则根本不会被看到。

遥测不会反映这个缺口。 Files 的用量事件不会为这次缺失的安全护栏检查留下任何标记:安全护栏证据字段全是空的。因此即使在环境上挂了一条关闭式失败的安全护栏,也不会有任何信号提示这些路由没有被覆盖。Files 事件上空白的安全护栏证据部分并不能证明安全护栏覆盖到了它。如果上传内容必须经过筛查,请在它到达网关之前完成。

/v1/batches 和 /v1/fine_tuning/jobs 不受影响。它们的请求体是调用方提交的结构化 JSON,而不是上传的二进制块,这些请求体和对应的响应仍然会被扫描。

推理与思考内容​

推理内容按方向划分,两侧并不对称。制定 PII 或关键词策略时请围绕这条边界来规划,不要假定安全护栏覆盖了具备推理能力的模型所接触的一切。

调用方发来的推理内容在扫描范围内。 被回放的推理内容和其他文本一样,都是调用方提供、进入模型的文本,因此会被扫描,并在相同的位置被脱敏:

路由扫描并脱敏的位置
/v1/responsesreasoning item 的 content[].text 及其 summary[].text
/v1/chat/completionsassistant 轮次的 reasoning_content
/v1/messagesthinking 块的文本——会被扫描,但请看下面的例外

Anthropic thinking 块永远不会被改写。 thinking 块内部的 mask 动作命中会被原样转发。对所有安全护栏类型都是如此,而不仅仅是内置的 pii——因为改写该块会让服务提供方为它签发的签名失效。

block 动作的规则仍然会在 thinking 块上生效,因此如果这类内容绝不能到达上游,请使用 block 动作。redacted_thinking 块则是另一种情况:它只携带服务提供方的加密数据,完全不向扫描贡献文本,因此没有任何规则能在其中命中,它同样会被原样中继。

模型生成的推理内容不在扫描范围内。 它在任何路由上都不会被扫描,也不会被脱敏——/v1/chat/completions、/v1/messages、/v1/responses 都是如此,流式和缓冲式都是如此。

这收窄了治理边界

模型生成的推理内容此前会在三条路径上被顺带扫描到:缓冲式的 /v1/messages、/v1/responses,以及承载这两类信封的透传路由。现在不会了。如果一条 block 规则的关键词只出现在模型生成的推理内容里、而不出现在模型返回的答案中,那么它此前会阻断这些响应,现在不再阻断。调用方可见的答案的扫描范围与此前完全一致。

如果你此前依赖了这一行为,请把该规则移到输入检查位置。否则请接受「模型生成的推理内容位于输出安全护栏范围之外」这一事实。

有一个例外同样不值得依赖。当请求不匹配可识别的信封时,缓冲 JSON 响应会作为原始流量处理,因此其已解码的字符串值可能包含推理内容。这是原始内容扫描,而不是感知推理的处理。格式错误的可识别响应不会回退到该路径。在原始 SSE 流中,只有单独的 JSON 字符串可被扫描。

加密的推理内容不会被脱敏​

在 /v1/responses 上,一个 reasoning item 可能携带 encrypted_content 字段。该字段存放的是服务提供方的加密数据。AISIX 会原样转发它,从不扫描,也从不改写。

因此对可读的 summary 做脱敏,并不会把敏感文本从服务提供方收到的那份密文副本中去掉。调用方能看到的明文被脱敏了,服务提供方拿到的仍然是该 item 完整的加密载荷。请把 reasoning item 的 encrypted_content 视为会以未脱敏形式离开你的边界的内容;如果这不可接受,请使用 block 动作而不是 mask 动作。

keyword 和 PII 安全护栏如何读取结构化内容​

在 /v1/chat/completions、/v1/completions、/v1/messages、/v1/messages/count_tokens、/v1/responses 和 MCP tools/call 上,内置 keyword 和 pii 安全护栏逐段判断内容,所判断的片段与脱敏改写的片段相同,而不是把整个请求或响应拼接成一段文本来判断。因此,阻断规则、监控模式下的 would_mask 与强制执行的脱敏,对命中内容的判断始终一致。

以下每一项都是单独的片段:

  • 一条消息或一个内容块的文本。
  • 工具调用参数、工具输入、调用方发送的工具结果,以及 MCP tools/call 结果(包括 MCP structuredContent)中的每个字符串值。
  • 这些结构化载荷中的每个对象键和每个数字。AISIX 会判断它们,但改写它们会改变载荷的契约,因此 pii 脱敏规则命中其中之一时会阻断请求或响应,而不是脱敏。
  • 工具名称。AISIX 会判断它,但从不改写:阻断规则会拒绝,脱敏命中则原样转发。

这决定了模式能匹配什么。^\d{4}$ 这样的锚定模式匹配一个完整片段,因此能匹配参数 {"pin":1234} 中的数字。模式不能跨片段匹配:不能跨两条消息,不能跨键和值,也不能跨工具名称和参数。请检查那些依赖跨越这些边界匹配的规则。透传路由不适用这一模型,它们把提取出的文本作为一整段扫描。

在原生 /v1/messages 上,keyword 和 pii 不会读取非流式响应中的服务端工具块(server_tool_use、mcp_tool_use 和 *_tool_result)。流式响应会对每个发出 input_json_delta 的块脱敏该内容,但其中的工具结果块不会被扫描或脱敏。

同一条消息中的两个文本块是两个片段,这在 /v1/chat/completions 和 /v1/messages 上相同。被拆分到两个文本块中的违禁词或电话号码(例如 ["call 138001", "38000"])既不会被阻断,也不会被脱敏。当这样的 Chat 请求发往只接收纯消息文本的服务提供方时,AISIX 先逐块脱敏再拼接各块,因此该服务提供方收到的文本与接收内容块的服务提供方相同。用量事件中的 redacted_entity_counts 对每个被脱敏的实体只计一次。

选择输入消息窗口​

安全护栏的 input_messages 设置会选择请求中可供输入检查使用的部分。它不决定安全护栏会读取该窗口内的哪些角色。有些类型随后还会应用自己的角色选择器,例如语义筛查和 Azure 文本审核的 text_source。

input_messages可供输入检查使用的窗口
all整个请求:system、developer、user、assistant 和 tool 消息。这是默认值。
latest_turn最后一条 assistant 消息之后的消息,并排除 system 和 developer 消息:即当前这一轮的 user 消息,以及回应它们的工具结果。

IDE 和 Agent 类客户端每次请求都会把整段会话重发一遍。在 all 下,旧消息仍可供安全护栏使用,因此可能在后续请求中继续命中。latest_turn 会在安全护栏应用该类型特定的文本选择规则之前,移除模型已回答的历史记录。

在 latest_turn 下,窗口内的工具结果是当前这一轮产生的那些:/v1/chat/completions 上的 tool 消息、/v1/messages 上的 tool_result 块,以及 /v1/responses 上的 function_call_output item。

在 Responses API 上,AISIX 将 assistant 消息、function_call、custom_tool_call 和 reasoning item 视为 assistant 轮次,并将 function_call_output 和 custom_tool_call_output item 视为工具结果。当工具调用 item 位于所选窗口内时,其名称、参数或输入可供输入检查使用。会读取该 item 的脱敏安全护栏可以改写参数或自定义工具输入,但不会改写工具名称这一结构字段。

“最后一条 assistant 消息”是在 system 和 developer 以外的消息上判定的。因此结尾处的 assistant 消息属于预填(prefill)——调用方写给模型续写的文本——它属于当前这一轮,而不是这一轮的边界。完全没有 assistant 消息的请求,其窗口是除 system 和 developer 消息外的整个请求。

developer 消息是 OpenAI 对 system 消息的新叫法,所有安全护栏都把它当作 system 消息处理。在 latest_turn 下,无论它出现在哪里都位于窗口之外,因此既不会被检查,也不会被脱敏;在 all 下,它可供输入检查使用,是否会被读取取决于该安全护栏类型的文本选择规则。Responses API 上的 developer 输入 item 同样如此。

该设置对所有安全护栏类型生效,且只作用于输入检查:hook_point: input,以及 both 的输入侧。它不影响输出检查,输出检查始终读取完整响应。在 AISIX Cloud 中,给 hook_point 为 output 的安全护栏设置 latest_turn 会以 400 INVALID_REQUEST 拒绝,而不是存下一个永远不会生效的设置。

安全护栏只改写自己窗口内的内容

安全护栏只能读取和改写自己窗口内的消息,而且其类型可能进一步缩小该集合。在 latest_turn 下,做脱敏的安全护栏会让会话历史保持客户端发送时的原样。如果某个安全护栏的职责就是对整段会话做 PII 脱敏,请让它保持 all。

无论哪种取值,被跳过的历史消息仍然会原样转发给模型。latest_turn 改变的是 AISIX 向输入检查提供什么,而不是 AISIX 发送什么。

有一条边界值得明说:如果客户端把一条被阻断的消息保留在历史里并重新发送,而中间没有 assistant 的回复,那么这条消息仍然位于当前轮次窗口中,读取该角色的安全护栏仍可能检查它。

可以在控制台的安全护栏表单中配置它——输入扫描范围(整个请求 或 仅最新一轮)会在 input 和 both 检查位置下出现。通过 Admin API 时,请在安全护栏上设置 input_messages。在 resources.yaml 中,请更新 guardrails 中对应的条目:

resources.yaml(安全护栏条目)
guardrails:
- name: block-secrets
kind: keyword
hook_point: input
input_messages: latest_turn
patterns:
- kind: literal
value: internal-project-codename

安全护栏匹配​

选择钩子点后,需要确定安全护栏的作用范围:

  • 在 AISIX Cloud 中,创建安全护栏并把它附加到环境或选定的模型别名、MCP 服务器、调用方 API Key 或团队。Attachment 允许一个定义以明确优先级应用到多个 Scope。
  • 对于开源 AISIX 网关,在 resources.yaml 中声明安全护栏,并为它添加一条 guardrail_attachments 条目。两种来源判定作用范围的方式一致,因此文件里声明了、但没有任何 Attachment 的安全护栏不会检查任何流量。
启用前先附加

安全护栏的作用范围完全由它的 Attachment 决定。没有任何 Attachment 的安全护栏在任何地方都不生效,而这是一个合法状态、不是错误——它原本挂靠的模型、API Key 或团队可能已被删除,删除时会一并移除指向它们的 Attachment。控制台会把这样的安全护栏标记为未附加,网关也会为它记录一条告警。

由此有两点值得提前规划。删除最后一个 Attachment 不会放大安全护栏,而是让它静默——用这个动作让规则下线,而 enabled 用来表示规则是否存在。另外,通过 Admin API 创建的安全护栏在你 POST 一条 Attachment 之前不挂靠任何范围;控制台在创建表单里选定范围时会替你写入,直接调用 API 则不会。

当多个安全护栏同时适用于同一个请求时,AISIX 会把它们作为一条链路进行评估。任意一个安全护栏给出阻断判定,都会阻断请求或响应。

模型别名 Scope 和 MCP 服务器 Scope 选择的是单次请求不会同时具备的两个维度:MCP 工具调用不会解析出模型,模型请求也不会路由到 MCP 服务器。因此模型 Scope 的安全护栏永远不会检查 MCP 工具调用,MCP 服务器 Scope 的安全护栏也永远不会检查模型流量。若要同时覆盖两者,请把安全护栏附加到环境 Scope,或者两种 Attachment 各加一条。

A2A 调用既不解析出模型,也不解析出 MCP 服务器。只有环境、调用方 API Key 和团队 Attachment 能覆盖它的消息文本,模型和 MCP 服务器 Attachment 覆盖不到。

如果同一个安全护栏通过 AISIX Cloud 中配置的多个匹配 Scope 附加到请求链路中,AISIX 只保留一份该安全护栏。优先级最高的 Attachment 生效;优先级相同时,Scope 越具体优先级越高,顺序为:调用方 API Key、团队、模型别名或 MCP 服务器、环境。模型别名与 MCP 服务器同级,因为单次请求不可能同时匹配这两者。

安全护栏作用范围由调用方寻址的模型决定​

模型 Attachment 匹配的是调用方寻址的那个模型。AISIX 在挑选目标之前把该模型解析一次,并在整个请求中沿用它。当调用方寻址的模型是多目标路由、语义路由或合议模型时,被寻址的模型就是这个组,因此在模型这一维度上只有该组自身的 Attachment 会运行。附加到其成员模型上的安全护栏不会在该请求上运行,它们只在请求直接寻址该成员时运行。这只收窄模型这一个维度:环境、调用方 API Key 和团队 Attachment 匹配组请求的方式与匹配其他请求完全一致。

举例来说,把一个关键词安全护栏附加到成员模型 m 上,并把 m 放进组 g。发往 m 的请求会被该安全护栏筛查;发往 g 的请求不会,即使 g 把这个请求分发给了 m。若要筛查经由该组进入的流量,请把安全护栏附加到 g 上。

通配符别名不是组,行为也不同。通配符模型本身就是调用方寻址的那个条目,因此附加到 openai/* 上的安全护栏会覆盖该模型服务的每一个请求,包括针对 openai/gpt-4o 的请求。精确别名优先于通配符,因此之后再新建一个 openai/gpt-4o 模型,会把这个名字从通配符的覆盖范围里拿走:此时请求寻址的是这个新别名,只有附加到它上面的安全护栏会运行。

执行模式​

安全护栏的 enforcement_mode 决定 AISIX 在安全护栏检测到匹配内容后如何处理。

请根据策略是否已准备好影响流量来选择模式:

模式策略命中时适用场景
block(默认)输入侧命中会在模型服务提供方调用前停止请求;输出侧命中会阻止调用方收到响应。脱敏规则会改写受支持的内容。强制执行已经验证的策略。
monitorAISIX 允许请求或响应原样通过,并记录安全护栏本应阻断或脱敏的结果。在策略影响请求前,使用真实流量衡量误报并调优新策略。

基于检测的安全护栏可能产生误报和漏报。监控模式可让你先把策略命中与有代表性的流量对照,再允许策略影响请求。

AISIX 会把每次观察作为监控命中记录在该请求的用量记录中。一条命中会携带安全护栏名称、命中的检查位置,以及该安全护栏本应执行的动作:would_block 附带由代码生成的安全护栏种类和结果摘要,would_mask 附带安全的命中次数,would_mask_unsupported 同样附带安全的命中次数,表示该接入面本就无法执行脱敏。参见无法执行脱敏的接入面。如果检查不可用,would_block 摘要还会包含由代码生成且取值范围有限的失败标签,例如 custom_timeout;它不会包含脚本提供的原因或被检查的内容。自定义脚本固定使用计数名称 custom,值为本应发生变化的文本片段数;脚本返回的计数名称和值会被忽略。

AISIX Cloud 会原样存储用量记录详情。因此,由旧版网关创建的记录可能保留脚本提供的旧版原因、计数名称和值,其中也可能包含由请求内容或密钥派生的值。升级网关只会保护新记录,不会改写历史记录;请相应限制对旧版用量数据的访问。

使用这些记录观察命中率并调优策略,然后再切换到 block。对于本应阻断的观察,AISIX 还会在网关日志中以 info 级别记录以下内容:

guardrail in monitor mode observed a violation; not blocking (enforcement_mode=monitor)

流式输出​

当强制执行的安全护栏覆盖 output hook 时,AISIX 会先保留流式响应内容,直到相关检查可以执行。根据安全护栏类型,AISIX 可能保留完整响应,也可能检查缓冲窗口。这样可以避免被阻断或未脱敏的内容在安全护栏判定前到达调用方,也能检测跨越流式分片的文本片段。

监控模式下的安全护栏不会使流式输出延迟。当所有输出安全护栏都以监控模式运行时,AISIX 会在流到达时转发。流结束后,AISIX 对完整的生成输出执行检查并记录所有匹配。该扫描不设大小截断,因此长响应中任何位置的匹配都会被记录,包括流式工具调用参数、/v1/responses 流,以及流式音频转写的转写文本。

下方缓冲区上限不适用于仅包含监控模式的输出链路。AISIX 会交付并观测超大响应,而不是拒绝它。如果任何强制执行的安全护栏也覆盖输出钩子,AISIX 会保留流,以便强制执行链路在交付前检查。

暴露流式缓冲设置的安全护栏类型使用以下默认值:

{
"max_buffer_bytes": 262144,
"on_buffer_exceeded": "fail_closed"
}

对于 AISIX Cloud,请把这些字段添加到安全护栏的 config 对象中。对于开源 AISIX 网关,请直接添加到 resources.yaml 的安全护栏条目中。fail_closed 会阻断过大的输出。fail_open 会释放 AISIX 无法完整检查或改写的输出,因此只有在可用性比严格输出治理更重要时才使用。未暴露这些字段的安全护栏类型使用内置上限,并在溢出时按关闭式失败处理。

max_buffer_bytes 统计被保留的模型生成内容:助手文本、推理内容和工具调用参数,不计入 SSE 和 JSON 帧结构。因此同一响应在 Chat Completions、/v1/messages、/v1/responses 和透传路由上会在同一位置触及上限。在 AISIX 识别不出 Chat、Completions 或 Responses 的透传流上,每一帧的整个 data: 载荷都会计入。

不携带生成内容的帧(例如 ping、keep-alive 和空 delta)不计入上述字节数,但仍会占用内存。因此每个被保留的流还另有原始字节上限,为 max_buffer_bytes 的 128 倍,默认即 32 MiB。超过任一上限都属于同一种溢出,均按 on_buffer_exceeded 处理。普通的逐 Token 流的帧结构小于该比例,会先触及内容上限;例如 OpenAI 输出中文文本的流,其帧结构约为内容的 75 倍。

链路采用保留整个响应的输出安全护栏中最小的 max_buffer_bytes;只要其中任一安全护栏失败关闭,溢出时就会拒绝。哪些安全护栏参与计算取决于其流式处理模式,请参阅 Messages 与 Responses 上的窗口模式。设为 fail_open 时,保留的内容不经扫描和脱敏直接放行,响应的剩余部分不做输出扫描地流式转发,用量记录会携带绕过原因 output_buffer_exceeded。设为 fail_closed 时,拒绝会记录一条 action 为 blocked_buffer_exceeded 的执行命中,并指明触及上限的安全护栏。调大该安全护栏的 max_buffer_bytes 即可放行更大的响应。

Messages 与 Responses 上的窗口模式​

Azure 文本审核、阿里云内容审核、阿里云 AI 安全护栏和 custom 安全护栏默认使用 window 模式。在 /v1/chat/completions 和透传路由上,window 模式会在每个窗口扫描通过后逐窗口释放流。/v1/messages 和 /v1/responses 无法逐窗口释放流,因此只要输出链路能够拦截或脱敏,它们就会保留整个响应,并在扫描完成后才交付。调用方因此会比逐 Token 中继更晚收到第一个字节。这一行为同时适用于 window 模式和 buffer_full 模式,也正是它让 custom 输出安全护栏能够判定流式 /v1/messages 响应。仅含监控模式的链路仍会实时中继流,并在流结束时扫描一次。

当流被整体保留,且链路中没有任何输出安全护栏使用 buffer_full 时,由 window 模式安全护栏自身的 max_buffer_bytes 和 on_buffer_exceeded 决定保留行为。多个此类安全护栏的合并方式与 buffer_full 安全护栏相同:取最小的上限;除非每一个都设置为失败开放,否则溢出时失败关闭。这两个字段按安全护栏分别设置:在 AISIX Cloud 中放在 config 内,在 resources.yaml 中直接写在安全护栏条目上。

只要链路中有任何 buffer_full 输出安全护栏,它在所有路由上的上限和溢出策略都只由其 buffer_full 安全护栏决定,同一链路中 window 模式安全护栏的设置既不会收紧也不会放宽它。两种情况下,blocked_buffer_exceeded 都会指明决定链路上限的那个安全护栏。

在 /v1/chat/completions 上,如果链路的输出安全护栏全部使用 window 模式,为扫描而收集的流式工具调用名称和参数同样受该合并后上限约束。收集的内容超过上限即属于溢出,按 on_buffer_exceeded 处理。

AISIX 无法扫描的帧​

本节只适用于强制执行的输出安全护栏正在保留流的情况。没有这类安全护栏时,/v1/responses 和 /v1/messages 会原样中继上游帧。

AISIX 会在检查或脱敏前整理缓冲的流,使两项操作收到同一组可读帧。data: 负载为单个 JSON 文档、空值或 [DONE] 结束标记时,该帧可读。

上游帧AISIX 的处理方式
一个帧中有多行 data:按照 server-sent events 规范用换行符拼接,再扫描和脱敏完整负载。
没有终止符的最后一个帧当负载可读或不含可扫描内容时,补全、处理并交付该帧。
负载不是单个 JSON 文档的已终止帧扫描原始文本中的阻断命中。如果文本通过检查,AISIX 仍会丢弃该帧,因为无法安全写回脱敏结果。
负载不可读且未终止的最后一个帧在检查运行前丢弃。其文本既不放行也不扫描。
注释、保活、空负载或 [DONE]保留,因为其中没有可扫描的内容。

AISIX 丢弃内容时,调用方会收到缺少帧的流。网关会写入 warn 日志,记录检查位置、原因以及丢弃的帧数和字节数。

如果移除后没有留下任何可扫描内容,AISIX 会拒绝该响应,而不是返回空的 200。/v1/messages 上被保留的流累计超过 1 MiB 仍没有帧终止符时,也会发生同样的拒绝。失败标记为 unscannable_body;触及保留缓冲区上限则使用 output_buffer_exceeded。在 /v1/responses 上,该拒绝为 422:

{
"error": {
"message": "response rejected: a guardrail could not evaluate it (unscannable_body)",
"type": "content_filter",
"code": "guardrail_unavailable"
}
}

在 /v1/messages 上,响应已经作为流开始返回,因此同样的拒绝会以终止性的 SSE error 事件送达,携带相同的消息。该事件使用 Anthropic 信封,因此 error.type 为 invalid_request_error,并携带同样的 guardrail_unavailable 代码:

{
"type": "error",
"error": {
"type": "invalid_request_error",
"code": "guardrail_unavailable",
"message": "response rejected: a guardrail could not evaluate it (unscannable_body)"
}
}

两种情况下该请求都会被记为安全护栏阻断。这些拒绝不受安全护栏失败策略设置影响;参见保留流时的故障。

缓冲并不意味着每个字节都被扫描过

AISIX 可以保留它没有建模的合法 JSON 事件。该帧能够解析,但扫描或脱敏环节不会从中提取文本,因此这些字节会原样到达调用方。请把缓冲式输出安全护栏理解为对 AISIX 能识别的响应文本进行强制治理,而不是证明每个被中继的字节都经过了检查。

相邻端点对不可读流式内容的处理方式不同:

端点行为
/v1/chat/completions从已解码事件重建每个输出 chunk,因此解码器无法读取的内容会被丢弃,而不是未经扫描地中继。如果上游对流式请求返回普通 JSON,解码器不会产生事件;配置的流式读取预算也可能触发故障转移并最终返回错误。
/v1/responses 和 /v1/messages根据上游实际响应类型处理。对流式请求返回的普通 JSON 会进入缓冲扫描和脱敏路径。
透传路由缓冲响应会遵循可识别的协议字段,对未识别的流量则遵循不透明内容规则。可识别的流会扫描其协议载体。不透明 SSE 流只扫描单独的 JSON 字符串;其他承载内容的载荷形态无法评估。透传安全护栏只返回判定,不会改写响应,因此被放行的内容会原样中继。

安全护栏无法完成检查时​

有两种情况会导致安全护栏检查无法完成:

故障受影响的安全护栏示例
筛查后端没有返回判定调用远程服务的安全护栏、语义筛查和自定义脚本连接或服务提供方错误、限流、超时、脚本异常或脚本判定无法读取。
AISIX 无法把送达的内容交给安全护栏所有安全护栏类型,包括 keyword 和 pii无法读取的 Anthropic 请求、无效的 MCP 工具结果,或包含无效 UTF-8 的纯文本音频转录。

远程安全护栏包括 AWS Bedrock Guardrails、Azure AI Content Safety 和文本审核、阿里云内容安全和 AI 安全护栏、Lakera Guard、OpenAI Moderation 以及 Presidio。

适用的失败策略会管辖这两种故障。内置关键词和 PII 安全护栏不会调用后端,但 AISIX 无法向它们提供内容时,其失败策略仍然决定处理结果。

fail_open 现在对 keyword 和 pii 生效

早期版本的网关对这两个类型完全忽略 fail_open。如果某条 keyword 或 pii 安全护栏携带 fail_open: true——而设置它的时候该字段还什么都不做——那么网关一升级,它就会停止拒绝 AISIX 读不懂的请求体,无需任何运维操作,也不需要改动控制面。请在升级前而不是升级后检查这些配置行。

各设置分别管哪个检查侧​

设置检查侧适用类型默认值
fail_open输入侧所有类型false
output_fail_open输出侧除 keyword 和 pii 外的所有类型false
fail_open输出侧仅 keyword 和 piifalse

会调用外部服务的类型带有自己的 output_fail_open 来管输出侧,于是 fail_open 只管输入侧。keyword 和 pii 没有 output_fail_open——它们从不调用外部服务——因此这一个取值同时管它们的两个检查侧;给这两个类型传 output_fail_open 会被拒绝。

在阻断模式下,true 会放行安全护栏无法检查的流量并记录绕过;false 会拒绝流量。因此两侧默认都是失败关闭。监控模式和保留流存在下文所述的更窄例外。

在 AISIX Cloud 中,fail_open 是安全护栏上的顶层字段,所有类型的创建和编辑表单上都有该选项;output_fail_open 位于安全护栏的 config 对象内,控制台表单和 Admin API 对所有拥有该字段的类型都提供它。在开源 AISIX 网关中,两者都直接写在 resources.yaml 的安全护栏条目上。

AISIX 无法读取的内容会如何处理​

对于不可读的请求、MCP 工具结果或非流式音频转录,AISIX 会独立评估每条适用的安全护栏。至少有一条安全护栏必须既读取该侧交互,又在该侧失败关闭,AISIX 才会拒绝内容。

适用的安全护栏结果用量记录
至少一条安全护栏读取该侧并失败关闭AISIX 使用失败标记 unscannable_body 拒绝内容。安全护栏阻断。
读取该侧的所有安全护栏都失败放行AISIX 放行内容。绕过原因 unscannable_body。
没有安全护栏读取该侧AISIX 按没有安全护栏时的方式处理内容。不记录安全护栏绕过或拒绝。

检查方向和失败策略必须属于同一条安全护栏。例如,仅输出侧且失败关闭的安全护栏不会让仅输入侧且失败放行的安全护栏拒绝不可读请求。由于 hook_point 默认为 both,失败行为默认为关闭式,这一区别在显式修改任一设置时才尤为重要。

推理和音频路由以 HTTP 422 返回该拒绝。MCP 返回 HTTP 200 和带错误标记的工具结果,让 Agent 可以把拒绝当作工具输出处理。各端点的具体信封请参见安全护栏拒绝。

AISIX 解码不出来的转录文本​

非流式 /v1/audio/transcriptions 或 /v1/audio/translations 响应可能包含无效 UTF-8。AISIX 会使用替换字符扫描尽力而为的解码结果,但原本仍会中继原始字节。输出侧失败策略决定这些不可读字节能否到达调用方:

输出安全护栏状态结果
至少一条适用的输出安全护栏失败关闭AISIX 返回带 unscannable_body 的 422,并把请求记录为安全护栏阻断。
所有适用的输出安全护栏都失败放行如果可读文本通过检查,AISIX 中继原始字节并记录绕过。
没有安全护栏读取输出AISIX 中继原始字节,不记录安全护栏事件。

失败关闭响应使用以下错误:

{
"error": {
"message": "response rejected: a guardrail could not evaluate it (unscannable_body)",
"type": "content_filter",
"code": "guardrail_unavailable"
}
}

只要有输出安全护栏适用,可读部分就会被扫描,即使最终中继原始字节。该文本中的策略命中仍可独立阻断响应,因此一个请求可以同时携带 guardrail_blocked 和值为 unscannable_body 的绕过原因。不可读请求体不同:AISIX 无法把其中任何内容交给安全护栏,因此不会进行尽力而为的扫描。

该行为只适用于无法解码的字节。合法 UTF-8 的 text、srt 或 vtt 转录不受影响。能够解析为 JSON 的 json 或 verbose_json 转录也不受影响,因为 JSON 解析器不会返回无效文本。AISIX 检查模型服务提供方实际返回的字节,而不是请求的 response_format。无论请求何种格式,无法解析为 JSON 的响应体都会走纯文本路径。

监控模式如何处理故障​

监控模式改变的是安全护栏自身的判定,而不是 AISIX 在准备或改写内容时发起的故障:

故障纯监控模式护栏链
筛查后端故障AISIX 放行请求。失败关闭的评估记录为 would_block;失败放行的评估记录绕过。
请求、MCP 结果或非流式转录不可读适用的失败策略仍决定 AISIX 拒绝还是放行内容。
保留流超过缓冲区、没有留下可扫描内容,或无法应用脱敏不会发生,因为监控模式不保留或改写输出。如果护栏链同时包含强制执行的安全护栏,则该安全护栏可以触发拒绝。

如果要在筛查后端不可用时拒绝流量,请使用 block 模式,并把适用的失败策略设为失败关闭。调用方可见的错误请参见安全护栏拒绝。

保留流时的故障​

保留流采用比上述不可读内容结果更严格的规则。

一旦强制执行的输出安全护栏保留流,AISIX 无论适用的失败策略如何,都会拒绝以下结果:

失败标记AISIX 拒绝的原因
unscannable_body保留流中没有留下任何可扫描内容。
output_buffer_exceeded放行溢出内容会暴露未被完整检查或改写的缓冲内容。只有 on_buffer_exceeded: fail_open 会改为放行。
mask_writeback_failedAISIX 无法安全地把要求的脱敏结果写回响应体。

keyword 使用默认的保留策略,pii 定义自己的保留策略,因此这两种类型中强制执行的输出安全护栏都可能遇到这些结果。纯监控模式条目不会遇到,因为它从不保留输出。

MCP 工具结果和非流式音频转录不属于保留流,因此仍由常规输出侧失败策略管辖。当该策略允许未经完整检查的流量通过时,AISIX 会按调用方响应与遥测所述记录绕过。

调用方响应与遥测​

当安全护栏阻断代理流量时,AISIX 会保留所请求端点的错误约定:

  • 兼容 OpenAI 的路由返回 422 和 content_filter。
  • 透传路由返回 422,并使用同样的 OpenAI 风格 content_filter 错误信封。
  • 兼容 Anthropic 的非流式错误返回 422 和 invalid_request_error,error.code 在策略命中时为 content_filter,在失败关闭的拒绝中为 guardrail_unavailable。
  • 兼容 Anthropic 的流式响应会以 Anthropic SSE 错误帧暴露阻断结果,携带相同的 error.type 和 error.code。
  • Realtime 会话会以一个 WebSocket 错误帧加一次关闭来暴露阻断结果,因为此时已经没有状态行可以承载它。
  • A2A 调用返回 422,并使用 JSON-RPC 错误信封。

各接口面的确切信封,以及关闭式失败的拒绝在消息中指名的失败标记词表,参见安全护栏拒绝。

MCP 安全护栏阻断会遵循 MCP 协议形态。AISIX 返回 HTTP 200 和一个标记了 isError 的 JSON-RPC result,而不是协议错误,因此调用方 Agent 会把该拒绝当作工具输出读取并据此调整。参见响应头与错误码。

安全护栏指标​

AISIX 会把每次计时的安全护栏执行记录到 aisix_guardrail_latency_seconds Prometheus 直方图中。标签标识安全护栏名称、种类、阶段和结果,其中包括监控模式下的 would_block/would_mask/would_mask_unsupported 结果以及 fail-open 的 bypassed 结果。在包含此行为的网关上,关闭式失败的 blocked、bypassed,以及监控模式下失败的 would_block,都会通过 error_type 标签携带取值范围有限的失败原因。普通策略命中使用 none。result 标签在关闭式失败的拒绝上仍然是 blocked,不会另起取值,因此请用 error_type 来区分故障与策略决策。流式窗口模式可以为一条输出响应记录多次执行;同步的逐字段 PII 和关键词脱敏操作不计入该直方图。可使用该指标跟踪各安全护栏的延迟(P50/P95/P99)、比较本地与远程检测,并观察阻断率和绕过率。标签和 PromQL 示例请参见指标参考。

要查看安全护栏在客户端等待首个流式帧的时间中的开销,请比较 aisix_request_ttft_seconds 的 side="downstream" 序列与其上游序列。参见区分网关等待与上游 TTFT。

绕过原因​

失败放行的安全护栏不会阻断请求或响应。AISIX 会把原因记录在 guardrail_bypassed_reason 中,并在 AISIX Cloud 中显示为 Bypass reason。所有代理路由的用量事件都会携带该字段;补写的 Batch 计费行不会携带,因为它们不是由网关请求或护栏链产生的。

取值可以是安全护栏类型的失败标记,例如 lakera_timeout、bedrock_5xx 或 custom_script_error,也可以是网关发起的 unscannable_body 或 output_buffer_exceeded。同一标记既标识失败关闭的拒绝,也标识失败放行的绕过。AISIX 把标记限制为最多 64 个小写字母、数字和下划线。如果一个请求发生多次绕过,则记录第一个原因。

请把绕过原因与阻断状态一起读取:

字段含义
只有绕过原因至少一项适用检查失败放行,流量继续处理。
绕过原因和 guardrail_blocked一项检查失败放行,但另一项检查或后续钩子阻断了请求。部分内容可能已经未经筛查地到达模型服务提供方。
绕过原因为空没有记录失败放行事件。这不代表每个被中继的字节都经过了检查。

当非 JSON 正文包含无效 UTF-8 时,有两条路径会扫描尽力而为的文本解码结果,但不查询失败策略。两者都不会记录 unscannable_body,失败关闭的安全护栏也不会改变这一行为:

路径行为
Batch 或 Fine-tuning 响应适用的输出钩子扫描尽力而为的解码结果,之后 AISIX 仍可中继原始字节。
不透明的非 JSON 透传路由请求或缓冲响应适用的钩子扫描尽力而为的文本解码结果,之后 AISIX 仍可中继原始正文。

格式错误的可识别请求或缓冲响应不使用该例外。AISIX 会应用已配置的失败策略,当该策略失败开放时,会记录 unscannable_body。

有一种形态看起来该列进上面这份清单,其实不该。/v1/audio/transcriptions、/v1/audio/translations 和 /v1/images/edits 上不是合法 UTF-8 的 multipart prompt 部分,会在任何安全护栏运行之前就被 400 拒绝——不论是否挂载了安全护栏,也不论 fail_open 取何值。那是对请求本身的结构性校验,不是安全护栏的决定,因此它既不作为安全护栏拒绝,也不会记录绕过。

被保留的流超出 max_buffer_bytes 时,如果设了 on_buffer_exceeded: fail_open,缓冲内容也会在未经检查或脱敏的情况下放行,并完全跳过流结束时的扫描。它会被记录为绕过,原因是网关发起的 output_buffer_exceeded。

两个 Prometheus 信号覆盖的事件并不相同。aisix_guardrail_bypasses_total 既统计以绕过收场的安全护栏执行,也统计网关自身发起的 unscannable_body 和 output_buffer_exceeded 放行。aisix_guardrail_latency_seconds 的 result="bypassed" 切片只统计计时过的安全护栏执行。因此后端故障会同时出现在这两个指标和用量记录里,而网关发起的放行会出现在绕过计数器和用量记录里,但不会出现在延迟直方图里。

下一步​

接下来,请选择服务提供方或配置网关内策略: