设计输入、输出、操作和人工审核防护栏,为生产环境中的 AI 故障设定边界,而不是假装概率系统是绝对安全的。

由 The Forward Deployed 编辑团队撰写并审核

评测告诉你系统平均而言是否正确。防护栏则是你在系统出错时、以及在有人试图诱导它出错时该做的事情。在真实部署中,模型偶尔会产出自信却不可接受的内容,用户或攻击者也偶尔会向它输入恶意内容。防护栏的作用就是防止这两种情况演变成客户的事故。

为什么这是本职工作,而不是走过场的勾选项

FDE 部署的场景中,错误输出的代价是真实的:受监管的顾问服务、客户的生产数据库、可以发送邮件或转移资金的工具。在这种环境下,一个糟糕的回答就不再只是用户体验问题,而会变成法律责任。Morgan Stanley 项目的关键正在于此:顾问不会信任一个可能在客户面前自信地给出错误答案的助手,因此让人类审核输出是承重结构。防护栏是你获得部署资格的前提。

在继续阅读之前:假设你被要求添加一个能起草并发送客户邮件的 AI 功能。说出那个应该让你夜不能寐的故障——它绝不是一句语法别扭的句子。

真正要紧的故障是基于错误信念执行的不可逆操作:模型把邮件发给了错误的客户,或者给正确的客户发了错误的内容。以下所有内容,都是为了确保这类故障不会在无人监督的情况下发生。

防护栏存在的三个位置

在系统与外部世界接触的三个点上构建防护栏。

输入防护栏——在模型之前

对到达模型的内容进行验证和清洗。在发送或记录之前剥离或标记个人身份信息(PII);强制执行长度和格式限制;并防御提示注入——即试图变成指令的不可信文本。提示注入位列 OWASP LLM 应用十大风险之首,防御方法是边界思维:模型读取的内容是数据,永远不是命令,并且你永远不要赋予模型任何可被注入指令滥用的可触达权限。

注入并不只来自用户输入的文字。更隐蔽的途径是间接提示注入:攻击指令被植入系统稍后会读取的内容中,比如检索到的文档、抓取的网页,或助手要总结的电子邮件。Greshake 等人证明,攻击者可以“通过策略性地向可能被检索的数据中注入提示,远程……利用集成了 LLM 的应用”,而 OWASP 2025 年的条目也划出了同样的界线:“当 LLM 接受来自外部来源(如网站或文件)的输入时,就会发生间接提示注入。”这意味着 RAG 管道就是一个注入面:你自己的语料库,或任何向它提供数据的东西,都可能把攻击带给模型。防御方法与检索中的边界思维相同:把检索到的文本当作数据而非指令,在提示中明确划定其边界,剥离或标记其中发现的类似指令的内容,并且绝不允许检索内容中的任何东西触发工具调用,除非它通过与用户输入相同的审查关卡。如果你的助手会读取文档并且能够执行操作,这就是你必须假设会发生的攻击。

输出护栏——在模型之后

在输出到达用户或下游系统之前进行检查。过滤不安全内容,验证输出是否基于你检索到的来源(而非凭空编造),脱敏任何漏网的 PII,并在下游系统需要解析时验证结构。对于有依据的助手来说,最有价值的输出护栏是依据性检查:答案是否真的源自检索到的段落,还是模型用自信的虚构填补了空白?

操作护栏——当系统能够执行操作时

一旦系统开始执行操作,“错误”就不再是一句糟糕的话,而变成一个糟糕的事件。为它在无人监督时能做的事划定边界:

  • 预算:对步骤数、工具调用次数或花费设置硬性上限,达到上限就必须停止并报告。
  • 检查点:在任何不可逆或影响范围大的操作(发送邮件、执行迁移、转移资金)之前,必须经过人工批准。
  • 终止开关:一种已知的、快速停止并回滚的方式。

设计问题从来不是“它能做什么?”而是“它能在没有人的情况下做什么?”对于不可逆的操作,目前的答案是什么都不能做,直到评测数据证明它配得上这种自主权。

差 / 好 / 卓越——“你如何保证它的安全?”

差——“我们加一条免责声明,说 AI 可能会出错。”这把全部安全责任推给了用户,对恶意输入或不可逆操作毫无作用。这是没运营过真实系统的人才会给出的答案。

好——“我们过滤不安全的输出并验证输入。”有真正的输入/输出护栏,确实达到了中级水平。不足之处:它没有提到会执行操作的系统,也没有提到你如何知道某个护栏正在失效。

很好——“分层防护:对输入进行清洗和注入检查,对输出进行真实性验证和 PII 脱敏,对于任何会执行操作的部分,设置预算、在不可逆步骤上设置人工检查点,并配备终止开关——同时记录每一次拦截,这样我就能看到到底拦截了什么。 你在三个环节都限制了爆炸半径,并让防护措施变得可观测。

一个最小化的防护栏

防护栏就是围绕模型调用的普通代码。骨架就是全部要点。(示例代码。)

# guard.py — deterministic checks around a non-deterministic model
def handle(request, model, tools):
    if contains_pii(request.text):
        request = redact_pii(request)              # input guardrail
    if looks_like_injection(request.text):
        return refuse("input flagged")             # refuse hostile input

    output = model(request)

    if not grounded_in(output, request.sources):   # output guardrail
        return refuse("answer not supported by sources")

    action = output.proposed_action
    if action and action.is_irreversible:          # action guardrail
        return await_human_approval(action)        # checkpoint, don't execute
    return output

让它真正起作用的特性在于:它作用于输入、输出和动作,当较弱的系统会尝试执行时,它能拒绝恶意请求,并且在任何不可逆操作之前停下来。其余的一切,比如更好的 PII 检测或更强的注入分类器,都只是对这个主干的打磨。

具名检查的内部——难点所在

骨架命名了两个函数并把难点藏在其中,所以打开来看看。

looks_like_injection()

起初是廉价的模式规则(“忽略你之前的指令”及其变体)加上一个小型训练分类器,但两者都无法捕获新型攻击。把它当作一个有实际漏报率的绊线,这也是下游的动作闸门即使在此检查通过时也必须守住的原因之一。

grounded_in()

是在设计讨论中需要坦诚说明的那个:在实践中,真实性检查本身就是一次模型调用。你把检索到的段落和起草的答案交给一个评判模型,询问答案是否有依据。其形式如下:

grounded_in(), unpacked — the check is itself a model call (illustrative)

Judge prompt:
  You are verifying a drafted answer against source passages.
  <passages> …the retrieved chunks… </passages>
  <answer> …the drafted answer… </answer>
  For each factual claim in the answer, decide whether the passages
  support it. Return JSON:
  {"grounded": true|false, "unsupported_claims": ["…"]}

Refuse or route to a human when grounded is false.

这个设计有三个值得明说的后果。它增加了成本:每个受保护的请求都要进行第二次模型调用,不过通常用一个比起草答案的模型更小的模型就足够了(成本与延迟)。它增加了延迟:该检查按顺序运行,在草稿完成之后、用户看到任何内容之前。而且它有自己的错误率:评判模型有时会放过一个无依据的答案,有时会拦截一个有依据的答案,所以你要像评估其他一切东西一样评估这个防护栏,用人工标注的示例给它打分,并追踪它漏掉了什么(评测)。一个从未测量过错误率的防护栏,只是站在控制位置上的一个猜测。

面试中要带上的要点

在 AI 系统设计轮中,当系统可以执行操作时,不要描述理想路径。以模型出错时会发生什么开场:划清自动运行与需要人工介入的界限,说出预算和终止开关,并说明你如何知道防护栏正在拦截问题。为模型出错的时刻而设计,正是这一轮面试要考察的直觉。

相关: 安全、合规与数据了解这些约束在受监管环境中的版本,以及评测了解如何衡量防护栏所约束的质量。
下一篇: 可观测性与调试。评测和防护栏只有在你能看到系统在生产环境中做了什么时才有用。

下一步:可观测性与调试