在不破坏生产环境的情况下发布 AI 功能:LLM 的阴影模式、灰度发布和 A/B 测试
一个团队在周二下午将 GPT-4o 换成了一个更新的模型。到周四,支持工单增加了 30%,但没人能说清原因 —— 新模型的回答稍短,拒绝了一些旧模型能处理的边缘情况请求,并且日期格式化的方式不同,导致下游解析器崩溃。团队选择了回滚。两个迭代周期的工作付诸东流。
这种故事不断上演。问题不在于新模型更差 —— 它在大多数方面可能表现得更好。问题在于团队发布的流程与发布 bug 修复程序完全相同:合并、部署、观察。这对代码有效,但对 LLM 则行不通。
LLM 的发布结合了软件部署中最困难的部分:你无法通过单元测试来建立信心,故障模式是弥散的(表现为糟糕的输出而非崩溃),并且在你的指标捕捉到问题之前,用户就已经感受到了质量下降。答案是借鉴成熟基础设施团队发布高风险变更的方法 —— 渐进式发布 —— 但要针对 LLM 系统的特定属性进行调整。
为 什么发布 LLM 不同于发布代码
在深入研究技术之前,有必要明确是什么让 LLM 部署不同于常规软件部署。
非确定性是不可约的。 即使将 temperature 设置为 0 并采用贪婪采样(greedy sampling),LLM API 在实践中也不是确定性的。研究记录了在输入完全相同的情况下,多次运行的准确率差异可达 15%。根本原因是 GPU 浮点运算:操作并非严格结合,并行序列处理期间的批处理大小(batch size)可变性会在推理时引入不同的舍入误差。这意味着你无法编写一个单元测试来提供关于模型变更的可靠信号 —— 同一个查询在多次运行中可能产生显著不同的输出。
微小的变化具有巨大的爆炸半径。 提示词的微调、微调数据的更新或模型版本的升级,其改变行为的方式与基准测试所捕捉到的内容截然不同。一个在 MMLU 上得分更高的新模型,在处理模糊的客户问题时可能表现不同,产生的输出可能更长从而破坏 UI 组件,或者拒绝前一个模型接受的某类请求。这些退化是真实的,但直到你拥有真实流量时才会显现。
反馈是延迟的。 与 500 错误不同,糟糕的 LLM 输出可能在数小时或数天后才会通过用户投诉、下游管道故障或支持工单浮出水面。这种延迟信号意味着你需要一种在不让用户面临风险的情况下运行新版本的方法,同时收集足够的数据来做出决策。
成本是变量,而非常量。 更换模型会改变你的 token 成本。一个质量提升 20% 的新模型,单次调用成本可能是原来的 3 倍。渐进式发布让你可以从小规模开始发现新模型的成本概况,以免它超出你的整体预算。
影子模式:在无风险的情况下验证真实流量
影子模式是任何重大 LLM 变更的最低风险起点。核心思路很简单:将生产环境的请求同时复制给当前模型(服务用户)和候选模型(不服务用户)。记录两者的输出,进行对比,并根据你的观察做出晋升决定。
典型实现是将所有生产流量照常路由到当前模型,同时由一个后台进程将相同的请求发送给候选模型。候选模型的响应绝不会展示给用户 —— 它们会被送往日志系统进行评估。
关键部分是评估层。没有它,影子模式只会给你留下一堆日志。你真正需要的是自动化对比:一个 LLM 评审员(LLM judge),根据与你的用例相关的标准(事实准确性、语气、任务完成度、格式合规性)评估这两个响应,对比 token 数量和成本,以及在现实并发负载下的延迟测量。
一种行之有效的模式是在部署任何内容之前,对历史生产请求运行影子模式代理。将上周的流量在候选模型中回放,并让评审员将输出与当前模型产生的输出进行对比。这能让你在触及生产基础设施之前,快速了解可能出现退化的领域。
影子模式也有实际成本。你同时运行两个模型,这在评估期间大约会使你的推理支出翻倍。将影子请求与基准响应关联起来的复杂性也增加了运维开销。影子模式是处理重大变更(模型升级、重大提示词结构调整、新的工具 schema )的正确工具,而非针对细微的提示词调整。
金丝雀发布:真实用户,小范围暴露
一旦影子模式让你确信候选模型没有明显的逻辑错误,金丝雀发布就会将风险转移到小规模的真实用户身上。
模式如下:将一小部分流量 —— 从 1% 开始,对于高风险应用甚至低至 0.1% —— 路由到候选模型,而其余流量保留在基准模型上。监控这两个群组的所有指标。如果指标保持在可接受的范围内,逐渐增加金丝雀的流量份额:1% → 5% → 20% → 50% → 100%。如果出现任何问题,爆炸半径是有限的,且回滚只需更改一次配置。
关键的基础设施要求是一致的用户分配。一个在某次请求中命中金丝雀的用户,在同一会话的后续请求中也应命中金丝雀。将每个单独的请求随机分配给金丝雀或基准模型会产生不连贯的用户体验 —— 用户在同一个对话中会看到不同的回复风格、格式和行为。
对于 LLM 工作负载,你在金丝雀发布期间跟踪的指标不同于典型的服务发布:
- 延迟百分位数 (p50, p95, p99) —— 而不仅仅是平均值,因为 LLM 的延迟分布高度偏斜
- 单次请求成本 —— token 数量随模型版本而变化,在 100% 流量时出现的成本意外是非常昂贵的
- 错误和拒绝率 —— 新模型可能会拒绝更多类别的请求,这可能是也可能不是你想要的
- 输出长度分布 —— 模式坍缩(极短的输出)或失控的冗长都表明出现了问题
- 用户反馈信号 —— 点踩、重新生成请求和会话放弃,以群组转化率进行衡量
对于生产环境的金丝雀发布,自动回滚不是可选项。设置明确的阈值 —— 如果 p99 延迟增加超过 40%,如果拒绝率跳升超过 5%,如果单次请求成本增量超过预算 —— 让金丝雀控制器将 100% 的流量路由回基准模型,而无需人类在凌晨 2 点介入。
A/B 测试:衡量真正重要的指标
金丝雀部署告诉你新模型是否可以安全部署。A/B 测试则告诉你它是否更好。这是两个不同的问题,混淆它们会导致发布的技术上稳定但却让用户体验变差的变更。
- https://medium.com/@singhrajni/why-should-you-deploy-your-ml-model-in-shadow-mode-68f0064170a6
- https://www.qwak.com/post/shadow-deployment-vs-canary-release-of-machine-learning-models
- https://www.zenml.io/blog/what-1200-production-deployments-reveal-about-llmops-in-2025
- https://www.codeant.ai/blogs/llm-shadow-traffic-ab-testing
- https://thinkingmachines.ai/blog/defeating-nondeterminism-in-llm-inference/
- https://arxiv.org/html/2408.04667v5
- https://medium.com/@komalbaparmar007/llm-canary-prompting-in-production-shadow-tests-drift-alarms-and-safe-rollouts-7bdbd0e5f9d0
- https://portkey.ai/blog/canary-testing-for-llm-apps/
- https://blog.devcycle.com/using-feature-flags-to-build-a-better-ai/
- https://lotuslabs.medium.com/a-b-testing-for-llms-measuring-ai-impact-using-business-metrics-173b4c00cff0
- https://www.llama.com/docs/deployment/a-b-testing/
- https://deepchecks.com/llm-production-challenges-prompt-update-incidents/
