跳到主要内容

护栏税:当安全分类器让你的延迟和账单翻倍时

· 阅读需 11 分钟
Tian Pan
Software Engineer

在安全评审中,有人问道:“如何防止用户通过越狱来泄露系统提示词(system prompt)?”于是你添加了一个输入分类器。接着又有人问:“如果模型生成了有害内容怎么办?”于是你又增加了一个输出分类器。然后法务部门要求对个人可识别信息(PII)进行脱敏,RAG 团队想要进行可靠性(groundedness)检查。现在,原本只需要一次模型调用的用户消息,变成了需要四次。你的 p95 延迟翻了一倍,推理费用上涨了 40%,而那个原本感觉是秒开的演示 Demo,现在在屏幕显示内容之前有了明显的停顿。

这就是“护栏税”(guardrail tax),而且几乎没有人为此做预算。出于本能去“添加护栏”感觉是免费的,因为每一个单独的检查都很便宜,而且显然是有益的。但护栏的组合并不是免费的——它们堆叠在关键路径(critical path)上,每一个都是增加延迟、消耗 Token 的串行环节,并且成为了一个新的依赖项,可能宕机、触发限流,或者干脆判错。

诚实的讨论应该从承认这一点开始:护栏不是一个功能开关(feature flag)。它是强行加在第一个推理系统之上的第二个推理系统,它有自己的失效模式和自己的账单。以下是如何思考护栏的实际成本,以及如何在不支付高昂代价的情况下获得保护的方法。

三明治架构是三次模型调用,而非一次

标准架构就像一个三明治:审核输入、调用模型、审核输出。在白板上画出来,它看起来像是一个带着两片薄面包的方框。但在生产环境中,它是三次独立的推理调用。如果你以幼稚的方式运行它们——先完成输入检查,然后启动主调用,最后运行输出检查——它们的延迟是累加的。

这对用户最直观感受到的指标至关重要:首字延迟(TTFT)。用户感知不到总的生成时间;他们感知到的是按下回车键到看到内容出现之间的间隙。输入护栏正好就处在这个间隙中。如果你的提示词注入(prompt-injection)分类器在主模型获准启动之前需要消耗 200 毫秒,那么你就在决定产品是灵动还是迟钝的关键延迟指标上增加了 200 毫秒。

输出端的表现更糟,因为它与流式传输(streaming)背道而驰。流式传输的核心价值在于立即开始显示 Token,从而大幅降低感知延迟。但事后(post-hoc)输出审核器希望在释放响应之前检查完整的响应。你无法同时拥有全响应安全门禁和真正的流式传输——门禁迫使你缓冲整个答案,进行审核,然后才释放它,这抛弃了流式传输带来的所有延迟优势。当团队在聊天产品中添加输出审核时,往往会通过惨痛的教训发现这一点:用户抱怨产品“变慢了”,即便总生成时间并没有改变。改变的是答案现在在停顿后一次性弹出,而不是像打字一样逐字呈现。

账单是真实存在的,但延迟才是隐形杀手

让我们诚实地算一笔账,因为这些数字影响是双向的。

从纯粹的金钱成本来看,护栏通常比人们担心的要便宜。一个轻量级的专用分类器——比如像 Prompt Guard 这样拥有 86M 参数的模型,或者专门构建的安全模型——其成本仅为前沿模型调用的一小部分。在大规模应用中,设计良好的护栏层成本可以控制在主模型支出的几个百分点以内。如果你对护栏的唯一反对理由是 Token 账单,那么你可能优化错了方向。

昂贵的资源是延迟,而且它的昂贵体现在不会出现在发票上的地方。每一次串行护栏跳转都是用户等待的时间。分层设置可以将常规路径的开销保持在 30 毫秒以下,但完整的 LLM-as-judge 检查——即你询问一个大模型“这个回复安全吗?”——可能会增加数百毫秒,其成本甚至能赶上它所保护的生成内容本身。如果在输入和输出端都这样做,你就为了捕捉那些 20 毫秒分类器就能解决 90% 的问题,而将延迟预算增加了两倍。

还有第三种成本更难衡量,且往往是最大的:误报摩擦(false-positive friction)。一个封禁合法请求的护栏对用户信任的破坏力,远比偶尔出现的糟糕输出要快得多。我见过最糟糕的生产环境故障都源于过于激进的输出护栏——拒绝良性的答案、对非敏感文本进行脱敏、将正常问题标记为攻击。每一次误报都是一个用户撞到了墙而不是得到了答案,他们不会提交 Bug,而是直接离开。当你调优护栏时,误报率(false-positive rate)是决定用户是否继续使用产品的数字;漏报率(false-negative rate)则是决定你是否会上新闻的数字。你必须同时关注这两者。

根据爆炸半径匹配护栏强度

核心错误在于,无论调用的实际作用如何,都给每一次调用包裹上同样沉重的“三明治”。护栏的成本应该随着它所保护的行为的“爆炸半径”(blast radius)而缩放,而不是出于习惯进行统一应用。

一个仅从公共文档中回答问题的只读聊天机器人,其爆炸半径很小。最坏的情况也不过是一句令人尴尬的话,而且即便如此也是有限的,因为模型不能执行任何操作。在这种情况下,一个廉价的分类器甚至是一个正则匹配(regex)就是相称的。而一个可以办理退款、发送电子邮件或运行 Shell 命令的智能体(agent)则拥有巨大的爆炸半径——一次错误的行为是不可逆且代价高昂的——它值得在行为上设置沉重且细致的门禁,而不是在通往该行为的每一次对话轮次中都设置。

这重新界定了护栏所属的位置。与其审核模型生成的每一个 Token,不如审核模型能的少数有影响力的行为。工具权限范围(Tool-permission scoping)——即被攻破的上下文根本无法触达危险函数——通常比试图通过输出分类器读取模型意图的控制效果更好。针对“该智能体是否可以在没有人工批准的情况下调用超过 50 美元的 issue_refund”的护栏,值得投入一个完整的 LLM 裁判(LLM judge)和人工干预(human in the loop)。而在头脑风暴期间,针对“模型是否说了一些轻微偏差的话”的护栏,可能不值得每轮增加 200 毫秒的延迟。

使检查与后果相匹配。确定性匹配器和正则表达式用于廉价、高流量、低风险的路径。小型分类器用于不确定的中间地带。沉重的 LLM 裁判仅保留给真正不可逆的操作。你的大部分流量都应该通过最便宜的层级。

夺回延迟:并发运行防护栏,而非串行

一旦你接受了某些防护栏(guardrails)是不可或缺的,那么工程上的问题就变成了如何并行支付这些开销,而不是串行支付。

最大的收益来自于拒绝串行化。输入防护栏和主模型调用并不一定非要按先后顺序运行。你可以与输入分类器同步,投机性地(speculatively)启动主生成任务;如果分类器的结果是“拦截”,你就丢弃正在进行的生成任务。为了换取在每个合法请求中将防护栏从关键路径(critical path)上移除,你在少数被拦截的请求上浪费了一些 token —— 这通常是一笔划算的交易,因为绝大多数流量都是合法的。防护栏的延迟会消隐在原本就要进行的调用阴影之中。

对于输出审核,流式处理的答案是分块、缓冲审核。与其等待完整的响应,不如在每一小块内容形成时就进行审核,并以小块的形式流式传送给用户。你通过接受微小的缓冲——一两句话的延迟——来维持流式感。NeMo Guardrails 等框架正是支持这种模式:在审核员检查缓冲块的同时持续释放 token,并在分块未通过时立即停止生成。相关的研究方向是流式内容监控器,它们会监控正在生成的 token,并在发现有害内容的瞬间停止生成,而不是在生成完成后才进行判断。早停(Early-stopping)比事后过滤(post-hoc filtering)更安全也更廉价,因为你不需要为那些本就要丢弃的 token 付费。

另一个杠杆是为执行防护的任务选择合适规模的模型。正则匹配(regex)也许能拦截 60–70% 的注入尝试;微调后的分类器能拦截 89–94%;而 LLM 裁判(LLM judge)虽然能拦截更多,但成本却是前者的数倍。分层方法——通过廉价的确定性检查清理大部分流量,小型分类器处理剩余的不确定部分,而昂贵的裁判只处理真正模棱两可的情况——能让你以极低的平均延迟实现大部分检测目标。如果分类器能解决问题,就不要用 LLM 裁判;如果正则匹配能解决,就不要用分类器。

将防护栏视为有预算限制的系统

失败的模式不在于添加了防护栏,而在于逐个添加它们。每一个防护栏都由心怀好意的评审者单独论证其合理性,直到你累积起了一个既没人设计过也没人衡量过的审核堆栈。六个月后,你的延迟变得莫名其妙地差,账单飙升,却没有任何一个 PR 该为此负责。

解决办法是将安全性视为一个有预算限制的子系统,而不是一堆应激反应。给它一个明确的延迟预算——例如,在常规路径上分配 50 ms ——并让每一个提议的防护栏根据该预算证明其占用的合理性。对防护层进行独立插桩(Instrument),这样你就能看到每个请求中,有多少时间和 token 花在了防护上,而不是生成上。将误报率(false-positive rate)作为核心产品指标进行跟踪,因为这个数字正在默默地消耗你的用户。定期回顾堆栈,剔除那些不再物有所值的防护项——比如为早已不存在的威胁模型添加的注入分类器,或者除了误报之外毫无产出的输出审核器。

防护栏并非免费,但它们也不是敌人。这笔税收是真实存在的,而目标是停止支付“零售价”。并发启动你的防护任务,根据实际保护对象的爆炸半径(blast radius)来调整每一个防护项的规模,并将整个防护层维持在可见的预算内。做到这一点的团队所交付的产品既安全又快速。没做到的团队只能交付安全的部分,却在速度上默默落败——而在大多数市场中,速度才是用户最先察觉到的。

References:Let's stay in touch and Follow me for more thoughts and updates