跳到主要内容
版本:1.4.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 二进制帧会直接中继,不执行安全护栏内容检查。

在透传路由上,安全护栏通过 passthrough_route 挂载作用域生效,并与调用方 API Key、团队和环境作用域并列。扫描文本遵循识别出的正文信封。携带可识别 LLM 信封(Chat、Completions、Responses)的请求会扫描提取出的消息或提示词文本;信封提取不出文本时回退为整个正文。不透明流量则把完整请求体和响应体作为文本扫描。在 1.4.0 中,这种提取并未覆盖这些信封的所有部分。流式响应只从 Chat delta.content、Responses output_text delta 和 Completions text 中扫描,因此 Anthropic Messages 流,以及 Chat 或 Responses 流中的工具调用参数,都会不经扫描地中继。缓冲的 Responses 回复只扫描消息文本,不扫描其工具调用。在请求侧,Anthropic 正文的顶层 system、tool_result 内容和回放的 tool_use 输入不会被扫描,回放的 Responses 工具调用的名称和参数也不会被扫描。

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

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

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 规则的关键词只出现在模型生成的推理内容里、而不出现在模型返回的答案中,那么它此前会阻断这些响应,现在不再阻断。调用方可见的答案的扫描范围与此前完全一致。

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

有一个例外同样不值得依赖。只要信封抽取不出任何文本,透传路由就会回落到把整个正文当作不透明文本扫描,而这种兜底会把推理内容一并卷进来。它适用于 AISIX 无法识别为已知信封的流量。它同样适用于能识别、但抽不出文本的信封,例如 output[] 中只有一个 reasoning item 的 Responses 响应。那是一种过度扫描的兜底,而不是对推理内容的处理。

脱敏无法触及 encrypted_content

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

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

输入检查读取哪些消息​

安全护栏的 input_messages 设置决定它的输入检查读取请求中的多少内容:

input_messages输入检查检查的内容
all请求中的每一条消息——system、user、assistant 和 tool 消息都包括在内。这是默认值,也是现有安全护栏的行为。
latest_turn只检查最后一条 assistant 消息之后的消息,并排除 system 消息:即当前这一轮的 user 消息,以及回应它的工具结果。

IDE 和 Agent 类客户端每次请求都会把整段会话重发一遍。在 all 下,一条命中规则的旧消息——某个密钥的正则、一个 PII 模式、一条语义拒绝示例——会持续阻断该会话后续的每一个请求,哪怕模型早就回答过它、哪怕新的提示词本身是干净的。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 位于所选窗口内时,AISIX 会扫描其名称、参数或输入。脱敏可以改写参数或自定义工具输入,但不会改写工具名称这一结构字段。

“最后一条 assistant 消息”是在非 system 消息上判定的。因此结尾处的 assistant 消息属于预填(prefill)——调用方写给模型续写的文本——它属于当前这一轮,而不是这一轮的边界。完全没有 assistant 消息的请求会被整体检查(同样不含 system 消息)。

该设置对所有安全护栏类型生效,且只作用于输入检查: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_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 执行检查并记录所有匹配。

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

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

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

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

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 信封,因此携带的是 invalid_request_error 且没有 code 字段,而不是 OpenAI 风格路由会返回的 content_filter 和 guardrail_unavailable:

{
"type": "error",
"error": {
"type": "invalid_request_error",
"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 响应体。透传安全护栏只返回判定,不会改写响应,因此被放行的内容会原样中继。

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

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

故障受影响的安全护栏示例
筛查后端没有返回判定调用远程服务的安全护栏、语义筛查和自定义脚本连接或服务提供方错误、限流、超时、脚本异常或脚本判定无法读取。
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 的安全护栏条目上。

0.11.0 变更

fail_open 此前默认为 true。未显式设置该字段的安全护栏,现在会在无法访问其后端服务时返回 422 阻断,而不再像以前那样放行未扫描的请求。通过 AISIX Cloud 创建的安全护栏不受影响——控制面一直会为该字段存储显式值。受影响的是 resources.yaml 中省略了该字段的安全护栏,以及升级后新建的安全护栏。

0.11.0 变更

mandatory 字段已移除。它此前会让远程安全护栏故障即使在 fail_open 为 true 时也阻断请求,并让无法访问的监控模式安全护栏变为致命故障。由于 fail_open 现在默认为 false,请改为显式设置——block 模式下的 fail_open: false 会拒绝安全护栏无法检查的内容。仍然携带 mandatory 的配置会被拒绝。

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放行溢出内容会暴露未被完整检查或改写的缓冲内容。
mask_writeback_failedAISIX 无法安全地把要求的脱敏结果写回响应体。

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

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

调用方响应与遥测​

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

  • 兼容 OpenAI 的路由返回 422 和 content_filter。
  • 透传路由返回 422,并使用同样的 OpenAI 风格 content_filter 错误信封。
  • 兼容 Anthropic 的非流式错误返回 422 和 invalid_request_error。
  • 兼容 Anthropic 的流式响应会以 Anthropic SSE 错误帧暴露阻断结果,错误类型取 Anthropic 合法值,且没有 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 结果以及 fail-open 的 bypassed 结果。在包含此行为的网关上,关闭式失败的 blocked、bypassed,以及监控模式下失败的 would_block,都会通过 error_type 标签携带取值范围有限的失败原因。普通策略命中使用 none。result 标签在关闭式失败的拒绝上仍然是 blocked,不会另起取值,因此请用 error_type 来区分故障与策略决策。流式窗口模式可以为一条输出响应记录多次执行;同步的逐字段 PII 和关键词脱敏操作不计入该直方图。可使用该指标跟踪各安全护栏的延迟(P50/P95/P99)、比较本地与远程检测,并观察阻断率和绕过率。标签和 PromQL 示例请参见指标参考。

绕过原因​

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

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

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

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

当解码丢失字节时,有两条路径会扫描尽力而为的解码结果,但不查询失败策略。两者都不会记录 unscannable_body,失败关闭的安全护栏也不会改变这一行为:

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

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

被保留的流超出 max_buffer_bytes 时,如果设了 on_buffer_exceeded: fail_open,缓冲内容也会在未经检查或脱敏的情况下放行,并完全跳过流结束时的扫描。该显式缓冲策略不会被记录为安全护栏绕过。

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

下一步​

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