这一期想聊一个被大量团队低估的问题:AI 安全不等于系统安全。作为长期运营大模型 API 中转站的 4sapi,我见过太多把「模型很乖」当成「系统很安全」的接入方案,最后在真实事故里交学费。

痛点:Agent 能执行命令,谁保证它不越界

Agent 的价值在于能执行命令:读写文件、调用工具、访问外部系统、发起网络请求。可只要多问一句——谁保证它不越界?——大多数方案就答不上来。

模型的每个输出本质上都只是「建议」。真正把动作落地的,是宿主环境里的一段普通程序。当一段建议被直接翻译成系统调用,Agent 就同时具备了「会被诱导」和「能执行」两个特性。一条精心构造的 Prompt Injection 可以让平时表现完全正常的 Agent 去删除文件、外发数据或修改配置,而模型自己对这一切毫无察觉。

近期的一系列 Agent 沙箱逃逸事件反复证明同一件事:降低有害模型行为与可靠地包含软件,是两个不同的目标。前者做的是「让模型不做坏事」,后者做的是「让软件跑不出边界」。把前者的成果当成后者的保障,就是事故的起点。

原理速览:AI 安全与系统安全是两套体系

前沿实验室在「概率性 AI 安全技术」上投入巨大:对齐训练、红队评测、越狱防护、输出过滤。这些技术有价值,但共同特征是概率性——任何防护都存在绕过空间,评测集永远无法覆盖全部恶意输入,模型的「善良」也无法被证明。

系统安全面对的是另一个问题:一个程序能不能跑出它被允许的边界。这个问题从操作系统时代起就有成熟答案——权限、隔离、沙箱、审计。系统安全的特征是确定性:策略是写死的代码,不依赖任何模型的心情。

问题恰恰出在混用上。不少团队把概率性技术用在了需要确定性保障的场景:用「模型不会生成危险动作」的假设,替代「进程没有权限执行危险动作」的事实。于是沙箱逃逸事件一再发生,而每次复盘都能看到同一个根因——把两类安全当成了同一件事。

一张表看懂两类安全的差别

维度 AI 安全 系统安全
核心目标 让模型不做坏事 让软件跑不出边界
典型手段 对齐训练、红队评测、输出过滤 权限、隔离、沙箱、审计
控制性质 概率性,无法证明完备 确定性,策略即事实
失效方式 越狱、Prompt Injection、对抗样本 逃逸、提权、滥用授权
判断主体 模型自身,可被诱导 策略引擎,不可被诱导
验收方式 评测集上的指标分数 边界约束是否被强制执行
出事后果 一次越狱对话 一次真实的数据泄露或破坏

这张表值得贴在每个 Agent 项目的墙上。模型的评测分数再高,也替代不了一条防火墙规则或一个只读挂载。

沙箱逃逸为什么总在发生

把近期的逃逸事件放到一起看,成因高度重复:

  1. 把概率当确定性:默认「模型不会生成危险动作」,于是不做权限隔离。
  2. 权限给得过大:Agent 进程直接拥有用户级全部权限,读写边界等于账号边界。
  3. 动作直通系统:模型输出的工具调用被直接映射成系统调用,中间没有任何校验层。
  4. 缺少审批与审计:高危动作无人确认,出事后无日志可查,复盘只能靠猜。

这四个成因里,第一个最致命。概率性安全的失效率再低,乘以每天百万级的 Agent 动作调用量,也一定会变成高频出现的真实事件。

确定性控制四层:权限 / 隔离 / 审批 / 审计

治理方案并不神秘,就是把系统安全的老办法按 Agent 的特性重新排布:

四层缺一不可。只做隔离不做审批,高危动作依然畅通;只做审批不做审计,出了问题无从复盘。

治理链:从模型动作到审计日志

治理链的完整形态可以画成一条单向管道:

模型提出动作(自然语言 / 工具调用)
        │
        ▼
策略引擎解析并分类意图
        │
        ▼
权限检查(角色 × 资源 × 操作 最小权限矩阵)
        │
        ▼
是否高危? ──是──► 人工或二次校验审批 ──不通过──► 拒绝并记录
        │否
        ▼
沙箱执行(只读文件系统 / 网络白名单 / 资源限额)
        │
        ▼
审计日志(入参、决策、执行结果、token 消耗)

注意这条链路里,模型只出现在第一格。模型可以「提议」,但「能不能执行」永远由后面的确定性代码回答。即便模型被 Prompt Injection 完全攻破,动作仍然过不了权限、审批、沙箱任何一关——这才是「确定性」的意义。

三种高危的接入姿势

第一,把输出过滤当成执行护栏。过滤层拦截的是「看起来危险」的文本,可攻击者只需要把危险指令拆散、编码、藏进工具参数里,过滤就形同虚设。第二,把人工 review 当成事后补救。让运营在出事后逐条翻日志,不如在事前把高危动作的审批做成强制流程。第三,把模型版本当成安全补丁。换一个更「乖」的模型并不等于堵住了漏洞,因为漏洞的根不在模型肚子里,而在宿主环境的执行边界上。

这三种姿势的共同点,都是把概率性手段放在确定性控制的位置上。评估一个 Agent 项目安不安全,真正要问的问题是:假如模型被完全攻破,系统还能不能守住?如果答案依赖「模型应该不会」,那这道防线就是纸糊的。

接入教程:带权限校验的 Agent 调用

落地示例用 openai 库,通过中转网关统一接入,再把权限校验插在模型调用与真实执行之间:

import os
from openai import OpenAI

# 经中转网关统一接入,base_url 使用中转入口
client = OpenAI(
    api_key=os.environ["DSH_API_KEY"],
    base_url="https://4sapi.com/v1",
)

READONLY_TOOLS = {"read_file", "list_dir", "web_search"}
HIGH_RISK_TOOLS = {"delete_file", "send_email", "write_db", "run_shell"}


def decide(action: str) -> str:
    """确定性决策:allow / approval / deny,与模型输出完全解耦。"""
    if action in READONLY_TOOLS:
        return "allow"
    if action in HIGH_RISK_TOOLS:
        return "approval"
    return "deny"


def run_in_sandbox(action: str, args: str) -> dict:
    """在受限环境中执行动作,失败也不会触及宿主系统。"""
    ...  # 容器或虚拟机内的实现


def ask_human(action: str, args: str) -> dict:
    """高危动作进入人工审批队列,返回审批结果。"""
    ...  # 对接工单或审批系统


def audit_log(action: str, args: str, decision: str, result: dict) -> None:
    """把入参、决策、结果写入审计存储。"""
    ...  # 写日志系统或对象存储


def run_agent(initial_prompt: str) -> None:
    messages = [{"role": "user", "content": initial_prompt}]
    for _ in range(10):  # 步数上限,防止失控循环烧 token
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=TOOL_SCHEMAS,  # 工具定义,此处省略
        )
        message = response.choices[0].message
        messages.append(message)
        if not message.tool_calls:
            break  # 模型不再调用工具,回合结束
        for call in message.tool_calls:
            action = call.function.name
            args = call.function.arguments
            decision = decide(action)  # 策略引擎先于执行,不经过模型
            if decision == "allow":
                result = run_in_sandbox(action, args)
            elif decision == "approval":
                result = ask_human(action, args)
            else:
                result = {"ok": False, "reason": "denied"}
            audit_log(action, args, decision, result)  # 全量留痕
            messages.append(
                {
                    "role": "tool",
                    "tool_call_id": call.id,
                    "content": repr(result),
                }
            )

这段代码的关键不是 openai 调用本身,而是三行确定性逻辑:decide() 决定动作等级,run_in_sandbox() 保证执行环境受限,audit_log() 保证一切可追溯。模型只能请求动作,永远不能自己授权。

再看一遍上面的调用循环:decide() 返回的三个等级正好对应四层控制里的权限与审批。allow 的动作走沙箱执行,approval 的动作等人工放行,deny 的动作连沙箱都不进。audit_log() 把每一步写进审计存储,事后复盘只需要按时间线回放。这套结构不需要任何花哨的框架,几十行确定性代码就能搭起来,却能把逃逸事件的破坏半径从「整个系统」缩小到「一次调用」。

中转侧治理:配额、负载与计费

权限治理在 Agent 侧,网关侧也有对应工作。中转站这类入口天然适合做三件事:

这三件事与权限治理是互补关系:网关管「流量能不能发出去」,Agent 侧管「动作能不能执行」,两侧都不依赖模型自己的判断。

成本与风险提示

另外要提醒一点:确定性控制是常年成本,不是一次性投入。沙箱资源、审批人力、审计存储、演练开销,都要按季度滚动进预算。很多项目上线时控制做得漂亮,半年后因为预算收紧悄悄撤掉了隔离层——这才是最危险的退化。控制体系应该像防火墙一样永远在线,而不是像活动一样办完即止。

上线前检查清单

九项全部勾上,Agent 才算具备上生产的资格。少任何一项,都是在用概率赌确定性。

总结

AI 安全负责让模型不做坏事,系统安全负责让软件跑不出边界;前者是概率性的,后者必须是确定性的。沙箱逃逸事件反复提醒:Agent 接入生产时,权限、隔离、审批、审计四层确定性控制必须放在模型之前,模型自己的判断永远只是参考。4sapi 在中转侧做好配额、负载均衡与计费治理,Agent 侧的确定性安全,需要每个接入方自己守住。欢迎在评论区发表想法/聊聊。