安全护栏行为
安全护栏会在 AISIX 转发受支持的网关流量之前、上游返回响应之后,或在这两处添加内容策略检查。其共用运行时控制项决定哪些路由和内容会被检查、匹配是否会阻止流量,以及调用方和操作员能够观测到什么。
内置关键词和 PII 安全护栏在网关内部评估内容。语义筛查会调用配置好的向量嵌入模型,自定义脚本则运行运维自定义的逻辑。服务提供方集成会把 提取出的内容发送到审核服务,并应用其决策。选择安全护栏服务提供方比较了这些选项。
所有类别都使用本页所述的行为。适用的检查无法完成时,通常由失败设置决定会发生什么;部分流式和解码路径则有更严格或固定的处理方式。
检查位置
安全护栏的检查位置决定 AISIX 在哪里执行检查,以及阻断内容时会发生什么:
| 检查位置 | AISIX 检查内容 | 被阻断时的效果 |
|---|---|---|
input | AISIX 发送到服务提供方之前的调用方请求。 | 不会调用服务提供方。 |
output | AISIX 返回给调用方之前的响应内容。 | 调用方不会收到被阻断的响应。 |
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/responses | reasoning item 的 content[].text 及其 summary[].text |
/v1/chat/completions | assistant 轮次的 reasoning_content |
/v1/messages | thinking 块的文本——会被扫描,但请看下面的例外 |
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 中对应的条目:
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(默认) | 输入侧命中会在模型服务提供方调用前停止请求;输出侧命中会阻止调用方收到响应。脱敏规则会改写受支持的内容。 | 强制执行已经验证的策略。 |
monitor | AISIX 允许请求或响应原样通过,并记录安全护栏本应阻断或脱敏的结果。 | 在策略影响请求前,使用真实流量衡量误报并调优新策略。 |
基于检测的安全护栏可能产生误报和漏报。监控模式可让你先把策略命中与有代表性的流量对照,再允许策略影响请求。
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 和 pii | false |
会调用外部服务的类型带有自己的 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 的安全护栏条目上。
fail_open 此前默认为 true。未显式设置该字段的安全护栏,现在会在无法访问其后端服务时返回 422 阻断,而不再像以前那样放行未扫描的请求。通过 AISIX Cloud 创建的安全护栏不受影响——控制面一直会为该字段存储显式值。受影响的是 resources.yaml 中省略了该字段的安全护栏,以及升级后新建的安全护栏。
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 的响应体都会走纯文本路径。