跳到主要内容

拒绝延迟税:为什么分层护栏会侵蚀你的 p95 延迟预算

· 阅读需 11 分钟
Tian Pan
Software Engineer

我最近交流的一个团队为他们的 AI 助手构建了一个所谓的“深度防御”(defense in depth)流水线。一个输入分类器检查提示词注入;一个越狱过滤器扫描对抗性模式;模型生成回复;一个输出审核环节扫描结果;一个拒绝检测器检查模型是否回避了问题,如果是,则通过重新表述步骤,用更委婉的框架再次提问。评估套件显示该提示词在 1.4 秒内生成了答案,但真实用户的等待时间中值是 3.8 秒,p95 则超过了 9 秒。

每一个安全层都是一次往返。每一次往返都包含网络跳数、排队时间、模型加载和解码。当你将它们串行地堆叠在生成调用前后时,你为产品设定的延迟预算就会灰飞烟灭——而几乎没人在设计评审时考虑到这一点。更糟糕的是:流水线中最慢、最昂贵的路径往往是那些触发了安全边缘提示词的路径,而这恰恰是你的安全机制存在所要处理的长尾场景。你正在默默地用普通用户的账单来补贴这些长尾流量。

堆叠往返的数学计算

大多数工程团队将护栏延迟预算设定在端到端响应时间的 10% 左右。如果你针对聊天体验的目标是 p95 达到 2 秒,那么你只有大约 200 毫秒的时间来处理所有与安全相关的事情。这个预算必须覆盖输入检查、输出检查、任何重试以及任何回退编排。这并不充裕。

一个典型的分层流水线如下所示。用户的文本首先触发一个提示词注入分类器——如果是在本地运行的小型 DeBERTa 风格模型,耗时约为 80 毫秒;如果是调用外部 API,耗时约为 250 毫秒。接着是主题或 PII 过滤器——再增加 50 到 150 毫秒。紧接着是主要的生成调用,根据模型和回复长度的不同,耗时从 800 毫秒到 4 秒不等。输出结果通过一个审核分类器,耗时 100 到 300 毫秒。如果触发了审核标记,你会通过更宽松的提示词或不同的模型重新路由,这会使生成成本翻倍。把这些加起来,在没有任何重试的理想路径(happy path)下,安全层就贡献了 230 到 700 毫秒的延迟。

陷阱不在于任何单个层太慢,而在于没人对总和负责。分类器团队优化他们的分类器,审核团队优化他们的审核,编排团队编写粘合代码。延迟预算是整个流水线的属性,但组织架构将其切分成了无人负责的碎片。

拒绝-重试路径是你的最差延迟,而非平均延迟

拒绝处理是延迟税累加的地方。当模型拒绝请求时——有时是因为真正的安全原因,通常是因为它将输入模式匹配为与受限内容表面相关——编排层通常会采取三种行动之一。它将拒绝结果返回给用户,这会引导用户放弃你的产品。它使用重新表述的提示词默默重试,再次支付完整的推理成本。或者,它升级到更强大的模型并使用更宽松的系统提示词,同时支付成本差价和额外的一次往返。

选项二和选项三很常见,因为它们看起来像是产品的胜利。用户得到的是答案而不是一堵墙。成本分析存在于与产品仪表盘分离的财务表格中。延迟影响存在于除了触发警报否则没人会看的 p99 图表中。拒绝-重试路径的运行成本和延迟大约是理想路径的 3 倍,而涉及安全边缘提示词的长尾流量可能占总流量的 5% 到 15%,具体取决于你的领域。算一下账:一个在延迟中位数看起来每次调用成本 0.02 美元的功能,在你的产品为了确保可靠性必须处理的那部分分布中,成本高达 0.06 到 0.10 美元。

过度拒绝(over-refusal)基准测试告诉我们,这不是一个小问题。针对安全微调模型的研究显示,模型拒绝真正有毒提示词的频率与拒绝良性提示词的频率之间存在很强的等级相关性(约 0.88)。在法律意义上更安全的模型也更有可能产生误报,而这些误报正是你的重试路径存在的意义。你的基础模型越安全,你在拒绝-重试税上的支出就越多。这并不是一个直观的权衡,大多数团队都是在事后才发现这一点的。

一个真实的安全延迟预算应该是怎样的

必须落实的规范直接借鉴了微服务延迟预算编制。每个安全层都有一个明确的 p95 目标,并且这些目标的总和会根据端到端 SLO 进行检查。当某个层性能下降时,它会表现为 CI 中的预算超标,而不是客户的投诉。

一个合理的初始框架是分层架构。第 0 层(Tier 0)是确定性的——正则表达式匹配器、黑名单、内容长度检查、简单规则。这些在个位数毫秒内运行,并清理大部分流量。第 1 层(Tier 1)是一个小型专用分类器——如 0.4B 参数的 DeBERTa 或类似模型——运行时间为 20 到 60 毫秒,处理下一层路由。第 2 层(Tier 2)是一个重量级的基于 LLM 的裁判,仅用于第 1 层返回“不确定”结论的情况。如果第 2 层在超过 5% 的流量上触发,或者你的护栏层在 p95 上增加了超过 80 毫秒,那么流水线对于实际的威胁形状来说就过度设计了。

分层的意义不仅在于速度。而是在于每一层都有自己的负责人、自己的评估体系和自己的预算额度。当审核团队想要更换他们的分类器时,他们有一个固定的预算范围。当提示词团队想要增加一个新的检查项时,预算会迫使他们移除或降级其他项。如果没有这种约束,每一个新的安全考量都会变成另一次串行往返,而预算会随着每一次 PR 逐渐耗尽。

在最廉价的层级路由拒绝类提示词

对一个很可能被拒绝的 prompt(提示词)所做的最昂贵的操作,就是让它跑完整个流水线,最后才发现模型不会回答。最廉价的做法是尽早识别其特征,将其路由到不同的界面,并完全跳过推理过程。

这正是拒绝预测分类器发挥价值的地方。一个基于你历史拒绝数据训练的小型模型可以预测(具有合理的精确率)主模型是否即将拒绝回答。将其作为第一层(Tier 1)检查。当它触发时,根本不要调用昂贵的模型。要么将用户引导至专门的“我们无法提供帮助”页面并解释替代方案,要么路由到专门为该类别微调的模型变体。这两种做法都比调用主模型、得到拒绝、重新构思并再次调用要廉价。

反对这种做法的论点通常是质量问题——“如果分类器错了,我们误拒了一个正常的查询怎么办?”正确的答案是进行衡量。绘制拒绝预测器的精确率(precision)和召回率(recall),对比你节省的成本和延迟。在我见过的多数流水线中,以 70% 的精确率预测拒绝,即使另外 30% 的用户体验有所下降,其节省的开支也超过了成本。你产品的具体数值会有所不同,但方法论是一样的:将拒绝处理视为预算下的路由决策,而不是一种事后才想到的平滑降级方案。

能流式处理的就流式处理,不能流式处理的就并行处理

两个架构层面的变动可以收回惊人的预算损耗。首先,并行运行输入侧检查,而不是串行运行。Prompt 注入分类器、PII(个人隐私信息)过滤器和主题检查之间并不相互依赖。它们可以分发执行,流水线只需等待最慢的那一个,而不是所有检查时间的总和。仅此一项就可以在不移除任何检查的情况下,将输入侧的开销削减 60% 到 70%。

其次,将输出侧的内容审核与生成过程同步流式运行,而不是放在生成之后。NVIDIA 的 NeMo Guardrails 和类似系统支持在流式传输时扫描部分输出,这使得审核层的工作可以与模型的解码重叠。当流式传输过程中触发审核标志时,编排层会切断连接并回退;如果没有触发,用户看到第一个 token 的时间与没有审核时完全一致。输出安全的延迟成本从“加载在最后”降低到了“隐藏在中间”。

这些并不是什么深奥的技术。它们等同于在安全层应用“并行进行数据库读取而非使用 for 循环”的逻辑。它们之所以不是标准做法,是因为安全流水线通常是通过拼凑各家供应商的 API 构建的,而这些 API 都假设自己是链路中唯一的调用。在架构评审中引入延迟预算可以暴露出这些假设,并迫使集成过程是经过设计的,而不仅仅是简单的组装。

承认长尾效应存在的延迟契约

最后一部分是面向客户的延迟策略。大多数团队发布一个单一的 p95 指标,并试图让每个请求都达到这个标准。对于具有分层安全流水线的 AI 功能来说,这个目标只是个幻想,长尾效应会不断打破它。坦诚的版本是一个分层契约:针对正常情况(happy case)的快速路径 SLO,以及针对安全升级路径的明确且更慢的 SLO,并通过 UX 告知用户当前处于哪种状态。

具体来说,这看起来像是一个“思考中”的指示器,当请求进入拒绝并重试路径时,它会切换到更详细的状态;或者对于系统决定进行深度安全审查的查询,直接切换到完全不同的界面。产品保持诚实并不会损失什么。而用户在没有任何信号的情况下等待 8 秒,却不知道发生了什么,这才是损失了一切。工程团队获得了与实际成本形态相匹配的预算,产品经理(PM)也获得了一个可以向领导层解释的指标。

更深层次的认识是,AI 产品中的安全并不是免费的延迟,也不是免费的成本。它是模型账单和响应时间之外的第三种资源,拥有自己的容量、自己的利用率曲线以及工程学科化的空间。那些在没有预算的情况下部署分层护栏(guardrails)的团队,实际上并没有在进行深度防御。他们交付的是 PM 无法解释的 p95 指标和首席财务官(CFO)无法建模的利润空间。税费已经在支付了;唯一的问题是是否有人注意到了这一点。

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