LLM 的影子流量、Canary 上线与渐进式部署
LLM 上线把软件部署里最难的几块凑到了一起:没有单元测试、失败模式弥散、信号延迟。顺序是 (1) 影子模式 —— 把生产请求复制给候选模型,记录、对比,对用户零影响;能抓明显的分布问题,但不是质量保证;(2) canary 上线 —— 渐进式流量切换 10% → 25% → 50% → 75% → 100%,每一步设闸门;跟踪延迟分位数、单请求成本、错误/拒答率、输出长度分布、用户反馈率;(3) 稳定性确认后对截然不同的备选做 A/B 测试。非确定性不可消除 —— 因 GPU 浮点非结合性加批大小方差,相同输入跨次跑会有最高 15% 的准确率波动。成本是个变量,不是常量 —— 一个好 20% 的模型每次调用可能贵 3 倍。回滚速度是决定性的:如果回滚需要重新部署,你就太慢了。策略放在 config/flag 里;模型放在带固定 digest 的 registry 里;回滚 = 翻策略 + 还原阈值 + 几秒内钉回旧模型。
类型: Learn 语言: Python(标准库,一个玩具级 canary 推进模拟器) 前置要求: 阶段 17 · 13(可观测性)、阶段 17 · 21(A/B 测试) 预计时间: ~60 分钟
学习目标
- 区分影子模式(零影响对比)、canary(实时流量渐进)和 A/B(稳定性确认后的对比)。
- 列举五个 LLM 专属的 canary 指标(延迟、单请求成本、错误/拒答、输出长度分布、用户反馈)。
- 解释为什么 LLM 非确定性(最高 15%)改变了上线中"稳定"的含义。
- 设计一条几秒(翻策略)而不是几小时(重新部署)的回滚路径。
问题背景
你上了一个新模型。离线 eval 显示 3% 准确率增益。你在生产里把它打开。24 小时内,成本涨了 40%,用户点踩涨了 8%,三张客户工单报告"答案怪怪的"。你回滚。重新部署花 3 小时。你的周末毁了。
这里每一环都本可避免。影子模式本能在任何用户看到之前抓住那 40% 的成本尖峰。canary 本能在点踩动起来时停在 10%。策略 flag 回滚本只需 30 秒。这套纪律就是填补"离线 eval 看起来不错"和"真实用户满意"之间那道缝的东西。
核心概念
影子模式
候选模型收到和生产一样的请求;输出被记录,不返回给用户。对用户零影响。记录:
- 输出内容(与生产做 diff)。
- token 数(成本差值)。
- 延迟。
- 拒答和错误。
能抓:成本爆炸、长度回退、明显的拒答变化、硬错误。抓不到:用户能感知到的质量差值。影子是个冒烟测试,不是质量测试。
Canary 上线
带闸门的渐进式流量切换。典型推进:1% → 10% → 25% → 50% → 75% → 100%。每一步在 5 个指标上设闸门:
- 延迟分位数 —— P50、P95、P99。越界:canary 的 P99 > 1.5x 基线。
- 单请求成本 —— 混合 $。越界:高出基线 >20%。
- 错误 / 拒答率 —— 5xx 加上显式拒答。越界:基线的 2 倍。
- 输出长度分布 —— 均值 + P99。越界:分布偏移。
- 用户反馈率 —— 点踩 / 工单提交。越界:基线的 1.5 倍。
非确定性是新的方差
相同输入产生不相同的输出。原因:
- GPU 浮点非结合性(浮点归约顺序随批变化)。
- 批大小方差(同一 prompt 在 128 的批里 vs 16 的批里)。
- 采样(temperature > 0)。
实测:相同 eval 集上跨次跑最高 15% 的准确率波动。上线中"稳定"意味着指标在预期方差内,不是和基线完全相同。把闸门设在噪声底之上。
成本是个变量
一个好 20% 的模型每次调用可能贵 3 倍。单请求成本是五个闸门之一。上一个打破单位经济性的"更好"模型,是个回滚理由。
回滚是武器
- 策略 flag(feature flag 系统):在 config 里翻百分比;几秒。
- 模型钉死(registry digest):钉死的模型不自动升级。
- 回滚 = 还原 flag + 把钉死的 digest 设回上一个。是几秒,不是几小时。
如果你的栈需要重新部署才能回滚,在上线之前先修这点。
工具
Argo Rollouts / Flagger —— Kubernetes 渐进式交付控制器。与 Istio/Linkerd 加权路由集成。
Istio 加权路由 —— 服务网格层的流量切分。
KServe / Seldon Core —— 内置 canary 的模型服务。
Feature flag —— LaunchDarkly、Flagsmith、Unleash。策略级翻转,无需重新部署。
指标节奏
canary 闸门视流量大小每 5-15 分钟检查一次。1% 流量、每分钟 10 个请求,每个窗口给 50-150 个数据点 —— 对延迟够了,但对用户反馈很噪。10% 给约 10 倍多。推进应该在每一步暂停得足够久,以积累足够样本。
A/B 这一步是可选的
如果新模型截然不同(不同行为、不同成本曲线、不同语气),在 canary 通过后于 50% 处对它做 A/B 测试。如果它只是个改进版,canary 闸门通过后直接到 100%。
你该记住的数字
- Canary 推进:1% → 10% → 25% → 50% → 75% → 100%。
- 非确定性上限:相同输入跨次跑最高 15% 方差。
- 五个 canary 指标:延迟、成本、错误/拒答、输出长度、用户反馈。
- 成本闸门:高出基线 >20% 是越界。
- 回滚:几秒,不是几小时。
i4-canary-ramp实际使用
code/main.py 模拟一个注入了回退的 canary 上线。报告上线在哪个阶段停下,以及哪个闸门触发了。
拿去用
这一课产出 outputs/skill-rollout-runbook.md。给定候选模型、基线和风险容忍度,设计 影子→canary→100% 的方案。
练习
- 跑
code/main.py。注入一个 25% 的成本回退。canary 在哪个阶段停下? - 你的新模型离线有 3% 准确率增益,但单请求成本 +18%。该上吗?取决于策略 —— 把两条路径都写出来。
- 设计一个端到端 60 秒以内的回滚。列出所需的基础设施。
- 非确定性在你的 eval 上显示 ±7%。把 canary 闸门设得不会误报。你用什么乘数?
- 影子模式在 canary 之前抓住一个 40% 的成本尖峰。写出在影子里触发的告警规则。
关键术语
| 术语 | 大家嘴上怎么说 | 它实际是什么 |
|---|---|---|
| 影子模式 | "复制给新模型" | 零影响地发给候选用于记录 |
| Canary | "渐进流量" | 带闸门的逐步对用户暴露的上线 |
| 闸门 | "上线检查" | 阻止推进的指标阈值 |
| 非确定性 | "LLM 方差" | 不可消除的跨次跑差异 |
| 策略 flag | "翻 flag 回滚" | config 级回滚,几秒不是几小时 |
| 模型钉死 | "registry digest" | 对某个模型版本的不可变引用 |
| Argo Rollouts | "K8s 渐进" | Kubernetes 原生的 canary/回滚控制器 |
| KServe | "推理 K8s" | 带 canary 原语的模型服务 |
| Istio 加权 | "网格切分" | 服务网格流量切分器 |